diff --git a/.github/workflows/build-master.yml b/.github/workflows/build-master.yml index 934f1b6..c192186 100644 --- a/.github/workflows/build-master.yml +++ b/.github/workflows/build-master.yml @@ -4,17 +4,10 @@ on: push: branches: - master - # Called by build-pull-request.yml so pull requests run this exact matrix and these exact - # steps. Defining it once is the point: the two used to be separate files, the PR one ran - # ubuntu only, and a Windows-only break sat undiscovered until master was already red. - workflow_call: jobs: build: - # The Release-commit guard applies to pushes only. On a pull request there are no - # github.event.commits, and without this the expression would be evaluated against - # nothing and silently decide the outcome. - if: "github.event_name != 'push' || !contains(github.event.commits[0].message, 'Release')" + if: "!contains(github.event.commits[0].message, 'Release')" runs-on: ${{ matrix.os }} strategy: max-parallel: 1 @@ -54,21 +47,8 @@ jobs: # Set PHP as default sudo update-alternatives --set php /usr/bin/php${{ matrix.php-version }} - # Install Composer. - # The installer is verified against the signature Composer publishes at - # composer.github.io/installer.sig before it is executed, which is the check - # Composer's own documentation prescribes. Piping curl straight into php runs - # whatever the network returned, and this job runs with repository secrets. - curl -sS https://getcomposer.org/installer -o composer-setup.php - EXPECTED_SIG="$(curl -sS https://composer.github.io/installer.sig)" - ACTUAL_SIG="$(php -r "echo hash_file('sha384', 'composer-setup.php');")" - if [ "$EXPECTED_SIG" != "$ACTUAL_SIG" ]; then - echo "Composer installer signature mismatch. Expected $EXPECTED_SIG, got $ACTUAL_SIG. Refusing to run it." - rm -f composer-setup.php - exit 1 - fi - php composer-setup.php --quiet - rm -f composer-setup.php + # Install Composer + curl -sS https://getcomposer.org/installer | php sudo mv composer.phar /usr/local/bin/composer sudo chmod +x /usr/local/bin/composer @@ -83,38 +63,18 @@ jobs: # Create PHP directory New-Item -ItemType Directory -Force -Path $phpDir - # Download PHP based on version. - # These point at the permanent archive, NOT at /~windows/releases/, which only - # keeps the current patch of each branch: when 8.4.24 shipped, the pinned - # 8.4.23 URL started returning 404 and this job failed on setup, before running - # a single test. The archive keeps every patch, so these URLs do not rot. - # Bumping the patch here is a deliberate manual step. - # Each build is pinned with its SHA-256 so a substituted or corrupted archive - # fails the job instead of being unzipped and executed. php.net publishes no - # checksum file next to these archives, so the hashes were computed from the - # official download and pinned here: that gives integrity (the artefact cannot - # change under us) rather than independent provenance. Update both together - # when bumping a patch. + # Download PHP based on version switch ($phpVersion) { - "8.1" { - $downloadUrl = "https://windows.php.net/downloads/releases/archives/php-8.1.34-Win32-vs16-x64.zip" - $expectedHash = "8e17e0804fe48d3a032c9bef16f0f922996e0b1b237061b7ce94485394db5d1b" - } - "8.4" { - $downloadUrl = "https://windows.php.net/downloads/releases/archives/php-8.4.23-Win32-vs17-x64.zip" - $expectedHash = "6cb93c23c5e87237881f2b3d8b93fdd70ff04a4a32bc16c9cf846aeb17f518dc" - } + # Archive URLs: /~windows/releases/ only keeps the current patch of each + # branch, so the pinned 8.4.23 link started 404ing when 8.4.24 shipped. + "8.1" { $downloadUrl = "https://windows.php.net/downloads/releases/archives/php-8.1.34-Win32-vs16-x64.zip" } + "8.4" { $downloadUrl = "https://windows.php.net/downloads/releases/archives/php-8.4.23-Win32-vs17-x64.zip" } default { throw "Unsupported PHP version: $phpVersion" } } - # Download, verify, then extract PHP + # Download and extract PHP $zipFile = "$phpDir\php.zip" Invoke-WebRequest -Uri $downloadUrl -OutFile $zipFile - $actualHash = (Get-FileHash -Path $zipFile -Algorithm SHA256).Hash.ToLower() - if ($actualHash -ne $expectedHash) { - throw "PHP $phpVersion checksum mismatch. Expected $expectedHash, got $actualHash. Refusing to extract." - } - Write-Host "PHP $phpVersion archive verified (SHA-256 $actualHash)" Expand-Archive -Path $zipFile -DestinationPath $phpDir -Force Remove-Item $zipFile @@ -134,19 +94,9 @@ jobs: echo "$phpDir" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append $env:PATH = "$phpDir;$env:PATH" - # Download and setup Composer. - # Pinned to a version rather than composer-stable.phar, because a moving target - # cannot be checksummed: the phar is executed on every job, so it is verified - # against the SHA-256 that getcomposer.org publishes for this exact version. - $composerVersion = "2.10.2" - $composerHash = "5ee7125f8a30a34d246cefdc0bc85b8a783b28f2aec968994118512350d28027" - $composerUrl = "https://getcomposer.org/download/$composerVersion/composer.phar" + # Download and setup Composer + $composerUrl = "https://getcomposer.org/composer-stable.phar" Invoke-WebRequest -Uri $composerUrl -OutFile "$phpDir\composer.phar" - $composerActual = (Get-FileHash -Path "$phpDir\composer.phar" -Algorithm SHA256).Hash.ToLower() - if ($composerActual -ne $composerHash) { - throw "Composer $composerVersion checksum mismatch. Expected $composerHash, got $composerActual. Refusing to run it." - } - Write-Host "Composer $composerVersion verified (SHA-256 $composerActual)" echo "@echo off" | Out-File -FilePath "$phpDir\composer.bat" -Encoding ascii echo "php `"%~dp0composer.phar`" %*" | Out-File -FilePath "$phpDir\composer.bat" -Encoding ascii -Append diff --git a/.github/workflows/build-pull-request.yml b/.github/workflows/build-pull-request.yml index dd0bd78..0fd9b88 100644 --- a/.github/workflows/build-pull-request.yml +++ b/.github/workflows/build-pull-request.yml @@ -1,12 +1,5 @@ name: build-pull-request -# Thin caller: the matrix, the setup and the checks all live in build-master.yml, so the two -# cannot drift. Previously this file duplicated them and ran ubuntu-latest only, which is why a -# pinned PHP download URL could 404 and fail master on every push while every PR went green. -# -# Duplicating the setup block here also tripped Sonar: the PowerShell that downloads the PHP zip -# and composer.phar counts as new code when copied into a new file, and it flags executing a -# downloaded artifact without verification. Calling the workflow keeps that code in one place. on: pull_request: branches: @@ -16,5 +9,45 @@ on: jobs: build: - uses: ./.github/workflows/build-master.yml - secrets: inherit + runs-on: ${{ matrix.os }} + strategy: + max-parallel: 1 + fail-fast: false + matrix: + os: [ubuntu-latest] + php-version: ["8.1", "8.4"] + + steps: + - uses: actions/checkout@v2 + + - name: Install PHP from official repositories + run: | + sudo apt-get update + sudo apt-get install -y php php-cli php-common php-curl php-json php-mbstring php-xml php-zip php-fileinfo + + - name: Composer validate + run: composer validate + + - name: Composer update + run: composer update --prefer-dist --no-interaction + + - name: Run PHPStan + run: vendor/bin/phpstan analyse --no-progress + + - name: Run PHPUnit + env: + CHECKOUT_PROCESSING_CHANNEL_ID: ${{ secrets.IT_CHECKOUT_PROCESSING_CHANNEL_ID }} + CHECKOUT_PREVIOUS_SECRET_KEY: ${{ secrets.IT_CHECKOUT_PREVIOUS_SECRET_KEY }} + CHECKOUT_PREVIOUS_PUBLIC_KEY: ${{ secrets.IT_CHECKOUT_PREVIOUS_PUBLIC_KEY }} + CHECKOUT_DEFAULT_SECRET_KEY: ${{ secrets.IT_CHECKOUT_DEFAULT_SECRET_KEY }} + CHECKOUT_DEFAULT_PUBLIC_KEY: ${{ secrets.IT_CHECKOUT_DEFAULT_PUBLIC_KEY }} + CHECKOUT_DEFAULT_OAUTH_CLIENT_ID: ${{ secrets.IT_CHECKOUT_DEFAULT_OAUTH_CLIENT_ID }} + CHECKOUT_DEFAULT_OAUTH_CLIENT_SECRET: ${{ secrets.IT_CHECKOUT_DEFAULT_OAUTH_CLIENT_SECRET }} + CHECKOUT_DEFAULT_OAUTH_PAYOUT_SCHEDULE_CLIENT_ID: ${{ secrets.IT_CHECKOUT_DEFAULT_OAUTH_PAYOUT_SCHEDULE_CLIENT_ID }} + CHECKOUT_DEFAULT_OAUTH_PAYOUT_SCHEDULE_CLIENT_SECRET: ${{ secrets.IT_CHECKOUT_DEFAULT_OAUTH_PAYOUT_SCHEDULE_CLIENT_SECRET }} + CHECKOUT_DEFAULT_OAUTH_ACCOUNTS_CLIENT_ID: ${{ secrets.IT_CHECKOUT_DEFAULT_OAUTH_ACCOUNTS_CLIENT_ID }} + CHECKOUT_DEFAULT_OAUTH_ACCOUNTS_CLIENT_SECRET: ${{ secrets.IT_CHECKOUT_DEFAULT_OAUTH_ACCOUNTS_CLIENT_SECRET }} + CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_ID: ${{ secrets.IT_CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_ID }} + CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_SECRET: ${{ secrets.IT_CHECKOUT_DEFAULT_OAUTH_ISSUING_CLIENT_SECRET }} + CHECKOUT_MERCHANT_SUBDOMAIN: ${{ secrets.IT_CHECKOUT_MERCHANT_SUBDOMAIN }} + run: vendor/bin/phpunit --verbose