Skip to content

feat(release): remind on milestone instead of automating the release - #648

Merged
FumingPower3925 merged 2 commits into
mainfrom
feat/release-checklist-on-milestone
Sep 15, 2026
Merged

FumingPower3925 merged 2 commits into
mainfrom
feat/release-checklist-on-milestone

Conversation

@FumingPower3925

Copy link
Copy Markdown
Contributor

Removes the machinery that tried to drive a release, and replaces it with a reminder early enough to act on.

Why the automation could not be made safe

GitHub fires no workflow for the created, edited or deleted activity types on draft releases. So nothing can prepare anything while a release is still a draft, and the tag exists the instant Publish is clicked.

A stale version stamp at that moment could only be repaired by moving the tag. A published tag must never move: every one is recorded in sum.golang.org, where a moved tag hands existing consumers a checksum mismatch that reads as a supply-chain compromise. Any workflow attempting that repair is racing the module proxy.

The stamps therefore have to be right before the tag exists, and the reliable way to get there is a reminder, not machinery.

What you get instead

When a version milestone is created, an issue opens titled Release checklist: vX.Y.Z, assigned to that milestone, listing every version and where it lives. Milestones for the non-release tracks are ignored.

This is what it renders for v1.7.0:


Opened automatically because the v1.7.0 milestone was created. Close it when every stamp below reads 1.7.0.

Only the celeris stamps are enforced: mage CheckRelease runs in Lint on every pull request and fails while any of them disagrees. Everything under the second heading is enforced by nothing, which is why it is written down here.

celeris — enforced by mage CheckRelease

One command does all of these:

VERSION=v1.7.0 mage PrepRelease

Or by hand:

  • server.goconst Version = "1.7.0"
  • README.md## What's new in v1.7.0
  • middleware/compress/go.modgithub.com/goceleris/celeris v1.7.0
  • middleware/metrics/go.modgithub.com/goceleris/celeris v1.7.0
  • middleware/otel/go.modgithub.com/goceleris/celeris v1.7.0
  • middleware/protobuf/go.modgithub.com/goceleris/celeris v1.7.0
  • README.md — replace the placeholder under that heading with the real release prose. CheckRelease fails while the placeholder remains, so this one cannot be forgotten silently.

The other three repositories — enforced by nothing

  • loadgen version.go, const fallbackVersion — only if loadgen is being released too; it versions independently of celeris.
  • loadgen internal/integrationtest/testserver/go.mod — the celeris pin.
  • probatorium — repin celeris in all ten modules that require it, to the released tag rather than a pseudo-version:
    for m in $(grep -rl 'goceleris/celeris v' --include=go.mod . | grep -v .claude); do
      (cd "$(dirname "$m")" && go get github.com/goceleris/celeris@v1.7.0 && go mod tidy)
    done
  • docs — publish this version's benchmark results, so the dashboard is not left a release behind. It has been, for three releases running.

After you publish the release

These run on their own from .github/workflows/release.yml. They are listed so a failure is recognisable rather than invisible:

  • middleware/compress/v1.7.0 is tagged
  • middleware/metrics/v1.7.0 is tagged
  • middleware/otel/v1.7.0 is tagged
  • middleware/protobuf/v1.7.0 is tagged
  • the Go module proxy is asked for the root module and every sub-module, then re-queried to confirm each is really available

Neither runs if the stamps disagree. A sub-module tag pinning the wrong celeris version cannot be corrected afterwards: every published tag is recorded in sum.golang.org, and moving one gives existing consumers a checksum mismatch that reads as a supply-chain compromise. The remedy for a bad published version is always a new version.


Why it cannot go stale

The body is generated from the same list mage CheckRelease enforces, so adding a stamp adds a line. Those facts move to internal/release for one reason: the mage files are package main behind a build tag, so nothing in them is reachable by go test ./... and none of it was covered. A checklist that drifts is worse than none, because it sends someone to edit a file that no longer carries a version.

What is left of release.yml

Only what cannot get the version wrong, because it reads it off the tag you created: verify the stamps, tag the four sub-modules, warm the Go proxy. The workflow_dispatch path and the job that created the tag are gone.

Nothing downstream runs if the gate fails, and that is now an explicit success check rather than treating a skipped upstream job as permission to continue. An earlier version of that condition did exactly that and burned a version on the proxy after the gate had declined it.

CheckRelease and PrepRelease are untouched. Neither is release automation. One reports drift on every pull request, which is what caught the sub-module pins sitting four releases behind (#619). The other is a single command the checklist tells you to run.

Controls

injected result
hardcode the stamp list instead of iterating it fails, naming the two stamps whose target line vanished
drop probatorium from the unenforced list fails, naming it and the step it carries
hardcode the version in the bump command fails on both the leaked version and the missing one
loosen the version pattern fails, naming the milestone titles that would wrongly get a reminder

Each injection was asserted against the real source before editing. Full suite, mage compiles and golangci-lint run ./... all clean.

Removes the machinery that tried to drive a release, and replaces it with
a reminder early enough to act on.

Why the automation could not be made safe. GitHub fires no workflow for
the created, edited or deleted activity types on DRAFT releases, so
nothing can prepare anything while a release is still a draft, and the
tag exists the instant Publish is clicked. A stale version stamp at that
moment could only be repaired by moving the tag -- and a published tag
must never move, because every one is recorded in sum.golang.org, where
a moved tag gives existing consumers a checksum mismatch that reads as a
supply-chain compromise. Any workflow doing that is racing the module
proxy.

So the stamps have to be right BEFORE the tag exists, and the reliable
way to get there is a reminder, not machinery.

Release Checklist opens an issue when a version milestone is created,
listing every version and where it lives: the four celeris stamps that
CheckRelease enforces, and the work in loadgen, probatorium and docs
that nothing enforces at all. The body is generated from the same list
CheckRelease uses, so it cannot drift from what is actually checked.

The stamp facts and the renderer move to internal/release, because the
mage files are package main behind a build tag and nothing in them is
reachable by go test ./... . A checklist that drifts would send someone
to edit a file that no longer carries a version.

release.yml keeps only what cannot get the version wrong, because it
reads it off the tag: verify the stamps, tag the sub-modules, warn the
proxy. The dispatch path and the job that created the tag are gone.
Nothing downstream runs if the gate fails, and that condition is now an
explicit success check rather than a skipped-counts-as-fine one.

CheckRelease and PrepRelease are untouched. Neither is release
automation: one reports drift on every pull request, the other is a
single command the checklist tells you to run.

Controls: hardcoding the stamp list instead of iterating it, dropping
probatorium from the unenforced list, hardcoding the version, and
loosening the version pattern so a non-release milestone would get a
reminder -- each fails its test, naming what it broke.
@FumingPower3925 FumingPower3925 added this to the v1.6.0 milestone Sep 15, 2026
@FumingPower3925
FumingPower3925 merged commit 468ce53 into main Sep 15, 2026
10 checks passed
@FumingPower3925
FumingPower3925 deleted the feat/release-checklist-on-milestone branch September 15, 2026 09:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant