Repository navigation
refactor!: move the gather's decisions into assemble.mjs, add max-prs - #69
Merged
Merged
Conversation
Review pass over the publish pipeline. The theme is that every decision the
gather makes now lives in the tested kernel, and the workflow does IO only.
New `select-artifacts` subcommand: given the artifacts list and the open-PR
list (with fork approval pre-resolved, since that costs an API call each), it
decides which artifact becomes the default branch and which PRs get a preview.
That replaces ~50 lines of inline bash — the SHA index, the jq default-branch
pick, the eligibility loop and the rank/cap loop — and it fixed two defects the
bash had:
- The default branch chose its SHA *with* the fork guard, then re-looked-up
the artifact in an index built *without* one. A fork's pull_request run
executes in the upstream repo's Actions, so a fork PR whose head SHA matched
the default branch's could have been staged as site/<default>. The kernel
uses the filtered artifact directly and never re-looks-up by SHA.
- The release cap and the PR cap broke timestamp ties in opposite directions
(`sort -r` kept PR #9 over #10; byDateDesc kept 1.10 over 1.9). Both now
share one comparator.
`select-releases` likewise emits its own cache key rather than having the
workflow re-derive it with awk/sort/sha256sum. The digest is byte-identical to
the pipeline it replaces, so existing cache entries stay warm.
New `max-prs` input, matching `max-releases`: cap the previews the site carries,
ranked by build date, applied after eligibility so an expired artifact or an
unapproved fork PR never consumes a slot. This repo's publish.yml now sets the
recommended 30/20, making it identical to a consumer's copy but for the `uses:`
path — the file people copy is the file that is exercised.
Also:
- Release-zip downloads are serial again. With the cache the steady state is
0-1 downloads (measured: 0-2s warm), which did not justify a three-pass
download-and-stage structure. PR artifacts can never be cached, so those
stay parallel.
- Removed the site-size guard. actions/deploy-pages already compares the
uploaded artifact against 1 GB and warns, reading the stored size — the same
number the local pack was approximating. The limit and the recovery are in
the docs and in the publish.yml people copy.
- Removed _sources entirely: the write went years ago, and the deprecated read
fallback has done its job.
- The parallel worker takes its row as positional args over a NUL stream,
not via `xargs -I{}`, which pasted the row into the script *text* — a branch
name may contain `$(...)`, and this workflow holds pages:write.
- docs.yml's `version-name` output is gone; nothing consumed it since the
injection step, and it implied a coupling that does not exist (both sides
derive the name independently, now pinned by a test).
- Comments trimmed throughout: history and changelog narration removed, traps
and contracts kept.
Tests: 48 unit checks (was 34), 79 harness/shape checks (was 71). New coverage
for cacheKey, selectArtifacts, the version-name contract, the cache-key outputs,
and the worker's argument wiring — each mutation-tested.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
On success the engine posts a `docs-preview` commit status on the triggering
commit, with the published URL as its target — so a PR carries a link to its own
preview, and a branch or tag build links the version it produced. Restores the
one thing the workflow_run migration cost that was worth having, without the
thing it was fixing.
Two properties, both asserted in test_shape.py:
- `if: success()` and `state=success` only. Publishing lives off the PR's
critical path because a wedged Pages origin is not the author's to fix; a
status that could turn red would hand that straight back. A missing status
means "not published (yet)".
- Fails CLOSED. The dest is resolved from the workflow_run payload (or from
`inputs.pr` for a dispatched fork preview) and posted only if that version
dir is actually in the tree just deployed — no link is better than one that
404s.
The Approve step now also outputs the head SHA it pinned, so a fork preview links
back to the same commit it was approved against, and takes `inputs.pr` through
`env:` rather than interpolating it into the script — the rule the rest of this
file already follows.
Also adds BUILT_BRANCH to the harness env, which mirrors the real job.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Review pass over the publish pipeline. The theme: every decision the gather makes now lives in the tested kernel, and the workflow does IO only.
select-artifactsNew subcommand deciding which artifact becomes the default branch and which PRs get a preview, given the artifacts list and the open-PR list (with fork approval pre-resolved, since that costs an API call each). It replaces ~50 lines of inline bash — the SHA index, the jq default-branch pick, the eligibility loop, the rank/cap loop — taking that step from 92 non-comment lines to 44.
It fixed two defects the bash had:
Fork isolation at an identical head SHA. The default branch chose its SHA with the
head_repository_idguard, then fetched the artifact from an index built without one:A fork's
pull_requestrun executes in the upstream repo's Actions, so a fork PR whose head SHA matched the default branch's could have won that lookup and been staged assite/<default>. Narrow — it needs an effectively-empty PR — but it is the fork boundary in a workflow holdingpages: write. The kernel uses the filtered artifact directly and never re-looks-up by SHA.Tie-break drift. The two caps broke timestamp ties in opposite directions —
sort -rkept PR #9 over #10,byDateDesckept1.10over1.9. Both now share one comparator.max-prsMatches
max-releases: cap the previews the site carries, ranked by build date, applied after eligibility so an expired artifact or an unapproved fork PR never consumes a slot. Defaults to0(unlimited), so bumping the pin changes nothing until you set it.This repo's
publish.ymlnow sets the recommended30/20, making it identical to a consumer's copy but for theuses:path — the file people copy is the file that is exercised. A shape check asserts both are literals, since that is what makes the policy unbypassable by a manual dispatch.Cache key
select-releasesemits its own key rather than having the workflow re-derive it withawk | sort | sha256sum. The digest is byte-identical to the pipeline it replaces (verified against live data at three cap values), so existing cache entries stay warm through the upgrade.Removals
actions/deploy-pagesalready compares the uploaded artifact against 1 GB and warns, reading the stored size — the same number the local pack was approximating. The limit and the recovery now live in the docs and in thepublish.ymlpeople copy. Note the tradeoff: their warning fires only once you are already over, so there is no early signal._sources. The write went some time ago; the deprecated read fallback has done its job.docs.yml'sversion-nameoutput. Nothing consumed it since the injection step, and it implied a coupling that does not exist — both sides derive the name independently. Now pinned by a test instead.Serial vs parallel downloads
Release-zip downloads are serial again. With the cache the steady state is 0–1 downloads (measured on recent runs: 0–2 s warm), which did not justify a three-pass download-and-stage structure. PR artifacts can never be cached — a head SHA changes on every push — so those stay parallel at
-P 8.Worker hardening
The parallel worker takes its row as positional args over a NUL stream instead of
xargs -I{}, which pasted the row into the script text. Git permits$and()in a branch name, and the default-branch label embeds it:Not reachable today, but "inert by construction" beats "inert because nothing currently populates it" in a privileged workflow.
Tests
48 unit checks (was 34), 79 harness/shape checks (was 71). New coverage for
cacheKey,selectArtifacts, the version-name contract, the cache-key step outputs, and the worker's argument wiring. Each new guard was mutation-tested — I broke the thing it protects and confirmed the check fails.Worth recording: the harness caught a bug in my own kernel (a comparator reading
.dateon objects carryingcreated_at, which tied every artifact and returned the oldest). The unit tests would not have — I would have written them against the same wrong field. Both layers earned their place.For consumers
Bumping the pin is a no-op unless you opt in:
max-prsdefaults to0. To adopt the caps, copy the updatedpublish.ymlfrom the tutorial. The only true break isdocs.yml's removedversion-nameoutput.Linking the preview back to the PR
The
workflow_runmigration moved publishing off the PR's critical path, which was right — but it also meant nothing on a PR told you the preview existed or where it was. This restores the link without restoring the problem.On a successful deploy the engine posts a
docs-previewcommit status on the triggering commit, with the published URL astarget_url. Two properties, both asserted intest_shape.py:if: success()plusstate=successonly. A wedged Pages origin is not the PR author's to fix, so a status that could turn red would hand that straight back to them. A missing status means "not published (yet)".workflow_runpayload (orinputs.prfor a dispatched fork preview) and the status is posted only if that dir is actually in the tree just deployed. No link beats a link that 404s.Fork previews link back to the exact SHA the approval was pinned to, so the contributor gets the URL from the maintainer's dispatch.
Seven harness checks cover the paths: PR build, default-branch build, dispatched fork preview, an unresolvable PR, and a manual re-deploy with no triggering commit.
While there: the Approve step now takes
inputs.prthroughenv:instead of interpolating it into the script — the convention the rest of that file already documents and follows.🤖 Generated with Claude Code