Opened 2 days ago
Last modified 3 hours ago
#65721 reopened enhancement
Download the Gutenberg build once per run
| Reported by: | lancewillett | Owned by: | lancewillett |
|---|---|---|---|
| Priority: | normal | Milestone: | 7.0.3 |
| Component: | Build/Test Tools | Version: | |
| Severity: | normal | Keywords: | has-patch has-unit-tests fixed-major |
| Cc: | Focuses: |
Description (last modified by )
Each matrix job (roughly 177 per run) independently streams the same ~36 MB Gutenberg archive from the GitHub Container Registry, about 6.4 GB of registry traffic per run. Any one flaky stream ("Premature close") can redden the run.
Proposed change: a prepare-gutenberg reusable workflow fetches and verifies the build once (SHA-256 and size), then uploads it as a run artifact. Every PHPUnit caller consumes the shared artifact and never contacts the registry. The prep job runs only when a consumer runs.
Coverage is unchanged: the same PHPUnit jobs run with the same configurations.
Follow up to the Gutenberg artifact-fetching work in #64393.
Change History (20)
This ticket was mentioned in PR #12701 on WordPress/wordpress-develop by @lancewillett.
2 days ago
#1
This ticket was mentioned in PR #12702 on WordPress/wordpress-develop by @lancewillett.
2 days ago
#2
Second of two changes on this ticket, and it builds on #12701. Because a cross-repo pull request can only target a branch that exists here, this one is opened against trunk, so its diff carries the download-once commit as well. Review only the second commit, "Cache the verified Gutenberg build across runs." Once #12701 lands, this branch reduces to that single commit.
## Summary
Cache the verified Gutenberg archive across runs.
- Key the cache on the immutable source SHA plus the blob SHA-256.
- Re-verify (SHA-256 and size) on every restore and fail closed on a mismatch.
- Resolve fresh metadata before every restore, so the cache can never select which build version is used.
- Write the cache only from protected-branch pushes; pull requests and forks restore only.
## Why
Downloading once per run cut the registry fetches from every matrix job to one. Caching removes even that fetch on a warm hit, which keeps a flaky external registry off the hot path entirely rather than just narrowing the window.
Two safety properties are worth calling out. The cache is keyed on content, not on a branch or a date, so a restore either matches the exact archive the metadata asked for or is rejected. And because metadata resolves first, a stale entry can never pin CI to an old Gutenberg build. Restricting writes to protected-branch pushes keeps untrusted pull requests out of a cache that trusted runs read.
Follow up to the Gutenberg artifact-fetching work in #64393 on Trac.
## Testing
actionlintpasses on the reusable workflow.- Unit tests cover cache-hit verification, mismatch rejection, and the metadata-before-restore ordering. Run with
npm run test:tools.
## Note on overlap with #12700
That pull request and this one both add tools/gutenberg/tests/download.test.js from scratch and both add a test:tools script to package.json, so they conflict with each other. Suggested order is to land #12700 first; this branch then rebases so its cache tests extend that file rather than replace it. Happy to do that rebase whenever it suits the review order.
@lancewillett commented on PR #12702:
2 days ago
#3
Closing to keep the flake fix minimal. #12701 (download once per run) removes the download flake by cutting ~177 GHCR fetches per run to one. Cross-run caching is a separate speed optimization, not needed for the flake, so deferring it as a possible small follow-up on top of #12701 rather than a parallel change. If revived, two things to fix from this branch: the prepare workflow verified and extracted the archive twice on a cold run, and the archive-path guard defended against input that cannot occur.
#5
@
2 days ago
- Description modified (diff)
- Summary Download the Gutenberg build once per run and cache it across runs → Download the Gutenberg build once per run
Reduced scope to download-once. The original second stage (cross-run caching) is deferred. Download-once alone removes the flaky per-job downloads by cutting the ~177 fetches per run to one, and caching is a separate speed optimization that can follow up on its own if wanted.
@lucasbustamante commented on PR #12701:
35 hours ago
#6
When things are calmer it might be worth backporting this fix to older branches as well to make CI more reliable when testing backported branches.
@lancewillett commented on PR #12701:
31 hours ago
#7
## Note on the download-once tradeoff
This PR moves the Gutenberg build download from once-per-matrix-job to once-per-run: a single prepare-gutenberg job the matrix depends on via needs:. That is a deliberate tradeoff worth an explicit call.
Why do it. Today each job resolves the mutable tag→SHA independently. If the tag moves mid-run, different jobs can silently test against different Gutenberg builds. Resolving once and sharing the SHA plus artifact closes that skew.
The cost. A failure in the single gate skips the whole matrix via needs:, where today a transient fetch failure fails only one job. CI is re-runnable, so the impact is a re-run, not lost work, but the blast radius is real.
How we bounded it. The download path now has bounded retry and timeout on the blob, a fresh-token refetch on a mid-retry 401, and the same retry and timeout on the token and manifest fetches that precede it. The whole single-gate path is hardened, not just the blob.
Why no per-job fallback. A fallback to the old per-job download would re-introduce the very skew this fixes (a fallen-back job could grab a different build) and keep two download paths to maintain.
Flagging this so it is a deliberate yes, not a nod-through. Happy to revisit if you would prefer the fallback.
@lancewillett commented on PR #12701:
30 hours ago
#8
I think this will break on branches 5.9–7.0 because the two required inputs are not passed
Great catch, @adimoldovan . Confirmed and fixed in eeee175aca
This ticket was mentioned in PR #12724 on WordPress/wordpress-develop by @lancewillett.
27 hours ago
#10
The download-once change ([62859]) added the callers of reusable-prepare-gutenberg.yml (phpunit-tests.yml, reusable-phpunit-tests-v3.yml, and test-coverage.yml) but the new reusable-prepare-gutenberg.yml file itself was not committed to trunk. Those workflows now reference a reusable workflow that does not exist, so:
Lint GitHub Actions workflow filesfails on trunk (actionlint:could not read reusable workflow file ... no such file or directory).- The PHPUnit Tests workflow cannot load on trunk, because
phpunit-tests.ymlcalls the missingprepare-gutenbergjob that the whole matrix depends on.
This adds the file. It resolves the Gutenberg build once per run, verifies it (SHA-256 and size), uploads it as a gutenberg-build run artifact, and exposes the resolved gutenberg-sha output, the interface the three callers already expect.
Verified with actionlint locally: the dangling reference resolves and the new file is clean.
#14
@
10 hours ago
- Resolution fixed
- Status closed → reopened
@jorbin Good call. Since 7.0 already calls reusable-phpunit-tests-v3.yml@trunk we just add a prepare-gutenberg job to 7.0's caller that references reusable-prepare-gutenberg.yml@trunk and pass its artifact/SHA into the existing calls, so neither reusable file needs a 7.0 copy.'
We'll work on a patch.
#15
@
10 hours ago
- Keywords fixed-major added
- Milestone 7.1 → 7.0.3
Fantastic! Marking as fixed-major and putting in the 7.0.3 milestone to signify the intent to backport the fix.
#17
@
6 hours ago
- Resolution → fixed
- Status reopened → closed
Backported to 7.0 in r62873; the 7.0 PHPUnit matrix is green with the build downloaded once per run. Closing as fixed for 7.0.3.
This ticket was mentioned in PR #12743 on WordPress/wordpress-develop by @lancewillett.
5 hours ago
#18
This updates the 7.0 PHPUnit workflow to reference reusable-prepare-gutenberg.yml@trunk instead of a pinned commit SHA.
The reusable-phpunit-tests-v3.yml callers already use @trunk. Keeping both shared workflows on the same ref prevents their output contract from drifting and preserves the one-version-to-maintain intent.
No PHP matrix or caller wiring changed.
## Testing
actionlint .github/workflows/phpunit-tests.ymlgit diff --check
## Use of AI Tools
AI assistance: Yes
Tool(s): Codex
Model(s): GPT-5
Used for: Applied and validated the one-line workflow reference change. The final diff was reviewed by me.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)
## Summary
Fetch and verify the Gutenberg build once per workflow run instead of once per matrix job.
.github/workflows/reusable-prepare-gutenberg.yml, which resolves the build metadata, downloads the archive, verifies it (SHA-256 and size), and uploads it as a run artifact.phpunit-tests.yml,reusable-phpunit-tests-v3.yml, andtest-coverage.ymlconsume that shared artifact and no longer contact the registry.Test coverage is unchanged: the same PHPUnit jobs run with the same configurations.
## Why
Every matrix job independently streams the same ~36 MB archive from the GitHub Container Registry. Across a full run that is roughly 6.4 GB of registry traffic for one build, and any single flaky stream can redden the whole run. Downloading once shrinks the exposure from every job to one. It also moves verification to a single place, so a bad archive fails fast and loudly instead of failing differently in each consumer.
Follow up to the Gutenberg artifact-fetching work in #64393.
This is the first of two changes on this ticket. The second caches the verified archive across runs.
## Testing
actionlintpasses on all four workflow files.