Files
michaelschiemer/src/Framework/Queue/Commands/JobChainCommands.php
Michael Schiemer 3ed2685e74 feat: add comprehensive framework features and deployment improvements
Major additions:
- Storage abstraction layer with filesystem and in-memory implementations
- Gitea API integration with MCP tools for repository management
- Console dialog mode with interactive command execution
- WireGuard VPN DNS fix implementation and documentation
- HTTP client streaming response support
- Router generic result type
- Parameter type validator for framework core

Framework enhancements:
- Console command registry improvements
- Console dialog components
- Method signature analyzer updates
- Route mapper refinements
- MCP server and tool mapper updates
- Queue job chain and dependency commands
- Discovery tokenizer improvements

Infrastructure:
- Deployment architecture documentation
- Ansible playbook updates for WireGuard client regeneration
- Production environment configuration updates
- Docker Compose local configuration updates
- Remove obsolete docker-compose.yml (replaced by environment-specific configs)

Documentation:
- PERMISSIONS.md for access control guidelines
- WireGuard DNS fix implementation details
- Console dialog mode usage guide
- Deployment architecture overview

Testing:
- Multi-purpose attribute tests
- Gitea Actions integration tests (typed and untyped)
2025-11-04 20:39:48 +01:00

248 lines
8.9 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Framework\Queue\Commands;
use App\Framework\Console\Attributes\ConsoleCommand;
use App\Framework\Queue\Contracts\JobChainManagerInterface;
use App\Framework\Queue\ValueObjects\JobChain;
use App\Framework\Queue\ValueObjects\ChainExecutionMode;
use App\Framework\Queue\Services\JobChainExecutionCoordinator;
use App\Framework\Queue\Services\DependencyResolutionEngine;
final readonly class JobChainCommands
{
public function __construct(
private JobChainManagerInterface $chainManager,
private JobChainExecutionCoordinator $coordinator,
private DependencyResolutionEngine $resolutionEngine
) {}
#[ConsoleCommand(name: 'queue:chain:create', description: 'Create a new job chain')]
public function createChain(
string $chainId,
string $name,
string $jobIds,
string $executionMode = 'sequential',
bool $stopOnFailure = true
): void {
$jobIdArray = array_map('trim', explode(',', $jobIds));
$mode = ChainExecutionMode::from($executionMode);
$jobChain = JobChain::create(
chainId: $chainId,
name: $name,
jobIds: $jobIdArray,
executionMode: $mode,
stopOnFailure: $stopOnFailure
);
$this->chainManager->createChain($jobChain);
echo "✅ Job chain created: {$name} ({$chainId})\n";
echo " Jobs: " . implode(' → ', $jobIdArray) . "\n";
echo " Mode: {$executionMode}\n";
echo " Stop on failure: " . ($stopOnFailure ? 'Yes' : 'No') . "\n";
}
#[ConsoleCommand(name: 'queue:chain:start', description: 'Start execution of a job chain')]
public function startChain(string $chainId): void
{
try {
$this->coordinator->startChainExecution($chainId);
echo "🚀 Chain execution started: {$chainId}\n";
} catch (\Exception $e) {
echo "❌ Failed to start chain: {$e->getMessage()}\n";
}
}
#[ConsoleCommand(name: 'queue:chain:status', description: 'Get status of a job chain')]
public function getChainStatus(string $chainId): void
{
try {
$status = $this->coordinator->getChainExecutionStatus($chainId);
echo "📋 Chain Status: {$status['name']} ({$chainId})\n\n";
$statusIcon = match($status['status']) {
'pending' => '⏳',
'running' => '🔄',
'completed' => '✅',
'failed' => '❌',
default => '❓'
};
echo " Status: {$statusIcon} {$status['status']}\n";
echo " Execution Mode: {$status['execution_mode']}\n";
echo " Stop on Failure: " . ($status['progress']['stop_on_failure'] ? 'Yes' : 'No') . "\n";
if ($status['started_at']) {
echo " Started: {$status['started_at']}\n";
}
if ($status['completed_at']) {
echo " Completed: {$status['completed_at']}\n";
}
echo "\n📊 Progress:\n";
echo " {$status['progress']['completed_jobs']}/{$status['progress']['total_jobs']} jobs completed ({$status['progress']['percentage']}%)\n\n";
echo "🔗 Job Status:\n";
foreach ($status['job_statuses'] as $jobStatus) {
$canExecute = $jobStatus['can_execute'] ? '✅' : '⏳';
$depStatus = "{$jobStatus['dependencies_satisfied']}/{$jobStatus['dependencies_total']} deps";
$position = $jobStatus['position'] + 1;
echo " {$canExecute} Job {$position}: {$jobStatus['job_id']} ({$depStatus})\n";
}
} catch (\Exception $e) {
echo "❌ Failed to get chain status: {$e->getMessage()}\n";
}
}
#[ConsoleCommand(name: 'queue:chain:list', description: 'List job chains by status')]
public function listChains(string $status = 'all'): void
{
$chains = match($status) {
'active' => $this->chainManager->getActiveChains(),
'pending' => $this->chainManager->getPendingChains(),
default => array_merge(
$this->chainManager->getPendingChains(),
$this->chainManager->getActiveChains()
)
};
echo "📋 Job Chains ({$status}):\n\n";
if (empty($chains)) {
echo " No chains found\n";
return;
}
foreach ($chains as $chain) {
$statusIcon = match($chain->status) {
'pending' => '⏳',
'running' => '🔄',
'completed' => '✅',
'failed' => '❌',
default => '❓'
};
$jobCount = count($chain->getJobIdsArray());
echo " {$statusIcon} {$chain->name} ({$chain->chainId})\n";
echo " Status: {$chain->status} | Jobs: {$jobCount} | Mode: {$chain->executionMode}\n";
if ($chain->startedAt) {
echo " Started: {$chain->startedAt}\n";
}
echo "\n";
}
}
#[ConsoleCommand(name: 'queue:chain:analyze', description: 'Analyze job chains for a specific job')]
public function analyzeChains(string $jobId): void
{
$analysis = $this->resolutionEngine->analyzeChains($jobId);
echo "🔍 Chain Analysis for job: {$jobId}\n\n";
if (empty($analysis['chains'])) {
echo " Job is not part of any chains\n";
return;
}
echo "📊 Total chains: {$analysis['total_chains']}\n\n";
foreach ($analysis['chains'] as $chain) {
$statusIcon = match($chain['status']) {
'pending' => '⏳',
'running' => '🔄',
'completed' => '✅',
'failed' => '❌',
default => '❓'
};
echo "🔗 {$statusIcon} {$chain['name']} ({$chain['chain_id']})\n";
echo " Status: {$chain['status']}\n";
echo " Mode: {$chain['execution_mode']}\n";
$position = $chain['job_position'] + 1;
echo " Position: {$position}/{$chain['total_jobs']}\n";
if ($chain['next_job_after_current']) {
echo " Next job: {$chain['next_job_after_current']}\n";
}
echo " Progress: {$chain['progress']['completed_jobs']}/{$chain['progress']['total_jobs']} ({$chain['progress']['percentage']}%)\n\n";
}
}
#[ConsoleCommand(name: 'queue:chain:delete', description: 'Delete a job chain')]
public function deleteChain(string $chainId): void
{
$chain = $this->chainManager->getChain($chainId);
if (!$chain) {
echo "❌ Chain not found: {$chainId}\n";
return;
}
if ($chain->isRunning()) {
echo "❌ Cannot delete running chain. Chain must be completed or failed.\n";
return;
}
$this->chainManager->deleteChain($chainId);
echo "✅ Chain deleted: {$chainId}\n";
}
#[ConsoleCommand(name: 'queue:chain:cleanup', description: 'Clean up old completed chains')]
public function cleanupOldChains(int $olderThanDays = 30): void
{
$deletedCount = $this->chainManager->cleanupOldChains($olderThanDays);
echo "🧹 Cleaned up old chains\n";
echo " Deleted: {$deletedCount} completed/failed chains older than {$olderThanDays} days\n";
}
#[ConsoleCommand(name: 'queue:chain:progress', description: 'Show detailed progress of a chain')]
public function showProgress(string $chainId): void
{
try {
$progress = $this->chainManager->getChainProgress($chainId);
echo "📊 Chain Progress: {$progress['name']} ({$chainId})\n\n";
$statusIcon = match($progress['status']) {
'pending' => '⏳',
'running' => '🔄',
'completed' => '✅',
'failed' => '❌',
default => '❓'
};
echo " Status: {$statusIcon} {$progress['status']}\n";
echo " Mode: {$progress['execution_mode']}\n";
echo " Progress: {$progress['completed_jobs']}/{$progress['total_jobs']} jobs ({$progress['percentage']}%)\n";
if ($progress['started_at']) {
echo " Started: {$progress['started_at']}\n";
}
if ($progress['completed_at']) {
echo " Completed: {$progress['completed_at']}\n";
}
// Progress bar
$barLength = 30;
$filledLength = (int) (($progress['percentage'] / 100) * $barLength);
$bar = str_repeat('█', $filledLength) . str_repeat('░', $barLength - $filledLength);
echo "\n [{$bar}] {$progress['percentage']}%\n";
} catch (\Exception $e) {
echo "❌ Failed to get chain progress: {$e->getMessage()}\n";
}
}
}