Skip to content

Releasing a new version

Step-by-step guide for maintainers publishing a Kollect release.

Related: CONTRIBUTING.md (commits), DEVELOPMENT.md (local tasks), ROADMAP.md (feature status).

Overview

Releases are tag-driven: push a tag vX.Y.Z on main and .github/workflows/release.yaml builds, scans, signs, and publishes artifacts. Version numbers are not bumped by CI — commit charts/kollect/Chart.yaml and CHANGELOG.md on main first.

While the API is v1alpha1, use minor (0.3.0, 0.4.0, …) for themed feature tranches or breaking operator behaviour; patch (0.2.1) for fixes on the current minor line. Breaking commits use ! in the subject (see CONTRIBUTING.md).

Versioning policy

Kollect uses frequent pre-1.0 minors with a presentation gate at ~v0.10.0 — not a single early GA at v0.1.0.

Policy Detail
Cadence Target one minor (or rc → minor) every 1–3 weeks while building toward v0.10
RC tags vX.Y.Z-rc.N — soak on green main; use workflow_dispatch with draft + prerelease
Breaking changes feat!: / BREAKING CHANGE:minor bump pre-v1.0
Phases vs semver ROADMAP.md phases 0–4 = build order; semver bands = release tranches

What v0.2.0-rc.1 shipped (2026-06-07)

The first post-strategy tranche was platform / sinks, not UI:

  • Sink family CRDs (ADR-0414) and removal of monolithic KollectSink
  • Family sink reconcilers, validating webhooks, e2e bootstrap for family CRDs
  • Git transport retry, SSH host keys, Forgejo/Gitea MR auth fixes

Read API + UI milestones moved to the v0.5–v0.10 band (ADR-0408).

Version ladder (summary)

Band Theme
0.2.x Platform / sink families — rc.1 shipped
0.3.x Quality, perf, coverage ratchet — v0.3.0 shipped
0.4.x Parquet sink, supply-chain attestations — v0.4.0v0.4.1 shipped
0.5.x Sink config layering + export/git hardening (v0.5.0)
0.6.0 Cut the export tranche on main (ADR-0306, ADR-0419, MongoDB, status.preview) + audit correctness/security fixes + ResourceExportMode wiring (⬜ next target)
0.7.x BigQuery + NATS sinks (full backends) · parallel-export docs · coverage floor 72 → 75 → 80%
UI Frozen — mock SPA maintenance-only; Read API freeze deferred; may remove pre-v1

Full ladder: ROADMAP.md § Near-term tranches.

Retroactive version anchors

History before the first GitHub release is split with lightweight tags (changelog anchors only):

Tag Commit Milestone
v0.0.1 13546aff Kubebuilder scaffold
v0.0.2 1e6f6719 Core v1alpha1 CRDs
v0.0.3 66421337 Helm chart, extraction, inventory HTTP
v0.0.4 4234960b ADR-0201 platform pivot MVP
v0.1.0-rc.1rc.3 2026-06-05 – 06 Pre-strategy RCs (finalizers, helm, e2e, release pipeline)
v0.2.0-rc.1 2026-06-07 Sink-family tranche

Push changelog anchor tags once (if not already on the remote):

git tag v0.0.1 13546aff
git tag v0.0.2 1e6f6719
git tag v0.0.3 66421337
git tag v0.0.4 4234960b
git push origin v0.0.1 v0.0.2 v0.0.3 v0.0.4

Pre-release checklist

git checkout main && git pull
RELEASE_SHA="$(git rev-parse HEAD)"
echo "Tagging: ${RELEASE_SHA}"
task verify
task lint
task test
task helm-test
task changelog:verify

Ensure CI, preflight, and kind-smoke (e2e-smoke.yaml) are green on ${RELEASE_SHA} on GitHub Actions.

L4 pre-release gate

Before tagging, require one of:

  1. Green e2e-nightly workflow run on ${RELEASE_SHA} (re-run via workflow_dispatch if the scheduled cron has not yet picked up the commit), or
  2. Manual test-e2e workflow dispatch on that SHA, or
  3. Local task test:e2e on the release commit (document run ID / timestamp in the release notes).

L3 integration (test-integration in CI) remains the merge gate for sink backends; nightly L4 no longer duplicates export-integration or object-store jobs.

Git export test repository (optional)

For full remote git SHA assert in e2e-nightly, e2e-extended, and test-e2e git-export jobs, set repository variable GIT_EXPORT_TEST_REPO in GitHub → Settings → Actions → Variables (clone URL of a dedicated test repo). Workflows pass ${{ vars.GIT_EXPORT_TEST_REPO }} with GITHUB_TOKEN; this cannot be set from workflow YAML. Without the variable, git-export jobs verify inventory HTTP hash only (degraded mode).

Current release status

Item Status
v0.5.0 ✅ Shipped 2026-06-07 — sink config layering (ADR-0416)
On main post-tag ADR-0306 full-resource export, ADR-0419 git layout, MongoDB sink, status.preview (Unreleased in changelog)
Next target v0.6.0 — cut the export/layout/MongoDB/preview tranche + audit "Now" fixes + ResourceExportMode wiring
After v0.6 v0.7.x — BigQuery + NATS sinks, parallel-export docs, coverage → 80%

RC pre-release on GitHub Actions

The release workflow accepts draft and prerelease inputs only on workflow_dispatch. Pushing a tag matching v*.*.* triggers a non-draft release automatically — use rc tags with dispatch inputs when you need draft/prerelease metadata.

Steps (maintainer, on green main):

git checkout main && git pull
RELEASE_SHA="$(git rev-parse HEAD)"
git tag v0.3.0-rc.1 "${RELEASE_SHA}"
git push origin v0.3.0-rc.1

Then trigger a draft pre-release rebuild if needed:

gh workflow run release.yaml \
  -f tag=v0.3.0-rc.1 \
  -f draft=true \
  -f prerelease=true

Monitor: gh run list --workflow=release.yaml --limit 3

Skip tag push if you only want local validation — task release-dry-run covers assets without publishing to GHCR or GitHub Releases.

Version and changelog

1. Preview unreleased notes

task changelog

2. Choose the version

Change Example bump
Themed feature tranche / breaking operator behaviour 0.2.00.3.0
Bug fixes on current minor 0.2.00.2.1
Soak before minor GA Tag 0.3.0-rc.1 first

3. Bump the Helm chart

Edit charts/kollect/Chart.yaml:

version: 0.3.0
appVersion: "0.3.0"

Align version and appVersion with the git tag (v0.3.00.3.0).

4. Regenerate CHANGELOG.md

task changelog:write
git add charts/kollect/Chart.yaml CHANGELOG.md
git commit -m ":bookmark: chore(release): prepare v0.3.0"

Cut a release

On green main at the commit you intend to ship:

git tag v0.3.0
git push origin main
git push origin v0.3.0   # triggers release workflow — only after CI green on this SHA

CI publishes the GitHub Release, GHCR image, OCI Helm chart, and attached assets.

Dry-run locally before tagging:

VERSION=0.3.0 task release-dry-run
ls -la dist/

Rebuild assets for an existing tag: Actions → ReleaseRun workflow → enter the tag (optional draft / prerelease inputs).

What CI publishes

Output Location
Container image (operator) ghcr.io/konih/kollect:<version> (and :v<version>), linux/amd64 + arm64
Container image (kollect-ui) ghcr.io/konih/kollect-ui:<version> (and :v<version>), linux/amd64 + arm64
OCI SBOM + SLSA provenance GHCR attestations on both images
GitHub Release git-cliff section + install footer; assets below
install-crds.yaml CRD bundle
install.yaml Full operator install (image pinned to tag)
kollect-<version>.tgz Helm chart tarball
sbom.spdx.json SPDX SBOM for operator image (Syft)
sbom-ui.spdx.json SPDX SBOM for kollect-ui image (Syft)
checksums.txt SHA256 of release files
<asset>.sigstore.json Sigstore bundle for each release asset (cosign keyless)
release-provenance.intoto.jsonl Combined SLSA provenance attestation for release assets
Helm chart (OCI) oci://ghcr.io/konih/kollect

Release notes are assembled by hack/assemble-release-notes.sh and .github/release-notes-install.md.

Verify after release

Container images (GHCR)

TAG=v0.2.0-rc.1   # or your release tag
REPO=konih/kollect

OP_DIGEST="$(crane digest ghcr.io/${REPO}/kollect:${TAG#v})"
UI_DIGEST="$(crane digest ghcr.io/${REPO}/kollect-ui:${TAG#v})"

cosign verify \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  --certificate-identity-regexp '^https://github.com/konih/kollect/.+' \
  "ghcr.io/${REPO}/kollect@${OP_DIGEST}"

cosign verify \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  --certificate-identity-regexp '^https://github.com/konih/kollect/.+' \
  "ghcr.io/${REPO}/kollect-ui@${UI_DIGEST}"

SLSA provenance and SPDX SBOM attestations are published to GHCR (via actions/attest) and the repository Attestations page:

gh attestation verify "ghcr.io/${REPO}/kollect@${OP_DIGEST}" \
  --owner konih --repo kollect

GitHub Release assets (OpenSSF Scorecard Signed-Releases)

Each release asset ships with a Sigstore bundle (<file>.sigstore.json) and a combined SLSA provenance bundle (release-provenance.intoto.jsonl). Verify a downloaded artifact:

TAG=v0.2.0-rc.1
VERSION="${TAG#v}"
gh release download "${TAG}" --pattern 'kollect-*.tgz' --dir /tmp/kollect-verify
cd /tmp/kollect-verify

cosign verify-blob \
  --bundle "kollect-${VERSION}.tgz.sigstore.json" \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  --certificate-identity-regexp '^https://github.com/konih/kollect/.+' \
  "kollect-${VERSION}.tgz"

Checksums: sha256sum -c checksums.txt after downloading all unsigned assets.

Rebuild an existing tag with signing

gh workflow run release.yaml \
  -f tag=v0.2.0-rc.1 \
  -f draft=false \
  -f prerelease=true

Confirm CHANGELOG.md on main has an empty Unreleased section (run task changelog:write after tagging if needed).