diff --git a/composer.json b/composer.json index 4f6d81f..a2cb206 100755 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ }, "require": { "php": ">=8.0", - "utopia-php/console": "0.0.*" + "utopia-php/console": "0.1.*" }, "require-dev": { "phpunit/phpunit": "^9.3", diff --git a/composer.lock b/composer.lock index b8c7907..1f26377 100644 --- a/composer.lock +++ b/composer.lock @@ -4,24 +4,24 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d4caa5a83ab682866adc836405017a7b", + "content-hash": "ee85106451dc2f1c5cb1f93e292a848c", "packages": [ { "name": "utopia-php/console", - "version": "0.0.1", + "version": "0.1.1", "source": { "type": "git", "url": "https://github.com/utopia-php/console.git", - "reference": "f77104e4a888fa9cb3e08f32955ec09479ab7a92" + "reference": "d298e43960780e6d76e66de1228c75dc81220e3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/console/zipball/f77104e4a888fa9cb3e08f32955ec09479ab7a92", - "reference": "f77104e4a888fa9cb3e08f32955ec09479ab7a92", + "url": "https://api.github.com/repos/utopia-php/console/zipball/d298e43960780e6d76e66de1228c75dc81220e3e", + "reference": "d298e43960780e6d76e66de1228c75dc81220e3e", "shasum": "" }, "require": { - "php": ">=7.4" + "php": ">=8.0" }, "require-dev": { "laravel/pint": "1.2.*", @@ -50,38 +50,37 @@ ], "support": { "issues": "https://github.com/utopia-php/console/issues", - "source": "https://github.com/utopia-php/console/tree/0.0.1" + "source": "https://github.com/utopia-php/console/tree/0.1.1" }, - "time": "2025-10-20T14:41:36+00:00" + "time": "2026-02-10T10:20:29+00:00" } ], "packages-dev": [ { "name": "doctrine/instantiator", - "version": "2.0.0", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" + "reference": "23da848e1a2308728fe5fdddabf4be17ff9720c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/23da848e1a2308728fe5fdddabf4be17ff9720c7", + "reference": "23da848e1a2308728fe5fdddabf4be17ff9720c7", "shasum": "" }, "require": { - "php": "^8.1" + "php": "^8.4" }, "require-dev": { - "doctrine/coding-standard": "^11", + "doctrine/coding-standard": "^14", "ext-pdo": "*", "ext-phar": "*", "phpbench/phpbench": "^1.2", - "phpstan/phpstan": "^1.9.4", - "phpstan/phpstan-phpunit": "^1.3", - "phpunit/phpunit": "^9.5.27", - "vimeo/psalm": "^5.4" + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.5.58" }, "type": "library", "autoload": { @@ -108,7 +107,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/2.0.0" + "source": "https://github.com/doctrine/instantiator/tree/2.1.0" }, "funding": [ { @@ -124,20 +123,20 @@ "type": "tidelift" } ], - "time": "2022-12-30T00:23:10+00:00" + "time": "2026-01-05T06:47:08+00:00" }, { "name": "laravel/pint", - "version": "v1.25.1", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "5016e263f95d97670d71b9a987bd8996ade6d8d9" + "reference": "c67b4195b75491e4dfc6b00b1c78b68d86f54c90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/5016e263f95d97670d71b9a987bd8996ade6d8d9", - "reference": "5016e263f95d97670d71b9a987bd8996ade6d8d9", + "url": "https://api.github.com/repos/laravel/pint/zipball/c67b4195b75491e4dfc6b00b1c78b68d86f54c90", + "reference": "c67b4195b75491e4dfc6b00b1c78b68d86f54c90", "shasum": "" }, "require": { @@ -148,13 +147,13 @@ "php": "^8.2.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.87.2", - "illuminate/view": "^11.46.0", - "larastan/larastan": "^3.7.1", - "laravel-zero/framework": "^11.45.0", + "friendsofphp/php-cs-fixer": "^3.92.4", + "illuminate/view": "^12.44.0", + "larastan/larastan": "^3.8.1", + "laravel-zero/framework": "^12.0.4", "mockery/mockery": "^1.6.12", - "nunomaduro/termwind": "^2.3.1", - "pestphp/pest": "^2.36.0" + "nunomaduro/termwind": "^2.3.3", + "pestphp/pest": "^3.8.4" }, "bin": [ "builds/pint" @@ -180,6 +179,7 @@ "description": "An opinionated code formatter for PHP.", "homepage": "https://laravel.com", "keywords": [ + "dev", "format", "formatter", "lint", @@ -190,7 +190,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2025-09-19T02:57:12+00:00" + "time": "2026-01-05T16:49:17+00:00" }, { "name": "myclabs/deep-copy", @@ -254,16 +254,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.6.1", + "version": "v5.7.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2" + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", - "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", "shasum": "" }, "require": { @@ -306,9 +306,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.1" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" }, - "time": "2025-08-13T20:13:15+00:00" + "time": "2025-12-06T11:56:16+00:00" }, { "name": "phar-io/manifest", @@ -802,16 +802,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.29", + "version": "9.6.34", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "9ecfec57835a5581bc888ea7e13b51eb55ab9dd3" + "reference": "b36f02317466907a230d3aa1d34467041271ef4a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9ecfec57835a5581bc888ea7e13b51eb55ab9dd3", - "reference": "9ecfec57835a5581bc888ea7e13b51eb55ab9dd3", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b36f02317466907a230d3aa1d34467041271ef4a", + "reference": "b36f02317466907a230d3aa1d34467041271ef4a", "shasum": "" }, "require": { @@ -833,7 +833,7 @@ "phpunit/php-timer": "^5.0.3", "sebastian/cli-parser": "^1.0.2", "sebastian/code-unit": "^1.0.8", - "sebastian/comparator": "^4.0.9", + "sebastian/comparator": "^4.0.10", "sebastian/diff": "^4.0.6", "sebastian/environment": "^5.1.5", "sebastian/exporter": "^4.0.8", @@ -885,7 +885,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.29" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.34" }, "funding": [ { @@ -909,7 +909,7 @@ "type": "tidelift" } ], - "time": "2025-09-24T06:29:11+00:00" + "time": "2026-01-27T05:45:00+00:00" }, { "name": "sebastian/cli-parser", @@ -1080,16 +1080,16 @@ }, { "name": "sebastian/comparator", - "version": "4.0.9", + "version": "4.0.10", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "67a2df3a62639eab2cc5906065e9805d4fd5dfc5" + "reference": "e4df00b9b3571187db2831ae9aada2c6efbd715d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/67a2df3a62639eab2cc5906065e9805d4fd5dfc5", - "reference": "67a2df3a62639eab2cc5906065e9805d4fd5dfc5", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/e4df00b9b3571187db2831ae9aada2c6efbd715d", + "reference": "e4df00b9b3571187db2831ae9aada2c6efbd715d", "shasum": "" }, "require": { @@ -1142,7 +1142,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.9" + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.10" }, "funding": [ { @@ -1162,7 +1162,7 @@ "type": "tidelift" } ], - "time": "2025-08-10T06:51:50+00:00" + "time": "2026-01-24T09:22:56+00:00" }, { "name": "sebastian/complexity", @@ -1924,16 +1924,16 @@ }, { "name": "theseer/tokenizer", - "version": "1.2.3", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", "shasum": "" }, "require": { @@ -1962,7 +1962,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + "source": "https://github.com/theseer/tokenizer/tree/1.3.1" }, "funding": [ { @@ -1970,7 +1970,7 @@ "type": "github" } ], - "time": "2024-03-03T12:36:25+00:00" + "time": "2025-11-17T20:03:58+00:00" } ], "aliases": [], diff --git a/src/Orchestration/Adapter/DockerCLI.php b/src/Orchestration/Adapter/DockerCLI.php index c755eb5..6627119 100644 --- a/src/Orchestration/Adapter/DockerCLI.php +++ b/src/Orchestration/Adapter/DockerCLI.php @@ -21,9 +21,11 @@ public function __construct(?string $username = null, ?string $password = null) { if ($username && $password) { $output = ''; + $stderr = ''; - if (! Console::execute('docker login --username '.$username.' --password-stdin', $password, $output)) { - throw new Orchestration("Docker Error: {$output}"); + if (Console::execute('docker login --username '.$username.' --password-stdin', $password, $output, $stderr) !== 0) { + $error = empty($stderr) ? $output : $stderr; + throw new Orchestration("Docker Error: {$error}"); } } } @@ -34,8 +36,9 @@ public function __construct(?string $username = null, ?string $password = null) public function createNetwork(string $name, bool $internal = false): bool { $output = ''; + $stderr = ''; - $result = Console::execute('docker network create '.$name.($internal ? '--internal' : ''), '', $output); + $result = Console::execute('docker network create '.$name.($internal ? '--internal' : ''), '', $output, $stderr); return $result === 0; } @@ -46,8 +49,9 @@ public function createNetwork(string $name, bool $internal = false): bool public function removeNetwork(string $name): bool { $output = ''; + $stderr = ''; - $result = Console::execute('docker network rm '.$name, '', $output); + $result = Console::execute('docker network rm '.$name, '', $output, $stderr); return $result === 0; } @@ -58,8 +62,9 @@ public function removeNetwork(string $name): bool public function networkConnect(string $container, string $network): bool { $output = ''; + $stderr = ''; - $result = Console::execute('docker network connect '.$network.' '.$container, '', $output); + $result = Console::execute('docker network connect '.$network.' '.$container, '', $output, $stderr); return $result === 0; } @@ -70,8 +75,9 @@ public function networkConnect(string $container, string $network): bool public function networkDisconnect(string $container, string $network, bool $force = false): bool { $output = ''; + $stderr = ''; - $result = Console::execute('docker network disconnect '.$network.' '.$container.($force ? ' --force' : ''), '', $output); + $result = Console::execute('docker network disconnect '.$network.' '.$container.($force ? ' --force' : ''), '', $output, $stderr); return $result === 0; } @@ -82,8 +88,9 @@ public function networkDisconnect(string $container, string $network, bool $forc public function networkExists(string $name): bool { $output = ''; + $stderr = ''; - $result = Console::execute('docker network inspect '.$name.' --format "{{.Name}}"', '', $output); + $result = Console::execute('docker network inspect '.$name.' --format "{{.Name}}"', '', $output, $stderr); return $result === 0 && trim($output) === $name; } @@ -107,6 +114,7 @@ public function getStats(?string $container = null, array $filters = []): array } $output = ''; + $stderr = ''; if (\count($containerIds) <= 0 && \count($filters) > 0) { return []; // No containers found @@ -120,7 +128,7 @@ public function getStats(?string $container = null, array $filters = []): array $containersString .= ' '.$containerId; } - $result = Console::execute('docker stats --no-trunc --format "id={{.ID}}&name={{.Name}}&cpu={{.CPUPerc}}&memory={{.MemPerc}}&diskIO={{.BlockIO}}&memoryIO={{.MemUsage}}&networkIO={{.NetIO}}" --no-stream'.$containersString, '', $output); + $result = Console::execute('docker stats --no-trunc --format "id={{.ID}}&name={{.Name}}&cpu={{.CPUPerc}}&memory={{.MemPerc}}&diskIO={{.BlockIO}}&memoryIO={{.MemUsage}}&networkIO={{.NetIO}}" --no-stream'.$containersString, '', $output, $stderr); if ($result !== 0) { return []; @@ -208,11 +216,13 @@ private function parseIOStats(string $stats) public function listNetworks(): array { $output = ''; + $stderr = ''; - $result = Console::execute('docker network ls --format "id={{.ID}}&name={{.Name}}&driver={{.Driver}}&scope={{.Scope}}"', '', $output); + $result = Console::execute('docker network ls --format "id={{.ID}}&name={{.Name}}&driver={{.Driver}}&scope={{.Scope}}"', '', $output, $stderr); if ($result !== 0) { - throw new Orchestration("Docker Error: {$output}"); + $error = empty($stderr) ? $output : $stderr; + throw new Orchestration("Docker Error: {$error}"); } $list = []; @@ -239,8 +249,9 @@ public function listNetworks(): array public function pull(string $image): bool { $output = ''; + $stderr = ''; - $result = Console::execute('docker pull '.$image, '', $output); + $result = Console::execute('docker pull '.$image, '', $output, $stderr); return $result === 0; } @@ -254,6 +265,7 @@ public function pull(string $image): bool public function list(array $filters = []): array { $output = ''; + $stderr = ''; $filterString = ''; @@ -261,10 +273,11 @@ public function list(array $filters = []): array $filterString = $filterString.' --filter "'.$key.'='.$value.'"'; } - $result = Console::execute('docker ps --all --no-trunc --format "id={{.ID}}&name={{.Names}}&status={{.Status}}&labels={{.Labels}}"'.$filterString, '', $output); + $result = Console::execute('docker ps --all --no-trunc --format "id={{.ID}}&name={{.Names}}&status={{.Status}}&labels={{.Labels}}"'.$filterString, '', $output, $stderr); if ($result !== 0 && $result !== -1) { - throw new Orchestration("Docker Error: {$output}"); + $error = empty($stderr) ? $output : $stderr; + throw new Orchestration("Docker Error: {$error}"); } $list = []; @@ -322,6 +335,7 @@ public function run( string $restart = self::RESTART_NO ): string { $output = ''; + $stderr = ''; foreach ($command as $key => $value) { if (str_contains($value, ' ')) { @@ -379,10 +393,11 @@ public function run( (empty($hostname) ? '' : " --hostname {$hostname}"). (empty($vars) ? '' : ' '.\implode(' ', $vars)). " {$image}". - (empty($command) ? '' : ' '.implode(' ', $command)), '', $output, 30); + (empty($command) ? '' : ' '.implode(' ', $command)), '', $output, $stderr, 30); if ($result !== 0) { - throw new Orchestration("Docker Error: {$output}"); + $error = empty($stderr) ? $output : $stderr; + throw new Orchestration("Docker Error: {$error}"); } // Use first line only, CLI can add warnings or other messages @@ -404,6 +419,8 @@ public function execute( array $vars = [], int $timeout = -1 ): bool { + $stderr = ''; + foreach ($command as $key => $value) { if (str_contains($value, ' ')) { $command[$key] = "'".$value."'"; @@ -421,13 +438,14 @@ public function execute( $vars = $parsedVariables; - $result = Console::execute('docker exec '.\implode(' ', $vars)." {$name} ".implode(' ', $command), '', $output, $timeout); + $result = Console::execute('docker exec '.\implode(' ', $vars)." {$name} ".implode(' ', $command), '', $output, $stderr, $timeout); if ($result !== 0) { if ($result == 124) { throw new Timeout('Command timed out'); } else { - throw new Orchestration("Docker Error: {$output}"); + $error = empty($stderr) ? $output : $stderr; + throw new Orchestration("Docker Error: {$error}"); } } @@ -440,11 +458,14 @@ public function execute( public function remove(string $name, bool $force = false): bool { $output = ''; + $stderr = ''; + + $result = Console::execute('docker rm '.($force ? '--force' : '')." {$name}", '', $output, $stderr); - $result = Console::execute('docker rm '.($force ? '--force' : '')." {$name}", '', $output); + $combinedOutput = $output.$stderr; - if (! \str_starts_with($output, $name) || \str_contains($output, 'No such container')) { - throw new Orchestration("Docker Error: {$output}"); + if (! \str_starts_with($combinedOutput, $name) || \str_contains($combinedOutput, 'No such container')) { + throw new Orchestration("Docker Error: {$combinedOutput}"); } return ! $result;