Skip to content

The release path cannot be exercised without publishing a real release #23

Description

@jakewan

Context

Surfaced while removing git-cliff and rehoming the release ritual into CONTRIBUTING.md. Documenting how to cut a release is what made it clear there is no documented way to practise cutting one — and that the mechanism the repo already reaches for doesn't work.

Problem

The release workflow has never executed. The repository has no tags and no releases, so every step it performs — cosign signing, SLSA provenance attestation, GitHub Release creation, and the cross-compiled artifact matrix — is unproven.

just release-check cannot close that gap, and says so itself: it builds with --snapshot, which "skips cosign signing and never runs provenance (CI-only, OIDC-bound), so this proves the build, not the hardening." It never runs the workflow at all.

The repo already anticipates this. .goreleaser.yaml sets prerelease: auto for exactly this purpose, its comment naming v0.0.0-test as "the end-to-end hardening dry-run", and commit 7549c86's message states that "a throwaway prerelease tag remains the only real proof."

That tag cannot get through the workflow. release.yml strips the leading v from the tag, extracts the matching ## [<version>] section from CHANGELOG.md, and fails hard when nothing matches — and a throwaway tag has no changelog section by definition. Traced with the workflow's own extraction logic: v0.0.0-test resolves to version 0.0.0-test, matches no heading, yields an empty notes file, and exits 1 at the "No release notes found" guard.

The guard is not the defect. It is what prevents publishing a release with an empty body — one of the two failures fixed in 7549c86. So the two requirements are in genuine tension: the check that makes a real release correct is the same check that makes a rehearsal impossible.

The consequence is that the repository's highest-consequence workflow — the only one holding contents: write and id-token: write, and the one that publishes and signs public artifacts — is also its only completely unexercised one. Both defects found in it so far were found by reading it, not by running it, and the next one will surface on a real tag or not at all.

Suggested approaches

Options, not a prescription:

  • Let the notes extraction supply a placeholder body when the tag carries a prerelease component, keeping the hard failure for a release tag.
  • Document a dry-run procedure under CONTRIBUTING.md § Releases that adds a temporary matching changelog section, and names the cleanup it leaves behind (the tag, the published prerelease, the section).
  • Give the workflow an explicit dry-run trigger that runs build, sign, and attest without publishing, so no tag or release needs cleaning up afterward.

The first two keep the proof honest — they exercise the real tag-push path. The third is safer to repeat but proves slightly less, since the trigger differs from the one a real release uses.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions