From 1680dc5b49bdc4e511762d37f210558521856e6d Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Thu, 8 Oct 2026 18:54:19 +0200 Subject: [PATCH] Publish self-contained turbo binaries as the PIE release assets PIE installs a pre-packaged binary by copying the one extension file into the extension directory (phpstan_turbo.so on Unix; on Windows the DLL named like the asset, other DLLs go next to php.exe), so the thin per-version extensions phpstan/phpstan ships since the shared core, which load phpstan_turbo_core from their own directory, cannot be PIE assets: `pie install phpstan/turbo` would install an extension that fails to load. - the turbo-compile, turbo-compile-musl-arm64 and turbo-compile-windows jobs gain a `pie` variant, built only for release tags (and on pull requests labelled turbo-pie): the same version self-contained, with a plain `make pgo` (configure --enable-phpstan-turbo on Windows), uploaded as phpstan_turbo_pie--php[-zts] - the new turbo-pie-release job packages them by PIE's asset naming (.github/scripts/turbo-pie-assets.sh) and publishes the phpstan/turbo-ext release of the tag, which the dist release.yml did from the thin binaries until now - the Windows assets carry the Visual Studio generation the leg built against, so PHP 8.6 is published as vs18 (it was named vs17, which PIE on the official vs18 builds of 8.6 never matched) Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01LSeeJXEA18Ma6X4qyx1xMx --- .github/scripts/turbo-pie-assets.sh | 115 ++++++++++++++++ .github/workflows/phar.yml | 204 ++++++++++++++++++++++++++-- 2 files changed, 304 insertions(+), 15 deletions(-) create mode 100644 .github/scripts/turbo-pie-assets.sh diff --git a/.github/scripts/turbo-pie-assets.sh b/.github/scripts/turbo-pie-assets.sh new file mode 100644 index 00000000000..a61522cb997 --- /dev/null +++ b/.github/scripts/turbo-pie-assets.sh @@ -0,0 +1,115 @@ +#!/usr/bin/env bash +# Packages the self-contained turbo binaries of a phar.yml run (the `pie` +# variant of the turbo-compile jobs) as PIE release assets for the +# phpstan/turbo-ext release of . +# +# PIE installs a pre-packaged binary by copying one file into the extension +# directory — phpstan_turbo.so on Unix (UnixBuild::prePackagedBinary, +# UnixInstall), the DLL named like the asset on Windows, whose other DLLs it +# puts next to php.exe instead (WindowsInstall) — so the thin extension the +# phpstan/phpstan dist ships, which loads the platform's core from its own +# directory, cannot be one; these are built self-contained. +# +# An artifact phpstan_turbo_pie--php[-zts] carries +# phpstan_turbo.so, or on Windows php_phpstan_turbo.dll and a `compiler` +# file naming the Visual Studio generation (vs16/vs17/vs18) the official PHP +# of that minor is built with — PIE matches it against the target PHP's. +# Asset names follow PIE's PrePackagedBinaryAssetName and +# WindowsExtensionAssetName (matched lowercase): +# php_phpstan_turbo-_php---[-zts].zip +# php_phpstan_turbo-----x86_64.zip (with the same-named .dll) +# +# Usage: turbo-pie-assets.sh +set -euo pipefail + +artifacts=$1 +version=$2 +out=$3 + +mkdir -p "$out" +work="$(mktemp -d)" +trap 'rm -rf "$work"' EXIT +status=0 +packaged=0 +found=0 + +for dir in "$artifacts"/phpstan_turbo_pie-*; do + [ -d "$dir" ] || continue + found=$((found + 1)) + name="${dir#"$artifacts"/phpstan_turbo_pie-}" + target="${name%-php*}" + ver="${name##*-php}" + minor="${ver%-zts}" + zts="" + if [ "$ver" != "$minor" ]; then + zts="-zts" + fi + + case "$target" in + linux-gnu-* | linux-musl-* | macos-arm64) + case "$target" in + linux-gnu-*) os=linux; libc=glibc; arch="${target#linux-gnu-}" ;; + linux-musl-*) os=linux; libc=musl; arch="${target#linux-musl-}" ;; + *) os=darwin; libc=bsdlibc; arch=arm64 ;; + esac + if [ ! -f "$dir/phpstan_turbo.so" ]; then + echo "::error::$dir carries no phpstan_turbo.so" >&2 + status=1 + continue + fi + asset="php_phpstan_turbo-${version}_php${minor}-${arch}-${os}-${libc}${zts}" + rm -rf "${work:?}"/* + cp "$dir/phpstan_turbo.so" "$work/phpstan_turbo.so" + (cd "$work" && zip -q -X "$asset.zip" phpstan_turbo.so) + ;; + windows-x86_64) + if [ ! -f "$dir/php_phpstan_turbo.dll" ] || [ ! -f "$dir/compiler" ]; then + echo "::error::$dir carries no php_phpstan_turbo.dll and compiler file" >&2 + status=1 + continue + fi + vs="$(tr -d '[:space:]' < "$dir/compiler")" + case "$vs" in + vs16 | vs17 | vs18) ;; + *) + echo "::error::$dir names an unknown compiler: $vs" >&2 + status=1 + continue + ;; + esac + ts=nts + if [ -n "$zts" ]; then + ts=ts + fi + asset="php_phpstan_turbo-${version}-${minor}-${ts}-${vs}-x86_64" + rm -rf "${work:?}"/* + cp "$dir/php_phpstan_turbo.dll" "$work/$asset.dll" + (cd "$work" && zip -q -X "$asset.zip" "$asset.dll") + ;; + *) + echo "::error::$dir is for an unknown target: $target" >&2 + status=1 + continue + ;; + esac + + if [ -e "$out/$asset.zip" ]; then + echo "::error::two artifacts map onto $asset.zip" >&2 + status=1 + continue + fi + mv "$work/$asset.zip" "$out/$asset.zip" + echo "$name -> $asset.zip" + packaged=$((packaged + 1)) +done + +if [ "$found" -eq 0 ]; then + echo "::error::no phpstan_turbo_pie-* artifacts found in $artifacts" >&2 + status=1 +fi +if [ "$packaged" -ne "$found" ]; then + echo "::error::packaged $packaged assets from $found artifacts" >&2 + status=1 +fi + +exit "$status" diff --git a/.github/workflows/phar.yml b/.github/workflows/phar.yml index c80e93dee06..9b3ae9d8f47 100644 --- a/.github/workflows/phar.yml +++ b/.github/workflows/phar.yml @@ -716,6 +716,12 @@ jobs: # extension — the tests of the turbo-differential jobs load it from # there, and the commit job checks that every leg of a platform carries # the same core. + # The `pie` variant legs build the same version self-contained instead, + # profile-guided like the core — PIE installs a pre-packaged binary by + # copying the one extension file, so the PIE release assets cannot be + # the thin extension (see .github/scripts/turbo-pie-assets.sh). Nothing + # else ships them, so they only run for release tags (turbo-pie-release + # publishes them) and on pull requests labelled turbo-pie. runs-on: ${{ matrix.target.runs-on }} # Container legs pin the binaries' userland floor independently of the # runner image, like turbo-compile-core. Every Linux target has a ZTS @@ -738,12 +744,12 @@ jobs: # one platform's failed core must not skip every platform's legs: a leg # whose core is missing fails at its download instead if: ${{ !cancelled() }} - timeout-minutes: 20 + timeout-minutes: ${{ matrix.variant == 'pie' && 45 || 20 }} env: PHP_MINOR: ${{ matrix.php-version }} PHP_ZTS: ${{ matrix.ts == 'zts' && '1' || '0' }} - TURBO_ARTIFACT: "phpstan_turbo-${{ matrix.target.name }}-php${{ matrix.php-version }}${{ matrix.ts == 'zts' && '-zts' || '' }}" + TURBO_ARTIFACT: "${{ matrix.variant == 'pie' && 'phpstan_turbo_pie' || 'phpstan_turbo' }}-${{ matrix.target.name }}-php${{ matrix.php-version }}${{ matrix.ts == 'zts' && '-zts' || '' }}" strategy: fail-fast: false @@ -766,6 +772,9 @@ jobs: runs-on: "macos-latest" family: "macos" ts: ["nts", "zts"] + # split: the thin extension the dist ships; pie: self-contained, for + # the PIE release assets (see above) + variant: ${{ fromJSON((startsWith(github.ref, 'refs/tags/') || contains(github.event.pull_request.labels.*.name, 'turbo-pie')) && '["split", "pie"]' || '["split"]') }} # Homebrew and setup-php ship an NTS PHP on macOS, and no # thread-safe distribution is common enough there to build for exclude: @@ -814,6 +823,7 @@ jobs: php-version: "${{ matrix.php-version }}" - name: "Download the platform's core" + if: matrix.variant == 'split' uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: "phpstan_turbo_core-${{ matrix.target.name }}" @@ -821,18 +831,41 @@ jobs: # The strict set lives in turbo-ext/Makefile (STRICT_WARN_FLAGS). - name: "Compile the extension with strict warnings" + if: matrix.variant == 'split' working-directory: "turbo-ext" run: | make SPLIT=1 extension WARN_FLAGS="$(make -s print-warn-flags)" -j"$(getconf _NPROCESSORS_ONLN 2>/dev/null || nproc)" + # see the same steps in turbo-compile-core: the training run of + # `make pgo` runs bin/phpstan on the checkout + - uses: "ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda" # v4.0.0 + if: matrix.variant == 'pie' && matrix.target.family == 'macos' + with: + ignore-cache: true + + - name: "Install Composer dependencies (pinned composer)" + if: matrix.variant == 'pie' && matrix.target.family != 'macos' + run: | + curl -fsSLo composer.phar "https://getcomposer.org/download/$COMPOSER_VERSION/composer.phar" + echo "$COMPOSER_PHAR_SHA256 composer.phar" | sha256sum -c - + php composer.phar install --no-interaction --no-progress + rm composer.phar + + # A plain `make pgo` builds one self-contained phpstan_turbo.so. + - name: "Compile the self-contained extension with strict warnings (profile-guided)" + if: matrix.variant == 'pie' + working-directory: "turbo-ext" + run: | + make pgo WARN_FLAGS="$(make -s print-warn-flags)" -j"$(getconf _NPROCESSORS_ONLN 2>/dev/null || nproc)" + # Before signing and the upload; a plain `make strip` leaves the core # (already stripped and signed by its own job) untouched. - name: "Strip symbol tables" working-directory: "turbo-ext" run: | - ls -l phpstan_turbo.so phpstan_turbo_core.so + ls -l phpstan_turbo*.so make strip - ls -l phpstan_turbo.so phpstan_turbo_core.so + ls -l phpstan_turbo*.so # see turbo-compile-core - name: "Ad-hoc sign the extension (macOS)" @@ -858,6 +891,7 @@ jobs: [ "$(php -r 'echo (int) ((bool) PHP_ZTS);')" = "$PHP_ZTS" ] - name: "Upload extension artifact" + if: matrix.variant == 'split' uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: ${{ env.TURBO_ARTIFACT }} @@ -866,6 +900,14 @@ jobs: turbo-ext/phpstan_turbo_core.so if-no-files-found: "error" + - name: "Upload self-contained extension artifact" + if: matrix.variant == 'pie' + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: ${{ env.TURBO_ARTIFACT }} + path: "turbo-ext/phpstan_turbo.so" + if-no-files-found: "error" + turbo-compile-musl-arm64: name: "Compile Turbo Extension (musl arm64)" # The arm64 counterpart of the linux-musl-x86_64 leg in turbo-compile. @@ -877,12 +919,12 @@ jobs: runs-on: "ubuntu-24.04-arm" needs: turbo-compile-core-musl-arm64 if: ${{ !cancelled() }} - timeout-minutes: 20 + timeout-minutes: ${{ matrix.variant == 'pie' && 45 || 20 }} env: PHP_MINOR: ${{ matrix.php-version }} PHP_ZTS: ${{ matrix.ts == 'zts' && '1' || '0' }} - TURBO_ARTIFACT: "phpstan_turbo-linux-musl-arm64-php${{ matrix.php-version }}${{ matrix.ts == 'zts' && '-zts' || '' }}" + TURBO_ARTIFACT: "${{ matrix.variant == 'pie' && 'phpstan_turbo_pie' || 'phpstan_turbo' }}-linux-musl-arm64-php${{ matrix.php-version }}${{ matrix.ts == 'zts' && '-zts' || '' }}" strategy: fail-fast: false @@ -890,6 +932,8 @@ jobs: php-version: ["8.3", "8.4", "8.5", "8.6"] # thread-safe for every minor too, see turbo-compile ts: ["nts", "zts"] + # see turbo-compile + variant: ${{ fromJSON((startsWith(github.ref, 'refs/tags/') || contains(github.event.pull_request.labels.*.name, 'turbo-pie')) && '["split", "pie"]' || '["split"]') }} steps: - name: Harden the runner (Audit all outbound calls) @@ -904,6 +948,7 @@ jobs: fetch-depth: 0 # the version is baked from git log over turbo-ext/src - name: "Download the platform's core" + if: matrix.variant == 'split' uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: "phpstan_turbo_core-linux-musl-arm64" @@ -915,7 +960,7 @@ jobs: run: | docker run -d --name alpine-build \ -v "$PWD:/work" -w /work \ - -e PHP_MINOR -e PHP_ZTS \ + -e PHP_MINOR -e PHP_ZTS -e COMPOSER_VERSION -e COMPOSER_PHAR_SHA256 \ "$ALPINE_IMAGE" sleep 7200 - name: "Install PHP and build tools" @@ -926,6 +971,7 @@ jobs: run: docker exec alpine-build git config --global --add safe.directory /work - name: "Compile the extension with strict warnings" + if: matrix.variant == 'split' run: | docker exec -i -w /work/turbo-ext alpine-build sh -e <<'EOF' make SPLIT=1 extension WARN_FLAGS="$(make -s print-warn-flags)" -j"$(getconf _NPROCESSORS_ONLN 2>/dev/null || nproc)" @@ -933,6 +979,22 @@ jobs: ls -l phpstan_turbo.so phpstan_turbo_core.so EOF + # see turbo-compile: the training run of `make pgo` runs bin/phpstan + # on the checkout + - name: "Compile the self-contained extension with strict warnings (profile-guided)" + if: matrix.variant == 'pie' + run: | + docker exec -i -w /work alpine-build sh -e <<'EOF' + curl -fsSLo composer.phar "https://getcomposer.org/download/$COMPOSER_VERSION/composer.phar" + echo "$COMPOSER_PHAR_SHA256 composer.phar" | sha256sum -c - + php composer.phar install --no-interaction --no-progress + rm composer.phar + cd turbo-ext + make pgo WARN_FLAGS="$(make -s print-warn-flags)" -j"$(getconf _NPROCESSORS_ONLN 2>/dev/null || nproc)" + make strip + ls -l phpstan_turbo.so + EOF + - name: "Verify the built extension reports the expected version" run: | docker exec -i alpine-build sh -e <<'EOF' @@ -944,6 +1006,7 @@ jobs: EOF - name: "Upload extension artifact" + if: matrix.variant == 'split' uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: ${{ env.TURBO_ARTIFACT }} @@ -952,6 +1015,14 @@ jobs: turbo-ext/phpstan_turbo_core.so if-no-files-found: "error" + - name: "Upload self-contained extension artifact" + if: matrix.variant == 'pie' + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: ${{ env.TURBO_ARTIFACT }} + path: "turbo-ext/phpstan_turbo.so" + if-no-files-found: "error" + turbo-shared-core-gate: name: "Turbo Shared Core Gate" # The core every platform ships is compiled against one PHP version's @@ -1312,7 +1383,8 @@ jobs: name: "Compile Turbo Extension (Windows)" # The extension DLL of one PHP version, linked against the core of # turbo-compile-core-windows (its import library) and delay-loading it - # from its own directory at run time (main.cpp, config.w32). + # from its own directory at run time (main.cpp, config.w32). The `pie` + # variant legs build it self-contained instead, see turbo-compile. # windows-latest and windows-2025 serve the VS2026 image, whose 14.5x # toolset links DLLs the official php.net binaries (built with vs17, # toolset 14.4x) refuse to load — PHP's loader rejects modules linked @@ -1323,17 +1395,19 @@ jobs: needs: turbo-compile-core-windows # see turbo-compile if: ${{ !cancelled() }} - timeout-minutes: 30 + timeout-minutes: ${{ matrix.variant == 'pie' && 45 || 30 }} env: PHP_SDK_COMMIT: "c73faaf1cce914e2fc04da5587132f8f425996af" # php-sdk-2.7.1, see turbo-compile-core-windows - TURBO_ARTIFACT: "phpstan_turbo-windows-x86_64-php${{ matrix.php-version }}${{ matrix.ts == 'zts' && '-zts' || '' }}" + TURBO_ARTIFACT: "${{ matrix.variant == 'pie' && 'phpstan_turbo_pie' || 'phpstan_turbo' }}-windows-x86_64-php${{ matrix.php-version }}${{ matrix.ts == 'zts' && '-zts' || '' }}" strategy: fail-fast: false matrix: php-version: ["8.3", "8.4", "8.5", "8.6"] ts: ["nts", "zts"] + # see turbo-compile + variant: ${{ fromJSON((startsWith(github.ref, 'refs/tags/') || contains(github.event.pull_request.labels.*.name, 'turbo-pie')) && '["split", "pie"]' || '["split"]') }} # The official 8.3 Windows binaries are built with VS16 (linker # 14.2x) and PHP's module loader is one-directional (module toolset # generation <= core's), so the 8.3 DLL must be linked with the v142 @@ -1384,6 +1458,7 @@ jobs: # the DLL and its import library, into the extension directory where # config.w32 expects the library - name: "Download the core" + if: matrix.variant == 'split' uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: "phpstan_turbo_core-windows-x86_64${{ matrix.ts == 'zts' && '-zts' || '' }}" @@ -1399,28 +1474,42 @@ jobs: - name: "Build the extension" shell: cmd working-directory: turbo-ext + env: + VARIANT: ${{ matrix.variant }} run: | for /d %%d in (C:\php-devel\php-*-devel-*) do set DEVEL=%%d set PATH=%DEVEL%;C:\php-sdk\bin;C:\php-sdk\msys2\usr\bin;%PATH% call phpize.bat if errorlevel 1 exit /b 1 - call configure.bat --enable-phpstan-turbo=extension + if "%VARIANT%"=="pie" ( + call configure.bat --enable-phpstan-turbo + ) else ( + call configure.bat --enable-phpstan-turbo=extension + ) if errorlevel 1 exit /b 1 nmake # See the same step in turbo-compile. The extension loads its core - # from its own directory, so the core goes next to the built DLL first. + # from its own directory, so the core goes next to the built DLL first; + # the self-contained one (pie) carries the PHP generation it is built + # for instead, which the PIE asset name needs. - name: "Verify the built extension reports the expected version" shell: bash env: MATRIX_TS: ${{ matrix.ts }} + MATRIX_VS: ${{ matrix.vs }} + VARIANT: ${{ matrix.variant }} run: | - CORE=$([ "$MATRIX_TS" = "zts" ] && echo phpstan_turbo_core-zts.dll || echo phpstan_turbo_core.dll) DLL=$(find turbo-ext -name "php_phpstan_turbo.dll" | head -1) [ -n "$DLL" ] mkdir -p ext-out cp "$DLL" ext-out/php_phpstan_turbo.dll - cp "turbo-ext/$CORE" ext-out/ + if [ "$VARIANT" = "pie" ]; then + echo "$MATRIX_VS" > ext-out/compiler + else + CORE=$([ "$MATRIX_TS" = "zts" ] && echo phpstan_turbo_core-zts.dll || echo phpstan_turbo_core.dll) + cp "turbo-ext/$CORE" ext-out/ + fi REPORTED="$(php -d extension="$(cygpath -w "$PWD/ext-out/php_phpstan_turbo.dll")" -r 'echo phpversion("phpstan_turbo");')" EXPECTED="$(sed -n "s/.*EXPECTED_EXTENSION_VERSION = '\([^']*\)'.*/\1/p" src/Turbo/TurboExtensionEnabler.php)" echo "built extension reports: $REPORTED, enabler expects: $EXPECTED" @@ -1435,7 +1524,7 @@ jobs: uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: ${{ env.TURBO_ARTIFACT }} - path: "ext-out/*.dll" + path: ${{ matrix.variant == 'pie' && 'ext-out/*' || 'ext-out/*.dll' }} if-no-files-found: "error" turbo-artifact: @@ -1486,6 +1575,91 @@ jobs: path: "turbo-ext-dist" if-no-files-found: "error" + turbo-pie-release: + name: "Turbo Extension PIE Release" + # Publishes the self-contained binaries of the `pie` variant legs as the + # phpstan/turbo-ext release of the tag, named per PIE's release-asset + # conventions (.github/scripts/turbo-pie-assets.sh) so that + # `pie install phpstan/turbo` finds them. The tag itself is created in + # phpstan/turbo-ext by subsplit-turbo-ext.yml. On pull requests labelled + # turbo-pie it only packages them, as an artifact. + if: startsWith(github.ref, 'refs/tags/') || contains(github.event.pull_request.labels.*.name, 'turbo-pie') + needs: + - turbo-compile + - turbo-compile-musl-arm64 + - turbo-compile-windows + runs-on: "ubuntu-latest" + timeout-minutes: 15 + + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 + with: + egress-policy: audit + + # before the download: checkout empties the workspace + - name: "Checkout" + uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 + with: + persist-credentials: false + sparse-checkout: .github/scripts + + - name: "Download the self-contained extension artifacts" + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + pattern: "phpstan_turbo_pie-*" + path: "turbo-pie-artifacts" + + - name: "Package them as PIE release assets" + env: + VERSION: ${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || 'dev' }} + run: | + bash .github/scripts/turbo-pie-assets.sh turbo-pie-artifacts "$VERSION" pie-assets + ls -l pie-assets + + - name: "Upload the PIE release assets" + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: "turbo-pie-assets" + path: "pie-assets" + if-no-files-found: "error" + + - name: "Create the phpstan/turbo-ext release and upload the assets" + if: startsWith(github.ref, 'refs/tags/') + env: + GH_TOKEN: ${{ secrets.PHPSTAN_BOT_TOKEN }} + TAG: ${{ github.ref_name }} + run: | + # The release must point at the tag subsplit-turbo-ext.yml creates + # in phpstan/turbo-ext when this repository is tagged; fail loudly + # if it has not appeared, and re-run this job once it has. + if ! gh api "repos/phpstan/turbo-ext/git/ref/tags/$TAG" > /dev/null; then + echo "::error::Tag $TAG does not exist in phpstan/turbo-ext yet. The subsplit-turbo-ext.yml workflow creates it; re-run this job once it has." + exit 1 + fi + + # Releases in the phpstan org are immutable — a published release + # can never gain or replace assets. Create a draft, upload every + # asset into it, and only then publish. A re-run resumes a + # half-finished draft; a published release is final. + state="$(gh api repos/phpstan/turbo-ext/releases --jq 'first(.[] | select(.tag_name == env.TAG) | if .draft then "draft" else "published" end)')" + case "${state:-absent}" in + published) + echo "Release $TAG is already published and immutable, nothing to do." + exit 0 + ;; + draft) + echo "Draft release $TAG already exists, resuming the upload." + ;; + absent) + gh release create "$TAG" --repo phpstan/turbo-ext --verify-tag --draft \ + --title "$TAG" \ + --notes "Prebuilt \`phpstan_turbo\` binaries for [PIE](https://github.com/php/pie): \`pie install phpstan/turbo\`. Matches [PHPStan $TAG](https://github.com/phpstan/phpstan/releases/tag/$TAG)." + ;; + esac + gh release upload "$TAG" --repo phpstan/turbo-ext --clobber pie-assets/* + gh release edit "$TAG" --repo phpstan/turbo-ext --draft=false + turbo-differential: name: "Run with Turbo Extension (differential tests)" # The differential tests (native vs the PHP implementations of this