docs: record what shipping the release pipeline taught us
Moves plan 017 to completed with a recap, and lifts the three findings that generalise into NOTES.md: a preset major that renders empty notes with everything green, a 403 that looks like branch protection and is a token scope, and tag-triggered workflows running the tagged commit's own definitions.
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
# 017 — Releases that happen by themselves
|
||||
|
||||
**Shipped as `v0.0.1`.** A merge to `main` now reads the Conventional
|
||||
Commits since the last tag, cuts the tag and the Gitea release whose body
|
||||
is the generated changelog, and the four publishing workflows build that
|
||||
tag and attach their artifacts. Nothing is released by hand.
|
||||
|
||||
## What it looks like now
|
||||
|
||||
`release.yml` on push to `main` → semantic-release → tag → four `v*`
|
||||
workflows in parallel (serialised in practice by the capacity-1 runner):
|
||||
|
||||
| workflow | publishes | attaches |
|
||||
| --- | --- | --- |
|
||||
| `arch-package` | pacman registry | `…-x86_64.pkg.tar.zst` |
|
||||
| `android-apk` | generic registry (Obtainium) | `…-android-arm64.apk` |
|
||||
| `desktop-assets` | — | `…-linux-amd64.tar.gz` |
|
||||
| `homebrew-formula` | the public tap | — (builds from source) |
|
||||
|
||||
Verified on the real thing: all five green, three assets on the release,
|
||||
the tap at `0.0.1`, and the Obtainium `latest` URL serving 200.
|
||||
|
||||
## The five decisions, and what they cost
|
||||
|
||||
1. **semantic-release, not a shell script.** The first draft of this plan
|
||||
proposed hand-rolling it and the argument did not survive checking:
|
||||
`@semantic-release/exec` is first-party and current, and the
|
||||
Gitea-shaped part is one `curl`. What I would have hand-rolled —
|
||||
commit parsing, semver ordering, note rendering — is the part with the
|
||||
edge cases and none of it is Gitea-shaped.
|
||||
2. **`@saithodev/semantic-release-gitea` is a dead end** and was offered
|
||||
before it was checked: last published 2022, `got@10`, and no peer
|
||||
dependency on semantic-release at all.
|
||||
3. **No `@semantic-release/git`.** `main` is protected, so a changelog
|
||||
commit-back is rejected by the pre-receive hook — and would be
|
||||
rejected *after* the tag was pushed, leaving a tagged release the run
|
||||
reports as failed. The release page is the changelog;
|
||||
`.release-notes.md` is a gitignored carrier and `CHANGELOG.md` is a
|
||||
signpost.
|
||||
4. **Versions restart at `0.0.1`**, a downgrade on every channel. No
|
||||
`epoch`, no `versionCode` offset: both are permanent, a reinstall is
|
||||
once. Documented in `packaging/homebrew/README.md` and
|
||||
`docs/android-release.md`.
|
||||
5. **No macOS and no Windows.** `GOOS=darwin CGO_ENABLED=0` fails at
|
||||
`wails/v3/pkg/mac` and there is no macOS runner, so Homebrew-from-source
|
||||
stays that channel. Windows cross-compiles in ~2.5 s and is withheld
|
||||
because no build of it has ever been *run*.
|
||||
|
||||
## Four things that only showed up by running it
|
||||
|
||||
- **`conventional-changelog-conventionalcommits@10` renders empty
|
||||
notes.** Silently: right version, right tag, every step green, and a
|
||||
release body that is a bare `## 0.0.1 (date)` heading with nothing
|
||||
beneath it. Held at `9`, in `release.yml` and `make release-dry`, with
|
||||
the reason beside both. **Check the rendered notes, never the exit
|
||||
code.**
|
||||
- **semantic-release core dry-run-pushes to the release branch** as a
|
||||
permission check, independently of any plugin. `PACKAGE_TOKEN` had
|
||||
package-write and repo-*read* — enough to clone, not enough for this —
|
||||
and it failed with a flat `403 Forbidden` that reads exactly like
|
||||
branch protection. It is not: a `--dry-run` push never reaches the
|
||||
pre-receive hook, which a one-line experiment settled. The token needed
|
||||
`write:repository`.
|
||||
- **The floor tag must go on `HEAD^`, not `HEAD`.** Seeded on the merge
|
||||
commit itself it leaves nothing between the floor and HEAD, and
|
||||
semantic-release correctly reports there is nothing to release. The
|
||||
first run did exactly that and cut nothing.
|
||||
- **A tag-triggered workflow runs from the tagged commit's tree.**
|
||||
Moving `v0.0.0` back to `6fb7b5e` ran the *pre-merge* homebrew
|
||||
workflow, which predates the `v0.0.0` skip guard, and pushed a `0.0.0`
|
||||
formula to the public tap. Self-corrected at `0.0.1`. The corollary is
|
||||
general: a guard added today does not protect a tag pointing at
|
||||
yesterday.
|
||||
|
||||
## Two mechanisms confirmed, having been assumptions
|
||||
|
||||
- **A tag pushed with a user PAT does start the `v*` workflows**; one
|
||||
pushed with the Actions token does not (go-gitea#33123). Both halves
|
||||
are load-bearing and both were observed: the floor seed triggered
|
||||
nothing, and the release tag triggered all four.
|
||||
- **Tags are not protected** on this repo, only `main` — which is what
|
||||
lets semantic-release tag at all.
|
||||
|
||||
## Left behind deliberately
|
||||
|
||||
`v0.0.0` stays on `origin` as the floor. It carries no release, and all
|
||||
four publishers skip it by name.
|
||||
Reference in New Issue
Block a user