Versioning
Releases are tagged vX.Y.Z, and the vX major tag moves to the latest compatible release, following the r-lib/actions convention. @v1 was frozen at the pre-2.0.0 snapshot when the breaking quarto-publish change cut @v2, so it has picked up no fixes since - not even non-breaking ones. Pin preview.yml, preview-deploy.yml, cleanup-pr-previews.yml, quarto-publish.yml, check-bibliography-dois.yml, check-phi.yml, check-links.yml, check-non-standard-chars.yml, claude.yml, claude-code-review.yml, update-snapshots.yml, and check-news.yml to @v2; test-coverage.yml, check-equation-renders.yml, lint-yaml.yml, lint-markdown.yml, lint-qmd.yml, lint-changed-lines.yml, lint-changed-files.yml, check-new-line-breaks.yml, check-secrets.yml, check-junk-files.yml, request-dependabot-review.yml, sync-upstream.yml, altdoc-multiversion-docs.yml, report-failure.yml, gemini.yml, gemini-code-review.yml, antigravity-code-review.yml, cursor-code-review.yml, opencode-code-review.yml, ai-code-review.yml, bump-dev-version.yml, version-check.yml, small-model-agent.yml, check-ai-tells.yml, lint-workflows.yml, spellcheck.yml, check-typos.yml, check-extra.yml, check-formatting.yml, claude-manage-project.yml, r-cmd-check.yml, and check-code-similarity.yml only ever shipped at @v2 (too new to exist at the frozen @v1 tag). See Workflows for the full breakdown, including which capabilities still pin @v1 and why. Breaking changes bump the major tag (v2, and so on) with migration steps.
Widening permissions is a breaking change
A reusable workflow’s job-level permissions: block is part of its caller contract: a nested job cannot request more than the caller grants, so adding a permission - even a read - fails every caller written against the old contract at parse time (Invalid workflow file), with no API-visible diagnostic (a startup_failure run exposes no jobs, logs, or annotations; the error text appears only on the run page in the UI). gha#685 measured this when gha#638 added issues: read to ai-code-review.yml and the v2 slide delivered it: every consumer run concluded startup_failure until the caller-side grant landed. So treat a permissions: widening like any other breaking change: prefer a major-version bump; where a bump is disproportionate, sweep the registered consumers (REVDEPS.md) and PR the caller-side grant before sliding the tag, and name the required caller edit in the change’s changelog fragment.
Advancing a major tag
Unlike r-lib/actions, a major tag here does not slide automatically on every push to main - merging a change does not, by itself, change what a consumer pinned to @v1/@v2 picks up next. Advancing the tag is a deliberate, manual step, so a change can be validated on one or a few consumer repos before everyone else has to deal with it:
- Merge the change to
main. - Optionally, point a consumer’s
uses:line at@main, at a specific commit SHA, or temporarily at a feature branch of this repo, and let that consumer’s own CI run against the unreleased change. - Once confident, advance the shared major tag to
main’s current tip by runningslide-major-tag.ymlviaworkflow_dispatch(frommain), or withgit tag -f/git push --forcedirectly. Every consumer pinned to that tag picks up the change the next time its CI runs.
A brand-new capability’s own PR merging to main does not make it usable at @v2 yet - if a consumer repo’s PR needs to reference a just-merged workflow at @v2 right away, check whether @v2 has actually been advanced past that merge first (step 3 above), or the consumer’s reference resolves to a workflow-not-found error rather than a stale-but-working one.
To prevent manual tag advancement from being forgotten when main moves ahead of the major tag, a tag drift check (check-tag-drift) runs as part of CI to surface when main has unreleased commits ahead of the active major tag (e.g. v2). It emits a GitHub Actions notice and job summary pointing to slide-major-tag.yml whenever tag advancement is pending.
See CHANGELOG.md for what changes as a major tag moves.
Changelog entries are added as fragment files under changelog.d/ (one per PR, so parallel PRs never conflict on the shared changelog) and collated into CHANGELOG.md at release time; see changelog.d/README.md for the fragment format and release flow.
Pinning third-party actions
Every third-party action is pinned to a full commit SHA, with the human-readable version in a trailing comment:
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0This follows GitHub’s recommended hardening posture: a SHA is immutable, so a re-pointed tag or a compromised upstream cannot silently change what runs. That matters here because jobs like the preview deploy run with contents: write and pull-requests: write. .github/dependabot.yml bumps these pins as upstreams publish releases, so they stay current instead of freezing. When adding a new third-party action, pin it the same way.
First-party Morrison-Lab/gha/* self-references and most examples/ templates intentionally track the moving major tag (currently @v1, except preview.yml, preview-deploy.yml, cleanup-pr-previews.yml, quarto-publish.yml, test-coverage.yml, check-equation-renders.yml, check-bibliography-dois.yml, check-phi.yml, check-links.yml, check-non-standard-chars.yml, claude.yml, claude-code-review.yml, update-snapshots.yml, lint-yaml.yml, lint-markdown.yml, lint-qmd.yml, lint-changed-lines.yml, lint-changed-files.yml, check-new-line-breaks.yml, check-secrets.yml, check-junk-files.yml, request-dependabot-review.yml, sync-upstream.yml, check-news.yml, altdoc-multiversion-docs.yml, report-failure.yml, gemini.yml, gemini-code-review.yml, antigravity-code-review.yml, cursor-code-review.yml, opencode-code-review.yml, ai-code-review.yml, bump-dev-version.yml, version-check.yml, small-model-agent.yml, check-ai-tells.yml, lint-workflows.yml, spellcheck.yml, check-typos.yml, check-extra.yml, check-formatting.yml, claude-manage-project.yml, r-cmd-check.yml, and check-code-similarity.yml at @v2 - see the versioning note above), so they are not SHA-pinned.
Reverse dependencies
REVDEPS.md tracks repos that call these workflows, so consumers can be notified before a breaking change. If your repo uses gha, please add it there.