- Create AnsibleDeployStage using framework's Process module for secure command execution - Integrate AnsibleDeployStage into DeploymentPipelineCommands for production deployments - Add force_deploy flag support in Ansible playbook to override stale locks - Use PHP deployment module as orchestrator (php console.php deploy:production) - Fix ErrorAggregationInitializer to use Environment class instead of $_ENV superglobal Architecture: - BuildStage → AnsibleDeployStage → HealthCheckStage for production - Process module provides timeout, error handling, and output capture - Ansible playbook supports rollback via rollback-git-based.yml - Zero-downtime deployments with health checks
107 lines
4.1 KiB
PHP
107 lines
4.1 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace App\Framework\Deployment\Ssl\Commands;
|
||
|
||
use App\Framework\Config\Environment;
|
||
use App\Framework\Console\Attributes\ConsoleCommand;
|
||
use App\Framework\Console\ConsoleOutput;
|
||
use App\Framework\Console\ExitCode;
|
||
use App\Framework\Deployment\Ssl\Services\SslCertificateService;
|
||
use App\Framework\Deployment\Ssl\ValueObjects\SslConfiguration;
|
||
|
||
/**
|
||
* Console command to renew SSL certificates
|
||
*/
|
||
#[ConsoleCommand(
|
||
name: 'ssl:renew',
|
||
description: 'Renew Let\'s Encrypt SSL certificates'
|
||
)]
|
||
final readonly class SslRenewCommand
|
||
{
|
||
public function __construct(
|
||
private SslCertificateService $sslService,
|
||
private Environment $environment,
|
||
private ConsoleOutput $output
|
||
) {}
|
||
|
||
public function execute(?bool $force = null): ExitCode
|
||
{
|
||
$this->output->writeLine('🔄 Renewing SSL Certificates...');
|
||
$this->output->writeLine('');
|
||
|
||
try {
|
||
// Load configuration from environment
|
||
$config = SslConfiguration::fromEnvironment($this->environment);
|
||
|
||
$this->output->writeLine('Domain: ' . $config->domain->value);
|
||
$this->output->writeLine('');
|
||
|
||
// Check current status
|
||
$this->output->write('Checking current certificate status... ');
|
||
$currentStatus = $this->sslService->getStatus(
|
||
$config->domain,
|
||
$config->certbotConfDir->toString()
|
||
);
|
||
|
||
if (!$currentStatus->exists) {
|
||
$this->output->writeLine('❌ Not found');
|
||
$this->output->writeLine('');
|
||
$this->output->writeLine('No certificate exists for this domain.');
|
||
$this->output->writeLine('Run "ssl:init" to obtain a new certificate first.');
|
||
return ExitCode::FAILURE;
|
||
}
|
||
|
||
$this->output->writeLine('✅ Found');
|
||
$this->output->writeLine('');
|
||
|
||
// Display current status
|
||
$this->output->writeLine('Current Status:');
|
||
$this->output->writeLine(' Valid Until: ' . $currentStatus->notAfter?->format('Y-m-d H:i:s'));
|
||
$this->output->writeLine(' Days Until Expiry: ' . $currentStatus->daysUntilExpiry);
|
||
$this->output->writeLine(' Health: ' . $currentStatus->getHealthStatus());
|
||
$this->output->writeLine('');
|
||
|
||
// Check if renewal is needed
|
||
if (!$currentStatus->needsRenewal() && $force !== true) {
|
||
$this->output->writeLine('ℹ️ Certificate does not need renewal yet.');
|
||
$this->output->writeLine(' Certificates are automatically renewed 30 days before expiry.');
|
||
$this->output->writeLine('');
|
||
$this->output->writeLine('Use --force flag to force renewal anyway.');
|
||
return ExitCode::SUCCESS;
|
||
}
|
||
|
||
if ($force === true && !$currentStatus->needsRenewal()) {
|
||
$this->output->writeLine('⚠️ Forcing renewal even though certificate is still valid...');
|
||
$this->output->writeLine('');
|
||
}
|
||
|
||
// Renew certificate
|
||
$this->output->writeLine('Renewing certificate...');
|
||
$status = $this->sslService->renew($config);
|
||
|
||
$this->output->writeLine('');
|
||
$this->output->writeLine('✅ Certificate renewed successfully!');
|
||
$this->output->writeLine('');
|
||
|
||
// Display new status
|
||
$this->output->writeLine('New Certificate Information:');
|
||
$this->output->writeLine(' Valid Until: ' . $status->notAfter?->format('Y-m-d H:i:s'));
|
||
$this->output->writeLine(' Days Until Expiry: ' . $status->daysUntilExpiry);
|
||
$this->output->writeLine(' Health: ' . $status->getHealthStatus());
|
||
$this->output->writeLine('');
|
||
|
||
$this->output->writeLine('Next step: Reload/restart your web server to use the new certificate');
|
||
|
||
return ExitCode::SUCCESS;
|
||
|
||
} catch (\Exception $e) {
|
||
$this->output->writeLine('');
|
||
$this->output->writeLine('❌ Error: ' . $e->getMessage());
|
||
$this->output->writeLine('');
|
||
return ExitCode::FAILURE;
|
||
}
|
||
}
|
||
}
|