publish: one wrapper owns all branching (engine + tag trampoline + fork warn)#29
Merged
Conversation
…kflow_call engine
publish.yml was both the assemble+deploy engine AND a dispatch entry point
(workflow_call + workflow_dispatch with the fork-preview/tag logic), and ci.yml
reached it two different ways (nested workflow_call for non-tags, gh-workflow-run
of publish.yml for the tag trampoline). That split the dispatch surface oddly and
meant this repo dispatched publish.yml directly while consumers used a wrapper.
Unify on one wrapper:
- publish.yml becomes a pure workflow_call ENGINE (drop on: workflow_dispatch). Its
only caller is publish-dispatch.yml. version-name is now optional (default "").
- add publish-dispatch.yml: on {workflow_call, workflow_dispatch}, both just
`uses: ./.github/workflows/publish.yml` and thread version-name/guard/pr through,
carrying the privileged perms. This repo dogfoods the exact wrapper consumers add
(theirs pins publish.yml@<tag>; ours uses the local path).
- ci.yml: the `publish` job calls publish-dispatch.yml (workflow_call); `publish-tag`
does `gh workflow run publish-dispatch.yml`. Both route through the wrapper, never
publish.yml directly.
Mechanics unchanged: inline non-tag deploys serve fine (first of their SHA); tags
deploy via the dispatch trampoline (re-serves a same-SHA deploy). Docs updated
(tutorial wrapper now workflow_call+dispatch and ci.yml routes through it;
architecture, workflow reference, CLAUDE.md).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…im ci.yml & docs.yml
Fold the tag trampoline and the fork-PR preview hint into the wrapper so a consumer's
ci.yml is a single `publish` job and docs.yml carries no fork logic.
publish-dispatch.yml now branches on the originating event into three jobs:
- deploy — internal PR / default-branch push (inline, uses publish.yml), or any
workflow_dispatch (the trampoline's re-dispatch / fork preview / manual).
- trampoline — a tag push: wait for the parallel release job to attach docs.zip, then
`gh workflow run publish-dispatch.yml` so the deploy is a workflow_dispatch
that re-serves (a same-SHA tag deploy is otherwise dropped — #383).
- warn — a fork PR: read-only, never deploys, posts the manual-opt-in hint
(moved here from docs.yml's build job).
- ci.yml: ONE `publish` job (`if: repository == …`) for every event, `actions: write`
added for the trampoline; drop the separate `publish-tag` job and the ref/fork guards.
- docs.yml: drop the fork-preview warning step and the `preview-workflow` input.
Security boundary preserved: a fork PR's token is read-only and the `deploy` job is
if-excluded for forks, so untrusted code never reaches a write token — only `warn` runs.
Docs updated (tutorial: one publish job + full wrapper; architecture; reference; CLAUDE.md).
Co-Authored-By: Claude Opus 4.8 <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.
What
Refactors the publish workflows so
publish-dispatch.ymlis the one wrapper that owns all publish branching,publish.ymlis a pureworkflow_callengine, and a consumer'sci.ymlneeds a singlepublishjob (nopublish-tag, no fork guards) whiledocs.ymlcarries no fork logic.publish.yml→ engineworkflow_callonly (droppedworkflow_dispatch). Its sole caller ispublish-dispatch.yml.version-nameis now optional.publish-dispatch.yml→ the wrapper, branches on the originating eventdeploy— internal PR / default-branch push (inline,uses: publish.yml, injectsversion-name), or anyworkflow_dispatch(the trampoline's re-dispatch, a fork preview, a manual re-deploy).trampoline— a tag push: waits for the parallelreleasejob to attachdocs.zip, thengh workflow run publish-dispatch.ymlso the deploy is aworkflow_dispatchthat re-serves (a same-SHA tag deploy is otherwise silently dropped — #383).warn— a fork PR: read-only, never deploys, posts the manual-opt-in hint (moved here fromdocs.yml).ci.yml/docs.ymlslim downci.yml: onepublishjob (if: repository == …) for every event,actions: writefor the trampoline; dropped the separatepublish-tagjob and all ref/fork guards.docs.yml: dropped the fork-preview warning step and thepreview-workflowinput.Security boundary preserved
A fork PR's
GITHUB_TOKENis read-only and thedeploy/trampolinejobs areif-excluded for forks, so only the read-onlywarnjob runs — untrusted code never reaches a write token (andpull_requestruns the workflow file from the trusted base, not the fork).Mechanics unchanged
Inline non-tag deploys serve fine (first of their SHA); tags re-serve via the dispatch trampoline; the origin-verify backstop is intact.
Testing notes
deploy).warnbranches only run on a tag push / a fork PR respectively — worth a real exercise after merge. The fork path in particular leans on fork-PR permission capping (thepublishjob requests write perms but only the perm-lesswarnjob runs for forks); confirm with a fork PR.Supersedes the earlier "extract wrapper" scope of this PR (same branch).
🤖 Generated with Claude Code