Skip to content

test: probe whether a workflow_run Pages deploy re-serves at a repeat SHA - #63

Merged
coretl merged 1 commit into
mainfrom
probe/workflow-run-deploy
Aug 21, 2026
Merged

coretl merged 1 commit into
mainfrom
probe/workflow-run-deploy

Conversation

@coretl

@coretl coretl commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Temporary experiment. Merging this is what starts it — workflow_run only fires for workflow files on the default branch, and runs the default branch's copy, so it cannot be exercised from a PR.

Why

Publishing nests in ci.yml, so its deploy sits inside every PR's CI run — 654 s on blueapi (run 31081562099), against a 27–45 s docs build. Moving it to workflow_run would take it off that path and delete a lot of machinery: the version-name injection, the publish-dispatch.yml shim, the warn job, and the tag asset race that #62 was fixing.

It turns on one unknown. workflow_run sets GITHUB_SHA to the default branch's HEAD, not the commit that was built (GitHub's events table; confirmed on scikit-learn, whose consecutive workflow_run runs from different PRs all carry the same head_sha). deploy-pages stamps pages_build_version = GITHUB_SHA, and deploy-pages#383 reports repeats being silently no-op'd. That suggests at most one serving deploy per merge to main.

But we hold a counterexample: a repeat workflow_dispatch at an already-deployed SHA does re-serve — the re-dispatch trampoline depends on it and works in production. So event type matters beyond pages_build_version, and the rule can't be assumed to generalise to an untested event. The Pages deployment record is created whether or not the origin moves, so the API can't answer this either. Only a real deploy against the real origin can.

What this does

file change
_probe-workflow-run.yml new. Logs the workflow_run context, then deploys through the real engine
publish.yml accept workflow_run in the deploy guard. No other change
ci.yml disable the inline publish so workflow_run is the only deploy path

The probe's deploy job is guarded on a successful, non-fork triggering run: workflow_run hands a write token to a job triggered by fork-PR code, which is the pwn-request footgun publish.yml's read-only warn job exists to avoid.

Tags are disabled on the inline path too, deliberately — a re-dispatch deploy is a workflow_dispatch, which we already know re-serves, and that's exactly the confound observation 2 must avoid.

The three observations, after merge

  1. Baseline. Push to main → probe fires → deploys. First deploy of that SHA, should serve. Also proves the gather finds the build with no version-name injection, since the triggering run has completed.
  2. The critical one. With main still at that commit, git tag v0.0.0a1 origin/main && git push origin v0.0.0a1. release.yml attaches its docs.zip; the probe then deploys at the same SHA as observation 1, assembling a site that now contains v0.0.0a1. Different bytes, same SHA — the real-world tag case.
    • origin's switcher.json gains v0.0.0a1 ⇒ workflow_run re-serves, migration is on
    • Verify fails with missing from live: v0.0.0a1 ⇒ dropped, keep the inline trigger
  3. PR previews. A docs PR, pushed twice. Both deploys land at the same unchanged main HEAD. Does /pr-<n>/ appear, and does it update on the second push? Highest-volume case, first to break if the collision is real.

v0.0.0a1 sorts last under -v:refname and isPrerelease() excludes it from stable/ and the root redirect, so it can't disturb the live site.

Risk

The site content this deploys is the normal, correctly assembled site — the only question is whether the origin moves. Worst case it goes stale, not broken, and publish.yml's Verify step makes that a red check rather than a silent lie. Recovery is a manual workflow_dispatch of publish-dispatch.yml, which we know re-serves.

All three changes revert together once the question is answered. The performance work (release cap, asset caching, single-pass artifact index, _sources → actions/cache) is independent of the result and proceeds in parallel.

🤖 Generated with Claude Code

… SHA

Publishing currently nests in ci.yml, so its ~650s (on blueapi) sits inside every
PR's CI run. Moving it to `workflow_run` would take it off that path and delete a
lot of machinery — the version-name injection, the dispatch shim, the warn job,
and the tag asset race. Whether that is possible turns on one unknown.

`workflow_run` sets GITHUB_SHA to the default branch's HEAD rather than the commit
that was built, and deploy-pages stamps pages_build_version = GITHUB_SHA, which
actions/deploy-pages#383 reports as silently no-op'd when it repeats. That
*suggests* at most one serving deploy per merge. But we hold a counterexample: a
repeat workflow_dispatch at an already-deployed SHA does re-serve — publish.yml's
re-dispatch trampoline depends on it and works in production. So event type
matters beyond pages_build_version and the rule cannot be assumed to generalise.

The Pages deployment record is created whether or not the origin moves, so the API
cannot answer this. Only a real deploy against the real origin can.

- _probe-workflow-run.yml: log the workflow_run context (is github.sha really the
  default-branch HEAD?), then deploy through the real engine so the assembled site
  stays correct and publish.yml's Verify step reports the answer in-run. Guarded on
  a successful, non-fork triggering run — workflow_run hands a write token to a job
  triggered by fork-PR code, which is the footgun the warn job exists to avoid.
- publish.yml: accept workflow_run in the deploy job's guard. No other change;
  version-name "" is an existing mode, and the triggering run has completed, so its
  docs artifact is discoverable by the normal gather with no injection.
- ci.yml: disable the inline publish so workflow_run is the only deploy path.
  Tags included, deliberately — a re-dispatch deploy is a workflow_dispatch, which
  we already know re-serves, and that is the confound the experiment must avoid.

Temporary. All three changes revert together once the question is answered.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coretl
coretl force-pushed the probe/workflow-run-deploy branch from e962b42 to 6232958 Compare August 21, 2026 11:06
@coretl

coretl commented Aug 21, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto main now that #64 has landed — clean, no conflicts, and all three parts are intact (the workflow_run clause in publish.yml's deploy guard, the disabled inline publish in ci.yml, and the probe workflow itself).

Two things #64 changes about how this experiment will read:

The deploy is now ~60–80 s rather than ~650 s on a large site, so the experiment measures the trigger rather than the gather. Good — that was the point of doing #64 first.

Cache saving will behave differently under workflow_run. The save conditions are github.ref == refs/heads/<default>, and under workflow_run github.ref is always the default branch — even when the triggering run was a PR's. So every probe deploy will save, where today only main deploys do.

That is benign, and arguably an improvement: the release-zip cache is keyed by asset id and is PR-independent, and the default-branch cache holds main's docs.zip whichever event gathered it. But it is a real behaviour change, so worth watching in observation 3 — if workflow_run wins, the save condition should be re-expressed against github.event.workflow_run.head_branch rather than github.ref, which will then be the honest thing to key on.

Still blocked on merge to start: workflow_run only fires for workflow files on the default branch.

@coretl
coretl merged commit b242374 into main Aug 21, 2026
5 checks passed
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