diff --git a/.github/scripts/download-php-windows-devel.sh b/.github/scripts/download-php-windows-devel.sh index 14267a84ae2..24a5988a66c 100644 --- a/.github/scripts/download-php-windows-devel.sh +++ b/.github/scripts/download-php-windows-devel.sh @@ -3,22 +3,35 @@ # (the short SHA of the last commit touching turbo-ext/src, the Makefile's # computation) and fetches the php-devel-pack matching the installed PHP # (MATRIX_TS: nts/zts, MATRIX_VS: its toolset infix) into C:\php-devel and the -# php-sdk-binary-tools at PHP_SDK_COMMIT into C:\php-sdk. +# php-sdk-binary-tools at PHP_SDK_COMMIT into C:\php-sdk. For a prerelease +# (PHP_PRERELEASE, the minor's entry find-php-windows-prereleases.sh resolved +# for this run) the pack and its checksum come from that entry. set -euo pipefail SHA=$(git log -1 --format=%H -- turbo-ext/src | cut -c1-7) echo "extension version: $SHA" echo "PHPSTANTURBO_VERSION=$SHA" >> "$GITHUB_ENV" -FULL=$(php -r 'echo PHP_VERSION;') -# thread-safe devel packs carry no infix, NTS ones carry -nts -INFIX=$([ "$MATRIX_TS" = "zts" ] && echo "" || echo "-nts") -PACK="php-devel-pack-$FULL$INFIX-Win32-$MATRIX_VS-x64.zip" -echo "devel pack: $PACK" -curl -fsSLo devel-pack.zip "https://windows.php.net/downloads/releases/$PACK" \ - || curl -fsSLo devel-pack.zip "https://windows.php.net/downloads/releases/archives/$PACK" \ - || curl -fsSLo devel-pack.zip "https://downloads.php.net/~windows/qa/$PACK" \ - || curl -fsSLo devel-pack.zip "https://downloads.php.net/~windows/qa/archives/$PACK" +if [ -n "${PHP_PRERELEASE:-}" ] && [ "$PHP_PRERELEASE" != "null" ]; then + # shellcheck disable=SC2016 # PHP code, not shell expansions + read -r PACK SHA256 < <(php -r ' + $entry = json_decode(getenv("PHP_PRERELEASE"), true, 512, JSON_THROW_ON_ERROR); + $devel = $entry[getenv("MATRIX_TS") === "zts" ? "ts" : "nts"]["devel"]; + echo $devel["path"], " ", $devel["sha256"], "\n"; + ') + echo "devel pack: $PACK" + curl -fsSLo devel-pack.zip "https://downloads.php.net/~windows/qa/$PACK" \ + || curl -fsSLo devel-pack.zip "https://downloads.php.net/~windows/qa/archives/$PACK" + echo "$SHA256 devel-pack.zip" | sha256sum -c - +else + FULL=$(php -r 'echo PHP_VERSION;') + # thread-safe devel packs carry no infix, NTS ones carry -nts + INFIX=$([ "$MATRIX_TS" = "zts" ] && echo "" || echo "-nts") + PACK="php-devel-pack-$FULL$INFIX-Win32-$MATRIX_VS-x64.zip" + echo "devel pack: $PACK" + curl -fsSLo devel-pack.zip "https://windows.php.net/downloads/releases/$PACK" \ + || curl -fsSLo devel-pack.zip "https://windows.php.net/downloads/releases/archives/$PACK" +fi unzip -q devel-pack.zip -d /c/php-devel git clone -q https://github.com/php/php-sdk-binary-tools.git /c/php-sdk diff --git a/.github/scripts/find-php-windows-prereleases.sh b/.github/scripts/find-php-windows-prereleases.sh new file mode 100755 index 00000000000..a459e865286 --- /dev/null +++ b/.github/scripts/find-php-windows-prereleases.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +# For an unreleased PHP minor, setup-php installs a nightly build of its +# development branch on Windows, which has no devel pack to compile an +# extension against. The Windows legs of phar.yml then replace that runtime +# with the official prerelease (install-php-windows-prerelease.sh) and build +# against its devel pack (download-php-windows-devel.sh). +# +# For each minor in MINORS that windows.php.net has no stable build of, this +# writes the current prerelease from its QA manifest to the `prereleases` +# step output: +# {"": {"version": "8.6.0RC3", +# "nts": {"zip": {"path", "sha256"}, "devel": {"path", "sha256"}}, +# "ts": {...}}} +# A minor with a stable build is left out: setup-php installs that release, +# whose devel pack is published next to it. It runs once per workflow run, +# so every job installs the same prerelease even when a new one comes out +# in the meantime. +# +# Usage: MINORS="8.3 8.4 8.5 8.6" find-php-windows-prereleases.sh +set -euo pipefail + +: "${MINORS:?set MINORS to the PHP minors the Windows legs build for}" +fetch() { curl -fsSL --retry 3 --retry-all-errors "https://downloads.php.net/~windows/$1/releases.json"; } +stable=$(fetch releases) +qa=$(fetch qa) + +prereleases=$(jq -cn --argjson stable "$stable" --argjson qa "$qa" --arg minors "$MINORS" ' + def files($builds; $ts): + $builds | to_entries + | map(select(.key | test("^" + $ts + "-vs[0-9]+-x64$"))) + | if length == 1 then .[0].value | {zip: (.zip | {path, sha256}), devel: (.devel_pack | {path, sha256})} + else error("expected one \($ts) x64 build, found \(length)") end; + reduce ($minors | split(" ") | .[] | select(. != "")) as $minor ({}; + if $stable | has($minor) then . + elif $qa | has($minor) | not then error("windows.php.net has neither a stable nor a QA build of PHP \($minor)") + else .[$minor] = {version: $qa[$minor].version, nts: files($qa[$minor]; "nts"), ts: files($qa[$minor]; "ts")} + end)') + +jq -r 'if length == 0 then "every minor has a stable build" else to_entries[] | "PHP \(.key): prerelease \(.value.version)" end' <<< "$prereleases" +echo "prereleases=$prereleases" >> "${GITHUB_OUTPUT:-/dev/stdout}" diff --git a/.github/scripts/find-turbo-origins.sh b/.github/scripts/find-turbo-origins.sh index ae6acd5e04c..4cd324f8d73 100755 --- a/.github/scripts/find-turbo-origins.sh +++ b/.github/scripts/find-turbo-origins.sh @@ -49,7 +49,7 @@ BUILD_INPUT_PATHS=( .github/scripts/install-alpine-php.sh .github/scripts/download-composer.sh .github/scripts/composer-tags.pub - .github/scripts/install-php86-windows.sh + .github/scripts/install-php-windows-prerelease.sh .github/scripts/download-php-windows-devel.sh turbo-ext/bin/shared-core ) diff --git a/.github/scripts/install-php-windows-prerelease.sh b/.github/scripts/install-php-windows-prerelease.sh new file mode 100644 index 00000000000..ef3a4859afc --- /dev/null +++ b/.github/scripts/install-php-windows-prerelease.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +set -euo pipefail + +# setup-php supplies Composer, php.ini and CA certificates, but for an +# unreleased minor it installs a nightly build, which has no matching +# development pack. Replace the runtime and bundled extensions with the +# official prerelease find-php-windows-prereleases.sh resolved for this run +# (PHP_PRERELEASE, its JSON entry for the minor), preserving that +# configuration. +PHP_DIR="$(cygpath -u "$(php -r 'echo dirname(PHP_BINARY);')")" +case "$MATRIX_TS" in + zts) TS=ts ;; + nts) TS=nts ;; + *) echo "Unknown thread-safety mode: $MATRIX_TS" >&2; exit 1 ;; +esac +# shellcheck disable=SC2016 # PHP code, not shell expansions +read -r VERSION PACK SHA256 < <(php -r ' + $entry = json_decode(getenv("PHP_PRERELEASE"), true, 512, JSON_THROW_ON_ERROR); + $zip = $entry[$argv[1]]["zip"]; + echo $entry["version"], " ", $zip["path"], " ", $zip["sha256"], "\n"; +' "$TS") +ARCHIVE="$RUNNER_TEMP/$PACK" +curl -fsSLo "$ARCHIVE" "https://downloads.php.net/~windows/qa/$PACK" \ + || curl -fsSLo "$ARCHIVE" "https://downloads.php.net/~windows/qa/archives/$PACK" +echo "$SHA256 $ARCHIVE" | sha256sum -c - +unzip -qo "$ARCHIVE" -d "$PHP_DIR" +EXPECTED_VERSION="$VERSION" php -r ' + if (PHP_VERSION !== getenv("EXPECTED_VERSION") || (bool) PHP_ZTS !== (getenv("MATRIX_TS") === "zts")) { + fwrite(STDERR, "PHP prerelease version or thread-safety mismatch\n"); + exit(1); + } + echo "Using PHP ", PHP_VERSION, PHP_ZTS ? " ZTS\n" : " NTS\n"; +' diff --git a/.github/scripts/install-php86-windows.sh b/.github/scripts/install-php86-windows.sh deleted file mode 100644 index f7eebca091e..00000000000 --- a/.github/scripts/install-php86-windows.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# setup-php supplies Composer, php.ini and CA certificates, but its 8.6 -# nightly has no matching development pack. Replace the runtime and bundled -# extensions with the official prerelease, preserving that configuration. -PHP_DIR="$(cygpath -u "$(php -r 'echo dirname(PHP_BINARY);')")" -case "$MATRIX_TS" in - zts) INFIX="" ;; - nts) INFIX="-nts" ;; - *) echo "Unknown thread-safety mode: $MATRIX_TS" >&2; exit 1 ;; -esac -PACK="php-$PHP86_WINDOWS_VERSION$INFIX-Win32-vs18-x64.zip" -ARCHIVE="$RUNNER_TEMP/$PACK" -curl -fsSLo "$ARCHIVE" "https://downloads.php.net/~windows/qa/$PACK" \ - || curl -fsSLo "$ARCHIVE" "https://downloads.php.net/~windows/qa/archives/$PACK" -unzip -qo "$ARCHIVE" -d "$PHP_DIR" -php -r ' - if (PHP_VERSION !== getenv("PHP86_WINDOWS_VERSION") || (bool) PHP_ZTS !== (getenv("MATRIX_TS") === "zts")) { - fwrite(STDERR, "PHP prerelease version or thread-safety mismatch\n"); - exit(1); - } - echo "Using PHP ", PHP_VERSION, PHP_ZTS ? " ZTS\n" : " NTS\n"; -' diff --git a/.github/workflows/phar.yml b/.github/workflows/phar.yml index 71e85748584..b99ffd2822a 100644 --- a/.github/workflows/phar.yml +++ b/.github/workflows/phar.yml @@ -30,8 +30,6 @@ env: # The php-parser version whose grammar tables and semantic actions the # native parser engine (turbo-ext/src/parser/) was ported against. SUPPORTED_PHP_PARSER_VERSION: "v5.9.0" - # setup-php installs an 8.6 nightly, which has no matching Windows devel pack. - PHP86_WINDOWS_VERSION: "8.6.0RC2" jobs: compiler-tests: @@ -335,6 +333,45 @@ jobs: make -C "$RUNNER_TEMP/turbo-ext" install INSTALL_ROOT="$RUNNER_TEMP/install-root" find "$RUNNER_TEMP/install-root" -name 'phpstan_turbo.so' | grep . + php-windows-prereleases: + name: "Find PHP Prereleases for Windows" + # For an unreleased minor, setup-php installs a nightly build on Windows, + # which has no devel pack to compile against; the Windows legs replace it + # with the official prerelease. This resolves the current one for every + # minor in this workflow's matrices that has no stable Windows build yet + # (.github/scripts/find-php-windows-prereleases.sh), once per run, so all + # legs install the same build even when a new release candidate comes out + # in the meantime. + runs-on: "ubuntu-latest" + timeout-minutes: 5 + + permissions: + contents: read # actions/checkout of this repository + + outputs: + prereleases: ${{ steps.find.outputs.prereleases }} + + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 + with: + egress-policy: audit + + - name: "Checkout" + uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 + with: + persist-credentials: false + sparse-checkout: | + .github/scripts + .github/workflows + + - name: "Find the prerelease of every PHP minor without a stable Windows build" + id: find + run: | + MINORS="$(yq -r '[.jobs[].strategy.matrix["php-version"] | select(. != null) | .[]] | unique | .[] | select(test("^[0-9]+\\.[0-9]+$"))' .github/workflows/phar.yml | tr '\n' ' ')" + echo "PHP minors in the matrices: $MINORS" + MINORS="$MINORS" bash .github/scripts/find-php-windows-prereleases.sh + turbo-origins: name: "Find Reusable Turbo Extension Builds" # Most pushes and pull requests leave the extension's build inputs @@ -1469,7 +1506,9 @@ jobs: # is the one that ships VS2022. # PHP 8.6's vs18 development packs need the VS2026 toolchain. runs-on: ${{ matrix.php-version == '8.6' && 'windows-2025-vs2026' || 'windows-2022' }} - needs: turbo-compile-core-windows + needs: + - php-windows-prereleases + - turbo-compile-core-windows # see turbo-compile if: ${{ !cancelled() }} timeout-minutes: ${{ matrix.variant == 'pie' && 45 || 30 }} @@ -1518,18 +1557,20 @@ jobs: coverage: "none" php-version: "${{ matrix.php-version }}" - - name: "Install the matching PHP 8.6 prerelease runtime" - if: matrix.php-version == '8.6' + - name: "Install the PHP prerelease runtime" + if: fromJSON(needs.php-windows-prereleases.outputs.prereleases)[matrix.php-version] shell: bash env: MATRIX_TS: ${{ matrix.ts }} - run: bash .github/scripts/install-php86-windows.sh + PHP_PRERELEASE: ${{ toJSON(fromJSON(needs.php-windows-prereleases.outputs.prereleases)[matrix.php-version]) }} + run: bash .github/scripts/install-php-windows-prerelease.sh - name: "Compute the extension version and download the build tools" shell: bash env: MATRIX_TS: ${{ matrix.ts }} MATRIX_VS: ${{ matrix.vs }} + PHP_PRERELEASE: ${{ toJSON(fromJSON(needs.php-windows-prereleases.outputs.prereleases)[matrix.php-version]) }} run: bash .github/scripts/download-php-windows-devel.sh # the DLL and its import library, into the extension directory where @@ -1751,6 +1792,7 @@ jobs: # it at all; the ZTS legs so as not to depend on setup-php's thread-safe # Linux builds. needs: + - php-windows-prereleases - turbo-compile - turbo-compile-windows runs-on: ${{ matrix.runs-on }} @@ -1856,11 +1898,12 @@ jobs: coverage: "none" php-version: "${{ matrix.php-version }}" - - name: "Install the matching PHP 8.6 prerelease runtime" - if: matrix.target == 'windows-x86_64' && matrix.php-version == '8.6' + - name: "Install the PHP prerelease runtime" + if: matrix.target == 'windows-x86_64' && fromJSON(needs.php-windows-prereleases.outputs.prereleases)[matrix.php-version] env: MATRIX_TS: ${{ matrix.ts }} - run: bash .github/scripts/install-php86-windows.sh + PHP_PRERELEASE: ${{ toJSON(fromJSON(needs.php-windows-prereleases.outputs.prereleases)[matrix.php-version]) }} + run: bash .github/scripts/install-php-windows-prerelease.sh - uses: "ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda" # v4.0.0 if: ${{ !matrix.container }} @@ -2038,6 +2081,7 @@ jobs: turbo-run: name: "Run with Turbo Extension" needs: + - php-windows-prereleases - turbo-compile - turbo-compile-windows runs-on: ${{ matrix.operating-system }} @@ -2109,11 +2153,12 @@ jobs: coverage: "none" php-version: "${{ matrix.php-version }}" - - name: "Install the matching PHP 8.6 prerelease runtime" - if: matrix.operating-system == 'windows-latest' && matrix.php-version == '8.6' + - name: "Install the PHP prerelease runtime" + if: matrix.operating-system == 'windows-latest' && fromJSON(needs.php-windows-prereleases.outputs.prereleases)[matrix.php-version] env: MATRIX_TS: ${{ matrix.ts }} - run: bash .github/scripts/install-php86-windows.sh + PHP_PRERELEASE: ${{ toJSON(fromJSON(needs.php-windows-prereleases.outputs.prereleases)[matrix.php-version]) }} + run: bash .github/scripts/install-php-windows-prerelease.sh - name: "Download extension artifact" uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 diff --git a/turbo-ext/README.md b/turbo-ext/README.md index b66fef16ae7..a82b5ee05e5 100644 --- a/turbo-ext/README.md +++ b/turbo-ext/README.md @@ -537,7 +537,10 @@ Windows legs' tests are their check. infix and toolset in `include`) and of the jobs testing them (`turbo-differential`, `turbo-differential-musl`, `turbo-run`, `turbo-docker-run`), and give the new version a `gnu-php` image - for the Linux gnu legs. The cores need no change: they keep compiling + for the Linux gnu legs. Until the version has a stable Windows release, + the Windows legs install its current prerelease by themselves (the + `php-windows-prereleases` job reads windows.php.net's manifests), so + there is no version to pin. The cores need no change: they keep compiling against the version they are pinned to (`php-version` in `turbo-compile-core`'s targets, `["8.3"]` with the vs16 toolset in `turbo-compile-core-windows`). Run the smoke test of every version