Skip to content

Repair wp/5.9 branch Actions: Pass the environment to the performance test run - #662

Closed
desrosj wants to merge 52 commits into
wp/5.9from
fix-actions-5.9/pass-env-to-performance-tests
Closed

desrosj wants to merge 52 commits into
wp/5.9from
fix-actions-5.9/pass-env-to-performance-tests

Conversation

@desrosj

@desrosj desrosj commented Oct 7, 2026 •

Copy link
Copy Markdown
Owner

What?

Passes the job's environment, including CI, to the performance test run: runTestSuite() in bin/plugin/commands/performance.js now calls runShellScript() with { ...process.env }.

Why?

The Compare performance with base branch step hangs: it goes silent after >> Running the test. and runs until the job is cancelled. runShellScript (bin/plugin/lib/utils.js) gives the child process only NO_CHECKS, PATH and HOME, so CI never reaches the test run, and jest-environment-puppeteer launches Chromium without its CI options (--no-sandbox and others) that the end-to-end jobs get. This branch's Puppeteer Chromium (92) then loses its GPU process to the seccomp sandbox (exit_code=159, SIGSYS) and exits with GPU process isn't usable. Goodbye. (run 37573757667). This branch's Puppeteer doesn't notice the browser is gone, and Jest's environment setup has no timeout, so the job waits until it's cancelled.

With CI passed through, the post-editor performance tests on this branch launch and pass (run 37599766928, from a throwaway debug branch that made the same change). The comparison still fails afterwards when it builds or starts the base branch (wp/5.9), which doesn't have this stack's fixes; that is a separate problem, not addressed here.

How?

Backports the environment part of trunk's #48684 ("Refactor performance tests artifacts handling", WordPress@2cf161f), which passes ...process.env to the test run's runShellScript() call; wp/6.3 and newer have it. The rest of WordPress#48684 (artifact handling) isn't needed. Only the test run gets the environment; building the branches and starting wp-env are unchanged. node --check passes on the edited files.

Use of AI Tools

This PR was created by Claude Code under my supervision. All code should be treated as AI-produced and not yet reviewed by a human until this PR is marked Ready for Review.

🤖 Generated with Claude Code

desrosj and others added 30 commits September 22, 2026 16:14
React Native mobile support is no longer maintained or shipped as
part of Gutenberg. `rnmobile-android-runner.yml` still ran live
(no disabling condition) on `macos-latest`; `rnmobile-ios-runner.yml`
was already hardcoded `if: false` and targeted `macos-11`, a runner
image GitHub has since retired. Neither provides any signal worth
keeping CI resources on for a frozen, security-maintenance-only
branch.
`storybook-pages.yml` triggers only on `push` to `trunk` and its
single job explicitly checks out `ref: trunk` before building and
deploying Storybook to GitHub Pages. It never builds or deploys
anything from `wp/5.9`, and the GitHub Pages target it publishes to
is trunk's own deployment, not something this branch could safely
publish over. There is nothing for this branch's copy of the file to
do.
`upload-release-to-plugin-repo.yml` triggers only on the `release`
event. GitHub Actions only ever executes a `release`-triggered
workflow using the copy of the file that exists on the repository's
default branch, regardless of which branch a release is tagged from.
`wp/5.9`'s own copy of this file can never run — trunk's copy is
what always executes, publishing whatever release trunk itself cuts.
Keeping a dead copy on this branch serves no purpose.
stale-issue-add-needs-testing.yml, stale-issue-mark-stale.yml, and
stale-issue-needs-info.yml all trigger only on schedule. GitHub
Actions only ever runs a schedule-triggered workflow using the copy
of the file on the repository's default branch. wp/5.9's own copies
never execute, so removing them has no effect on the actual
issue-gardening automation, which continues to run from trunk.
flaky-tests.yml's only trigger is workflow_run, listening for the
End-to-End Tests workflow to complete. GitHub Actions only ever runs
a workflow_run-triggered workflow using the copy of the file that
exists on the repository's default branch, regardless of which
branch the completed workflow ran on. wp/5.9's own copy of this file
can never execute, so removing it has no effect on flaky-test
reporting, which continues to run from trunk's copy.

The custom .github/report-flaky-tests action this workflow called is
left in place, now orphaned, as optional cleanup out of scope for
this fix.
…ch filter, so PRs opened against this branch still get fixed-issue assignment and first-time-contributor labeling.
`actions/setup-node`'s `package-manager-cache` input defaults to `true`,
so caching can switch on implicitly -- it engages as soon as
`package.json` gains a `packageManager` (or `devEngines.packageManager`)
field naming npm. Relying on that field's absence leaves the caching
behaviour of release workflows dependent on unrelated packaging
metadata.

Every `actions/setup-node` step in the workflows that build and publish
release artifacts now sets `package-manager-cache: false` explicitly,
and the `build` job's `cache: npm` is dropped where present.

Cache poisoning is an attack in which a release workflow picks up build
state cached by an earlier, less-trusted run. These jobs now always
install from the lockfile, so a poisoned cache cannot reach a published
plugin ZIP or npm package.

Ports WordPress#79835, "Disable caching when publishing built
assets", adapted: upstream routes these jobs through the composite
`.github/setup-node` action and passes `cache: false`; this branch calls
`actions/setup-node` directly, so the equivalent input is set on each
step instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

# Conflicts:
#	.github/workflows/build-plugin-zip.yml
Updates every occurrence of actions/checkout across the .github/workflows
tree from v2.3.4 to the latest release, v7.0.1, pinned to its
full-length commit SHA.
Updates every occurrence of actions/setup-node across the .github/workflows
tree from v2.2.2 to the latest release, v7.0.0, pinned to its
full-length commit SHA.
pull-request-automation.yml is the only workflow on this branch that uses actions/cache, and it was never touched by this layer's other per-action bump commits (the file was present but simply missed). Updates its lone occurrence from v2.1.6 to the latest release, v6.1.0, pinned to its full-length commit SHA.
Updates both occurrences of actions/upload-artifact, in end2end-test.yml
and build-plugin-zip.yml, from v2.2.2 to the latest release, v7.0.1,
pinned to its full-length commit SHA.
The only occurrences of actions/download-artifact, both in
build-plugin-zip.yml, are updated from v2.0.8 to the latest release,
v8.0.1, pinned to its full-length commit SHA.
The only occurrence, in bundle-size.yml, is updated from v2.2.0 to
the latest release, v2.10.0, pinned to its full-length commit SHA.
The only occurrence, in performance.yml, was pinned to the bare
version tag 0.3.0 with no SHA. Updated to the latest release, v9.0.0,
pinned to its full-length commit SHA with a version comment, matching
every other action pin in the tree.

The step this action powers only runs when github.event_name == 'push',
and performance.yml's push trigger is scoped to branches: [trunk], so
this step never actually executes on wp/5.9 itself; the bump has no
behavioral effect on this branch but keeps the pin consistent and
current.
zizmor's ref-version-mismatch audit found that the pinned SHA for
preactjs/compressed-size-action in bundle-size.yml is tagged 2.10.0
upstream, but the trailing comment read # v2.10.0 (with a leading v
that the actual tag doesn't have). Corrects the comment to match the
real tag so it accurately documents which release the pinned commit
corresponds to. The pinned SHA itself is unchanged.

Corrected file:
- .github/workflows/bundle-size.yml
Ports upstream trunk PR WordPress#82744
(head commit 3fe3bd4 as of this writing; the PR
is still open/unmerged), "Workflows: Refine and complete the push and pull_request
branch filters":

- Narrows/widens `push:` branch filters on unit-test/end2end-test/static-checks/
  build-plugin-zip/create-block/workflow-lint (where present) to the precise
  upstream set (trunk, release/X.Y, wp/X.Y, wp/latest, wp/next), fixing workflows
  that previously never ran on a direct push to this branch.

Per the current version of that upstream PR, `pull_request:` triggers are
deliberately left unfiltered everywhere (a PR should get the same checks
regardless of which branch it targets) — the one exception upstream carves out is
`workflow-lint.yml`, where a previous trunk-only `pull_request` filter is removed
entirely rather than widened.

This supersedes this project's earlier port of an older revision of WordPress#82744, which
had added an explicit `pull_request` branch allowlist (plus a project-local
temporary `fix-actions-[0-9]+.[0-9]/**` pattern) to several files. That's been
reverted here to match the upstream author's updated approach — since
`pull_request` now stays unfiltered, the temporary project-local pattern is no
longer needed at all.
…h-triggered tasks (`add-milestone`, `first-time-contributor-account-link`) both bail out immediately outside of `trunk`, so running them here on every push does nothing.
`unit-test.yml`'s `mobile-unit-js` job (named `Mobile`) runs
`npm run native test`, which delegates to the
`packages/react-native-editor` package's own Jest suite via
`test/native/jest.config.js`. It is not disabled on this branch and
currently passes; it is being removed for scope and consistency, not
because it is broken.

React Native mobile support is no longer maintained or shipped as
part of Gutenberg, and the React Native workflows that accompanied it
(`rnmobile-android-runner.yml` and `rnmobile-ios-runner.yml`) were
already removed earlier in this series as not belonging on a frozen,
security-maintenance-only branch. This job is the last remaining piece
of that testing surface in CI, and keeping it spends runner time on a
platform this branch will never ship a fix for.

No other job depends on it: nothing in `.github/` references
`mobile-unit-js`, and it declares no `outputs` consumed elsewhere. The
`unit-js` (JavaScript) and `unit-php` (PHP) jobs are unaffected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtSAAJkQ9P6vPtDumqStg8

# Conflicts:
#	.github/workflows/unit-test.yml
The `pull_request`-triggered step in `performance.yml` hardcoded `trunk` as
the comparison branch. On a frozen branch like `wp/5.9` that is always the
wrong target: every pull request opened against `wp/5.9` was compared
against live upstream `trunk` rather than against its own base, so the job
had to build and install current `trunk`'s Gutenberg code using this
branch's pinned Node 14.21.3 toolchain. Current `trunk` requires Node
`>=24.18.0`, so the comparison branch could not even be installed and the
step failed outright before any measurement was taken.

Using the base branch instead of a hardcoded one is the fix upstream
applied in `WordPress#66196` (`ab467645855eef40b1570e5bd0a8db03aa62dad6`),
which renamed the step to `Compare performance with base branch` and
replaced `trunk` with the base ref. That commit does not apply cleanly to
this branch, whose `run:` line predates the `--ci` flag's introduction
elsewhere in the file and has different surrounding steps, so the same
change is made by hand here.

The base ref is read through the `GITHUB_BASE_REF` environment variable
rather than the `${{ github.base_ref }}` expression. Workflow expressions
are substituted into the script text before the shell runs, so a branch
name is interpolated as code; the environment variable form passes it as
data instead. The existing `$GITHUB_SHA` references are quoted at the same
time so every argument on the line is passed the same way, matching the
`release`-triggered step directly below it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtSAAJkQ9P6vPtDumqStg8
While `.wp-env.json` pins Core as `WordPress/WordPress#5.9-branch`, the
performance testing workflow builds its own, separate `.wp-env.json` file
for each run and only pins a specific version of Core when a
`--wp-version` argument is explicitly passed. The `pull_request`-triggered
step never passed one, so it silently fell back to unpinned, current
`trunk` Core.

Running a frozen branch's performance suite against a Core version years
newer than the branch was written for fails on architectural drift rather
than on any real performance change, so the version is pinned to the `5.9`
this branch targets. `https://wordpress.org/wordpress-5.9.zip`, the archive
`wp-env` downloads for that value, is confirmed to still be served.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtSAAJkQ9P6vPtDumqStg8
The `performance` job is gated behind
`if: ${{ github.repository == 'WordPress/gutenberg' }}`, so it is skipped
on every run in `desrosj/gutenberg`. That makes the job impossible to
exercise or debug while the stack is being validated here.

Removing the guard lets the job actually run on the fork so its failures
are visible.

*** DO NOT PORT THIS COMMIT UPSTREAM. *** It exists only so the stack can
be validated in the fork. The guard is correct on `WordPress/gutenberg`:
without it the job would run in every contributor's fork. Drop this commit
when migrating the stack to `WordPress/gutenberg`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The performance CLI clones `WordPress/gutenberg` (`gitRepoOwner` in
`bin/plugin/config.js`) and then fetches or checks out the branches and
commits it compares. On `desrosj/gutenberg`, a stacked pull request's
base branch and head commit only exist on the fork, so the run fails
with `couldn't find remote ref` or `not our ref`.

Reading the owner from `GITHUB_REPOSITORY_OWNER` makes the CLI clone the
repository the workflow runs in, falling back to `WordPress` when the
variable isn't set.

*** DO NOT PORT THIS COMMIT UPSTREAM. *** It exists only so the stack can
be validated in the fork. Drop this commit when migrating the stack to
`WordPress/gutenberg`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DtSAAJkQ9P6vPtDumqStg8
….yml.

unit-js tested against both Node 12 and 14 via a matrix, while
unit-php and mobile-unit-js each hardcoded node-version: 14 directly.
This branch is frozen and will never move off the Node version pinned
in .nvmrc, so testing an extra, older Node version (12) adds CI time
without adding real coverage, and hardcoding the same version number
in three separate places invites drift if .nvmrc is ever updated
without updating every workflow in step.

All three jobs now resolve their Node version from node-version-file:
'.nvmrc' — a single source of truth — instead of a matrix or a
hardcoded literal.

# Conflicts:
#	.github/workflows/unit-test.yml
The checks job tested against both Node 12 and 14 via a matrix. This
branch is frozen and will never move off the Node version pinned in
.nvmrc, so testing an extra, older Node version adds CI time without
adding real coverage.

Resolves the Node version from node-version-file: '.nvmrc' instead.
The admin job's matrix combined a real, meaningful dimension (part,
splitting the e2e suite into four parallel shards) with a Node
version dimension that only ever had one value ('14'). This branch is
frozen and will never move off the Node version pinned in .nvmrc, so
the node dimension added no coverage, just an unnecessary layer of
matrix indirection.

The part dimension is preserved; the Node version now resolves from
node-version-file: '.nvmrc' directly.
The build job's matrix had exactly one Node value ('14'), matching
.nvmrc, so it provided no real coverage — only indirection. Resolves
the Node version from node-version-file: '.nvmrc' directly instead.
The check job hardcoded node-version: 14, duplicating .nvmrc's value
in a second place that could silently drift from it. Resolves the
Node version from node-version-file: '.nvmrc' directly instead.
The performance job hardcoded node-version: 14, duplicating .nvmrc's
value in a second place that could silently drift from it. Resolves
the Node version from node-version-file: '.nvmrc' directly instead.
The build job hardcoded node-version: 14, duplicating .nvmrc's value
in a second place that could silently drift from it. Resolves the
Node version from node-version-file: '.nvmrc' directly instead.

# Conflicts:
#	.github/workflows/build-plugin-zip.yml
Replaces the floating `ubuntu-latest` runner label with a pinned version,
matching upstream trunk's own migration: `ubuntu-latest` -> `ubuntu-24.04`.
No `windows-latest` or `macos-latest` occurrences exist in this branch's
current workflow files (the one known prior `macos-latest` occurrence, in
`rnmobile-android-runner.yml`, was already removed by an earlier layer in
this stack), so only the `ubuntu-latest` substitution applies here.

Ports upstream trunk PR WordPress#71201
(commit WordPress@82768d0),
"Avoid using `*-latest` for runner images", merged 2025-08-28.

# Conflicts:
#	.github/workflows/unit-test.yml

# Conflicts:
#	.github/workflows/performance.yml
`packages/env` shells out to a standalone `docker-compose` binary (Docker
Compose v1) via the `docker-compose` npm package's default, non-`.v2`
export, and `packages/env/lib/commands/run.js` spawns the `docker-compose`
binary directly. GitHub's current runner images no longer ship that
standalone binary — only the `docker compose` v2 subcommand, provided by
the `docker` CLI plugin, is present. Every `wp-env` command fails
immediately with `Error: spawn docker-compose ENOENT` as a result, which
is the root cause of essentially all e2e and PHP-unit-test CI failures
that depend on `wp-env start`.

This is the same fix trunk applied in `a0943f2fba4a5df0e5ee051bfc567b7f6f7f1942`
("wp-env: Migrate to Compose V2 (WordPress#51339)", closing
WordPress#51249): switch every
`require( 'docker-compose' )` call to destructure the package's `.v2`
export instead of the default export, and change `run.js`'s direct
`spawn()` call from the `docker-compose` binary to `docker compose`
(prepending `compose` to the argument list and spawning `docker`).
`packages/env/lib/cli.js`'s error-handling comment/message is updated to
match, along with its unit test in `packages/env/test/cli.js`, which
asserted the old exact string.

The `docker-compose` npm package only exposes a `.v2` export starting at
`0.23.x`; this branch was pinned to `^0.22.2`, which predates it, so the
dependency is bumped to `^0.24.3` (the same target version trunk's fix
used) in both `packages/env/package.json` and `package-lock.json`.
`docker-compose@0.24.3` itself depends on `yaml@^2.2.2`, which conflicts
with the `yaml@1.7.2` already hoisted elsewhere in the lockfile, so that
dependency is recorded as a nested entry under `docker-compose` rather
than hoisted, consistent with how `lockfileVersion: 1` resolves version
conflicts.

`run.js` on this branch invokes the `run`/`--rm` compose subcommand
rather than the `exec` subcommand trunk's version used at the time of its
fix; that difference is preserved here rather than copied from trunk's
diff literally.

`yaml@2.x` is an ESM package, and Jest's default configuration excludes
`node_modules` from its transforms unless a package is explicitly
allow-listed, so unit tests fail with "Jest encountered an unexpected
token" the moment `docker-compose`'s own test suite (or anything else in
`packages/env`) requires it transitively. `test/unit/jest.config.js`'s
`transformIgnorePatterns` is extended to allow-list both `docker-compose`
and `yaml` so Jest can parse them.
Previously, the `unit-php` job ran on whatever PHP version the
untagged `wordpress`/`wordpress:cli` Docker images happened to
default to, and always ran both the single-site and multisite suites
sequentially in the same job run. This meant no PHP version was
actually pinned or verified, and a failure in one suite could mask or
be masked by the other.

Add a `strategy.matrix` testing PHP 5.6, 7.0, 7.4, 8.0, and 8.1 (the
lowest and highest supported minor per major PHP version this branch
supports), with `multisite: [false, true]` as a second axis so the
existing single-site and multisite runs become independent, isolated
legs instead of two steps in one run. `WP_ENV_PHP_VERSION` is set at
the job level so `wp-env`'s Docker Compose config pulls the matching
PHP-tagged `wordpress`/`wordpress:cli`/`wordpressdevelop/phpunit`
images for each leg. `fail-fast` is set to `false` so a failure on
one PHP version doesn't cancel the runs still verifying the others.
`unit-php` runs `npm run lint-php`, which is `wp-env run composer
run-script lint` -- PHP_CodeSniffer executed inside the `wp-env`-managed
`composer` container. Coupling the lint to the Docker environment has two
costs: a full `wp-env start` (database, WordPress, CLI and PHPUnit
containers, plus an `npm run build`) has to succeed before a pure static
check can run at all, and PHPCS output arrives as plain container logs
rather than as annotations on the diff.

Port the `phpcs` half of the upstream `unit-test.yml` split
(9ee17a7, WordPress#46510) to this branch: a
standalone runner-based `phpcs` job that checks out the repository, sets
up PHP 7.4 with `cs2pr`, installs Composer dependencies with
`ramsey/composer-install`, puts `vendor/bin` on `$GITHUB_PATH`, and runs
`phpcs --report-full --report-checkstyle=./.cache/phpcs-report.xml`,
feeding the checkstyle report to `cs2pr` on failure so violations appear
inline on the "Files changed" tab. The matching `npm run lint-php` step is
removed from `unit-php`, which is now purely a PHPUnit job.

The 7.4 in that job is deliberate, and is commented as such in the
workflow so it is not mistaken for drift. The PHP_CodeSniffer and WPCS
releases this branch depends on emit fatal deprecation notices on PHP 8.4
and newer -- for example "Implicitly marking parameter $phpcsFile as
nullable is deprecated" -- which abort the scan entirely, so raising it
requires upgrading those packages first.

PHPCS scan results are cached in `.cache/phpcs.json` between runs, keyed on
last Monday's date so the cache is discarded at least weekly, and on the
hashes of `composer.json` and `phpcs.xml.dist` so a ruleset or dependency
change invalidates it immediately. `phpcs.xml.dist` gains the matching
`cache` argument, and `.gitignore` re-includes `/.cache/.gitkeep` so the
directory the report and cache are written to is version-controlled while
its contents stay ignored.

The action versions match the ones this branch already pins elsewhere
(`actions/checkout` v7.0.1, `actions/cache` v6.1.0); `shivammathur/setup-php`
and `ramsey/composer-install` are new to this branch and are pinned to the
same SHAs the newer `wp/*` branches use.

Removing the lint step also removed the only thing that populated
`vendor/`. `lint-php`'s npm pre-hook, `prelint-php`, is `wp-env run
composer 'install --no-interaction'`, and nothing else in the job installs
Composer dependencies -- `pretest-unit-php` is only `wp-env start`, and
nothing in `packages/env/lib` invokes Composer -- while `phpunit.xml.dist`
bootstraps `phpunit/bootstrap.php`, which requires `vendor/autoload.php`.
An explicit `Install Composer dependencies` step therefore takes the
removed lint step's slot in `unit-php`, so the autoloader still exists by
the time PHPUnit runs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A current Composer release stops `composer install` before installing
anything:

    In PluginManager.php line 821:

      composer/installers contains a Composer plugin which is blocked by
      your allow-plugins config. You may add it to the list if you
      consider it safe.

Current Composer refuses to run any plugin that `composer.json` has not
explicitly trusted, and this branch's `composer.json` has no
`allow-plugins` section at all. The same block applies to the runner-based
`composer install` the `phpcs` job performs, where Composer is whatever
`shivammathur/setup-php` provides.

This dependency graph contains exactly two `composer-plugin` packages, both
of which are load-bearing:

- `composer/installers`, a direct `require`, which resolves this package's
  `wordpress-plugin` type to an install path.
- `dealerdirect/phpcodesniffer-composer-installer`, a `require-dev`, which
  registers `wp-coding-standards/wpcs`, `phpcompatibility/php-compatibility`
  and `sirbrillig/phpcs-variable-analysis` with PHP_CodeSniffer's
  `installed_paths`. Without it `phpcs` aborts with "the phpcs.xml.dist
  coding standard is not installed", which is also why `--no-plugins` is
  not a usable substitute for this configuration.

Both are therefore allowed. Verified with Composer 2.10.3 on PHP 8.1.31
against this branch's exact `composer.json`/`composer.lock`:
`composer install --no-interaction` exits 1 without this change and 0 with
it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`packages/env/lib/build-docker-compose-config.js` wires the `wp-env`
`composer` service to the bare, unversioned `composer` image. Unlike the
`wordpress`/`wordpress:cli`/`phpunit` services it is never templated with
`testsPhpVersion`/`developmentPhpVersion` (or, on CI, `WP_ENV_PHP_VERSION`),
so it floats to whatever PHP release Docker Hub's current `composer` build
happens to bundle -- 8.5.10 today -- regardless of which PHP version is
actually being tested.

That floating PHP is rejected by this branch's committed `composer.lock`:
`phpspec/prophecy` 1.14.0 (locked, and required transitively by the locked
`phpunit/phpunit` 8.5.21) declares `php ^7.2 || ~8.0, <8.2`. `composer
install --no-interaction` -- `prelint-php`, the first thing every PHP job
runs -- therefore fails with "Your lock file does not contain a compatible
set of packages", identically on every PHP version in the matrix, because
the PHP version being tested was never what determined it.

Pinning the container to an older PHP would address the symptom, but every
way of doing that also freezes Composer. Docker Hub rebuilds each
`composer:<version>` tag against a current PHP base image, so the PHP a tag
carries is a function of its build date rather than of its Composer
version, and no tag can mean "this PHP, newest Composer".

Composer already has a first-class answer that does not involve the image
at all. `config.platform.php` declares the PHP version the project targets,
and a lock file regenerated with it set records that value as
`platform-overrides`. `composer install` then verifies the locked packages
against the declared PHP rather than against whatever PHP the container
happens to run, so the `composer` service can stay on its floating tag and
keep receiving Composer's upstream security fixes and its current advisory
policy engine.

8.1 is the version to declare. It is the newest release under
`phpspec/prophecy`'s `<8.2` ceiling and the top of this branch's PHPUnit
matrix. A lower value such as 7.4 would also regenerate the lock, now that
the `spatie/phpunit-watcher` subtree and its `php >=8.0.2` `symfony`
packages are gone, but 8.1 is preferred: it verifies the lock against a PHP
version this branch actually tests, and it keeps the declared platform as
close as the lock allows to the current PHP the `composer` image runs.

The root `require` also declares `php` `>=5.6.20`, WordPress's minimum
supported PHP version, so the package states the oldest PHP it supports
separately from the platform it is resolved against, and the regenerated
lock records that floor in its `platform` entry.

`config.platform` is one of the few `config` keys that feeds the lock's
`content-hash`, so the `composer.json` edit and the regenerated
`composer.lock` have to land in the same commit or Composer reports the
lock as out of date.

The lock was regenerated with `composer update --lock`, not a full
`composer update`, so nothing was resolved afresh. Every package name,
version and dist reference is unchanged; the remaining differences are the
`content-hash`, the new `platform-overrides` entry, and
`squizlabs/php_codesniffer`'s source and dist URLs following that
repository's move to the `PHPCSStandards` organization (same 3.6.1, same
commit reference).

Verified with Composer 2.10.3 on PHP 8.5.10, which is what the floating
`composer` image currently carries: `composer install --no-interaction`
exits 2 before this change and 0 after it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Composer's dependency-resolution-time security policy refuses to load any
version of `wp-coding-standards/wpcs` that satisfies this branch's own
`^2.2` constraint:

    Root composer.json requires wp-coding-standards/wpcs ^2.2, found
    wp-coding-standards/wpcs[2.2.0, 2.2.1, 2.3.0] but these were not
    loaded, because they are affected by security advisories
    ("PKSA-mh9b-91zm-m1gy").

`PKSA-mh9b-91zm-m1gy` (CVE-2026-45293) is an `eval()`-injection bug in the
`WordPress.WP.EnqueuedResourceParameters` sniff, which ships only in the
`WordPress` and `WordPress-Extra` rulesets. `phpcs.xml.dist` on this branch
references `PHPCompatibility`, `WordPress-Core`, `WordPress-Docs` and a
handful of individually named sniffs, and never either of those two
rulesets, so the vulnerable sniff is never loaded at any `wpcs` version.

The advisory gate applies only to resolution (`composer update`,
`composer require`), never to `composer install` from a committed
`composer.lock`, so no CI job on this branch surfaces the block today.
That is not the same as not being exposed: anyone resolving dependencies
locally on a PHP version new enough to run a current Composer is stopped
dead by it. The exclusion belongs in `composer.json` either way, with the
reachability analysis recorded inline so the rationale travels with the
file.

Verified against this branch's exact `composer.json`/`composer.lock` under
Composer 2.10.3 on PHP 8.1.31: `composer update` exits 2 without this
change and 0 with it, and `PKSA-mh9b-91zm-m1gy` is the only ID the
resolver needs excluded.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every `JavaScript` (`test-unit`) run currently fails: Jest's console
assertions turn the routine, time-based "Browserslist: caniuse-lite is
outdated" warning into a hard test failure the moment any package (e.g.
`packages/babel-preset-default`) happens to touch `browserslist`/
`caniuse-lite` during a unit test run. This is unrelated to any code
change in this branch's own test suites — it is purely a function of
how much wall-clock time has passed since `caniuse-lite`'s pinned data
was last current, so it will recur indefinitely on a frozen branch that
never re-pins that dependency.

Mirrors trunk's own fix for the identical problem
(`a46893062588d0e46e001e911a9e475f994db00a`, "Tests: Ignore Browserslist
old data warning in unit tests") — set `BROWSERSLIST_IGNORE_OLD_DATA`
before Jest boots so `caniuse-lite`/`browserslist` never emit the
warning in the first place. This branch's `test/unit/jest.config.js`
has no existing `process.env.TZ` line to insert alongside (unlike
trunk's copy at the time of that commit), so the two-line insertion
lands directly after the `transpiledPackageNames` block instead.
WordPress core's "Activate" link on wp-admin/themes.php is a plain
<a href>: clicking it causes a real, full-page, server-driven navigation
(switch_theme(), then a redirect back to themes.php). activateTheme()
clicked that link without waiting for the navigation, so the single,
bare 30s page.waitForSelector() immediately afterward had to cover the
entire click -> switch_theme() -> redirect -> admin page reload chain
by itself.

Wrap the click in Promise.all() with page.waitForNavigation( {
waitUntil: 'networkidle0' } ), so the final page.waitForSelector() only
runs once the navigation has finished, and give that wait an explicit
45s timeout instead of Puppeteer's 30s default, as headroom for the
page's own post-navigation render.

The "no activate button found" branch (the theme is already active)
returns before the click and is unchanged.

This is the same change as the equivalent fixes on wp/6.5
(WordPress#83002) and wp/6.4 (#328), where
this race failed the Puppeteer suite in CI. It has not been seen to fail
on this branch; it is included so the helper behaves the same across
branches and a future failure here does not need to be rediscovered.
@desrosj
desrosj added this pull request to stack #643 October 7, 2026 12:48
@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown

🤖 PR meta 🤖

🏷️ Labels

This pull request needs exactly one label indicating its type, and has 0.

  • Required: any label starting with [Type].
  • Found: none.

Read more about Type labels in Gutenberg. If you cannot add labels yourself, a reviewer can do it for you.

@desrosj
desrosj removed this pull request from stack #643 October 7, 2026 18:02
@desrosj
desrosj changed the base branch from fix-actions-5.9/fix-xdebug-pecl-php7 to wp/5.9 October 7, 2026 18:03
@desrosj

desrosj commented Oct 7, 2026

Copy link
Copy Markdown
Owner Author

Closing: the performance tests workflow is removed from wp/5.9 in #169, so this pull request has nothing left to change.

@desrosj desrosj closed this Oct 7, 2026
@desrosj
desrosj deleted the fix-actions-5.9/pass-env-to-performance-tests branch October 7, 2026 18:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant