Skip to content

ci: deploy docs from a main ref via workflow_run on Release - #160

Merged
btravers merged 2 commits into
mainfrom
fix/docs-deploy-trigger
Jul 28, 2026
Merged

ci: deploy docs from a main ref via workflow_run on Release#160
btravers merged 2 commits into
mainfrom
fix/docs-deploy-trigger

Conversation

@btravers

Copy link
Copy Markdown
Collaborator

What & why

The docs deploy has never worked on a release. Run
30370012795
is the latest instance; beta.8 failed identically:

Tag "unthrown@5.0.0-beta.9" is not allowed to deploy to github-pages due to environment protection rules.

deploy-docs.yml triggers on release: published, so the run's ref is the
release tag. The github-pages environment uses custom branch policies with
a single main policy and no tag policies, so the Deploy job is rejected before
running a single step. Push-to-main runs have always succeeded, which is why
this went unnoticed.

Why it matters more than a red tick

Release commits never touch docs/** — I checked the last five
chore: release packages commits: zero docs/ files each. So the push
trigger's path filter never fires for a release, and the release trigger is the
only path that refreshes the site when a version ships.

The site is already stale in production: main is at 5.0.0-beta.9, while
/beta/ still advertises v5.0.0-beta.8.

It gets worse at GA. The root site is built from the latest stable tag
(prereleases filtered out), so today it correctly serves v4.3.0. When 5.0.0
ships, stable_tag becomes unthrown@5.0.0 and the root must rebuild from it —
but the pre-exit/release commit won't touch docs/** either. Without a working
post-publish deploy, the root would keep serving v4.3.0 docs after 5.0.0 is
published
, with no error anywhere.

The fix

Trigger on workflow_run of the Release workflow instead. workflow_run
events run against the default branch, so the ref is main, the existing policy
passes, and no new secret or repository setting is needed.

Alternatives considered and rejected (all recorded in the workflow's comment
block, since that is where a future maintainer will look):

  • Dispatch with GITHUB_TOKEN — GitHub does not create new workflow runs
    from GITHUB_TOKEN-triggered events, so it would be a silent no-op.
  • Dispatch with RELEASE_PAT — documented upstream as Contents +
    Pull requests write only; no Actions write.
  • Trigger on the release commit's push to main — the "Version Packages"
    merge lands before publish, so the new tag does not exist yet. At GA this
    would build the wrong version while appearing to succeed.
  • Widening the environment's tag policy — viable, but needs two patterns
    (unthrown@* and @unthrown/*@*, since the publishing run's tag varies per
    release and * does not match the / in a scoped tag). Kept as the
    documented fallback.

Also removes the seven-runs-racing burst: a monorepo release fired one run per
package Release and cancelled six of them. workflow_run fires once.

Known risk, documented in the file

Release is itself workflow_run-triggered, so this is a chain:
CI → Release → Deploy Documentation. Chained workflow_run triggers are not
guaranteed to fire, and this cannot be tested without a real release.

What to check on the next release: a Deploy Documentation run should appear
with event workflow_run and ref main, the Deploy job should not be rejected,
and /beta/ should advertise the newly published version. If no run appears,
apply the fallback documented in the workflow header. workflow_dispatch is
retained and deploys from main manually in the meantime.

Checklist

  • The full gate passes locally
  • Added/updated tests — n/a, CI plumbing
  • Added a changeset — n/a, no package runtime or API change
  • Updated TSDoc and CLAUDE.md if the public surface or a design rule changed — n/a
  • Commits follow Conventional Commits

🤖 Generated with Claude Code

`deploy-docs.yml` triggered on `release: published`, which runs against the
release *tag*. The `github-pages` environment allows deployments only from
`main` (custom branch policies, no tag policies), so the Deploy job was
rejected before running a step -- this path never worked (beta.8 and beta.9
both failed). `origin/main` is at 5.0.0-beta.9 while the live /beta/ site
still advertises beta.8, and at GA the root site would keep serving v4.3.0.

Trigger on `workflow_run` of the Release workflow instead: a `workflow_run`
event runs against the default branch, so the ref is `main` and the existing
environment policy passes -- no new secret, no repository-settings change.
The jobs are gated on a successful Release; `push` on `docs/**` and
`workflow_dispatch` are unchanged.

Release is itself `workflow_run`-triggered (on CI), so this is a chained
`workflow_run`, which GitHub does not guarantee will fire. The comment block
documents that risk, the manual-dispatch fallback, and the tag-policy fallback.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 28, 2026 15:14

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the documentation deployment workflow so that docs are deployed from main after the Release workflow completes, avoiding GitHub Pages environment protection failures that occur when deploying from a release tag ref.

Changes:

  • Switches the docs deploy trigger from release: published to workflow_run on the Release workflow (branch main).
  • Adds detailed rationale + fallback documentation in the workflow header.
  • Gates the build job so workflow_run-triggered deploys only proceed after a successful Release conclusion.
Comments suppressed due to low confidence (1)

.github/workflows/deploy-docs.yml:69

  • For workflow_run, this workflow executes on the default branch and ${{ github.sha }} can be the current main HEAD, not the triggering Release run’s head_sha. If main advances between the Release completion and this workflow starting, the deploy can build from the wrong commit/version. Pin actions/checkout to github.event.workflow_run.head_sha for workflow_run events.
    if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v7

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread .github/workflows/deploy-docs.yml
`workflow_run` has no conclusion filter in `on:`, so a failed or cancelled
Release still starts a Deploy Documentation run. Workflow-level concurrency is
evaluated when the run is created — before any job-level `if:` — so that run
joined the `pages` group and could cancel a real in-flight Pages deploy before
skipping its own `build`, silently leaving the site stale.

Make the group conditional: a run that cannot deploy gets a throwaway
`pages-noop-<run_id>` group; every deploy-capable path (successful
`workflow_run`, `push` on docs/**, `workflow_dispatch`) still resolves to
`pages` and serialises exactly as before. The `build` job's `if:` is kept — it
is what prevents the deploy; the group only governs contention.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@btravers
btravers merged commit d702c58 into main Jul 28, 2026
14 checks passed
@btravers
btravers deleted the fix/docs-deploy-trigger branch July 28, 2026 15:24
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.

2 participants