diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 090d5a6..9cec328 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -62,3 +62,18 @@ jobs: args: release --clean env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # .goreleaser.yaml builds the release as a draft so every archive is + # attached before anyone can see it. Publishing is the separate step, + # because with release immutability enabled a published release has its + # assets and its git tag locked — an upload arriving afterwards has + # nothing to attach to. + # + # If this step fails the draft survives, holding every artefact, and can + # be published by hand. That is the outcome worth having: an unpublished + # complete release rather than a published incomplete one. + - name: Publish the draft release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ github.ref_name }} + run: gh release edit "$TAG" --draft=false diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 1377847..ca54ae1 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -36,3 +36,27 @@ changelog: exclude: - "^docs:" - "^test:" + +release: + # GoReleaser defaults this to false, so without it a tag like v1.0.0-alpha.1 + # publishes as an ordinary release and takes GitHub's "Latest" badge — an + # alpha handed to everyone who lands on the repository as the current stable + # version. The workflow reports success either way, so nothing catches it. + # "auto" marks the release as a prerelease when the tag carries a prerelease + # indicator — v1.0.0-alpha.1, v1.0.0-rc1 and so on. GoReleaser documents the + # rule only as "in case there is an indicator for this in the tag", without + # saying what counts, so an unusual suffix is worth checking on the release + # itself rather than assuming. + prerelease: auto + + # Publish as a draft and let the workflow flip it live once every archive is + # attached. This is GitHub's prescribed order for immutable releases: assets + # and the git tag are locked the moment a release is published, so anything + # uploaded afterwards has to fight the lock. Creating the release complete + # and then publishing it means there is never an incomplete published + # release to repair. + # + # It is also the safer failure mode without immutability: a run that dies + # midway leaves an unpublished draft rather than a live release missing half + # its binaries. + draft: true