Repository navigation
changelog: the per-crate publish-dry-run entry #21
Workflow file for this run
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
| name: Release | |
| # The reference pipeline from docs/release-flow.md, dogfooded with local | |
| # action paths — in publish-go-live mode: no per-release signing. A candidate | |
| # run builds the draft and its rcN marker; the release manager merges the | |
| # merge-back PR and then publishes the draft, the one human gate. The draft's | |
| # target is pinned to the merged commit (the guidance summary prints the | |
| # command), so publishing creates vX.Y.Z on exactly the content the marker | |
| # sealed, and the tag push runs the final path: version coherence, the tree | |
| # seal, the (idempotent) flip, and the moving major. An optional signed | |
| # companion (vX.Y.Z-sig, the only tags the ruleset requires signatures on) | |
| # attests the release commit out of band. | |
| on: | |
| push: | |
| branches: ["release/v**"] | |
| # Exclude the signed provenance companion (vX.Y.Z-sig): it attests the | |
| # release commit out of band and carries no changelog section or marker, so | |
| # it must not drive the release pipeline — it routes to the attest workflow. | |
| tags: ["v*", "!v*-sig"] | |
| workflow_dispatch: # cut-release dispatches the first run explicitly | |
| permissions: | |
| contents: write | |
| jobs: | |
| gate: | |
| name: release readiness gate | |
| runs-on: ubuntu-latest | |
| # The moving major (v1) names no version to check. The pipeline pushes it | |
| # with the workflow token, which fires no events, but a maintainer moving | |
| # it by hand does — and without this the gate would derive "1" from the ref | |
| # and refuse the repository's own major tag. | |
| if: ${{ github.ref_type != 'tag' || contains(github.ref_name, '.') }} | |
| outputs: | |
| version: ${{ steps.expect.outputs.version }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Derive the expected version from the ref | |
| id: expect | |
| run: | | |
| set -euo pipefail | |
| # Only human-pushed final tags reach the pipeline; workflow-token | |
| # marker pushes trigger nothing. | |
| case "${GITHUB_REF_TYPE}:${GITHUB_REF_NAME}" in | |
| branch:release/v*) version="${GITHUB_REF_NAME#release/v}" ;; | |
| tag:v*) version="${GITHUB_REF_NAME#v}" ;; | |
| *) version="" ;; | |
| esac | |
| echo "version=${version}" >> "$GITHUB_OUTPUT" | |
| # No crate here: the declared version is the changelog's newest released | |
| # section, checked against the ref-derived one. | |
| - uses: ./.github/actions/check-release-readiness | |
| with: | |
| expected-version: ${{ steps.expect.outputs.version }} | |
| # The seal is four API calls and no build, so it belongs here: a tag that | |
| # does not carry the reviewed content fails in seconds, not after the | |
| # artifact jobs. | |
| - name: The tag must seal the newest candidate marker (early gate) | |
| if: github.ref_type == 'tag' | |
| uses: ./.github/actions/publish-draft-release | |
| with: | |
| version: ${{ steps.expect.outputs.version }} | |
| seal-only: "true" | |
| draft: | |
| name: build the draft pre-release (candidate path) | |
| needs: gate | |
| # Only release branches build candidates: a workflow_dispatch from any other | |
| # branch derives no version and must not create a draft. | |
| if: github.ref_type == 'branch' && startsWith(github.ref_name, 'release/v') | |
| runs-on: ubuntu-latest | |
| env: | |
| VERSION: ${{ needs.gate.outputs.version }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| # Notes render, draft create/refresh, marker push — one step. | |
| - uses: ./.github/actions/draft-release | |
| id: draft | |
| with: | |
| version: ${{ env.VERSION }} | |
| # publish-draft: the admin publishing the draft is the go-live. The step | |
| # refuses at candidate time if a signature rule still covers the version | |
| # itself, so the publish-created tag is never one the repository rejects. | |
| - uses: ./.github/actions/release-guidance | |
| with: | |
| version: ${{ env.VERSION }} | |
| marker-tag: ${{ steps.draft.outputs.marker }} | |
| commit: ${{ github.sha }} | |
| draft-url: ${{ steps.draft.outputs.url }} | |
| go-live: publish-draft | |
| publish: | |
| name: publish the release (final path) | |
| needs: gate | |
| # The vX.Y.Z tag normally arrives from the admin publishing the draft | |
| # (markers are workflow-token pushes and trigger nothing); a hand-pushed | |
| # tag runs the same seal. | |
| if: github.ref_type == 'tag' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| # Tree seal against the newest marker, the draft flip, and the moving | |
| # major — one step. | |
| - uses: ./.github/actions/publish-draft-release | |
| with: | |
| version: ${{ needs.gate.outputs.version }} | |
| moving-major: "true" |