On merge, release.yml reads the Conventional Commits since the last tag, seeds a v0.0.0 floor (once, with the Actions token so it deliberately triggers nothing), and releases v0.0.1 — tag, Gitea release, and a body generated from the commits. arch-package, homebrew-formula, android-apk and the new desktop-assets are all keyed on v* and attach their artifacts to the release.
The five old v1.x tags are already deleted from origin.
Verified locally
Against a scratch remote: computes 0.0.1, renders correct sectioned notes, and gitea-release.sh rejects a non-semver argument, refuses an empty notes file, and passes backticks / quotes / $VARS through as data rather than syntax.
Two faults found by running it, not by reading it
conventional-changelog-conventionalcommits@10 silently renders empty release notes. Right version, right tag, every step green, and a release body that is a bare ## 0.0.1 (date) heading with nothing under it. Pinned to @9, with the reason recorded next to both pins. Four of my seven original pins were also wrong majors — guesses, fixed with npm view.
main is protected, so @semantic-release/git cannot commit the changelog back — and it would have failed after pushing the tag, leaving a tagged release the run then reports as failed. The plugin is gone; @semantic-release/changelog writes a gitignored .release-notes.md purely to carry notes into the release script, and the release page is the changelog. Whitelisting the CI user was declined: it weakens a protection set on purpose and lets a bot push to main without the checks every human PR passes.
Deliberate, and user-visible
Releases restart at 0.0.1, a downgrade from 1.6.0 on every channel: pacman and Homebrew silently offer no upgrade, and Android refuses outright — its remedy is an uninstall that takes the device's library with it. Chosen over a permanent epoch / versionCode offset. Documented in packaging/homebrew/README.md and docs/android-release.md.
macOS is not built here at all: GOOS=darwin CGO_ENABLED=0 fails at wails/v3/pkg/mac, the darwin backend being Objective-C behind cgo, so Homebrew-from-source stays the macOS channel. Windows cross-compiles cleanly in ~2.5s and is withheld: no Windows build of this app has ever been run.
After merge
Two things to check rather than assume: whether the PAT-pushed tag actually starts the four v* workflows (the workflow_dispatch fallback is already wired if not), and the release body rather than the exit code.
Implements `.planning/plans/active/017-release-automation.md`.
On merge, `release.yml` reads the Conventional Commits since the last tag, seeds a `v0.0.0` floor (once, with the **Actions** token so it deliberately triggers nothing), and releases **v0.0.1** — tag, Gitea release, and a body generated from the commits. `arch-package`, `homebrew-formula`, `android-apk` and the new `desktop-assets` are all keyed on `v*` and attach their artifacts to the release.
The five old `v1.x` tags are already deleted from origin.
## Verified locally
Against a scratch remote: computes `0.0.1`, renders correct sectioned notes, and `gitea-release.sh` rejects a non-semver argument, refuses an empty notes file, and passes backticks / quotes / `$VARS` through as data rather than syntax.
## Two faults found by running it, not by reading it
- **`conventional-changelog-conventionalcommits@10` silently renders empty release notes.** Right version, right tag, every step green, and a release body that is a bare `## 0.0.1 (date)` heading with nothing under it. Pinned to `@9`, with the reason recorded next to both pins. Four of my seven original pins were also wrong majors — guesses, fixed with `npm view`.
- **`main` is protected, so `@semantic-release/git` cannot commit the changelog back** — and it would have failed *after* pushing the tag, leaving a tagged release the run then reports as failed. The plugin is gone; `@semantic-release/changelog` writes a gitignored `.release-notes.md` purely to carry notes into the release script, and the release page is the changelog. Whitelisting the CI user was declined: it weakens a protection set on purpose and lets a bot push to `main` without the checks every human PR passes.
## Deliberate, and user-visible
Releases restart at `0.0.1`, a **downgrade** from `1.6.0` on every channel: pacman and Homebrew silently offer no upgrade, and Android refuses outright — its remedy is an uninstall that takes the device's library with it. Chosen over a permanent `epoch` / `versionCode` offset. Documented in `packaging/homebrew/README.md` and `docs/android-release.md`.
macOS is not built here at all: `GOOS=darwin CGO_ENABLED=0` fails at `wails/v3/pkg/mac`, the darwin backend being Objective-C behind cgo, so Homebrew-from-source stays the macOS channel. Windows cross-compiles cleanly in ~2.5s and is withheld: no Windows build of this app has ever been run.
## After merge
Two things to check rather than assume: whether the PAT-pushed tag actually starts the four `v*` workflows (the `workflow_dispatch` fallback is already wired if not), and **the release body rather than the exit code**.
The config has been sitting in .releaserc.yml complete and uninvoked;
this is the workflow that runs it, and the one Gitea-shaped adaptation
it needs.
@semantic-release/github speaks GitHub's API, not Gitea's /api/v1, so
@semantic-release/exec calls scripts/gitea-release.sh instead. That
script reads the notes out of CHANGELOG.md rather than taking them as an
argument: release notes are rendered commit messages, so interpolating
the notes into a shell command would be an injection whose input is the
commit log.
The tag is pushed with a user PAT because Gitea does not start a
workflow from a ref pushed by a workflow's own token, and the three
publishing workflows are keyed on it.
arch-package.yml ran on push to main and took its version from
`git describe`, so the pacman registry accumulated one package per
merge and not one of them corresponded to a version a user could be
told to install. It builds the tag release.yml cuts instead.
pkgver's literal drops to 0.0.1 with it. That is a downgrade from the
1.x already in the registry, so pacman offers no upgrade and an
existing install has to be removed once; epoch=1 would have avoided
that and is declined in a comment, because an epoch can never be
removed again.
A release page with nothing to download is one nobody can use. The
Arch package and the APK are already built and merely go unattached;
the plain Linux binary is new, and is what answers 'get the latest
version' without a package manager.
scripts/release-asset.sh waits for the release to exist first.
semantic-release pushes the tag in prepare and creates the release in
publish, so the tag push that starts these workflows happens before
there is an id to upload to -- and a capacity-1 runner serialises that
into working by accident, which is the worst kind of bug.
macOS is absent because it cannot be built here: GOOS=darwin
CGO_ENABLED=0 fails at wails/v3/pkg/mac, the darwin backend being
Objective-C behind cgo. Homebrew builds from source on the user's Mac
and stays the macOS channel. Windows cross-compiles cleanly and is
still withheld: no build of it has ever been run.
All three skip v0.0.0, which is semantic-release's version floor rather
than a shipment.
CLAUDE.md said .releaserc.yml was a config nothing ran and that there
were five workflows; both stop being true with this branch. The CI
section now names release.yml as the entry point and records the four
things in it that are load-bearing, including the two silent failure
modes worth pinning against.
packaging/homebrew/README.md and docs/android-release.md say where a
user would actually look that upgrading from 1.x needs a reinstall --
Homebrew offers nothing silently, and Android refuses outright.
semantic-release resolves the release branch and then pushes a commit
and a tag to it, so a local branch named main is a better starting
point than the --detach the other five workflows use. Still pinned to
the pushed commit rather than to whatever main points at by the time
the container starts.
The floor tag falls back to the PAT when GITEA_TOKEN is unset, which is
safe rather than merely convenient: all four publishers skip v0.0.0
explicitly, so the worst case is four jobs that start and immediately
say there is nothing to build.
main is protected (enable_push: false, empty whitelist), so
@semantic-release/git's commit-back is rejected by the pre-receive
hook -- and it would be rejected *after* the tag was pushed, leaving a
tagged release the run then reports as failed. Found by trying to push
this branch to main.
Whitelisting the CI user was the alternative and is declined: it
weakens a protection someone set deliberately and lets a bot push to
main without the checks every human PR has to pass.
So the release page is the changelog. The changelog plugin now writes a
gitignored .release-notes.md, which exists only to carry the notes into
gitea-release.sh without interpolating them into a shell command, and
CHANGELOG.md is a signpost -- a file claiming to be a changelog while
silently never updating is worse than no file.
Tags are not protected, so the tag push is unaffected.
TestCacheTTLExpiry set a 1s TTL and immediately asserted a hit, so it
depended on an upper bound of elapsed wall-clock time between Set and
Get. Nothing can promise that: on the capacity-1 runner, with the rest
of the suite running in parallel, the goroutine can be descheduled for
longer than the TTL and the entry is then correctly gone.
It failed that way on this PR while passing five times out of five
locally, and it touches no code this branch changed.
Two entries now: one with an hour to live carries the presence
assertions, one with a second carries the expiry. Sleeping past a TTL
is always safe, so only the direction that cannot flake is timed.
logan
merged commit 0019310ca4 into main2026-08-18 00:22:13 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Implements
.planning/plans/active/017-release-automation.md.On merge,
release.ymlreads the Conventional Commits since the last tag, seeds av0.0.0floor (once, with the Actions token so it deliberately triggers nothing), and releases v0.0.1 — tag, Gitea release, and a body generated from the commits.arch-package,homebrew-formula,android-apkand the newdesktop-assetsare all keyed onv*and attach their artifacts to the release.The five old
v1.xtags are already deleted from origin.Verified locally
Against a scratch remote: computes
0.0.1, renders correct sectioned notes, andgitea-release.shrejects a non-semver argument, refuses an empty notes file, and passes backticks / quotes /$VARSthrough as data rather than syntax.Two faults found by running it, not by reading it
conventional-changelog-conventionalcommits@10silently renders empty release notes. Right version, right tag, every step green, and a release body that is a bare## 0.0.1 (date)heading with nothing under it. Pinned to@9, with the reason recorded next to both pins. Four of my seven original pins were also wrong majors — guesses, fixed withnpm view.mainis protected, so@semantic-release/gitcannot commit the changelog back — and it would have failed after pushing the tag, leaving a tagged release the run then reports as failed. The plugin is gone;@semantic-release/changelogwrites a gitignored.release-notes.mdpurely to carry notes into the release script, and the release page is the changelog. Whitelisting the CI user was declined: it weakens a protection set on purpose and lets a bot push tomainwithout the checks every human PR passes.Deliberate, and user-visible
Releases restart at
0.0.1, a downgrade from1.6.0on every channel: pacman and Homebrew silently offer no upgrade, and Android refuses outright — its remedy is an uninstall that takes the device's library with it. Chosen over a permanentepoch/versionCodeoffset. Documented inpackaging/homebrew/README.mdanddocs/android-release.md.macOS is not built here at all:
GOOS=darwin CGO_ENABLED=0fails atwails/v3/pkg/mac, the darwin backend being Objective-C behind cgo, so Homebrew-from-source stays the macOS channel. Windows cross-compiles cleanly in ~2.5s and is withheld: no Windows build of this app has ever been run.After merge
Two things to check rather than assume: whether the PAT-pushed tag actually starts the four
v*workflows (theworkflow_dispatchfallback is already wired if not), and the release body rather than the exit code.Release automationto ci(release): cut releases from main automatically