Release lifecycle, versioning & operations
kube-agents follows strict Semantic Versioning 2.0.0 (MAJOR.MINOR.PATCH) without a v prefix for official releases across container images, OCI Helm charts, and Terraform modules.
The release pipeline guarantees that installer scripts (install.sh, uninstall.sh, upgrade.sh) and container runtime images are bit-for-bit synchronized from the exact same commit and, absent an emergency bypass, validated on a live GKE cluster before any release tag is published.
Tag and artifact taxonomy
Section titled “Tag and artifact taxonomy”Every commit and build progresses through five distinct lifecycle tiers:
| Tier | Format | Trigger | Purpose and guarantees |
|---|---|---|---|
| Candidate Build | <COMMIT_SHA> (bare 40-char SHA) |
Push to main branch |
Developer build in GHCR; container images built once. |
| Release Candidate (RC) | rc_YYMMDDHHMM_<SHORT_SHA> |
3-hour cron / manual dispatch | Candidate build selected for live cluster testing. |
| RC Validated | rc_YYMMDDHHMM_<SHORT_SHA>_validated |
Successful GKE E2E suite | Quality gate: proof that install.sh succeeded on a real GKE cluster. |
| Staging Promoted | staging_YYMMDDHHMM_<SHORT_SHA> |
Successful nightly matrix | Quality gate for GA: the full nightly E2E matrix passed on the commit. Also the deploy trigger for the staging estate. |
| GA Stable | X.Y.Z (pure numeric SemVer) |
Release publish workflow | Official production release tagged on a stamped commit parented by the target commit (staging-promoted by default). |
Only a staging-promoted commit is releasable. An rc_*_validated tag records the narrow
three-hourly suite; the GA gate reads the staging_<ts>_<sha> tag that nightly-pipeline.yml
pushes after the full matrix passes
(scripts/release/README.md).
The gate matches that tag’s shape rather than the bare staging_ prefix, because the prefix is
also a hand-pushable redeploy trigger.
Automated SemVer 2.0 calculation
Section titled “Automated SemVer 2.0 calculation”When the GA release workflow runs, scripts/release/calculate_next_version.sh inspects Conventional Commits in the range <LATEST_GA_TAG>..<TARGET_COMMIT> (resolving to the latest staging-promoted commit on the standard automated path, or the specified commit / HEAD under emergency bypass):
| Commit type in release range | Current version | Calculated next version | Precedence and action |
|---|---|---|---|
fix:, chore:, docs:, perf: |
0.2.0 |
0.2.1 |
Patch bump |
feat: |
0.2.0 |
0.3.0 |
Minor bump, Patch resets to 0 |
feat!:, fix!:, BREAKING CHANGE: |
0.2.0 |
0.3.0 |
Minor bump (SemVer 2.0 Clause 4 in 0.y.z) |
feat!:, fix!:, BREAKING CHANGE: |
1.2.0 |
2.0.0 |
Major bump (in 1.x.x+) |
| (No new commits in release range) | 0.2.0 |
0.2.0 |
No changes (skip_release=true) |
SemVer 2.0 Clause 4 and the 1.0.0 manual governance rule
Section titled “SemVer 2.0 Clause 4 and the 1.0.0 manual governance rule”During initial development (0.y.z), any breaking change increments MINOR (0.2.1 -> 0.3.0) and resets PATCH to 0, per SemVer 2.0 Clause 4.
The automated version calculator never promotes 0.y.z to 1.0.0 on its own. Declaring API stability and graduating to 1.0.0 is a manual governance decision by project maintainers, triggered explicitly through the explicit_release_version: "1.0.0" workflow input.
Once 1.0.0 is established, the automated calculator resumes standard SemVer rules: breaking changes bump MAJOR, new features bump MINOR, and bug fixes bump PATCH.
Cutting a GA release
Section titled “Cutting a GA release”Prerequisites
Section titled “Prerequisites”Before triggering a production release:
- Target commit must exist on the
mainbranch. - Target commit must carry a
staging_<ts>_<sha>tag created by the nightly promotion pipeline (scripts/release/README.md). Anrc_*_validatedtag is not checked alongside it: a staging tag is only ever derived from a candidate that already carries one. - All four required container images (
k8s-operator,platform-agent,credential-proxy,replay-proxy) must exist in GHCR under<TARGET_COMMIT>. - GitHub CLI (
gh) version 2.40.0 or newer installed and authenticated withrepoandworkflowpermissions (gh auth status).
Triggering the release workflow
Section titled “Triggering the release workflow”Execute .github/workflows/release-publish.yml from the GitHub Actions web interface or via the GitHub CLI:
# Standard automated release (SemVer calculated automatically from Conventional Commits):gh workflow run release-publish.yml --repo gke-labs/kube-agents
# Releasing a specific staging-promoted commit:gh workflow run release-publish.yml --repo gke-labs/kube-agents \ -f target_commit="<TARGET_COMMIT_SHA>"
# Manual version override (e.g. promoting 0.y.z to 1.0.0):gh workflow run release-publish.yml --repo gke-labs/kube-agents \ -f explicit_release_version="1.0.0"Every release is started by hand: the workflow has no schedule:. It carries the gate an
unattended run would need — release only a staging-promoted candidate, skip quietly when nothing
new has landed since the last GA tag, and stop for a human when a breaking change is waiting to
ship — behind a schedule_gate input that defaults to bypass, so a dispatch publishes exactly as
it did before. dry-run reports the verdict in the job summary and publishes nothing; evaluate
acts on it, as a cron tick would.
scripts/release/README.md is
canonical for that gate and for the cadence.
Emergency hotfix runbook
Section titled “Emergency hotfix runbook”The release gatekeeper enforces staging promotion (staging_<ts>_<sha>, the full nightly GKE E2E matrix) by default. In emergency situations, maintainers can bypass the live GKE validation gate while preserving all cryptographic and build integrity invariants.
Eligibility criteria
Section titled “Eligibility criteria”Emergency gate bypass (skip_staging_validation: true) is strictly reserved for two scenarios: zero-day CVE vulnerabilities in container dependencies requiring immediate publication, or critical production regressions where waiting for the next nightly promotion or cluster provisioning would prolong user-facing downtime.
Enforced security invariants
Section titled “Enforced security invariants”Even during an emergency bypass, the pipeline enforces three hard safety barriers:
scripts/release/verify_release_eligibility.shchecks that all four container images (k8s-operator,platform-agent,credential-proxy,replay-proxy) exist in GHCR under<TARGET_COMMIT>. Releases of unbuilt commits hard-fail.emergency_override_reasonmust contain a non-whitespace justification; empty or whitespace-only reasons abort the workflow (exit 1).- Target SemVer tags must not already exist on another commit; tag collisions abort the release.
Executing an emergency hotfix
Section titled “Executing an emergency hotfix”To publish an emergency release via the GitHub CLI. Always specify target_commit to pin the exact hotfix commit SHA; omitting target_commit causes the workflow to default to the tip of main (HEAD), releasing all intervening commits without prior GKE E2E validation:
# Emergency release from a specific commit SHA (version calculated automatically):gh workflow run release-publish.yml --repo gke-labs/kube-agents \ -f skip_staging_validation=true \ -f emergency_override_reason="CVE-2026-XXXX: Critical vulnerability in base container dependencies" \ -f target_commit="<HOTFIX_COMMIT_SHA>"
# Emergency release from a specific commit SHA with explicit SemVer override:gh workflow run release-publish.yml --repo gke-labs/kube-agents \ -f skip_staging_validation=true \ -f emergency_override_reason="Critical regression fix for gateway admission deadlock" \ -f target_commit="<HOTFIX_COMMIT_SHA>" \ -f explicit_release_version="0.3.1"Post-release reconciliation
Section titled “Post-release reconciliation”After an emergency publication, complete these three reconciliation steps:
- Confirm the published release tag, container images in GHCR, and signed Helm OCI chart via
gh release view <VERSION>. - Manually trigger
.github/workflows/rc-release-pipeline.ymlagainst the hotfix commit (-f commit_sha="<HOTFIX_COMMIT_SHA>", matching the SHA passed astarget_commit) to run the full GKE E2E suite and ensure the fix validates cleanly on live infrastructure. Do not pass the tagged release commit:tag_ga_release.shcreates a stamped release commit on detached HEAD that lacks SHA-tagged container images in GHCR, causing the RC pipeline’s image verification step to fail. - Attach the GitHub Actions run URL and emergency justification to the corresponding tracking issue or incident report.
Clean promotion and artifact guarantees
Section titled “Clean promotion and artifact guarantees”The release publish workflow enforces byte-for-byte fidelity with tested candidate binaries across seven layers:
- Container images are compiled only once on push to
main.scripts/release/promote_release_images.shretags existing<TARGET_COMMIT>manifests to numericX.Y.Zin GHCR usingdocker buildx imagetools create. - Promoted container images in GHCR are cryptographically signed using Keyless Cosign via GitHub Actions OIDC tokens (
scripts/release/sign_release_images.sh). scripts/release/publish_helm_chart.shpackagescharts/kube-agentsat versionX.Y.Z(matchingappVersion), pushes the OCI package tooci://ghcr.io/gke-labs/kube-agents/charts/kube-agents:X.Y.Z, and signs the OCI manifest via Cosign.scripts/release/tag_ga_release.shcreates a single-parent release commit on detached HEAD, stampsBAKED_RELEASE_VERSION="X.Y.Z"into root scripts (install.sh,uninstall.sh,upgrade.sh), and tags the stamped commit.verify_local_source_refininstall.shandupgrade.shverifies that unversioned source directories matchBAKED_RELEASE_VERSIONand that Git checkouts match the requested tag commit SHA, halting execution if local scripts diverge from container images.scripts/release/package_release_bundle.shstages an offline release bundle directly from the tagged release commit usinggit archive, writes the.release-bundleprovenance marker, and packages both.tar.gzand.ziparchives.scripts/release/generate_release_sbom.shuses Syft to generate Software Bill of Materials (SBOMs) in both SPDX 2.3 JSON and CycloneDX 1.5 JSON formats for filesystem assets and OCI container images, published alongsidechecksums.txtcontaining SHA256 checksums for all release assets.
Offline distribution bundles and SBOMs
Section titled “Offline distribution bundles and SBOMs”For air-gapped or restricted network environments where cloning the repository or pulling directly from GitHub is disallowed, official releases provide pre-packaged distribution bundles and Software Bill of Materials (SBOM).
Distribution bundle assets
Section titled “Distribution bundle assets”Each GA release attaches the following distribution artifacts to the GitHub Release:
kube-agents-<VERSION>.tar.gzandkube-agents-<VERSION>.zip: Complete, self-contained offline distribution bundles containing Terraform provisioning modules (terraform/), Kubernetes operator manifests (k8s-operator/), deployment configs (deploy/), Helm charts (charts/), utility scripts (scripts/), examples (examples/), installer scripts (install.sh,upgrade.sh,uninstall.sh), and the mirrored image catalog (images.json). Tracked example files (terraform.tfvars.example) are preserved, while sensitive tokens and local caches are sanitized.kube-agents-<VERSION>.tgz: Packaged Helm chart with matchingversionandappVersion.kube-agents-<VERSION>.spdx.jsonandkube-agents-<VERSION>.cdx.json: Software Bill of Materials (SBOM) for the filesystem bundle in SPDX 2.3 and CycloneDX 1.5 JSON formats.k8s-operator-<VERSION>.spdx.json,platform-agent-<VERSION>.spdx.json,credential-proxy-<VERSION>.spdx.json,replay-proxy-<VERSION>.spdx.json: Container image SBOMs in SPDX 2.3 JSON format generated by Syft for each of the four release images.checksums.txt: SHA256 cryptographic checksums covering all distribution tarballs, zips, charts, and SBOM JSON files.checksums.txt.bundle: Keyless Cosign signature bundle attesting to the provenance and authenticity ofchecksums.txtsigned via GitHub Actions OIDC.
Provenance attribution and .release-bundle marker
Section titled “Provenance attribution and .release-bundle marker”Every packaged release bundle contains a .release-bundle metadata file at its root, attesting to the release provenance:
name=kube-agentsversion=<VERSION>tag=<VERSION>commit=<STAMPED_RELEASE_COMMIT_SHA>build_date=YYYY-MM-DDTHH:MM:SSZThe commit field records the SHA of the tagged release commit (the single-parent stamped commit created on detached HEAD parented by the candidate commit).
When install.sh or upgrade.sh executes from an unversioned directory outside Git, verify_local_source_ref verifies source integrity in two steps:
BAKED_RELEASE_VERSIONstamped into the script must match the requested release ref (passed via--image-tag, defaulting to the script’s own version).- If
.release-bundleis present with matchingversionortag, it attributes the source directory to the official release bundle and logs:
✓ Verified install sources match official release bundle <VERSION>.(or ✓ Verified upgrade sources match official release bundle <VERSION>. during upgrades). If the marker is missing but BAKED_RELEASE_VERSION matches, it reports matching the baked release.
Verifying release bundle integrity and provenance
Section titled “Verifying release bundle integrity and provenance”Consumers can verify both the cryptographic provenance and integrity of downloaded release assets. First, verify the authenticity of checksums.txt using Keyless Cosign:
cosign verify-blob \ --bundle checksums.txt.bundle \ --certificate-identity-regexp "^https://github\.com/gke-labs/kube-agents/" \ --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \ checksums.txtOnce checksums.txt is verified against GitHub Actions OIDC provenance, verify downloaded files against the checksums:
sha256sum -c checksums.txt --ignore-missingInspecting Software Bill of Materials (SBOM)
Section titled “Inspecting Software Bill of Materials (SBOM)”SBOMs are generated using Syft and can be inspected using standard security and compliance tooling:
# Inspect filesystem package inventory from SPDX SBOM using jq:jq '.packages[] | {name: .name, version: .versionInfo, license: .licenseConcluded}' kube-agents-<VERSION>.spdx.json
# Inspect filesystem components in CycloneDX format:jq '.components[] | {name: .name, version: .version, type: .type}' kube-agents-<VERSION>.cdx.json
# Inspect container image packages (e.g. operator runtime dependencies):jq '.packages[] | {name: .name, version: .versionInfo}' k8s-operator-<VERSION>.spdx.jsonHelm chart versioning
Section titled “Helm chart versioning”The chart version tracks the application appVersion: the release workflow packages the
chart with both version and appVersion set to the exact SemVer release tag X.Y.Z, so every
chart release corresponds to exactly one application release. There is no chart-only release
train — a chart-template fix ships with the next X.Y.Z tag.
Pinning Terraform module versions in GitOps
Section titled “Pinning Terraform module versions in GitOps”When configuring GitOps repositories, pin companion Terraform modules using the exact SemVer Git tag:
module "gke_cluster" { source = "git::https://github.com/gke-labs/kube-agents.git//terraform/modules/gke-cluster?ref=0.3.0" project_id = var.project_id cluster_name = "production-host-01" location = "us-central1"}