From 3ff0742ff163d835f65c439a52931b384da689b1 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Thu, 16 Apr 2026 14:08:06 +0800 Subject: [PATCH] Enhance error handling in artifact downloading process --- src/StaticPHP/Artifact/ArtifactDownloader.php | 8 +++--- .../Exception/DownloaderException.php | 13 +++++++++- src/StaticPHP/Exception/ExceptionHandler.php | 25 ++++++++++++++++++- src/StaticPHP/Runtime/Shell/Shell.php | 5 ++++ 4 files changed, 46 insertions(+), 5 deletions(-) diff --git a/src/StaticPHP/Artifact/ArtifactDownloader.php b/src/StaticPHP/Artifact/ArtifactDownloader.php index 6cc57439..aefe471b 100644 --- a/src/StaticPHP/Artifact/ArtifactDownloader.php +++ b/src/StaticPHP/Artifact/ArtifactDownloader.php @@ -545,6 +545,7 @@ class ArtifactDownloader } $try = false; + $last_exception = null; foreach ($queue as $item) { try { $instance = null; @@ -605,6 +606,7 @@ class ArtifactDownloader InteractiveTerm::finish("Download artifact {$artifact->getName()} {$item['display']} failed !", false); InteractiveTerm::error("Failed message: {$e->getMessage()}", true); } + $last_exception = $e; $try = true; continue; } catch (ValidationException $e) { @@ -612,11 +614,12 @@ class ArtifactDownloader InteractiveTerm::finish("Download artifact {$artifact->getName()} {$item['display']} failed !", false); InteractiveTerm::error("Validation failed: {$e->getMessage()}"); } + $last_exception = $e; break; } } $vvv = !ApplicationContext::isDebug() ? "\nIf the problem persists, consider using `-v`, `-vv` or `-vvv` to enable verbose mode, or disable parallel downloading for more details." : ''; - throw new DownloaderException("Download artifact '{$artifact->getName()}' failed. Please check your internet connection and try again.{$vvv}"); + throw new DownloaderException("Download artifact '{$artifact->getName()}' failed. Please check your internet connection and try again.{$vvv}", previous: $last_exception, artifact_name: $artifact->getName()); } private function downloadWithConcurrency(): void @@ -672,8 +675,7 @@ class ArtifactDownloader $artifact_name = $artifact->getName(); } $failed_downloads[] = ['artifact' => $artifact_name, 'error' => $e]; - InteractiveTerm::setMessage("[{$downloaded}/{$total}] Download failed: {$artifact_name}"); - InteractiveTerm::advance(); + throw $e; } // remove from pool unset($fiber_pool[$index]); diff --git a/src/StaticPHP/Exception/DownloaderException.php b/src/StaticPHP/Exception/DownloaderException.php index 50e82844..0a3da332 100644 --- a/src/StaticPHP/Exception/DownloaderException.php +++ b/src/StaticPHP/Exception/DownloaderException.php @@ -10,4 +10,15 @@ namespace StaticPHP\Exception; * This exception is used to indicate that a download operation has failed, * typically due to network issues, invalid URLs, or other related problems. */ -class DownloaderException extends SPCException {} +class DownloaderException extends SPCException +{ + public function __construct(string $message = '', int $code = 0, ?\Throwable $previous = null, private readonly ?string $artifact_name = null) + { + parent::__construct($message, $code, $previous); + } + + public function getArtifactName(): ?string + { + return $this->artifact_name; + } +} diff --git a/src/StaticPHP/Exception/ExceptionHandler.php b/src/StaticPHP/Exception/ExceptionHandler.php index 053d82a3..2d8c404d 100644 --- a/src/StaticPHP/Exception/ExceptionHandler.php +++ b/src/StaticPHP/Exception/ExceptionHandler.php @@ -72,7 +72,7 @@ class ExceptionHandler } } if (!ApplicationContext::isDebug()) { - self::logError('⚠ If you want to see more details in console, use `-vvv` option.'); + self::logError('⚠ If you want to see more details in console, use `-v`, `-vv` or `-vvv` option.'); } return self::getReturnCode($e); } @@ -232,6 +232,9 @@ class ExceptionHandler if ($e instanceof ExecutionException) { self::logError(''); self::logError('Failed command: ' . ConsoleColor::gray($e->getExecutionCommand())); + if ($e->getCode() !== 0) { + self::logError(' - Exit code: ' . ConsoleColor::gray((string) $e->getCode())); + } if ($cd = $e->getCd()) { self::logError(' - Command executed in: ' . ConsoleColor::gray($cd)); } @@ -243,6 +246,26 @@ class ExceptionHandler } } + // get downloader info + if ($e instanceof DownloaderException) { + if ($artifact_name = $e->getArtifactName()) { + self::logError('Failed artifact: ' . ConsoleColor::gray($artifact_name)); + } + $cause = $e->getPrevious(); + if ($cause instanceof ExecutionException) { + self::logError(''); + self::logError('Last failed command: ' . ConsoleColor::gray($cause->getExecutionCommand())); + if ($cause->getCode() !== 0) { + self::logError(' - Exit code: ' . ConsoleColor::gray((string) $cause->getCode())); + } + if ($cd = $cause->getCd()) { + self::logError(' - Command executed in: ' . ConsoleColor::gray($cd)); + } + } elseif ($cause instanceof DownloaderException || $cause instanceof ValidationException) { + self::logError('Cause: ' . ConsoleColor::gray($cause->getMessage())); + } + } + // validation error if ($e instanceof ValidationException) { self::logError('Failed validation module: ' . ConsoleColor::gray($e->getValidationModuleString())); diff --git a/src/StaticPHP/Runtime/Shell/Shell.php b/src/StaticPHP/Runtime/Shell/Shell.php index f9f4f175..37601997 100644 --- a/src/StaticPHP/Runtime/Shell/Shell.php +++ b/src/StaticPHP/Runtime/Shell/Shell.php @@ -174,6 +174,7 @@ abstract class Shell $process = proc_open($cmd, $descriptors, $pipes, $cwd, env_vars: $env, options: PHP_OS_FAMILY === 'Windows' ? ['create_process_group' => true] : null); $output_value = ''; + $process_completed = false; try { if (!is_resource($process)) { throw new ExecutionException( @@ -251,11 +252,15 @@ abstract class Shell } } + $process_completed = true; return [ 'code' => $status['exitcode'], 'output' => $output_value, ]; } finally { + if (!$process_completed && is_resource($process)) { + proc_terminate($process); + } fclose($pipes[1]); fclose($pipes[2]); if ($file_res !== null) {