.github, hack, docs: release-cut runs ci on a push to main and tags as the releaser app; release-roll builds from the pushed tag; Actions files are .yaml and actions are at version tags #21

Merged
mikluko merged 1 commit from mikluko/release-on-tag into main 2026-10-04 15:00:22 +00:00
mikluko commented 2026-10-04 13:57:42 +00:00 (Migrated from github.com)

The flow:

  1. A push to main -> release-cut: job ci calls ci.yaml; job cut needs it, reads CHANGELOG.md, and pushes vX.Y.Z on the pushed commit as the releaser GitHub App. It tags only where mikluko/action-changelog reports the newest entry due: it names a version, no tag names it, and it is not a prerelease. Otherwise the action writes why to the step summary and the Cut step is skipped.
  2. The tag push -> release-roll, titled "Releasing vX.Y.Z": it fails unless the tag is the changelog's newest version at that commit, then builds and signs the images, attests, pushes the chart, and creates the GitHub release last.
  3. A failed release-roll run is recovered by re-running it; release-cut skips a tagged version.
  4. release-roll run by hand is the dry run, titled "Dry run of a release from ": the changelog's newest version if the action reports it due, a prerelease included, nothing pushed, nothing tagged.

release-cut follows uptime-com/up2-monitoring's release.yaml and the cut-release workflow it calls, inlined because that workflow is private. ci.yaml runs on pull_request and workflow_call only: on main it runs as release-cut's job, and its checks there are named ci / ... under release-cut. A prerelease entry is not cut; a prerelease tag pushed by hand still rolls.

Every Actions file moves to .yaml: the workflows, setup-hugo/action.yaml and .github/zizmor.yaml. GitHub treats a renamed workflow file as a new workflow, so the run history of ci, docs, e2e and release starts fresh under the new files and the old entries stay listed for the deleted files. Nothing in the tree depends on a workflow file name from outside it: there is no status badge.

Every action is referenced by its major version tag in place of a commit SHA, and zizmor's unpinned-uses policy is ref-pin. setup-ko, cosign-installer and zizmor-action publish no major tag and stay at v0.10, v4.1.2 and v0.6.4. helm/kind-action stays at v1.15.0: its v1 tag is seven commits behind that release. .github/actionlint.yaml ignores two findings actionlint v1.7.12 raises from stale inputs of create-github-app-token@v3.

Verifying a release

From the first release cut this way, the cosign certificate identity is https://github.com/mikluko/nats-operator/.github/workflows/release-roll.yaml@refs/tags/v<version>, and gh attestation verify takes --signer-workflow mikluko/nats-operator/.github/workflows/release-roll.yaml --source-ref refs/tags/v<version>. Releases 0.1.0 through 0.3.1 keep release.yml@refs/heads/main. SECURITY.md has both.

Before the first cut

The app behind vars.RELEASER_APP_CLIENT_ID and secrets.RELEASER_APP_CERT is installed on this repository with Contents: read and write. release-cut narrows its token to that.

Workflows: read and write is not requested. A tag on a commit whose .github/workflows/ differs from every branch tip is rejected without it, according to community reports (community discussions 151442 and 141159, actions/checkout#1421); GitHub's documentation does not state the rule, and it is unverified here. That case is main moving past a workflow change while ci runs on the pushed commit. release-cut then fails at the push, and pushing the tag by hand on that commit starts release-roll.

Not proven until a real cut

  • ci.yaml called from release-cut on a push to main.
  • The app token mints, and its tag push starts release-roll.
  • The tag push is accepted with the permissions the app has.
  • The signing identity and the attestation's source ref are the ones SECURITY.md states.
  • gh release create --verify-tag on the tag the app pushed.

Any v* tag pushed by someone with write access starts release-roll; it publishes only if the tag is the changelog's newest version at the tagged commit. A tag ruleset admitting the app alone would close that, and is not part of this change.

The flow: 1. A push to `main` -> `release-cut`: job `ci` calls `ci.yaml`; job `cut` needs it, reads `CHANGELOG.md`, and pushes `vX.Y.Z` on the pushed commit as the releaser GitHub App. It tags only where `mikluko/action-changelog` reports the newest entry `due`: it names a version, no tag names it, and it is not a prerelease. Otherwise the action writes why to the step summary and the Cut step is skipped. 2. The tag push -> `release-roll`, titled "Releasing vX.Y.Z": it fails unless the tag is the changelog's newest version at that commit, then builds and signs the images, attests, pushes the chart, and creates the GitHub release last. 3. A failed `release-roll` run is recovered by re-running it; `release-cut` skips a tagged version. 4. `release-roll` run by hand is the dry run, titled "Dry run of a release from <ref>": the changelog's newest version if the action reports it due, a prerelease included, nothing pushed, nothing tagged. `release-cut` follows `uptime-com/up2-monitoring`'s `release.yaml` and the `cut-release` workflow it calls, inlined because that workflow is private. `ci.yaml` runs on `pull_request` and `workflow_call` only: on `main` it runs as `release-cut`'s job, and its checks there are named `ci / ...` under `release-cut`. A prerelease entry is not cut; a prerelease tag pushed by hand still rolls. Every Actions file moves to `.yaml`: the workflows, `setup-hugo/action.yaml` and `.github/zizmor.yaml`. GitHub treats a renamed workflow file as a new workflow, so the run history of `ci`, `docs`, `e2e` and `release` starts fresh under the new files and the old entries stay listed for the deleted files. Nothing in the tree depends on a workflow file name from outside it: there is no status badge. Every action is referenced by its major version tag in place of a commit SHA, and zizmor's `unpinned-uses` policy is `ref-pin`. `setup-ko`, `cosign-installer` and `zizmor-action` publish no major tag and stay at `v0.10`, `v4.1.2` and `v0.6.4`. `helm/kind-action` stays at `v1.15.0`: its `v1` tag is seven commits behind that release. `.github/actionlint.yaml` ignores two findings actionlint v1.7.12 raises from stale inputs of `create-github-app-token@v3`. ## Verifying a release From the first release cut this way, the cosign certificate identity is `https://github.com/mikluko/nats-operator/.github/workflows/release-roll.yaml@refs/tags/v<version>`, and `gh attestation verify` takes `--signer-workflow mikluko/nats-operator/.github/workflows/release-roll.yaml --source-ref refs/tags/v<version>`. Releases 0.1.0 through 0.3.1 keep `release.yml@refs/heads/main`. `SECURITY.md` has both. ## Before the first cut The app behind `vars.RELEASER_APP_CLIENT_ID` and `secrets.RELEASER_APP_CERT` is installed on this repository with Contents: read and write. `release-cut` narrows its token to that. Workflows: read and write is not requested. A tag on a commit whose `.github/workflows/` differs from every branch tip is rejected without it, according to community reports (community discussions 151442 and 141159, actions/checkout#1421); GitHub's documentation does not state the rule, and it is unverified here. That case is `main` moving past a workflow change while ci runs on the pushed commit. `release-cut` then fails at the push, and pushing the tag by hand on that commit starts `release-roll`. ## Not proven until a real cut - `ci.yaml` called from `release-cut` on a push to `main`. - The app token mints, and its tag push starts `release-roll`. - The tag push is accepted with the permissions the app has. - The signing identity and the attestation's source ref are the ones `SECURITY.md` states. - `gh release create --verify-tag` on the tag the app pushed. Any `v*` tag pushed by someone with write access starts `release-roll`; it publishes only if the tag is the changelog's newest version at the tagged commit. A tag ruleset admitting the app alone would close that, and is not part of this change.
Sign in to join this conversation.
No description provided.