Repository navigation
Prereleases
letsgo tag --pre
letsgo promote <rc-tag>
letsgo update --channel <name>
A prerelease is a real release that most consumers should not get. letsgo keeps that distinction in every channel it publishes to — the tap, the registry, the updater — rather than only in a checkbox on the release page.
letsgo tag --pre proposes a prerelease instead of a stable version, as
-rc.N, and otherwise works exactly as letsgo tag does:
reading conventional commits, and diffing the exported API where the module has
one.
The channel is the first dot-separated prerelease identifier, so rc.1 is
the rc channel and beta.3 is beta.
-
Homebrew. A prerelease never writes the stable formula
foo. Every release — stable or not — writesfoo@next, pointing at whichever is newer of the newest prerelease and the newest stable. Sobrew install you/tap/foois always the stable line andfoo@nextis always the bleeding edge, with no moment where one silently becomes the other. -
Docker. A prerelease pushes
<version>and<channel>. A stable release pushes<version>and moves<major>.<minor>,<major>,latestand each channel tag only if it is newer than what that tag already points at. Av1.2.9backported afterv1.3.0moves1.2and nothing else. Build metadata maps+to_, which Docker tags allow and semver does not. -
Self-update.
selfupdate.Options.Channelselects: empty is stable, a name is stable plus that channel, and"next"is stable plus any prerelease. Drafts and yanked releases are never selected.
letsgo update --channel rc # stable or rc, whichever is higher
letsgo update --channel next # any prerelease
letsgo update # stable onlyNotes, the API gate and letsgo tag all ask the same question — what came
before this? — and they all get the same answer, which depends on the kind of
release being made:
- the previous release of a prerelease is the highest release of any kind
below it, so
v1.3.0-rc.2compares againstrc.1; - the previous release of a stable release is the highest stable release
below it, so
v1.3.0compares againstv1.2.8and not against its own RCs.
That is what makes a stable release's notes cover everything since the last stable, rather than only the handful of commits since the final RC.
letsgo promote v1.3.0-rc.1 turns an RC into the stable release it was
rehearsing:
- The RC release is set back to prerelease and not-latest, unconditionally and first. Whatever else happens, the repository does not end the run with an RC sitting on the latest badge.
-
v1.3.0is tagged on the RC's own commit. - The release is rebuilt and compared against the RC's manifest. The only differences allowed are the version string, the archive names, and the digests those imply. Anything else — a changed toolchain, a moved dependency — fails the promotion, listing the fields.
- A new stable release is created and marked latest.
The RC is not modified. Its tag, assets and notes stay exactly as published,
so letsgo verify v1.3.0-rc.1 still passes afterwards. The stable manifest
records promoted_from: {tag, manifest_sha256}, and the stable notes cover
everything since the previous stable with a collapsed "Prerelease history"
section.
Promotion is rebuilt rather than copied because a promotion that renamed the RC's archives would be shipping bytes whose recorded version does not match the version in their own name.
| Flag | Description |
|---|---|
--yes |
Promote without asking |
--append-notes |
Add the notes after an existing description instead of replacing it |
--work |
Scratch directory for the rebuild |
--token |
Forge token (default: $GITHUB_TOKEN or $GH_TOKEN) |
--tap-token |
Token for the Homebrew tap, when it lives elsewhere |
--release-token |
Token for the release, when it differs from the tap's |
It refuses a yanked RC, a draft, a tag that is not a prerelease, and a target tag that already exists. It is re-runnable after a failure, resuming from the step that failed.
Flipping an RC from pre-release to release in the UI fires release: released,
which a workflow can turn into a promotion:
on:
release:
types: [released]
jobs:
promote:
# released also fires for ordinary stable releases; only a prerelease tag promotes.
if: contains(github.event.release.tag_name, '-')
runs-on: ubuntu-latest
permissions: { contents: write, id-token: write, attestations: write }
steps:
- uses: actions/checkout@v7
with: { ref: '${{ github.event.release.tag_name }}', fetch-tags: true }
- uses: actions/setup-go@v7
with: { go-version-file: go.mod }
- uses: danielriddell21/letsgo-action@v1
with:
command: promote
args: ${{ github.event.release.tag_name }}Editing a release with the default GITHUB_TOKEN does not start workflows, so
use an App token if the flip should trigger this automatically. Either way a
re-trigger is harmless: promote refuses once v1.3.0 already exists.
release prerelease=true in letsgo.mod publishes every release from that
config as a prerelease, rather than having a workflow edit the release
afterwards. Promotion stays a manual act, and marking the release as the full
release is what fires released.
Start here
Releasing
Checking
Extending
Running it
About