ADR-0042: CalVer releases published to Docker Hub on every push to main
- Status: accepted; the changelog mechanics below are superseded by ADR-0051
- Date: 2026-09-05
- Deciders: Mahdi Amirabdollahi
Context
CI built and tested the code but published nothing. pom.xml was frozen at 0.1.0-SNAPSHOT, the image job built ghcr.io/sudoitir/artemis-studio:ci and discarded it, and there were no git tags, no CHANGELOG.md, and no GitHub Releases. Running the product meant cloning and building it.
ADR-0007 committed to a single container image and Compose-first delivery. That only pays off if the image is actually somewhere a user can docker pull it.
Artemis Studio is a continuously-delivered application, not a library. Nothing downstream links against it or imports its API, so a SemVer MAJOR.MINOR.PATCH contract has no consumer to serve — the numbers would be assigned by judgement call with no rule behind them. What a user of an ops tool actually wants to know from a version string is how old is this build.
The project is also pre-stable (alpha, Phases 0–8), so nothing published yet should present itself as production-ready.
Decision
We will release on every push to main, versioned with CalVer YYYY.MM.PATCH.
PATCHresets to0on the first release of each calendar month (UTC).- The version is computed in CI from
dateand the existing git tags — one past the highestPATCHfor the current month. It is never committed topom.xml(no bump commit, no drift); CI sets it in the working tree withversions:setbefore packaging. - We will publish to Docker Hub only, as
sudoit1/artemis-studio, dropping theghcr.ioimage. Docker Hub is the default registry fordocker pull, is discoverable, and serves anonymous pulls without a login — the right default for an open-source operations tool. - Images are multi-arch (
linux/amd64+linux/arm64) and carry three tags::<version>(immutable),:<YYYY.MM>(moving month pointer),:dev(moving channel pointer). - Each release also cuts an annotated git tag and a GitHub Release carrying the promoted
CHANGELOG.mdsection, the runnable jar, and its SHA-256 checksum. - Until the first stable release, no
:latesttag is published and GitHub Releases are markedprerelease: true..claude/rules/10-release.mdnames the exact edits that flip this. CHANGELOG.mdfollows Keep a Changelog. Adding an entry under## [Unreleased]for any user-visible change is a merge-time obligation; the release job promotes that section in place. (Superseded by ADR-0051: the changelog is generated from commit messages into one file per release underchangelog/. Versioning, tagging and publishing are unchanged.)
Consequences
- A user runs the product with one
docker pull/docker compose up.just upbecomes the prod stack against the published image; the local-build dev stack moves tojust dev-up. - No release ceremony exists to forget or get wrong — merging to
mainis the release. The cost is thatmainmust always be releasable, which it already had to be. - CalVer carries no compatibility signal. A breaking change looks like any other version bump. Mitigation is procedural and mandatory: a
### Breakingblock at the top of the version's changelog section with the migration step. This ADR is the record that we accepted that trade knowingly. - The release job pushes a commit (the changelog promotion) and a tag back to
main. It is guarded with[skip ci]to avoid triggering itself. If branch protection is enabled onmain, the job's identity needs a bypass or a PAT. - Dropping
ghcr.iomeans anyone who pinned that image path must repoint to Docker Hub. Acceptable per the project's no-backward-compatibility principle, and there are no known consumers. - Monthly
PATCHreset means version ordering is only correct when compared within the same month by numericPATCH; across months theYYYY.MMprefix orders them.sort -Vhandles both.
Alternatives considered
- SemVer — no downstream consumer to signal API compatibility to, so version numbers would be arbitrary. CalVer answers the question users actually ask.
- Keep GHCR, or publish to both — two registries means two defaults to keep in sync in compose files and docs, for no user benefit. One place to look.
YYYY.0M.0Ddate-exact CalVer — precise but needs an ugly second-level.Nsuffix for same-day releases and is awkward as a Maven version.YYYY.MM.PATCHis a valid Maven version and reads cleanly.- Tag-triggered releases (manual
git tagor a release workflow_dispatch) — a ceremony that can be skipped or fumbled. Continuous release frommainremoves the step. - A version file bumped by a bot (release-please / git-cliff) — needs Conventional Commits adopted repo-wide, a separate and larger process change. The commit history here is prose.