feat(Deployment): Integrate Ansible deployment via PHP deployment pipeline

- 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
This commit is contained in:
2025-10-26 14:08:07 +01:00
parent a90263d3be
commit 3b623e7afb
170 changed files with 19888 additions and 575 deletions

View File

@@ -6,7 +6,6 @@ namespace App\Framework\Deployment\Ssl\Commands;
use App\Framework\Config\Environment;
use App\Framework\Console\Attributes\ConsoleCommand;
use App\Framework\Console\ConsoleInput;
use App\Framework\Console\ConsoleOutput;
use App\Framework\Console\ExitCode;
use App\Framework\Deployment\Ssl\Services\SslCertificateService;
@@ -27,17 +26,17 @@ final readonly class SslRenewCommand
private ConsoleOutput $output
) {}
public function execute(ConsoleInput $input): int
public function execute(?bool $force = null): ExitCode
{
$this->output->writeln('🔄 Renewing SSL Certificates...');
$this->output->writeln('');
$this->output->writeLine('🔄 Renewing SSL Certificates...');
$this->output->writeLine('');
try {
// Load configuration from environment
$config = SslConfiguration::fromEnvironment($this->environment);
$this->output->writeln('Domain: ' . $config->domain->value);
$this->output->writeln('');
$this->output->writeLine('Domain: ' . $config->domain->value);
$this->output->writeLine('');
// Check current status
$this->output->write('Checking current certificate status... ');
@@ -47,55 +46,60 @@ final readonly class SslRenewCommand
);
if (!$currentStatus->exists) {
$this->output->writeln('❌ Not found');
$this->output->writeln('');
$this->output->writeln('No certificate exists for this domain.');
$this->output->writeln('Run "ssl:init" to obtain a new certificate first.');
$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->writeln('✅ Found');
$this->output->writeln('');
$this->output->writeLine('✅ Found');
$this->output->writeLine('');
// Display current status
$this->output->writeln('Current Status:');
$this->output->writeln(' Valid Until: ' . $currentStatus->notAfter?->format('Y-m-d H:i:s'));
$this->output->writeln(' Days Until Expiry: ' . $currentStatus->daysUntilExpiry);
$this->output->writeln(' Health: ' . $currentStatus->getHealthStatus());
$this->output->writeln('');
$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()) {
$this->output->writeln(' Certificate does not need renewal yet.');
$this->output->writeln(' Certificates are automatically renewed 30 days before expiry.');
$this->output->writeln('');
$this->output->writeln('Use --force flag to force renewal anyway (not implemented yet).');
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->writeln('Renewing certificate...');
$this->output->writeLine('Renewing certificate...');
$status = $this->sslService->renew($config);
$this->output->writeln('');
$this->output->writeln('✅ Certificate renewed successfully!');
$this->output->writeln('');
$this->output->writeLine('');
$this->output->writeLine('✅ Certificate renewed successfully!');
$this->output->writeLine('');
// Display new status
$this->output->writeln('New Certificate Information:');
$this->output->writeln(' Valid Until: ' . $status->notAfter?->format('Y-m-d H:i:s'));
$this->output->writeln(' Days Until Expiry: ' . $status->daysUntilExpiry);
$this->output->writeln(' Health: ' . $status->getHealthStatus());
$this->output->writeln('');
$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->writeln('Next step: Reload/restart your web server to use the new certificate');
$this->output->writeLine('Next step: Reload/restart your web server to use the new certificate');
return ExitCode::SUCCESS;
} catch (\Exception $e) {
$this->output->writeln('');
$this->output->writeln('❌ Error: ' . $e->getMessage());
$this->output->writeln('');
$this->output->writeLine('');
$this->output->writeLine('❌ Error: ' . $e->getMessage());
$this->output->writeLine('');
return ExitCode::FAILURE;
}
}