A release workflow: image published on a tag, a Forgejo release with notes, and the version shown in the interface #37

Closed
opened 2026-09-05 14:04:23 +00:00 by tiagoagueda · 0 comments
Owner

Why

v0.1.0 was tagged by hand, the image was built by hand on ragnar with docker compose build, and CI has no image job: the earlier attempt was removed because the runner executes jobs inside a container with no Docker daemon to talk to. Nothing publishes an image anyone can pull, the Compose file builds from source, and the version in pyproject.toml is displayed nowhere — Server settings (#24) wants it on its Overview page, and Troubleshooting has no way to ask "which version are you running?".

Shape

1. On a v* tag, a release workflow:

  • Check that the tag matches pyproject.toml's version and that CHANGELOG.md has a section for it; fail loudly otherwise. Releases that disagree with their own changelog are how confusion starts.
  • Build the sdist and wheel with uv build, and create the Forgejo release with that changelog section as its notes and the two files attached. This part runs on the current runner as it is.

2. The image, which the current runner cannot build. Two ways, either acceptable:

  • A second runner with a docker label, on ragnar (arm64) and, if one is available, an amd64 machine, running the job natively with buildx for a multi-architecture image — the Raspberry Pi needs arm64, most servers amd64. This is the conventional path and the one Forgejo's documentation describes.
  • A daemonless build inside the container with kaniko or buildah, which needs no privileged runner but is slower and fussier about caching.

Either way the result is pushed to Forgejo's container registry as source.tiagoagueda.com/tiagoagueda/postulo:0.2.0, :0.2 and :latest, and docker/compose.yml switches from build: to image: with a pinned minor, so an installation is a pull, not a compile. scripts/check-image.sh runs against the pushed image before the tags move.

3. The version in the interface. importlib.metadata.version("postulo") once, in the ui context processor, shown small in the footer and in full on Server settings → Overview (#24), and returned by /healthz so monitoring can see an upgrade happen.

4. The GitHub mirror receives tags with the push; whether it should carry a matching GitHub Release (a second API call from the same workflow) is the open question below.

Classification

Enhancement, to the release process. Not breaking — but the Compose switch to a published image is a change operators will notice, and the upgrade note must say that build: still works for anyone who wants it.

Open questions

  1. Which runner: a Docker-labelled runner on ragnar, or daemonless in the container? The homelab argues for the runner.
  2. Mirror releases to GitHub, or leave GitHub as source-only?
  3. Sign the image (cosign) from the start, or once someone asks? Proposal: once the pipeline is stable; a signature nobody verifies is ceremony.
## Why v0.1.0 was tagged by hand, the image was built by hand on ragnar with `docker compose build`, and CI has no image job: the earlier attempt was removed because the runner executes jobs inside a container with no Docker daemon to talk to. Nothing publishes an image anyone can pull, the Compose file builds from source, and the version in `pyproject.toml` is displayed nowhere — Server settings (#24) wants it on its Overview page, and *Troubleshooting* has no way to ask "which version are you running?". ## Shape **1. On a `v*` tag**, a `release` workflow: - **Check** that the tag matches `pyproject.toml`'s version and that `CHANGELOG.md` has a section for it; fail loudly otherwise. Releases that disagree with their own changelog are how confusion starts. - **Build** the sdist and wheel with `uv build`, and **create the Forgejo release** with that changelog section as its notes and the two files attached. This part runs on the current runner as it is. **2. The image**, which the current runner cannot build. Two ways, either acceptable: - **A second runner with a `docker` label**, on ragnar (arm64) and, if one is available, an amd64 machine, running the job natively with `buildx` for a **multi-architecture image** — the Raspberry Pi needs arm64, most servers amd64. This is the conventional path and the one Forgejo's documentation describes. - **A daemonless build inside the container** with `kaniko` or `buildah`, which needs no privileged runner but is slower and fussier about caching. Either way the result is pushed to **Forgejo's container registry** as `source.tiagoagueda.com/tiagoagueda/postulo:0.2.0`, `:0.2` and `:latest`, and `docker/compose.yml` switches from `build:` to `image:` with a pinned minor, so an installation is a pull, not a compile. `scripts/check-image.sh` runs against the pushed image before the tags move. **3. The version in the interface.** `importlib.metadata.version("postulo")` once, in the `ui` context processor, shown small in the footer and in full on Server settings → Overview (#24), and returned by `/healthz` so monitoring can see an upgrade happen. **4. The GitHub mirror** receives tags with the push; whether it should carry a matching GitHub Release (a second API call from the same workflow) is the open question below. ## Classification Enhancement, to the release process. Not breaking — but the Compose switch to a published image is a change operators will notice, and the upgrade note must say that `build:` still works for anyone who wants it. ## Open questions 1. Which runner: a Docker-labelled runner on ragnar, or daemonless in the container? The homelab argues for the runner. 2. Mirror releases to GitHub, or leave GitHub as source-only? 3. Sign the image (cosign) from the start, or once someone asks? Proposal: once the pipeline is stable; a signature nobody verifies is ceremony.
tiagoagueda added this to the 0.2.0 milestone 2026-09-05 14:04:23 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Postulo/postulo#37
No description provided.