Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 11 additions & 61 deletions .github/workflows/build-master.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,10 @@
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
Expand Down Expand Up @@ -54,21 +47,8 @@
# 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

Check failure on line 51 in .github/workflows/build-master.yml

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Avoid executing downloaded artifacts directly without verification.

See more on https://sonarcloud.io/project/issues?id=checkout_checkout-sdk-php&issues=AaAZluGn7--lkSgONTEK&open=AaAZluGn7--lkSgONTEK&pullRequest=365
sudo mv composer.phar /usr/local/bin/composer
sudo chmod +x /usr/local/bin/composer

Expand All @@ -83,38 +63,18 @@
# 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

Expand All @@ -134,19 +94,9 @@
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

Expand Down
51 changes: 42 additions & 9 deletions .github/workflows/build-pull-request.yml
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -16,5 +9,45 @@

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

Check warning on line 32 in .github/workflows/build-pull-request.yml

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Dependency versions are not predictable. Use a lock-file enforcing command instead.

See more on https://sonarcloud.io/project/issues?id=checkout_checkout-sdk-php&issues=AaAZluJO7--lkSgONTEL&open=AaAZluJO7--lkSgONTEL&pullRequest=365

- 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
Loading