Test distribution of turbo-ext#6047
Open
ondrejmirtes wants to merge 10 commits into
Open
Conversation
…x and macOS The turbo-ext workflow moves into phar.yml so the extension is built, tested, and committed to phpstan/phpstan alongside phpstan.phar. The matrix covers PHP 8.4 + 8.5 across linux-gnu (x86_64/arm64, built in ubuntu:22.04 containers via ppa:ondrej/php for a glibc 2.34 symbol floor), linux-musl (alpine:3.23 containers), and a lipo-merged macOS universal binary that is codesigned and notarized on push builds. Every leg runs the differential battery (smoke, signature parity, parser corpus). The commit job stages the binaries into turbo-ext/<target>/phpstan_turbo-<minor>.so gated by a .version file carrying EXPECTED_EXTENSION_VERSION, since native builds are not byte-reproducible; phar staging is now conditional on its checksum so a turbo-only update does not churn the phar. Portability fixes the wider matrix required: - php-parser assigns compat tokens ids counting down from -1 on hosts whose tokenizer lacks them (T_PIPE on 8.4); the dense phpTokenToSymbol conversion sized the map as (zend_ulong) -1 + 1 = 0, failing every parse on PHP 8.4, and the emulative lexer can emit those negative ids into the actual token stream where the LALR loop indexed dropTokens/ phpTokenToSymbol out of bounds. The map is now bias-indexed and the parser corpus gained emulative-lexer snippets that pin the behavior. - zend_weakrefs_hash_destroy() only exists since PHP 8.5; 8.4 builds use an equivalent spelled with the 8.4-available API. - PHP-CPP needs an explicit <cstdint> include on musl, carried as a second local patch; the Makefile phpcpp target applies all patches. - Alpine's gcc warns -Wattributes on zend_vm_opcodes.h's preserve_none; added to the third-party-header pragma guard. - gcc 11 (the glibc-floor compiler) predates P1825 implicit derived-to-base moves, so returning a zv::Arr local from a zv::Val-returning function now spells the move explicitly. - Linux links -static-libstdc++ -static-libgcc so the shipped .so depends only on libc. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013j6ePxd1BobZkr8YVLnNet
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013j6ePxd1BobZkr8YVLnNet
The macos-x86_64/macos-arm64 compile legs, the lipo + notarization job, the macOS run leg, and the macOS staging in the commit job need the Developer ID certificate and App Store Connect API key secrets to be useful. Revert this commit to bring the macOS build back once they are configured; the family-conditional steps in turbo-compile stay in place so the revert applies cleanly. The worker extension selector already treats a missing turbo-ext/macos directory as "no extension available". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013j6ePxd1BobZkr8YVLnNet
TurboExtensionEnabler require_onces turbo-ext/stubs/*.php relative to its own location, so the stub shells must travel with the phar; and the PHPStanTurbo namespace must survive scoping untouched — the stubs extend the native extension's classes by exact name (php-scoper's namespace matching is segment-aware, so the existing PHPStan entry does not cover PHPStanTurbo). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013j6ePxd1BobZkr8YVLnNet
TurboExtensionSelector locates the binary committed next to phpstan.phar (turbo-ext/<platform>/phpstan_turbo-<minor>.so) that matches the current runtime — PHP minor version, OS family, machine architecture, and libc (no constant exists for libc, so musl is detected by the same filesystem heuristic datadog-setup.php uses). ProcessHelper appends -d extension=<path> to the worker command when a matching binary exists, so phar installations get the extension in the analysis workers without any php.ini setup; a source checkout keeps loading its locally built extension through php.ini, which the selector detects and stays out of. Workers run the regular entrypoint, so TurboExtensionEnabler still gates activation on the expected extension version. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013j6ePxd1BobZkr8YVLnNet
…ther tests A turbo-artifact job aggregates the compile matrix outputs into the distribution layout (<target>/phpstan_turbo-<minor>.so, derived from artifact names so matrix changes need no edit here) and uploads them as one turbo-ext-files artifact. The reusable test workflows in phpstan/phpstan download it next to the phar under test, so those runs exercise the freshly built extension through the worker selector. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013j6ePxd1BobZkr8YVLnNet
|
|
||
| - name: "Cache PHP-CPP" | ||
| id: "cache-php-cpp" | ||
| uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 |
| echo "built extension reports: $REPORTED, enabler expects: $EXPECTED" | ||
| [ "$REPORTED" = "$EXPECTED" ] | ||
|
|
||
| - uses: "ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda" # v4.0.0 |
| sudo bash -c "echo 'extension=$GITHUB_WORKSPACE/turbo-ext/phpstan_turbo.so' >> $(php -r 'echo php_ini_loaded_file();')" | ||
| php -m | grep phpstan_turbo | ||
|
|
||
| - uses: "ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda" # v4.0.0 |
Comment on lines
+147
to
+197
| turbo-version: | ||
| name: "Turbo Extension Version Check" | ||
| runs-on: "ubuntu-latest" | ||
| timeout-minutes: 5 | ||
|
|
||
| steps: | ||
| - name: Harden the runner (Audit all outbound calls) | ||
| uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 | ||
| with: | ||
| egress-policy: audit | ||
|
|
||
| - name: "Checkout" | ||
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||
| with: | ||
| persist-credentials: false | ||
| fetch-depth: 0 # git log over turbo-ext/src needs full history | ||
|
|
||
| - name: "Install PHP" | ||
| uses: "shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240" # v2.37.2 | ||
| with: | ||
| coverage: "none" | ||
| php-version: "8.5" | ||
|
|
||
| - name: "Check the shadowed PHP and C++ implementations are in sync (method parity)" | ||
| run: "php turbo-ext/bin/side-by-side.php --check" | ||
|
|
||
| - name: "Check the vendored php-parser matches the version the native parser engine was ported against" | ||
| run: | | ||
| INSTALLED="$(jq -r '.packages[] | select(.name == "nikic/php-parser") | .version' composer.lock)" | ||
| echo "composer.lock: $INSTALLED, native engine ported against: $SUPPORTED_PHP_PARSER_VERSION" | ||
| if [ "$INSTALLED" != "$SUPPORTED_PHP_PARSER_VERSION" ]; then | ||
| echo "::error::nikic/php-parser was updated to $INSTALLED but turbo-ext/src/parser/ was ported against $SUPPORTED_PHP_PARSER_VERSION." | ||
| echo "::error::Update procedure: diff ParserAbstract.php helpers between the two versions (grammar tables need nothing — they are read at run time), run turbo-ext/bin/generate-parser-actions.php and port any flagged closure bodies via action-overrides/, run turbo-ext/tests/parser-corpus.php until byte-identical, run the full suite, then bump SUPPORTED_PHP_PARSER_VERSION here and the extension version pin." | ||
| exit 1 | ||
| fi | ||
|
|
||
| - name: "Check TurboExtensionEnabler::EXPECTED_EXTENSION_VERSION against the last commit touching turbo-ext/src or a shadowed PHP class" | ||
| run: | | ||
| # A change to either side of a shadowed pair must bump the version: | ||
| # watch turbo-ext/src plus the repo-local PHP files from the manifest | ||
| # (vendored entries are governed by composer.lock instead). | ||
| mapfile -t MAPPED_PHP < <(jq -r '.[] | select(.vendored != true) | .php' turbo-ext/shadowed-classes.json) | ||
| EXPECTED_SHA="$(git log -1 --format=%H -- turbo-ext/src "${MAPPED_PHP[@]}" | cut -c1-7)" | ||
| ENABLER="$(sed -n "s/.*EXPECTED_EXTENSION_VERSION = '\([^']*\)'.*/\1/p" src/Turbo/TurboExtensionEnabler.php)" | ||
| echo "last commit touching turbo-ext/src or a shadowed PHP class: $EXPECTED_SHA" | ||
| echo "TurboExtensionEnabler::EXPECTED_EXTENSION_VERSION: $ENABLER" | ||
| if [ "$ENABLER" != "$EXPECTED_SHA" ]; then | ||
| echo "::error::TurboExtensionEnabler::EXPECTED_EXTENSION_VERSION must be the short SHA of the last commit touching turbo-ext/src/ or a PHP class listed in turbo-ext/shadowed-classes.json." | ||
| echo "::error::After changing either side, verify the implementations still match and add a follow-up commit setting the constant to that commit's short SHA (the binary bakes its version from git at build time)." | ||
| exit 1 | ||
| fi |
Comment on lines
+355
to
+392
| turbo-artifact: | ||
| name: "Aggregate Turbo Extension Artifact" | ||
| needs: "turbo-compile" | ||
| runs-on: "ubuntu-latest" | ||
| timeout-minutes: 5 | ||
|
|
||
| steps: | ||
| - name: Harden the runner (Audit all outbound calls) | ||
| uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 | ||
| with: | ||
| egress-policy: audit | ||
|
|
||
| - name: "Download extension artifacts" | ||
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | ||
| with: | ||
| pattern: "phpstan_turbo-*" | ||
| path: "turbo-artifacts" | ||
|
|
||
| - name: "Arrange the distribution layout" | ||
| # phpstan_turbo-<target>-php<minor> → <target>/phpstan_turbo-<minor>.so, | ||
| # derived from the artifact names so new targets need no change here. | ||
| # The integration/extension/other test workflows in phpstan/phpstan | ||
| # download this artifact next to the phar under test. | ||
| run: | | ||
| for dir in turbo-artifacts/phpstan_turbo-*; do | ||
| name="${dir#turbo-artifacts/phpstan_turbo-}" | ||
| target="${name%-php*}" | ||
| minor="${name##*-php}" | ||
| install -D -m 644 "$dir/phpstan_turbo.so" "turbo-ext-dist/$target/phpstan_turbo-$minor.so" | ||
| done | ||
| find turbo-ext-dist -type f | sort | ||
|
|
||
| - name: "Upload aggregated artifact" | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||
| with: | ||
| name: "turbo-ext-files" | ||
| path: "turbo-ext-dist" | ||
| if-no-files-found: "error" |
Comment on lines
+394
to
+457
| turbo-run: | ||
| name: "Run with Turbo Extension" | ||
| needs: "turbo-compile" | ||
| runs-on: ${{ matrix.setup.operating-system }} | ||
| timeout-minutes: 60 | ||
|
|
||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| setup: | ||
| - operating-system: "ubuntu-latest" | ||
| artifact: "phpstan_turbo-linux-gnu-x86_64-php8.5" | ||
| script: ["make tests", "make phpstan"] | ||
|
|
||
| steps: | ||
| - name: Harden the runner (Audit all outbound calls) | ||
| uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 | ||
| with: | ||
| egress-policy: audit | ||
|
|
||
| - name: "Checkout" | ||
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||
| with: | ||
| persist-credentials: false | ||
|
|
||
| - name: "Install PHP" | ||
| uses: "shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240" # v2.37.2 | ||
| with: | ||
| coverage: "none" | ||
| php-version: "8.5" | ||
|
|
||
| - name: "Download extension artifact" | ||
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | ||
| with: | ||
| name: "${{ matrix.setup.artifact }}" | ||
| path: "turbo-ext" | ||
|
|
||
| - name: "Install extension" | ||
| run: | | ||
| sudo bash -c "echo 'extension=$GITHUB_WORKSPACE/turbo-ext/phpstan_turbo.so' >> $(php -r 'echo php_ini_loaded_file();')" | ||
| php -m | grep phpstan_turbo | ||
|
|
||
| - uses: "ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda" # v4.0.0 | ||
|
|
||
| - name: "Verify the extension is active" | ||
| run: | | ||
| php -r ' | ||
| require "src/Turbo/TurboExtensionEnabler.php"; | ||
| PHPStan\Turbo\TurboExtensionEnabler::enableIfLoaded(); | ||
| require "vendor/autoload.php"; | ||
| if (!(new ReflectionClass("PHPStan\\Analyser\\ScopeOps"))->getParentClass() || !(new ReflectionClass("PHPStan\\Analyser\\ScopeOps"))->getParentClass()->isInternal()) { | ||
| fwrite(STDERR, "turbo extension is not shadowing ScopeOps" . PHP_EOL); | ||
| exit(1); | ||
| } | ||
| ' | ||
|
|
||
| - name: "Run" | ||
| # The memo's per-worker footprint grows with files-per-worker; macOS | ||
| # runners have few cores, so each of their few workers analyses enough | ||
| # files to tip the default 450M limit that a many-worker Linux runner | ||
| # stays under. | ||
| env: | ||
| PHPSTAN_MEMORY_LIMIT: ${{ matrix.setup.operating-system == 'macos-latest' && '1G' || '450M' }} | ||
| run: ${{ matrix.script }} |
| # stays under. | ||
| env: | ||
| PHPSTAN_MEMORY_LIMIT: ${{ matrix.setup.operating-system == 'macos-latest' && '1G' || '450M' }} | ||
| run: ${{ matrix.script }} |
| turbo-run: | ||
| name: "Run with Turbo Extension" | ||
| needs: "turbo-compile" | ||
| runs-on: ${{ matrix.setup.operating-system }} |
| CURRENT="$(cat phpstan-dist/turbo-ext/.version 2>/dev/null || echo none)" | ||
| echo "enabler expects: $EXPECTED, dist carries: $CURRENT" | ||
| if [[ "$EXPECTED" != "$CURRENT" ]]; then | ||
| echo "result=different" >> "$GITHUB_OUTPUT" |
| if [[ "$EXPECTED" != "$CURRENT" ]]; then | ||
| echo "result=different" >> "$GITHUB_OUTPUT" | ||
| else | ||
| echo "result=same" >> "$GITHUB_OUTPUT" |
| else | ||
| echo "result=same" >> "$GITHUB_OUTPUT" | ||
| fi | ||
| echo "version=$EXPECTED" >> "$GITHUB_OUTPUT" |
The turbo compile legs run in containers where the workspace volume is owned by the host runner user while steps run as the container user; git then refuses the repository with "detected dubious ownership" and the Makefile's $(shell git log ...) version baking silently degrades to "dev", failing the expected-version verification. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013j6ePxd1BobZkr8YVLnNet
|
|
||
| turbo-compile: | ||
| name: "Compile Turbo Extension" | ||
| runs-on: ${{ matrix.target.runs-on }} |
Comment on lines
+199
to
+361
| turbo-compile: | ||
| name: "Compile Turbo Extension" | ||
| runs-on: ${{ matrix.target.runs-on }} | ||
| # Container legs pin the built binaries' userland floor independently of | ||
| # the runner image: ubuntu:24.04 sets the glibc symbol floor (2.39 — | ||
| # Ubuntu 24.04+, Debian 13+; swap in an older container to lower it), | ||
| # alpine:3.24 builds the musl variants. | ||
| container: ${{ matrix.target.container }} | ||
| timeout-minutes: 30 | ||
|
|
||
| env: | ||
| PHP_MINOR: ${{ matrix.php-version }} | ||
|
|
||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| php-version: ["8.4", "8.5"] | ||
| target: | ||
| - name: "linux-gnu-x86_64" | ||
| runs-on: "ubuntu-latest" | ||
| container: "ubuntu:24.04" | ||
| family: "gnu" | ||
| artifact: "phpstan_turbo" | ||
| - name: "linux-gnu-arm64" | ||
| runs-on: "ubuntu-24.04-arm" | ||
| container: "ubuntu:24.04" | ||
| family: "gnu" | ||
| artifact: "phpstan_turbo" | ||
| - name: "linux-musl-x86_64" | ||
| runs-on: "ubuntu-latest" | ||
| container: "alpine:3.24" | ||
| family: "musl" | ||
| artifact: "phpstan_turbo" | ||
| - name: "linux-musl-arm64" | ||
| runs-on: "ubuntu-24.04-arm" | ||
| container: "alpine:3.24" | ||
| family: "musl" | ||
| artifact: "phpstan_turbo" | ||
|
|
||
| steps: | ||
| # Container legs install PHP and tooling before checkout: actions/checkout | ||
| # needs git inside the container, and the build needs php/php-config | ||
| # symlinks pointing at the matrix PHP version. | ||
| - name: "Install PHP and build tools (Ubuntu container)" | ||
| if: matrix.target.family == 'gnu' | ||
| run: | | ||
| export DEBIAN_FRONTEND=noninteractive | ||
| apt-get update | ||
| apt-get install -y software-properties-common gnupg ca-certificates git make g++ patch zstd | ||
| add-apt-repository -y ppa:ondrej/php | ||
| apt-get install -y "php$PHP_MINOR-cli" "php$PHP_MINOR-dev" "php$PHP_MINOR-mbstring" "php$PHP_MINOR-xml" composer | ||
| ln -sf "/usr/bin/php$PHP_MINOR" /usr/local/bin/php | ||
| ln -sf "/usr/bin/php-config$PHP_MINOR" /usr/local/bin/php-config | ||
|
|
||
| - name: "Install PHP and build tools (Alpine container)" | ||
| if: matrix.target.family == 'musl' | ||
| run: | | ||
| V="$(echo "$PHP_MINOR" | tr -d .)" | ||
| apk add --no-cache bash git make g++ musl-dev linux-headers patch tar zstd composer \ | ||
| "php$V" "php$V-dev" "php$V-ctype" "php$V-mbstring" "php$V-tokenizer" \ | ||
| "php$V-iconv" "php$V-openssl" "php$V-phar" "php$V-dom" "php$V-xml" \ | ||
| "php$V-xmlwriter" "php$V-simplexml" | ||
| ln -sf "/usr/bin/php$V" /usr/local/bin/php | ||
| ln -sf "/usr/bin/php-config$V" /usr/local/bin/php-config | ||
|
|
||
| - name: "Checkout" | ||
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||
| with: | ||
| persist-credentials: false | ||
| # The Makefile bakes the extension version via git log over the | ||
| # watched paths; a shallow clone would resolve every build to the | ||
| # checked-out commit itself — on pull requests the synthetic merge | ||
| # commit, which matches nothing. | ||
| fetch-depth: 0 | ||
|
|
||
| # In container legs the workspace volume is owned by the host runner | ||
| # user while steps run as the container user, so git refuses the repo | ||
| # ("dubious ownership") — which would silently bake the version as | ||
| # "dev" via the Makefile's $(shell git log ...) fallback. | ||
| - name: "Trust the checkout despite the container/host uid mismatch" | ||
| run: git config --global --add safe.directory "$GITHUB_WORKSPACE" | ||
|
|
||
| - name: "Install PHP (macOS)" | ||
| if: matrix.target.family == 'macos' | ||
| uses: "shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240" # v2.37.2 | ||
| with: | ||
| coverage: "none" | ||
| php-version: "${{ matrix.php-version }}" | ||
|
|
||
| - name: "Cache PHP-CPP" | ||
| id: "cache-php-cpp" | ||
| uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 | ||
| with: | ||
| path: "turbo-ext/PHP-CPP" | ||
| key: "php-cpp-${{ env.PHP_CPP_COMMIT }}-php-${{ matrix.php-version }}-${{ matrix.target.name }}-${{ matrix.target.container }}-${{ hashFiles('turbo-ext/patches/*.patch') }}" | ||
|
|
||
| - name: "Clone and build PHP-CPP" | ||
| if: steps.cache-php-cpp.outputs.cache-hit != 'true' | ||
| working-directory: "turbo-ext" | ||
| # `make phpcpp` applies the local patches from turbo-ext/patches/ | ||
| # before building. | ||
| run: | | ||
| git clone https://github.com/CopernicaMarketingSoftware/PHP-CPP.git PHP-CPP | ||
| git -C PHP-CPP checkout "$PHP_CPP_COMMIT" | ||
| ln -sfn include PHP-CPP/phpcpp | ||
| make phpcpp -j"$(getconf _NPROCESSORS_ONLN 2>/dev/null || nproc)" | ||
|
|
||
| - name: "Compile phpstan_turbo with strict warnings" | ||
| working-directory: "turbo-ext" | ||
| # Exemptions, each caused by third-party macro expansions, not our code: | ||
| # -Wno-assume: zend's parameter-parsing macros expand __builtin_assume | ||
| # with (potential) side effects | ||
| # -Wno-unused-parameter: PHP_METHOD's fixed signature | ||
| # (execute_data/return_value are not used by every method) | ||
| # -Wno-unicode: zend arginfo macros stringify namespaced class names; | ||
| # clang lexes the \N in "PhpParser\NodeVisitor" as a universal | ||
| # character name (GCC ignores the unknown -Wno- flag) | ||
| run: | | ||
| ln -sfn include PHP-CPP/phpcpp | ||
| make WARN_FLAGS="-Wall -Wextra -Werror -Wno-assume -Wno-unused-parameter -Wno-unicode" -j"$(getconf _NPROCESSORS_ONLN 2>/dev/null || nproc)" | ||
|
|
||
| - name: "Verify the built extension reports the expected version" | ||
| run: | | ||
| REPORTED="$(php -d extension="$PWD/turbo-ext/phpstan_turbo.so" -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" | ||
| [ "$REPORTED" = "$EXPECTED" ] | ||
|
|
||
| - uses: "ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda" # v4.0.0 | ||
| if: matrix.target.family == 'macos' | ||
|
|
||
| - name: "Install Composer dependencies (containers)" | ||
| if: matrix.target.family != 'macos' | ||
| # The distro composer wrapper runs under the distro-default PHP, not | ||
| # the matrix one — fine here: installing from composer.lock is | ||
| # PHP-version-independent, and platform requirements are ignored | ||
| # because they would be checked against the wrapper's PHP; the matrix | ||
| # PHP running the tests below has all the required extensions and | ||
| # fails loudly if one goes missing. (Never `php /usr/bin/composer` — | ||
| # on Alpine that is a shell script, which php would happily "run" by | ||
| # printing it and exiting 0 without installing anything.) | ||
| run: composer install --no-interaction --no-progress --ignore-platform-reqs | ||
|
|
||
| - name: "Smoke test (differential: native vs PHP implementations)" | ||
| run: php -d extension="$PWD/turbo-ext/phpstan_turbo.so" turbo-ext/tests/smoke.php | ||
|
|
||
| - name: "Signature parity (reflect native classes against the PHP twins)" | ||
| run: php -d extension="$PWD/turbo-ext/phpstan_turbo.so" turbo-ext/tests/signature-parity.php | ||
|
|
||
| - name: "Parser actions are generated (regenerate and diff)" | ||
| run: | | ||
| php turbo-ext/bin/generate-parser-actions.php | ||
| git diff --exit-code turbo-ext/src/parser/ | ||
|
|
||
| - name: "Parser corpus (differential: native vs PHP ASTs must be byte-identical)" | ||
| run: php -d extension="$PWD/turbo-ext/phpstan_turbo.so" -d memory_limit=4G turbo-ext/tests/parser-corpus.php | ||
|
|
||
| - name: "Upload extension artifact" | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||
| with: | ||
| name: "${{ matrix.target.artifact }}-${{ matrix.target.name }}-php${{ matrix.php-version }}" | ||
| path: "turbo-ext/phpstan_turbo.so" | ||
| if-no-files-found: "error" |
The generated preload.php required every src and php-parser file by path, but when the turbo extension is active the enabler has already declared the shadowed classes through their stubs — the worker then fataled with "Cannot redeclare class PHPStan\Analyser\ ConditionalExpressionHolder". The exclusion list comes from turbo-ext/shadowed-classes.json, so new shadowed pairs are picked up automatically; without the extension the skipped classes load lazily through the autoloader. Caught by an end-to-end run of the compiled phar with the extension binaries laid out next to it: the analysis workers received the -d extension= flag from the selector, activated the extension, and hit the redeclaration on the preload path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013j6ePxd1BobZkr8YVLnNet
… images Running the distro composer through `php` broke both distros: Alpine's /usr/bin/composer is a shell wrapper, which php happily "executes" by printing its text and exiting 0 — leaving no vendor directory for the smoke test — and jammy's composer 2.2.6 fatals under PHP 8.5 in its stream code. The step now runs the distro wrapper as intended (its own PHP carries everything composer needs), with platform requirements ignored since they would be checked against the wrapper's PHP rather than the matrix PHP that runs the tests. The containers also need the patch binary for cweagans/composer-patches — GitHub's VM runners have it preinstalled, minimal images do not. Containers move to ubuntu:24.04 (composer 2.7; raises the shipped gnu binaries' glibc symbol floor to 2.39 — swap in an older container to lower it again) and alpine:3.24, and the PHP-CPP cache key includes the container image so an image bump cannot reuse a stale build. Verified by replicating both container legs locally end to end: packages, composer install with the patches applied, PHP-CPP + strict extension build, smoke and signature parity. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013j6ePxd1BobZkr8YVLnNet
…4 leg Back on jammy the shipped gnu binaries keep their glibc 2.34 symbol floor (RHEL 9, Ubuntu 22.04+, Debian 12+), but its distro composer 2.2.6 fatals under PHP 8.5 — so the container legs now download a checksum-pinned composer.phar and run it under the matrix PHP, which also restores proper platform-requirements checking. The containers additionally need unzip (jammy; without it composer falls back to git-source installs and fails on pinned commits — Alpine's busybox ships one) next to the patch binary — tools GitHub's VM runners preinstall but minimal images lack. The linux-musl-arm64 leg is removed: JavaScript-based actions cannot run in Alpine containers on arm64 runners, where the runner only ships an x64 musl Node. The selector treats the missing binary as "no extension available". Both container legs were replicated locally end to end (packages, pinned composer install with patches applied, PHP-CPP + strict build, smoke, signature parity); the jammy binary's maximum required symbol version is GLIBC_2.34. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013j6ePxd1BobZkr8YVLnNet
| fetch-depth: 0 # git log over turbo-ext/src needs full history | ||
|
|
||
| - name: "Install PHP" | ||
| uses: "shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240" # v2.37.2 |
|
|
||
| - name: "Install PHP (macOS)" | ||
| if: matrix.target.family == 'macos' | ||
| uses: "shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240" # v2.37.2 |
| echo "built extension reports: $REPORTED, enabler expects: $EXPECTED" | ||
| [ "$REPORTED" = "$EXPECTED" ] | ||
|
|
||
| - uses: "ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda" # v4.0.0 |
| persist-credentials: false | ||
|
|
||
| - name: "Install PHP" | ||
| uses: "shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240" # v2.37.2 |
| sudo bash -c "echo 'extension=$GITHUB_WORKSPACE/turbo-ext/phpstan_turbo.so' >> $(php -r 'echo php_ini_loaded_file();')" | ||
| php -m | grep phpstan_turbo | ||
|
|
||
| - uses: "ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda" # v4.0.0 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.