Release automation: tag → build → push → Forgejo release with changelog #4
Labels
No labels
area/ai
area/backend
area/frontend
area/infra
area/scheduler
area/wled
good-first-issue
priority/high
priority/low
priority/medium
type/bug
type/chore
type/ci-cd
type/docs
type/feature
type/qa
v1.0.0
v1.1.0
v1.2.0
v2.0.0
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
rbrooks/Iris-WLED#4
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Goal
Cutting a release should be a single manual
git tag; everything downstream is automated.Scope
.forgejo/workflows/release.ymltriggered onv*tags.linux/amd64image and push to the Forgejo registry (:<version>and:latest).CHANGELOG.mdfor the matching version section.git tag vX.Y.Z && git push --tags.Acceptance criteria
vX.Y.Ztag builds and publishes images automaticallyDepends on: registry push, CHANGELOG.md.
Already implemented in
.forgejo/workflows/release.yml: on av*tag it builds+pushes the multi-arch image (see #3), extracts the matching## [version]section fromCHANGELOG.mdas release notes (awk), and creates a Forgejo release via the API. Human step is justgit tag vX.Y.Z && git push --tags, as intended.Before this works at release time, same prerequisites as #3 (registry enabled + Actions token
packages:write/contents:write) — the release-creationcurlalso needscontents:writeon the token. And when cutting v1.0.0 (#10), the## [1.0.0]CHANGELOG section must have its date finalized so the notes extract cleanly.Recommend the same
v0.0.1-rc1dry-run to validate build→push→release end-to-end before the real tag. Leaving open until validated by a tag run (part of #10's flow).Release pipeline proven end to end — first execution in the repo's history
Cut a throwaway
v0.0.1-rc1tag againstmain(run #217, job Publish image & release). Succeeded on the first attempt, and every leg was verified by its effect rather than by reading the log.Acceptance criteria
vX.Y.Ztag builds and publishes images automatically —git.rhoving.com/rbrooks/iris-wled:0.0.1-rc1landed. Pulled it on the dev host:linux/amd64, 87 MB, digestsha256:d71fa897…, distinct from:mainas expected.prerelease: true,draft: false.## [0.0.1-rc1]section, so theawkproduced nothing and the guard wroteRelease v0.0.1-rc1rather than an empty body. The happy path (a real version with a matching section) is still unproven; that only happens on a real release.The parts that were genuinely at risk
Registry auth on the release path worked. This was the main unknown —
REGISTRY_TOKENwas swapped in during #64 andrelease.ymlhad never run with it. Worth restating why a green login step proves nothing here:docker/login-actionsucceeds with a wrong-scope token and the failure only appears at the blob upload. The image existing in the registry is the proof.The Forgejo release API call worked on
secrets.GITHUB_TOKEN— a different credential from the registry push, so it could have failed independently even with the image published. It didn't.The pre-release guard (#105) did its job
Registry tags after the run:
Without #105, this run would have retagged
:latestto a disposable RC build. Since the run succeeded and:latestdoes not exist, the guard demonstrably fired.Cleanup
Forgejo release deleted
Git tag deleted, local and remote — the repo is back to zero tags
Registry tag
:0.0.1-rc1still exists. Deleting a container package version needs the Forgejo packages API, and the MCP server exposes no package tools while my shell has noFORGEJO_ACCESS_TOKEN. It is harmless but untidy:This is the same registry-hygiene gap as the accumulating
:sha-*tags — an owner-level cleanup rule would handle both.What remains unproven
The
{{major}}.{{minor}}and:latesttags have never actually been pushed, because a pre-release deliberately suppresses both. They are the same push step with the same credentials as0.0.1-rc1, so the risk is low — but "low" is not "verified", and the first real proof will bev1.0.0itself.#4 is done. This also unblocks #3's remaining criterion and #104, which needs
:X.Y.Zto exist before an IaC role can pin one.The two legs left unproven at close are now proven — by the real v1.0.0 release
The closing comment above was careful to say what the
v0.0.1-rc1dry run could not establish:v1.0.0has since been cut (tag0251a6a, release created 2026-09-01T19:59:22Z). Checked by effect rather than by reading the workflow:Registry tags — queried anonymously via
/v2/rbrooks/iris-wled/tags/list:Both tags the RC run suppressed are present. The
!contains(github.ref_name, '-')guard from #105 therefore behaves correctly in both directions: it withheld:latestfromv0.0.1-rc1and released it forv1.0.0. A guard only ever tested in its blocking direction is half-tested, so this closes that gap too.Release object — id 476,
tag_name: v1.0.0,draft: false,prerelease: false. The pre-release detection also inverted correctly:truefor the RC,falsehere.CHANGELOG extraction, happy path — the release body is 16,218 characters and byte-identical to the
## [1.0.0]section ofCHANGELOG.md. Compared programmatically by re-running the workflow's own awk logic against the file and diffing, not by eye:So the
awkextracts the right section, stops at the next## [, and the guard correctly does not fire when there is real content. Both branches of that step are now exercised.Every acceptance criterion is met, and nothing about this workflow remains unverified. Recording it here because the issue closed with a caveat, and a reader would otherwise have to go and check.
The practical consequence is for #104:
:X.Y.Zexists, so that issue is no longer blocked.