diff --git a/.github/workflows/build-assets.yml b/.github/workflows/build-assets.yml index b788cd03..64d24873 100644 --- a/.github/workflows/build-assets.yml +++ b/.github/workflows/build-assets.yml @@ -68,6 +68,8 @@ jobs: needs: - build-phar runs-on: ${{ matrix.operating-system }} + env: + SPC_VERSION: 2.8.5 strategy: fail-fast: false matrix: @@ -76,7 +78,7 @@ jobs: - ubuntu-24.04-arm - macos-15-intel - macos-26 - - windows-2025 +# - windows-2025 - disabled for now, seems broken permissions: # id-token:write is required for build provenance attestation. id-token: write @@ -92,19 +94,19 @@ jobs: # Source URL: https://static-php.dev/en/guide/manual-build.html#build-locally-using-spc-binary-recommended case "${{ matrix.operating-system }}" in ubuntu-24.04) - curl -fsSL -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-linux-x86_64 + curl -fsSL -o spc.tgz https://github.com/crazywhalecc/static-php-cli/releases/download/${{ env.SPC_VERSION }}/spc-linux-x86_64.tar.gz ;; ubuntu-24.04-arm) - curl -fsSL -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-linux-aarch64 + curl -fsSL -o spc.tgz https://github.com/crazywhalecc/static-php-cli/releases/download/${{ env.SPC_VERSION }}/spc-linux-aarch64.tar.gz ;; macos-15-intel) - curl -fsSL -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-macos-x86_64 + curl -fsSL -o spc.tgz https://github.com/crazywhalecc/static-php-cli/releases/download/${{ env.SPC_VERSION }}/spc-macos-x86_64.tar.gz ;; macos-26) - curl -fsSL -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-macos-aarch64 + curl -fsSL -o spc.tgz https://github.com/crazywhalecc/static-php-cli/releases/download/${{ env.SPC_VERSION }}/spc-macos-aarch64.tar.gz ;; *) @@ -112,13 +114,14 @@ jobs: exit 1 ;; esac + tar zxvf spc.tgz chmod +x spc echo "SPC_BINARY=./spc" >> $GITHUB_ENV echo "PIE_BINARY_OUTPUT=pie-${{ runner.os }}-${{ runner.arch }}" >> $GITHUB_ENV - name: Download SPC (Windows) if: runner.os == 'Windows' run: | - curl.exe -fsSL -o spc.exe https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-windows-x64.exe + curl.exe -fsSL -o spc.exe https://github.com/crazywhalecc/static-php-cli/releases/download/${{ env.SPC_VERSION }}/spc-windows-x64.exe chmod +x spc.exe echo "SPC_BINARY=.\spc.exe" >> $env:GITHUB_ENV echo "PIE_BINARY_OUTPUT=pie-${{ runner.os }}-${{ runner.arch }}.exe" >> $env:GITHUB_ENV diff --git a/src/Platform/PackageManager.php b/src/Platform/PackageManager.php index 561a23a4..572391a0 100644 --- a/src/Platform/PackageManager.php +++ b/src/Platform/PackageManager.php @@ -12,8 +12,6 @@ use function array_unshift; use function implode; -use function str_contains; -use function strtolower; /** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */ enum PackageManager: string @@ -69,7 +67,7 @@ public function install(array $packages): void return; } catch (ProcessFailedException $e) { - if (Platform::isInteractive() && self::isProbablyPermissionDenied($e)) { + if (Platform::isInteractive() && Process::processProbablyPermissionDenied($e)) { array_unshift($cmd, Sudo::find()); Process::run($cmd); @@ -80,24 +78,4 @@ public function install(array $packages): void throw $e; } } - - private static function isProbablyPermissionDenied(ProcessFailedException $e): bool - { - $mergedProcessOutput = strtolower($e->getProcess()->getErrorOutput() . $e->getProcess()->getOutput()); - - $needles = [ - 'permission denied', - 'you must be root', - 'operation not permitted', - 'are you root', - ]; - - foreach ($needles as $needle) { - if (str_contains($mergedProcessOutput, $needle)) { - return true; - } - } - - return false; - } } diff --git a/src/Util/Process.php b/src/Util/Process.php index 8e32df16..41abed8e 100644 --- a/src/Util/Process.php +++ b/src/Util/Process.php @@ -7,6 +7,8 @@ use Symfony\Component\Process\Exception\ProcessFailedException; use Symfony\Component\Process\Process as SymfonyProcess; +use function str_contains; +use function strtolower; use function trim; /** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */ @@ -45,4 +47,25 @@ public static function run( ->mustRun($outputCallback) ->getOutput()); } + + public static function processProbablyPermissionDenied(ProcessFailedException $e): bool + { + $mergedProcessOutput = strtolower($e->getProcess()->getErrorOutput() . $e->getProcess()->getOutput()); + + $needles = [ + 'permission denied', + 'you must be root', + 'operation not permitted', + 'are you root', + 'has to be run with superuser privileges', + ]; + + foreach ($needles as $needle) { + if (str_contains($mergedProcessOutput, $needle)) { + return true; + } + } + + return false; + } } diff --git a/test/unit/Util/ProcessTest.php b/test/unit/Util/ProcessTest.php new file mode 100644 index 00000000..8e1d1c20 --- /dev/null +++ b/test/unit/Util/ProcessTest.php @@ -0,0 +1,45 @@ + */ + public static function permissionDeniedProvider(): array + { + return [ + 'apt 1 denied stderr' => ['', 'Error: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)', true], + 'apt 1 denied stdout' => ['Error: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)', '', true], + 'apt 2 denied stderr' => ['', 'Error: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?', true], + 'apt 2 denied stdout' => ['Error: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?', '', true], + 'dnf denied stderr' => ['', 'Error: This command has to be run with superuser privileges (under the root user on most systems).', true], + 'dnf denied stdout' => ['Error: This command has to be run with superuser privileges (under the root user on most systems).', '', true], + 'apk denied stderr' => ['', 'ERROR: Unable to open log: Permission denied', true], + 'apk denied stdout' => ['ERROR: Unable to open log: Permission denied', '', true], + 'no permission denied' => ['some other error', 'exit code 1', false], + ]; + } + + #[DataProvider('permissionDeniedProvider')] + public function testProcessProbablyPermissionDenied(string $stdout, string $stderr, bool $expected): void + { + $symfonyProcess = $this->createMock(SymfonyProcess::class); + $symfonyProcess->method('getOutput')->willReturn($stdout); + $symfonyProcess->method('getErrorOutput')->willReturn($stderr); + + $exception = $this->createMock(ProcessFailedException::class); + $exception->method('getProcess')->willReturn($symfonyProcess); + + self::assertSame($expected, Process::processProbablyPermissionDenied($exception)); + } +}