RELEASING.md
Rama main · réplica de lectura del repositorio original.
# Releasing Zoomies
This is for the people who cut releases. Everything here is what
`.github/workflows/release.yml` and `internal/updates` already do; the page
exists so that none of it has to be rediscovered during a release that has gone
wrong.
## What a release is
A release is a GitHub release. Publishing one starts `release.yml`, which
builds the binaries for five platforms and the controller, agent and runner
images, signs `checksums.txt`, and attaches the binaries, the checksums, the
signature and the provenance to the release. A run can also be started by hand
with the `workflow_dispatch` input `tag`, for a tag that has no release yet, a
draft or a pre-release; a published full release is refused (see "When the build
fails" below).
`install.sh` resolves `latest` through GitHub's `/releases/latest` redirect, so
it can name a release whose files are not attached yet, and it waits a few
minutes for them. `zoomies upgrade` and the Updates page read the releases list
and skip any release that lacks `checksums.txt` or the binary for the machine
asking.
## The shape of a tag
| Kind | Tag | Where it is offered |
| --- | --- | --- |
| Full release | `v` and exactly three numbers: `v1.3.7` | `install.sh`, `zoomies upgrade`, the Updates page |
| Pre-release | `v`, numbers, a hyphen and a suffix: `v1.3.7-rc1`, `v0.1-alpha` | Nowhere by default. GitHub keeps it out of `/releases/latest` and the updater skips it |
| The `dev` channel | `dev` | Maintained by CI from `main`; publishing or editing it is skipped by the release workflow |
A full release has exactly three numbers because that is the only shape the
updater accepts. `updates.ValidTag` is `^v[0-9]+\.[0-9]+\.[0-9]+$`; the tag goes
into a download address and into a request a privileged helper reads, so the
check is strict on purpose. The version package orders three numbers and cannot
place a fourth, so a build stamped `1.3.6.1` compares as "differs" from every
other build.
The `setup` job in `release.yml` refuses a full-release tag of any other shape
before it builds anything, and a test in `internal/docs` keeps its pattern equal
to `updates.ValidTag`. GitHub will still let someone publish a release with any
tag, because the workflow runs after publication: the refusal shows as a red
run, not as a release that was never created. Nothing is offered from it, since
no files are attached.
## Before you publish
1. The tag is `vMAJOR.MINOR.PATCH` with three numbers, on a commit whose CI is
green on `main`.
2. The repository secret `RELEASE_SIGNING_KEY` is set. A release cut without it
fails rather than publishing unsigned, and so does one cut before the public
half is committed. See [Signing releases](docs/upgrading.md#signing-releases)
for how the key is made and rotated.
3. For something that should not reach anyone yet, use a pre-release tag with a
hyphen, not a full release.
## Check what you published
When the run finishes, list the assets. An updater offers a release only if it
carries `checksums.txt` and the binary for the machine asking, so a release
that lacks them is invisible to every controller:
```sh
gh release view v1.3.7 --json assets --jq '.assets[].name'
gh run list --workflow=release.yml --limit 3
```
## When the build fails
Do the cheapest thing that fits the cause.
1. **A transient failure** (a runner vendor outage, a network error while
pulling a base image). Re-run the failed jobs of the same run in the Actions
tab. The run keeps its original `release` event, which the workflow allows to
attach files to the release that triggered it. This path has not been
exercised in this repository's history; if it does not work, go to step 3.
A failure at the step "Sign the release" is the likeliest cause of all:
`RELEASE_SIGNING_KEY` is missing, or is not the private half of the public
key the binary trusts (`internal/releasekey`). Correct the secret and re-run
the failed jobs; the tag stays as it is, and a run that fails there attaches
nothing, so nothing unsigned was published. `v1.3.6` failed this way.
2. **A draft or a pre-release.** Start the workflow by hand from the Actions
tab, giving the tag. Rebuilding replaces the files behind it, which is fine
for something nobody is meant to have yet.
3. **A published full release whose cause is in the tree.** The workflow
refuses to rebuild it by hand: "A released tag is a promise; cut a new tag
rather than replacing what people have already downloaded." Fix the cause on
`main` and publish the next patch version (`v1.3.8` after a failed `v1.3.7`).
The failed release stays, with no files, and no updater offers it. Say in its
notes which release replaces it.
Do not repair a failed release by publishing the same version with a fourth
number. It builds, and it is then the release GitHub calls "Latest" while no
controller can be updated to it. That is what the shape check above now stops.