Skip to content

Releasing a new version

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.

On this page

Section
πŸ“‹ Overview
βœ… Pre-release checklist
🏷️ Version and changelog
πŸš€ Tag and publish
πŸ“¦ What CI publishes
πŸ”§ Rebuild or fix a failed release
βœ”οΈ Verify after release

Overview

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 v1alpha1: 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).

Pre-release checklist

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:run

Before 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 job

Before tag: confirm E2E and Integration (and full CI) are green on ${RELEASE_SHA} β€” not only β€œlatest green on main” from an older push.

Automated release gate workflow

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.

CI gate 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,status

Ensure:

  • [ ] All intended PRs are merged; main is 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:scan if unsure).
  • [ ] If CRDs/webhooks/RBAC changed: task manifests and task verify already clean.

Version and changelog

1. Preview unreleased notes

task changelog

Review grouping (Features, Bug Fixes, Breaking Changes). Skipped types: docs, test, chore, ci, build, style β€” see cliff.toml.

2. Choose the version

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

3. Bump the Helm chart

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).

4. Regenerate CHANGELOG.md

task changelog:write

Commit 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.

Tag and publish

Create and push the tag on the merged main HEAD (triggers CI):

git tag v0.3.0
git push origin v0.3.0

CI 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.

What CI publishes

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/

Rebuild or fix a failed release

Trivy or build failed on tag

  1. Fix the issue on main (dependency bump, .trivyignore with rationale, Dockerfile).
  2. Bump patch version if the tag was never successfully consumed, or publish a new tag (e.g. v0.3.1).
  3. Do not delete the failed tag on a public repo unless you are certain no one pulled it.

Rebuild assets for an existing tag (testing)

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.

Changelog wrong on GitHub Release

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.

Verify after release

  1. Open GitHub Releases β€” notes, attachments, tag.
  2. Pull the image: docker pull ghcr.io/platformrelay/mkurator:0.3.0
  3. 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>
  1. 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
  1. 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
  1. Confirm CHANGELOG.md on main no longer lists released items under Unreleased (regenerate was done pre-tag; post-tag commits may add new unreleased).

Quick reference

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