Run the upstream Flux controller minor release procedure for helm-controller, image-automation-controller, image-reflector-controller, kustomize-controller, notification-controller, source-controller, and source-watcher. Use when cutting a new controller minor release (vX.Y.0): creating the release series branch, drafting the minor changelog, tagging, merging the release branch back to main, and adding the backport label.
73
90%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Low
Low-risk findings worth noting
Use this skill for upstream Flux controller minor releases (vX.Y.0) only —
the release that opens a new release/vX.Y.x series. Do not use it for flux2,
pkg, or other non-controller repos.
Supported controllers:
helm-controllerimage-automation-controllerimage-reflector-controllerkustomize-controllernotification-controllersource-controllersource-watcherrun_in_background) so the user can keep steering and you get
notified on completion. A foreground watch blocks the session.https://github.com/fluxcd/source-controller/pull/2082), never the
<owner>/<repo>#<number> shorthand — full URLs are clickable from the user's
terminal.gh pr checks <num> -R fluxcd/<repo> --watch in the background as soon as the
PR is created, and the same for tag-triggered release workflows
(gh run watch <id> -R fluxcd/<repo>).gh pr checks --watch only
covers CI, not maintainer approval. Poll the review state in the background:
while :; do
state=$(gh pr view <num> -R fluxcd/<repo> --json mergeStateStatus,reviewDecision --jq '.reviewDecision+" "+.mergeStateStatus')
case "$state" in "APPROVED CLEAN") echo "$state"; break;; esac
sleep 30
done-s (sign-off). Never include Co-Authored-By lines,
your own name, or any AI attribution in commit messages, PR titles, or PR
descriptions. This applies to the skill-update PR too.Add changelog entry for vX.Y.0 and Release vX.Y.0). Use
git reset --soft HEAD~2 + re-commit, or an interactive rebase, then
git push --force-with-lease.Fixed, thanks! on the thread and resolve
it. Reply via
gh api repos/<owner>/<repo>/pulls/<n>/comments/<cid>/replies -f body='Fixed, thanks!'
and resolve via the GraphQL resolveReviewThread mutation.git tag -s -m ...). Never create release
tags through the GitHub API — that produces lightweight tags which break
git tag -v verification.Release/vX.Y.x. Every PR body is a single line pointing at the flux2 minor
release tracking issue: Part of: https://github.com/fluxcd/flux2/issues/NNNN.website/content/en/flux/releases/procedure.md,
section Controllers: minor releases
(https://fluxcd.io/flux/releases/procedure/#controllers-minor-releases).Part of: https://github.com/fluxcd/flux2/issues/NNNN).date to get the release date for the changelog entry.git fetch --all --tags --prune before reasoning about branches, tags, or
merged PRs. Do not trust stale local origin/* refs.git and gh commands as confirmation points if the user wants that.For the controller being released (target version vX.Y.0):
Create the release series branch from main and push it.
git switch -c release/vX.Y.x maingit push origin release/vX.Y.xCreate the release preparation branch from the series branch.
git switch -c release-vX.Y.0 release/vX.Y.xDraft the new CHANGELOG.md entry (see "How To Build The Changelog Entry").
Then commit it.
git add CHANGELOG.mdgit commit -s -m "Add changelog entry for vX.Y.0"Apply the release version bump exactly as documented.
go.mod to vX.Y.0.
Inspect the actual go.mod; do not assume the self-API path form
(source-watcher uses github.com/fluxcd/source-watcher/api/v2).config/manager/kustomization.yaml newTag to vX.Y.0.git add go.mod config/manager/kustomization.yamlgit commit -s -m "Release vX.Y.0"Push the release preparation branch.
git push origin release-vX.Y.0Open and merge the release PR into the release series branch.
release/vX.Y.x Head: release-vX.Y.0Release vX.Y.0, body Part of: <tracking issue URL>.Refresh the release series branch after the merge.
git switch release/vX.Y.xgit pull origin release/vX.Y.xvX.Y.0 on the merged commit before tagging.Create and push signed tags from the updated release series branch. Push the
api/ tag first — the release tag depends on it.
git tag -s -m "api/vX.Y.0" api/vX.Y.0git push origin api/vX.Y.0git tag -s -m "vX.Y.0" vX.Y.0git push origin vX.Y.0Verify the release workflow triggered by the vX.Y.0 tag. Watch it in the
background until it concludes successfully (images published + signed, SBOM,
SLSA provenance, GitHub release created).
Merge the release series branch into main via PR. This merges the whole
branch (changelog + version bump), not a cherry-pick.
main Head: release/vX.Y.xRelease/vX.Y.x. Body Part of: <tracking issue URL>.Last: open the backport label PR against main. Do this only after
step 10 has merged.
git switch maingit pull origin maingit switch -c label-X.Y main.github/labels.yaml, after the previous backport: entry:
- name: backport:release/vX.Y.x
description: To be backported to release/vX.Y.x
color: '#ffd700'git add .github/labels.yamlgit commit -s -m "Add backport:release/vX.Y.x label"git push origin label-X.Ymain, title Add backport:release/vX.Y.x label, body
Part of: <tracking issue URL>) and merge when green.main. If you open it before
step 10 merges, that merge moves main forward and the label PR must then
be rebased onto the new main and force-pushed
(git rebase main + git push --force-with-lease). Opening it last avoids
the rebase entirely.A minor changelog entry summarizes what is new in vX.Y.0 relative to the whole vX.(Y-1) line — not every commit since the previous minor. The hard part is selecting exactly the right PRs.
main since the previous minor
(v(X).(Y-1).0).
git log --merges --grep="Merge pull request" v(X).(Y-1).0..release/vX.Y.x
and extract the #NNNN.gh so squash/rebase merges are not missed:
gh pr list --base main --state merged --limit 200 --json number,mergedAt,title
filtered to merges after the previous minor's release timestamp.gh pr view <n> -R fluxcd/<repo> --json number,baseRefName,state,mergedAt,title.
Keep only PRs that are MERGED and have baseRefName == main. Use the PR
title from GitHub, never the local merge-commit subject (these drift; e.g. a
commit may say one thing while the PR title says another).> v(X).(Y-1).0 and < vX.Y.0). Read the ## (X).(Y-1).Z patch
sections already in CHANGELOG.md and drop any candidate whose change shipped
there. Note the patch changelogs cite the cherry-pick PR numbers
(against release/v(X).(Y-1).x), which differ from the original main PR
numbers — match by change, not by number.Release/v(X).(Y-1).x merge-back, and label PRs.Various dependency updates
bullet listing each PR link. Keep genuinely user-facing items as their own
bullets. See "Dependency update PRs" below before settling for a generic line.Write the new section at the top of CHANGELOG.md, matching the existing minor
entries in that repo:
## X.Y.0**Release date:** YYYY-MM-DD (from date)⚠️ upgrade warnings (API removals, required flux migrate, etc.).
When warning about a deprecated/beta API removal, link the upgrade instruction
to the canonical flux2 upgrade-procedure discussion
(https://github.com/fluxcd/flux2/discussions/5572), not to a one-off
flux migrate PR — the discussion is the maintained guide covering both the
Flux CLI and Flux Operator migration paths. Older changelog entries may still
point at a migrate PR; do not copy that, use the discussion link.### GitRepository, ### OCIRepository,
### HelmChart, ### Bucket, …) describing notable features in prose.### General updates subsection for k8s/Go/dependency posture.Fixes: and Improvements: bullet lists, each bullet a short title plus one
or more [#NNNN](https://github.com/fluxcd/<repo>/pull/NNNN) links.Surface borderline items (repo-internal docs, a dep bump whose content already shipped in a patch) to the user rather than guessing whether to headline them.
Do not reduce a dependency bump to a generic line without checking its substance.
Includes: fluxcd/pkg#NNNN), and look at the go.mod diff.git fetch --all --tags --prune before comparing
v(X).(Y-1).0..release/vX.Y.x or reasoning about merged PRs.main; the prep branch (step 2)
is cut from release/vX.Y.x.api/vX.Y.0 before vX.Y.0.go.mod for the self-API path; do not assume its form.After a controller minor ships, its API module often needs bumping in the
controllers that depend on it (e.g. image-reflector-controller/api in
image-automation-controller, or source-controller/api in helm-controller,
kustomize-controller, image-automation-controller, and source-watcher). This is a
separate follow-up PR per dependent repo, not part of the 11 release steps above.
main (which may already carry an earlier
bump from the same release round), then
go get github.com/fluxcd/<controller>/api@vX.Y.0 followed by go mod tidy.go.mod + go.sum only. Mirror an existing sibling PR
from the same round for the exact title/body/commit shape.config/default/kustomization.yaml (remote
…/releases/download/vX.Y.Z/<controller>.crds.yaml and .deployment.yaml
URLs). If it does, bump those URLs to the new version too. Some repos pin them
(source-controller is pinned by source-watcher, helm-controller, and
kustomize-controller) and some do not (image-automation-controller does not pin
image-reflector-controller). Do not assume — grep the repo.go build ./... and go vet ./....git branch -r --list 'origin/release/v*.x' | sort -Vgit tag -l 'v*' | sort -V | tailgit log --merges --grep="Merge pull request" v(X).(Y-1).0..release/vX.Y.xgh pr list --base main --state merged --limit 200 --json number,mergedAt,titlegh pr view <n> -R fluxcd/<repo> --json number,title,url,baseRefName,state,mergedAt88fb00c
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.