Write the GitHub release from the tag that publishes it - #55
Merged
Conversation
This workflow published five versions to npm and made no GitHub release for any of them. Nobody noticed until somebody asked the API for the latest one: `releases/latest` answered 404, the repository showed no Latest badge, and the releases page was 37 rolling `render-gate-bundle` prereleases with no version among them. For a repository whose whole job is to be installed by strangers, that is the first page they see. The five were written by hand today. This is so there is no sixth. It goes in the job that already has `contents: write` and already runs after a successful publish - so `publish` keeps `contents: read`, which the comment there asks for explicitly, and no release is written for a publish that did not happen. The job is renamed `after-publish`, because it does two things now and `major-tag` named only one of them. Three decisions worth the words: - **A missing CHANGELOG section warns, it does not fail.** The packages are on npm by the time this runs. Notes that are missing are visible on the page and editable in a minute; the release is still created, pointing at the file. A red run for something cosmetic is how people learn to ignore red release runs - unlike the stale alias next to it, which is silent and serves the wrong code, and rightly fails. - **`make_latest` is computed, not hard-coded.** A patch backported to an older line (v0.1.3 after v0.2.0) is a release but not the latest one, and saying otherwise would point every reader at older code. - **A re-run leaves an existing release alone.** Same doctrine as the publish step: find the work done and move on. Rewriting it would destroy notes edited by hand on the page, which is the likeliest reason to re-run at all. `!cancelled()` on the new step so the order between it and the alias move costs nothing in either direction. Tested by running the script against this repository's real data with a fake `github`: the newest tag gets make_latest=true and 74 lines of notes, a v0.1.3 backport gets make_latest=false, a missing section warns and still creates, a non-version tag does nothing, a 422 is absorbed, a 500 propagates, and the heading match distinguishes `## 0.2.1` from `## 0.2.10`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PLDFPfAK1MGq6qHeC6KKWH
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.
Description
This workflow published five versions to npm and made a GitHub release for none of them. It surfaced when the API was asked for the latest one:
releases/latestanswered 404render-gate-bundleprereleases, with no version among themFor a repository whose job is to be installed by strangers, that is the first page they see. The five releases (
v0.1.0–v0.2.2) were written by hand today. This is so there is no sixth.Where it goes, and why there
Into the job that already has
contents: writeand already runs after a successful publish — so:publishkeepscontents: read, which the comment there asks for explicitlyThe job is renamed
major-tag→after-publish, because it does two things now and the old name described only one. Not a required status check anywhere (the workflow is tag- and dispatch-triggered), so the rename costs nothing.Three decisions worth the words
make_latestis computed, not hard-codedv0.1.3afterv0.2.0) is a release but not the latest one. Hard-codingtruewould point every reader at older code.!cancelled()on the new step, so the order between it and the alias move costs nothing in either direction: a failure here cannot skip the alias move, and a failed alias move cannot skip this.How to test
The script was run against this repository's real data with a fake
githubobject, since a workflow step cannot be exercised any other way before merge:v0.2.2make_latest=true, 74 lines of notes — identical to what the hand-written release gotv0.1.3make_latest=false✅ does not hijack Latest## 0.2.1vs## 0.2.10Also verified:
release.ymlparses as YAML with both jobs; the embedded script is syntactically valid in the async-wrapper formgithub-scriptexecutes it in;npm teststill green.The first real proof is the next release tag — and if the step misbehaves, it cannot damage the publish that precedes it.
Checklist
npm testpassespublishstillcontents: readRELEASING.mdfollows the rename and documents where the notes come fromGenerated by Claude Code