Step-by-step guide for maintainers publishing a MKurator release. Consumers install published artifacts via INSTALL_AND_USE.md.
Related: CONTRIBUTING.md (commits), CICD.md (CI behaviour), ADR-0008, ADR-0016.
| Section | |
|---|---|
| 📋 | Overview |
| ✅ | Pre-release checklist |
| 🏷️ | Version and changelog |
| 🚀 | Tag and publish |
| 📦 | What CI publishes |
| 🔧 | Rebuild or fix a failed release |
| ✔️ | Verify after release |
Releases are tag-driven: push an annotated-style lightweight tag vX.Y.Z on
main and .github/workflows/release.yaml
builds, scans, signs, and publishes artifacts. Version numbers are not bumped
by CI — you commit Chart.yaml and CHANGELOG.md on main first.
Semver on 0.y.z while the API is pre-1.0 (v1beta1): use minor (0.3.0) for
user-visible features or breaking operator/API behaviour; patch (0.2.3)
for fixes only. Breaking commits use ! in the subject (see
CONTRIBUTING.md).
Record the commit you will tag (after the prepare commit, if any):
git checkout main && git pull
RELEASE_SHA="$(git rev-parse HEAD)"
echo "Tagging: ${RELEASE_SHA}"Run from the repository root on that commit:
task verify
task lint
task test:runBefore publishing a release tag, also run (or confirm green on GitHub for
${RELEASE_SHA}):
task ci:integration # Docker MQ + mqweb — workflow integration.yaml
task ci:e2e # kind + IBM MQ — workflow e2e.yaml (slow)
task vuln:check # same as CI test jobBefore tag: confirm E2E and Integration (and full CI) are green on
${RELEASE_SHA} — not only “latest green on main” from an older push.
Use .github/workflows/release-gate.yaml
(Actions → Release gate → Run workflow) on the commit you will tag:
| Input | Meaning |
|---|---|
sha |
Full or short commit (empty = latest main HEAD) |
poll_timeout_minutes |
How long to wait for external check-runs (default 120) |
The workflow re-runs task verify, task test:run, and Docker MQ integration on
that SHA, then polls GitHub check-runs until CI, Integration, and E2E
(kustomize) jobs succeeded on the same SHA. E2E is not run inside this workflow
(~90 min); you must already have (or wait for) a green E2E workflow run whose
headSha matches ${RELEASE_SHA}. Record the E2E run ID or URL when tagging.
If polling fails (timeout, skipped workflows, API limits), the manual e2e
checklist job fails with gh run list / gh run view commands — do not tag
until E2E is green on the exact SHA.
Do not tag until all three workflow runs succeeded on ${RELEASE_SHA}
(the commit at HEAD when you create the tag), not merely “latest green on
main” from an older push.
| Workflow | File | What it proves |
|---|---|---|
| CI | .github/workflows/ci.yaml |
verify, lint, test (+ Codecov upload), build, docker-build, helm-lint |
| Integration | .github/workflows/integration.yaml |
Live mqweb: queues, topics, channels, CHLAUTH, AUTHREC |
| E2E | .github/workflows/e2e.yaml |
Operator on kind + IBM MQ (Kustomize deploy) |
On GitHub: Actions → select the workflow → open the latest run on main →
confirm the commit SHA matches ${RELEASE_SHA} (copy from git rev-parse or
the run header). With the GitHub CLI:
gh run list --workflow ci.yaml --branch main --limit 5
gh run list --workflow integration.yaml --branch main --limit 5
gh run list --workflow e2e.yaml --branch main --limit 5
# Inspect a run: gh run view <run-id> --json headSha,conclusion,statusEnsure:
- All intended PRs are merged;
mainis at${RELEASE_SHA}. - CI, Integration, and E2E workflows show success for
${RELEASE_SHA}(re-run workflows on that commit if needed before tagging). - Commits since the last tag follow Conventional Commits + gitmoji.
- No accidental secrets in the tree (
task secrets:scanif unsure). - If CRDs/webhooks/RBAC changed:
task manifestsandtask verifyalready clean.
task changelogReview grouping (Features, Bug Fixes, Breaking Changes). Skipped types: docs,
test, chore, ci, build, style — see cliff.toml.
| Change | Example bump |
|---|---|
Breaking (feat!, refactor!, CRD contract) |
0.2.2 → 0.3.0 |
| New feature, non-breaking | 0.2.2 → 0.3.0 or 0.2.3 |
| Bug fixes only | 0.2.2 → 0.2.3 |
Edit charts/mkurator/Chart.yaml:
version: 0.3.0
appVersion: "0.3.0"Keep version and appVersion aligned with the git tag (v0.3.0 → 0.3.0).
task changelog:writeCommit chart bump and changelog together:
git add charts/mkurator/Chart.yaml CHANGELOG.md
git commit -m "chore(release): :bookmark: prepare v0.3.0"Use a conventional subject; the release tag itself does not need to be in this commit.
Land the prep commit on main via pull request — direct pushes to main
are blocked by the protect-main ruleset. Tags are not covered by the branch
ruleset, so tag pushes still work.
Create and push the tag on the merged main HEAD (triggers CI):
git tag v0.3.0
git push origin v0.3.0CI runs only when the tag matches v*.*.* (see workflow on.push.tags).
Do not move or force-push release tags once consumers may have pulled them. Fix forward with a new patch tag instead.
The release workflow (on tag push):
| Output | Location |
|---|---|
| Container image | ghcr.io/platformrelay/mkurator:0.3.0 (and :v0.3.0), multi-arch |
| OCI SBOM + SLSA provenance | GHCR attestations on the image |
| GitHub Release | Notes from git-cliff + install section; attached files below |
install-crds.yaml |
Kustomize CRD bundle |
install.yaml |
Full operator install (image pinned to tag) |
mkurator-0.3.0.tgz |
Helm chart tarball |
sbom.spdx.json |
SPDX SBOM |
checksums.txt |
SHA256 of release files |
| Helm chart (OCI) | oci://ghcr.io/platformrelay/mkurator |
Release notes are assembled by
hack/assemble-release-notes.sh (git-cliff
section from git-cliff-action output content, not the changelog path +
.github/release-notes-install.md).
Committed history lives in repo-root CHANGELOG.md.
Local dry-run of install manifests (without pushing):
bash hack/release-assets.sh 0.3.0 ghcr.io/platformrelay/mkurator
ls -la dist/- Fix the issue on
main(dependency bump,.trivyignorewith rationale, Dockerfile). - Bump patch version if the tag was never successfully consumed, or publish
a new tag (e.g.
v0.3.1). - Do not delete the failed tag on a public repo unless you are certain no one pulled it.
Use workflow_dispatch on the Release workflow with the existing tag name
(e.g. v0.3.0). This rebuilds and re-uploads GitHub Release assets without a new commit.
The tag input reaches the shell only as an env: variable (TAG_INPUT) and is
validated against the vX.Y.Z regex before any asset is built, signed, or published, so a
malformed or malicious tag fails fast with the literal value echoed and nothing is
published. (The initial checkout resolves refs/tags/<tag> as a git ref via format(),
not a shell command — a non-existent tag simply fails to check out.) See the
"No expression injection" principle in CICD.md.
Edit cliff.toml or commit messages on main only affect future releases.
For a published release, edit release notes manually on GitHub or re-run
workflow_dispatch after fixing generation on the tagged commit.
- Open GitHub Releases — notes, attachments, tag.
- Pull the image:
docker pull ghcr.io/platformrelay/mkurator:0.3.0 - Optional cosign verify on the container image (substitute digest from GHCR):
cosign verify \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-identity-regexp '^https://github.com/platformrelay/MKurator/.+' \
ghcr.io/platformrelay/mkurator@sha256:<digest>- Optional verify signed release assets (Sigstore bundle):
VERSION=0.6.0 # replace with the tag you published
curl -sLO "https://github.com/platformrelay/MKurator/releases/download/v${VERSION}/checksums.txt"
curl -sLO "https://github.com/platformrelay/MKurator/releases/download/v${VERSION}/checksums.txt.sigstore.json"
cosign verify-blob --bundle checksums.txt.sigstore.json --certificate-oidc-issuer \
https://token.actions.githubusercontent.com \
--certificate-identity-regexp '^https://github.com/platformrelay/MKurator/.+' \
--checksum checksums.txt- Smoke install from release YAML (see INSTALL_AND_USE.md):
VERSION=0.5.2 # replace with the tag you just published
curl -sLO "https://github.com/platformrelay/MKurator/releases/download/v${VERSION}/install-crds.yaml"
curl -sLO "https://github.com/platformrelay/MKurator/releases/download/v${VERSION}/install.yaml"
kubectl apply -f install-crds.yaml
kubectl apply -f install.yaml- Confirm
CHANGELOG.mdonmainno longer lists released items under Unreleased (regenerate was done pre-tag; post-tag commits may add new unreleased).
task changelog
# edit charts/mkurator/Chart.yaml → version + appVersion
task changelog:write
git add charts/mkurator/Chart.yaml CHANGELOG.md
git commit -m "chore(release): :bookmark: prepare vX.Y.Z"
# land the prep commit via PR — main blocks direct pushes (protect-main ruleset)
git tag vX.Y.Z # on the merged main HEAD
git push origin vX.Y.Z