Skip to content

Releasing

A release is a tag. Pushing v0.1.0 to the repository publishes and deploys everything a version of Voice consists of, and nothing else ever publishes: not a merge, not a commit to main, not a command run by hand.

What a tag does

Workflow Does Needs
deploy.yml builds the service image for amd64 and arm64, pushes it to ghcr.io/thinkmorestupidless/voice, and deploys it with ankka services deploy voice <image> the secrets ANKKA_URL, ANKKA_TOKEN, ANKKA_PROJECT and, for a certificate not publicly trusted, ANKKA_CA
marketplace.yml publishes the documentation as a Claude Code plugin, at this version, to thinkmorestupidless/ankka-marketplace the secret MARKETPLACE_REPO_TOKEN, a token with contents: write on that repository

Both decline to run, with a notice, when their secret is absent, so a tag pushed before the repository is configured shows green and does nothing. A tag with a hyphen, such as v0.2.0-rc.1, publishes no plugin.

The documentation site is not part of a release. It is published from main by docs.yml, so it describes main; the plugin a release publishes is the documentation as it stood at the tag. The interface's image is not built by the tag either: it is built and deployed by hand, as Deploy on ankka shows.

The version is the tag

The version comes from git and from nowhere else. sbt-dynver reads the nearest tag: on the commit tagged v0.1.0 the build's version is 0.1.0, and a commit past it or a tree with a changed file is a snapshot. version is never set in the build, and no tracked file holds it: the plugin's manifest says 0.0.0 in the tree, and the release job writes the version into its own checkout before copying the plugin out. A Docker tag may not contain +, so a snapshot's version has it replaced by - in the image tag.

What nothing can undo

An image tag that has been pulled is in use, and a plugin version in the marketplace is what agents have installed. So: never move or re-push a tag that has published, never publish by hand, and fix a mistake in a release with the next version. A re-run of the workflow for a tag is safe: the image is pushed again under the same tag, the deploy applies the same descriptor, and the marketplace job finds nothing to commit.

Prove the path before the tag

Everything a release does except the push and the deploy runs on a laptop:

sbt -batch test
cd web && npm run typecheck && npm test && npm run build && cd ..
just check
just docs
sbt service/Docker/publishLocal && docker build -t voice-web:local web

The last line builds both images as the workflows would; just deploy-local deploys them to a local kind installation.

Cut a release

From main, with the pull request that leads to it merged and green:

git switch main && git pull
git tag v0.1.0
git push origin v0.1.0

Then check: the workflow runs for the tag, every job green or declined by name; docker pull ghcr.io/thinkmorestupidless/voice:0.1.0, from a machine not logged in to ghcr.io, once the package has been made public in its settings; ankka services get voice -p voice showing the new image; and the voice entry in the marketplace's manifest at the version.