Skip to content

ci(release): derive versions from commits, check commit messages, pin the supply chain - #17

Merged
leinardi merged 6 commits into
masterfrom
ci/release-flow
Sep 25, 2026
Merged

leinardi merged 6 commits into
masterfrom
ci/release-flow

Conversation

@leinardi

@leinardi leinardi commented Sep 25, 2026 •

Copy link
Copy Markdown
Owner

Summary

Release versions are now derived from Conventional Commits. The release workflow is rewritten so a failed run can be recovered under the "Immutable tags" ruleset. Commit messages are checked in CI, and the supply chain is pinned. docs/release.md (new) explains the whole flow.

Commits

  1. build(pre-commit): the end-of-file-fixer and trailing-whitespace hooks skip testdata/, because the internal/cgroup/testdata fixtures are byte-for-byte /proc captures. The config now also documents why the commit-msg hook is installed.

  2. ci(ci): new conventional-commits job, on pull requests only. It runs the repo's own conventional-pre-commit hook, --force-scope included, on every non-merge commit in the PR. There is no PR-title check, because squash merging is disabled.

  3. ci(release): .github/workflows/release.yaml rewritten:

    • Inputs: version is optional; left empty, it is derived with svu, and a range with no feat/fix/breaking commit fails. It replaces the required semver input. dry_run is new.
    • Guard and checks: a guard job refuses any ref but master. The test, audit and integration jobs rerun the CI checks inside the release, all with Go taken from go.mod.
    • Build before tag: binaries and the multi-arch image are built before the tag exists. The tag is created locally, checked against the remote again, and only then pushed.
    • Publish, never replace: a published artifact is reused only once it is proven to be this run's.
      • The image is pushed by digest, attested and cosign-signed, then tagged.
      • An existing :<version> tag is reused only after gh attestation verify binds it to this repo, this workflow and this commit. Otherwise the run stops.
      • A signature counts only if it matches this workflow's identity and issuer.
      • Release assets are compared one by one by sha256 against the local build. Missing ones are uploaded without --clobber.
    • Latest pointers: :latest, :<major> and the GitHub "Latest" marker move only if the version is still the highest when each one moves. A warning fires if they end up disagreeing.
    • Reproducible builds: the build date, SOURCE_DATE_EPOCH and a fixed-length short SHA all come from the commit.
    • Removed: the create-tag and cleanup-tag steps, which cannot work under the immutable-tag ruleset.
  4. build(docker): both base images are pinned as tag@sha256. Dependabot keeps them moving.

  5. ci(deps): every uses: is pinned to a full commit SHA. gha-pre-commit-reviewdog-actions is pinned at v1.0.1, which now pins its own nested actions. Dependabot's gomod prefix is now fix(deps), so Go dependency bumps produce patch releases.

  6. ci(deps): the warm-up workflow is pinned to gh-reusable-workflows v1.2.1, which pins its own checkout, setup-python and cache actions. That closes the last mutable ref that could reach the release job, through the master cache scope. This is documented in docs/release.md under "What the workflows trust".

Follow-ups after this PR

  • Once conventional-commits has run here, add it as a required status check on the master ruleset 16797240 (see docs/release.md).
  • Add a master deployment-branch rule to the release environment.
  • After merge, dispatch Release with dry_run: true:
    • with no version: expect the no-bump failure, since everything since v0.10.0 is build/docs;
    • with 0.10.1: expect the full build, and no tag or image published.
  • The first real release (e.g. 0.10.1) needs an explicit version.

Verification done locally

  • pre-commit passes on every commit. actionlint with shellcheck at style level is clean on release.yaml.
  • The testdata sha256 sums are unchanged after running both hooks on all files.
  • In a scratch clone without the stray v1.0.0 tag, svu next gives v0.10.0 (no bump), v0.10.1 after a fix, and v0.11.0 after a feat.
  • make docker-build succeeds with the digest-pinned bases.

Pull request checklist

  • I am targeting the master branch
  • I have rebased this branch on top of the destination branch
  • I have executed make check locally before creating the commit and it has run successfully
  • I have performed a self-review of my own code
  • There are no WIP commits in this PR

Type of changes

  • 🐛 Bug fix
  • ✨ New feature
  • 🔧 Refactoring
  • 📜 Docs
  • 🧰 CI / tooling / infra
  • Other (describe in Summary)

…efore tagging

The release job now builds the binaries and the image before the tag exists, pushes the tag only after a fresh remote check, and publishes under one rule: a published artifact is reused only once proven to be this run's and is never replaced. The create-tag and cleanup-tag steps are gone; they cannot work under the immutable-tag ruleset. The required semver input is replaced by an optional version input, derived with svu when empty, and a dry_run input.
Every uses reference in the four workflows is now a full commit SHA with the version in a trailing comment, including the reusable pre-commit warm-up workflow and gha-pre-commit-reviewdog-actions v1.0.1, whose composite actions now pin their own nested actions. The warm-up workflow's nested actions are still tag-pinned upstream, and their cache writes can reach the release job; docs/release.md records that exposure and its fix.

Dependabot's gomod updates now use the fix(deps) prefix, so a Go dependency bump produces a patch release.
gh-reusable-workflows v1.2.1 pins the checkout, setup-python and cache actions it runs. The warm-up job writes the master cache scope that the release job restores, so those tag-pinned actions were the last mutable ref that could reach a release. docs/release.md drops the remaining-exposure note.
@leinardi
leinardi merged commit 59bc072 into master Sep 25, 2026
8 checks passed
@leinardi
leinardi deleted the ci/release-flow branch September 25, 2026 15:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant