Skip to content

Releasing

Maintainer process. Reading a release is covered in Security.

What a tag publishes

Pushing a v* tag runs release.yml, which produces:

Artifact Where
CLI archives for linux, darwin and windows on amd64 and arm64 GitHub Release
A checksum file, plus a cosign bundle signing it GitHub Release
An SPDX SBOM per archive GitHub Release
A multi-arch container image, signed and attested ghcr.io/ethan-kane-ops/k8s-pod-oom-oracle
The Helm chart, signed and attested oci://ghcr.io/ethan-kane-ops/charts/oom-oracle

Nothing is built on a laptop. just release writes the changelog, tags and pushes; the workflow does the rest.

cosign is version-pinned, and bumping it is manual

release.yml pins cosign-release on all three installer steps. Pinning the action by SHA says nothing about the binary it downloads, which otherwise tracks the newest release. v0.1.0 failed on exactly that: cosign v3 had changed sign-blob's flags, the signing step errored, and the tag published an image and a chart with no archives. Dependabot updates the action ref but not the version below it, so bump it by hand and let a release-candidate tag prove it.

The daemon is Linux-only

Darwin and windows archives exist because inspect and watch are HTTP clients, and the person reading a report is rarely on the node. daemon needs a Linux kernel and says so if started elsewhere.

Cutting one

just release-preview          # what the next version and changelog would be
just release-snapshot         # build every artifact locally, publish nothing
just release minor            # auto | patch | minor | major | vX.Y.Z

just release refuses to run when the working tree is dirty, when the tag already exists, when just check fails, or when CHANGELOG.md has no [Unreleased] heading to close.

Where the notes come from

A release section has two halves. Breaking changes and migration notes are written by hand under ## [Unreleased] as the work lands, because no commit subject can say which JSON field a consumer has to rename. The per-commit lists are generated by git-cliff at tag time.

hack/changelog-release.sh merges them: it renames [Unreleased] to the version being cut, folds the generated groups in underneath the hand-written prose, and opens a fresh empty [Unreleased] above. One heading per version, prose first.

Neither of git-cliff's own modes can do this. -o regenerates the file from commit subjects and deletes the hand-written notes outright. --prepend keeps them but inserts its section above them, leaving two headings for one version; hack/release-notes.sh stops at the first, so the published notes are commit subjects and the migration notes are silently missing. release-notes.sh now fails on a duplicated version heading rather than truncating, and hack/changelog_test.go covers both paths.

Release notes come from the changelog

hack/release-notes.sh v0.1.0 prints the CHANGELOG.md section for a version. just release runs it before tagging and the workflow runs it before building, and both fail if the section is empty.

The alternative would be generating notes from commit subjects at release time. That was the original behaviour, and it published a release whose only mention of a renamed API field was fix(oom): filter the victim across PID namespaces.

The chart version is the tag

charts/oom-oracle/Chart.yaml states the version three times: version, appVersion, and the image tag inside the artifacthub.io/images annotation. The workflow refuses to publish when any of them disagrees with the tag, and it checks after the image job has pushed, so a missed bump leaves a half-published release.

just release sets all three through hack/chart-version.sh, so there is nothing to remember. A chart whose version is not the version it installs is worse than no chart, because it looks authoritative.

Artifact Hub

Registration is manual, one-off, and needs a public repository with a published chart. Both are now true.

  1. Add the repository at artifact hub:
Field Value
Kind Helm charts
Name oom-oracle
Display name oom-oracle
URL oci://ghcr.io/ethan-kane-ops/charts/oom-oracle

The name becomes the URL segment, so the listing lands at artifacthub.io/packages/helm/oom-oracle/oom-oracle. An OCI repository holds one chart, so naming it after the chart rather than the owner keeps a second chart from having to squat on the name.

  1. Copy the repository ID it issues into artifacthub-repo.yml at the repository root.
  2. just chart-claim.

Step 3 pushes that file to the chart's OCI repository under the artifacthub.io tag, which is where Artifact Hub looks to confirm the listing belongs to this project. Without it the chart still lists; it just is not a verified publisher.

The ordering is the part worth knowing: the ID does not exist until step 1, so the file cannot be filled in ahead of time, and just chart-claim refuses to push it empty. The owner name and email in that file are published to a public registry, and the email has to match the Artifact Hub account because that match is the ownership check.

Version metadata

--version reports the tag, the full commit and the commit date, injected with -ldflags -X against internal/version. The commit date rather than the build date, so the same source produces the same binary.

If a release goes wrong

Delete the tag locally and remotely, delete the GitHub Release, and cut the next patch version. Do not move a tag: the image is signed by digest, and a moved tag leaves a signature pointing at content nobody can reach.