Publish a Keep a Changelog file as the release notes - #104
Merged
Conversation
The snippet pinned VERSION=v0.1.0, two releases behind, so a reader following the install instructions verbatim downloaded a binary without --assert-jq, -L, --retry or brotli support, and with the pre-v0.2.0 exit codes. The line names a tag rather than resolving the latest one on purpose: the comment above it already tells the reader to pick their own, and resolving it would mean parsing the GitHub API with jq, in a README whose argument is that you should not need jq to check an endpoint. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MxhyKzFVP99To7pYTxMyua
Release notes were GitHub's generated commit list, which is what this tool's users are least able to act on. Upgrading from v0.1.0 to v0.2.0 meant reading 39 commit subjects, 25 of them docs, chore or test, to find the one line that mattered: exit codes 91 and 103 are gone and transport failures moved from 93 to 92. A tool whose whole contract is its exit codes had buried a change to them at position 23 of 39. The format is Keep a Changelog 1.1.0. Its two categories a commit type cannot express -- Deprecated and Security -- are the two a CLI needs most, and dropping every docs, chore, test and refactor commit removes 63% of the history as noise. Backfilled to v0.1.0 only. Everything up to v0.0.7 predates Conventional Commits and the transfer from PlanitarInc: 48 commits that are mostly merges, Dependabot bumps and four "Update README.md"s, from which honest user-facing entries cannot now be recovered. Reconstructing them would invent prose nobody can verify, which is the inconsistency the format warns against, so that period is named and pointed at the git history instead. No Security section. The one candidate was a Dependabot bump of the indirect golang.org/x/net, which names no CVE; writing "Security" over an unverified vulnerability is worse than silence. The dependency reduction that is substantiated -- viper's removal, 35 of 38 non-stdlib dependencies -- is recorded under v0.1.0 as a change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MxhyKzFVP99To7pYTxMyua
CHANGELOG.md is now the release body, so the curated file and the published notes cannot disagree. `just release-notes <tag>` prints one section and goreleaser takes it verbatim through --release-notes, which short-circuits its own generation. A changelog that is merely decorative rots, and the format's own warning is that a changelog mentioning only some changes is more dangerous than none, because it still reads as the source of truth. Two things hold it current. The release extraction fails when the tag's section is missing or empty, which stops the release before any artifact is published; that is the moment a forgotten entry is still cheap to fix, unlike a tag already on the module proxy. And a pull request touching Go sources without touching CHANGELOG.md gets a warning, not a red build, for the reason toolchain-check warns: plenty of Go changes are genuinely invisible to a user, and failing on those would train everyone to click past the one that matters. Two goreleaser details drove the shape. `changelog.disable: true` looks like the way to stop the commit dump and is not -- it discards a --release-notes file too and publishes empty notes -- so the pipe stays enabled and unreachable, documented in place. And the notes file cannot live in dist/, because `release --clean` removes that directory before it reads anything; it goes to the runner's temp directory instead. Verified against the real pipeline at v0.3.0 with publishing skipped: goreleaser logs `loading file` for the extracted section and generates nothing of its own. CHANGELOG.md now also ships inside every release archive. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MxhyKzFVP99To7pYTxMyua
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Upgrading
http-assertmeans reading its commit log to find out what changed, exit codes included.Release notes are GitHub's generated commit list, so v0.2.0 shipped 39 subjects — 25 of them
docs,choreortest— with the one entry that could break a pipeline sitting at position 23: exit codes91and103were removed and transport failures moved from93to92. For a tool whose entire contract is its exit code, that is the wrong thing to bury. Nothing about the format can surface it either, because a commit type has no way to say deprecated or breaking, and every docs commit is weighted the same as every fix.Solution
Add a Keep a Changelog file, backfill it to v0.1.0, and make it the release body so the two cannot drift.
just release-notes <tag>prints one section; goreleaser takes it verbatim through--release-notes, which short-circuits its own generation. The same file ships inside every release archive.Release body before (generated, v0.3.0):
Release body after (the file's own section):
Keeping it honest
A decorative changelog is worse than none — it still reads as the source of truth while telling only part of the story. Two things hold it current, deliberately at different strengths:
*.gowithoutCHANGELOG.mdtoolchain-checksequenceDiagram participant Dev participant CI as "Release workflow" participant GR as "goreleaser" participant GH as "GitHub release" Dev->>CI: "push tag v0.4.0" CI->>CI: "just release-notes v0.4.0" Note over CI: "exits 1 if the section is missing or empty" CI->>GR: "release --clean --release-notes=..." GR->>GH: "publish the section verbatim"Why the goreleaser config looks odd
changelog.disable: trueis the obvious way to stop the commit dump and is the wrong one: it discards a--release-notesfile too and publishes empty notes. The pipe therefore stays enabled and unreachable, with a comment saying so, rather than being deleted or disabled.The notes file also cannot live in
dist/—release --cleanremoves that directory before it reads anything, so a file written there is gone by the time goreleaser looks for it. It goes to the runner's temp directory.Verified against the real pipeline at
v0.3.0with publishing skipped: goreleaser logsloading filefor the extracted section and generates nothing of its own.Scope of the backfill
v0.1.0 onwards only. Everything up to v0.0.7 predates Conventional Commits and the transfer from
PlanitarInc— 48 commits that are mostly merges, Dependabot bumps and fourUpdate README.mds. Honest user-facing entries cannot be recovered from that, and inventing them is the inconsistency the format warns against, so the period is named and pointed at the git history instead.There is no
Securitysection. The only candidate was a Dependabot bump of the indirectgolang.org/x/net, which names no CVE; writing "Security" over an unverified vulnerability is worse than silence. The dependency reduction that is substantiated — viper's removal, 35 of 38 non-stdlib dependencies — is recorded under v0.1.0 as a change.No visual change: this is a CLI, and the only rendered output that moves is the GitHub release body, shown above.
Other Changes
VERSION=v0.1.0, two releases behind, so a reader following it verbatim got a binary without--assert-jq,-L,--retryor brotli, and with the pre-v0.2.0 exit codes. Nowv0.3.0, and bumping it is step 3 of the release checklist added to the Development section.CHANGELOG.mdis included in every release archive alongsideREADME.mdandLICENSE.🤖 Generated with Claude Code