Skip to content

Latest commit

 

History

History
192 lines (142 loc) · 7.85 KB

File metadata and controls

192 lines (142 loc) · 7.85 KB

Releasing ACOR

This document describes how maintainers cut a release. It reflects the actual automation in the repo, not an aspirational process.

Overview

Concern Tool Config
Versioning SemVer git tags vX.Y.Z
Changelog changie .changie.yaml, changes/
Build & publish GoReleaser .goreleaser.yaml
Release trigger GitHub Actions on tag push .github/workflows/release.yaml

changie is the source of truth for release notes. Its per-version file (changes/vX.Y.Z.md) is what GoReleaser publishes as the GitHub release body.

Prerequisites

The release workflow itself needs no manual secrets — it uses the built-in GITHUB_TOKEN for both the GitHub release and pushing images to GHCR.

Run every command below from the repo root. The v prefix is mandatory and must match across the board: changie batch vX.Y.Z writes changes/vX.Y.Z.md, and the tag build guards on exactly changes/<tag>.md — so the changie version and the git tag have to be the identical vX.Y.Z string.

During development (every contributor)

Each change that should appear in the changelog gets a fragment:

changie new

This prompts for a kind (Added, Changed, Deprecated, Removed, Fixed, Security, Documentation), a body line, and the issue number. It writes a YAML fragment under changes/unreleased/. Commit it with your change.

The body is one sentence, two at the very most — the second only when it carries a consequence for the reader: a migration step, a measured number, or what deliberately did not change. These fragments become the GitHub release body verbatim, so they are read by people deciding whether to upgrade, not by people reviewing the diff; the investigation and the file list stay in the pull request. .changie.yaml enforces the shape with block: false and maxLength: 400, and CONTRIBUTING.md shows examples.

Cutting a release (maintainer)

  1. Confirm main is green and holds everything you want to ship.

  2. Pick the version (vX.Y.Z, SemVer). Base the bump on the kinds of the unreleased fragments in changes/unreleased/ — the major-version call is always yours.

    Then have the toolchain check the choice against the previous release:

    go run golang.org/x/exp/cmd/gorelease@v0.0.0-20260727155853-b88d891fe743 -base=vPREV.Y.Z

    gorelease compares types, not text, and reports the version the changes actually require. It refuses to run on a dirty tree, which is why this is a release step rather than a CI gate — CI uses make api-check against api/v1.txt instead, which works mid-edit and needs no published baseline. Treat a reported incompatible change inside v1 as a release blocker: the fix is to restore what was removed, not to bump the version.

  3. Batch the fragments into a version file:

    changie batch vX.Y.Z

    This consumes changes/unreleased/* and writes changes/vX.Y.Z.md.

  4. Regenerate the changelog:

    changie merge

    Rebuilds CHANGELOG.md from changes/header.tpl.md + all version files.

  5. Commit and merge to main via PR:

    git switch -c release/vX.Y.Z
    git add CHANGELOG.md changes/
    git commit -m "chore: release vX.Y.Z"
    git push -u origin release/vX.Y.Z

    Open the PR, get it green, and merge.

  6. Tag and push once merged. Sync main first so the tag lands on the merge commit, not your local release branch:

    git switch main && git pull
    git tag vX.Y.Z
    git push origin vX.Y.Z

    The tag push triggers .github/workflows/release.yaml.

What the tag build does

release.yaml runs on any v* tag and:

  1. Guards that changes/<tag>.md exists — fails fast if you forgot changie batch / changie merge.
  2. Runs GoReleaser with --release-notes changes/<tag>.md, which produces:
    • Binaries for darwin / linux / windows across 386, amd64, arm, arm64 (see .goreleaser.yaml for excluded combos), packaged as .tar.gz (.zip on Windows), each bundling LICENSE, README.md, CHANGELOG.md, CODE_OF_CONDUCT.md.
    • A CHECKSUMS file (sha256).
    • Docker images pushed to ghcr.io/skyoo2003/acor, tagged <tag>-alpine, vMAJOR.MINOR-alpine, vMAJOR-alpine, and latest-alpine (built from Dockerfile.goreleaser).
    • A GitHub release named vX.Y.Z with the changie notes as its body.

Release mode is replace, so re-running the tag build overwrites the existing release's assets rather than appending.

The v* glob also matches pre-release tags: pushing vX.Y.Z-rc.1 runs the same pipeline, so it needs its own changes/vX.Y.Z-rc.1.md and would move latest-alpine onto the RC. This flow assumes final vX.Y.Z tags — don't push a pre-release tag unless you mean to.

Verify

  • GitHub release exists with the expected notes and artifacts.
  • docker pull ghcr.io/skyoo2003/acor:vX.Y.Z-alpine works.
  • latest-alpine points at the new version.

Rollback

To redo a botched release: fix the fragments/changelog, delete the tag locally and remotely (git push origin :vX.Y.Z), then re-tag and push. The build recreates the release in place (replace mode, above).

Watch the Docker tags: deleting the git tag does not remove images already in GHCR — they stay until a build overwrites them. And latest-alpine (plus vMAJOR-alpine / vMAJOR.MINOR-alpine) always points at whichever tag built last, so re-running an older tag's build silently drags latest-alpine back to that older version. When redoing an older release, re-tag the newest one last.

Retracted versions

v1.0.0-v1.4.0 were published from tags that no longer exist here. Deleting a tag does not unpublish a module version — proxy.golang.org caches it forever — so they stayed resolvable, and go get github.com/skyoo2003/acor picked v1.4.0 over the v0.x line. They are retracted in the root go.mod:

retract [v1.0.0, v1.4.0]

v1.5.0 is the first supported v1 release. That is why the numbering jumps from v0.11.x: everything below v1.5.0 in the v1 line is retracted, and the range stops at v1.4.0 so it can never cover a real release.

There is no separate step to publish the retractions — they take effect with the release whose go.mod carries them. Two rules when editing this:

  • Keep the block on main. The go command reads retractions only from the highest published version's go.mod, so the next release silently un-retracts the old versions if the block is missing. CI enforces this — the Verify retraction block step in ci.yaml fails if the line is gone, so the rule no longer depends on anyone remembering it.
  • Only the first comment line reaches users. The go command truncates a retraction rationale at the first newline, so keep that line a complete, actionable sentence.

After tagging a release that carries the block, verify from a scratch module outside this repo, with vX.Y.Z standing for the tag just pushed:

curl -s https://proxy.golang.org/github.com/skyoo2003/acor/@v/vX.Y.Z.info
go list -m github.com/skyoo2003/acor@latest     # want vX.Y.Z
go list -m -versions github.com/skyoo2003/acor  # v1.0.0-v1.4.0 must be gone

The curl is needed because the proxy has to fetch the release before its retractions apply. Do not add -retracted: it lists retracted versions alongside the rest, so the output is the same before and after.