Release process¶
How an OpenCCU-Loom release is cut: where the version is carried, which changelogs move with it, what runs before the tag, and what the release workflow does after it.
Who this page is for
Maintainers cutting a release. The operational runbook the maintainers' tooling follows is .claude/skills/release/SKILL.md; this page describes the same process for a human reader.
1. Decide the number¶
Read the unreleased section of CHANGELOG.md and the commits since the last tag:
Patch for fixes, minor for features. The REST APIVersion in internal/north/rest/handlers/info.go is independent of the daemon version: it moves only when the north-bound contract changes, on its own SemVer.
2. Bump the version carriers¶
Three files carry the daemon version and move together, in one commit:
| File | Shape |
|---|---|
internal/build/version.go | Version = "X.Y.Z" |
packaging/ha-addon/openccu-loom/config.yaml | version: "X.Y.Z" |
packaging/ha-addon/openccu-loom-remote/config.yaml | version: "X.Y.Z" |
The add-on config.yaml version is the add-on image tag. release.yml compares both against the tag and fails the add-on build when one differs — by then the tag is already pushed.
The go-fabric module is not a version carrier; see Taking a go-fabric release.
3. Update the three changelogs¶
CHANGELOG.md— the curated section.release.ymllifts the## [X.Y.Z]section into the GitHub release body and fails when it is missing.packaging/ha-addon/openccu-loom/CHANGELOG.mdpackaging/ha-addon/openccu-loom-remote/CHANGELOG.md
The two add-on changelogs are what Home Assistant shows operators in the add-on store. Describe the change for an operator, not the pull request.
4. Comment-claims sweep¶
Before every release, run the comment-claims sweep (.claude/skills/comment-claims-sweep/SKILL.md): read-only checks of code comments that name a consumer or caller, "not wired yet" notes, file-header inventories and ratchet justifications against the code. The mechanical guards cover only declared-silent events and ratchet texts; other prose is caught by this sweep alone. Fix or reword every refuted claim before tagging.
5. Gates and pinned artefacts¶
The MQTT discovery golden for the security and combined entities carries the build version (sw_version) and fails on every bump. Refresh it and check that only the version moved:
go test -run TestSecurityAndCombinedDiscoveryPayloadsArePinned ./internal/north/mqtt/ -update-security-combined-golden
git diff internal/north/mqtt/testdata/ | grep '^[-+] '
The release runbook also refreshes the wire-fixture corpus under assets/wire-fixtures/:
Its /info fixture masks version and api_version as volatile, so a pure version bump should leave that corpus unchanged; a diff there means something other than the version moved.
The wider pre-release checklist is on the Testing page.
6. Pull request, merge, tag¶
Commit with a sign-off (git commit -s), open the pull request, let CI go green and merge. Then tag the merge commit on main with an annotated tag and push it:
Never tag before both add-on versions match.
7. What happens after the tag¶
Pushing a v* tag runs .github/workflows/release.yml:
- goreleaser — builds the SPA bundle and the SBOM first, then the binaries and the multi-arch images, and publishes the GitHub release with the changelog section as its body.
- Home Assistant add-ons — after the version check above, builds and publishes both add-on images.
- Client regeneration — the
notify-client-repojob dispatches adaemon-releaseevent to openccu-loom-client. It marks the release as breaking when theAPIVersionmajor is higher than at the previous tag. The receiving workflow regenerates the wire bindings and opens a pull request there; it does not bump, merge or tag. Whether the hand-written layer of the client must follow is a separate decision — see that repository'sdaemon-updateskill. Without theTYPES_REPO_DISPATCH_TOKENsecret the job warns and skips the dispatch. - Node-RED — not automated. node-red-contrib-openccu-loom pins
SUPPORTED_API_MAJORinlib/client.js; after anAPIVersionmajor bump every server node warns until it is raised there.
The release is finished when those follow-ups are handled, not at the tag.