Skip to content

publish: one wrapper owns all branching (engine + tag trampoline + fork warn)#29

Merged
coretl merged 2 commits into
mainfrom
refactor-publish-dispatch-wrapper
Jun 26, 2026
Merged

publish: one wrapper owns all branching (engine + tag trampoline + fork warn)#29
coretl merged 2 commits into
mainfrom
refactor-publish-dispatch-wrapper

Conversation

@coretl

@coretl coretl commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

What

Refactors the publish workflows so publish-dispatch.yml is the one wrapper that owns all publish branching, publish.yml is a pure workflow_call engine, and a consumer's ci.yml needs a single publish job (no publish-tag, no fork guards) while docs.yml carries no fork logic.

publish.yml → engine

workflow_call only (dropped workflow_dispatch). Its sole caller is publish-dispatch.yml. version-name is now optional.

publish-dispatch.yml → the wrapper, branches on the originating event

  • deploy — internal PR / default-branch push (inline, uses: publish.yml, injects version-name), or any workflow_dispatch (the trampoline's re-dispatch, a fork preview, a manual re-deploy).
  • trampoline — a tag push: waits 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 silently dropped — #383).
  • warn — a fork PR: read-only, never deploys, posts the manual-opt-in hint (moved here from docs.yml).

ci.yml / docs.yml slim down

  • ci.yml: one publish job (if: repository == …) for every event, actions: write for the trampoline; dropped the separate publish-tag job and all ref/fork guards.
  • docs.yml: dropped the fork-preview warning step and the preview-workflow input.

Security boundary preserved

A fork PR's GITHUB_TOKEN is read-only and the deploy/trampoline jobs are if-excluded for forks, so only the read-only warn job runs — untrusted code never reaches a write token (and pull_request runs 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

  • This PR's own CI exercises the inline chain (internal PR → deploy).
  • The tag trampoline and the fork warn branches 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 (the publish job requests write perms but only the perm-less warn job runs for forks); confirm with a fork PR.

Supersedes the earlier "extract wrapper" scope of this PR (same branch).

🤖 Generated with Claude Code

…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>
@coretl coretl changed the title publish: extract publish-dispatch.yml wrapper; make publish.yml a workflow_call engine publish: one wrapper owns all branching (engine + tag trampoline + fork warn) Jun 26, 2026
@coretl coretl merged commit 35587da into main Jun 26, 2026
7 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