Run the upstream Flux controller patch release procedure for helm-controller, image-automation-controller, image-reflector-controller, kustomize-controller, notification-controller, source-controller, and source-watcher. Use when preparing a new controller patch release from a release series branch, drafting changelog entries, tagging releases, and opening the follow-up changelog PRs back to main.
72
88%
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 patch releases only. Do not use it
for flux2, pkg, or other non-controller repos.
Supported controllers:
helm-controllerimage-automation-controllerimage-reflector-controllerkustomize-controllernotification-controllersource-controllersource-watcherhttps://github.com/fluxcd/helm-controller/pull/1465), never the
<owner>/<repo>#<number> shorthand — full URLs are clickable from the
user's terminal, the shorthand is not.gh pr checks <num> -R fluxcd/<repo> --watch in the background as
soon as the PR is created so CI status lands in the conversation the
moment it finishes. Do the same for tag-triggered release workflows
(gh run watch <id> -R fluxcd/<repo> in the background). Do not wait
until "everything is pushed" to start watching — start watching the first
PR while you prepare the second.gh pr checks --watch
only covers CI; it does not fire on maintainer approval. Poll the review
state in the background so you are notified the moment it flips to
APPROVED + CLEAN:
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 all PRs, including PRs that update this
skill file itself.Add changelog entry for vX.Y.Z and Release vX.Y.Z). When the fix
belongs in the changelog, amend the changelog commit; when it belongs in
the release bump, amend that one. 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. Find thread
IDs with
gh api graphql -f query='{ repository(owner:"<o>",name:"<r>") { pullRequest(number:<n>) { reviewThreads(first:50) { nodes { id isResolved comments(first:1){nodes{databaseId body}} } } } } }'.git tag -s -m ...). Never create
release tags through the GitHub API — that produces lightweight tags which
break git tag -v verification.main (step 11) and that PR being merged. Merging
the release PR and tagging is not the last step, and neither is opening
the cherry-pick PR. Before reporting completion, walk through each
controller against the numbered steps and confirm each one ran.website/content/en/flux/releases/procedure.md,
section Controllers: patch releases.git and gh commands as confirmation points if the user wants that.date to get the release date for changelog entries.origin/* refs.For each controller:
Refresh local state.
git fetch --all --tags --prunegit switch release/vX.Y.xgit pull origin release/vX.Y.xCreate the release preparation branch exactly from the release series branch.
git switch -c release-vX.Y.Z release/vX.Y.xDraft the new CHANGELOG.md entry.
date.go.mod diff for notable content (see "Dependency update
PRs" below) rather than settling for a generic bump line.Commit the changelog entry.
git add CHANGELOG.mdgit commit -s -m "Add changelog entry for vX.Y.Z"Apply the release version bump exactly as documented.
go.mod.config/manager/kustomization.yaml newTag to vX.Y.Z.git add go.mod config/manager/kustomization.yamlgit commit -s -m "Release vX.Y.Z"Push the release preparation branch.
git push origin release-vX.Y.ZOpen and merge the release PR into the release series branch.
release/vX.Y.xrelease-vX.Y.ZRefresh the release series branch after merge.
git switch release/vX.Y.xgit pull origin release/vX.Y.xCreate 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.Z" api/vX.Y.Zgit push origin api/vX.Y.Zgit tag -s -m "vX.Y.Z" vX.Y.Zgit push origin vX.Y.ZConfirm the non-api/ tag triggered the release workflow.
Cherry-pick only the changelog commit back to main.
git switch maingit pull origin maingit switch -c pick-changelog-vX.Y.Z maingit cherry-pick -x <Add changelog entry commit>git push origin pick-changelog-vX.Y.Zpick-changelog-vX.Y.Z to maingit switch -c fail, which silently leaves you on main, so the next
git cherry-pick lands the changelog commit on local main instead of
the pick branch. Before cherry-picking, confirm you are on
pick-changelog-vX.Y.Z; if the branch already exists, switch to it (or
delete and recreate it) rather than letting switch -c fail.For a patch release, gather:
origin/release/vX.Y.x since that tagWrite the new section at the top of CHANGELOG.md:
## X.Y.Z**Release date:** YYYY-MM-DDFixes: when there are bug-fix itemsImprovements: for dependency updates, docs, feature gates, or cleanupRules:
Do not reduce a dependency bump to a generic "Update fluxcd/pkg dependencies" line and move on — the user-facing substance is usually hidden inside the bump. Weigh Flux's own repos and third-party dependencies differently:
fluxcd/* modules are what matter. These are our own libraries, so a
bump is how a fix we made reaches the controller. Trace every module in the
go.mod diff back to its commits, e.g. in a local clone of fluxcd/pkg:
git log --oneline --no-merges <mod>/<old>..<mod>/<new> -- <mod>, filtering
out Prepare for release and the shared Upgrade k8s to ... commits. Read
the PR behind each remaining commit and describe the fix in the controller's
own terms. Doing this across all the modules of one release usually leaves a
handful of real changes, which is a short enough list to reason about
per controller.k8s.io/* actually moves, e.g. "which
bring Kubernetes to 1.36.4". Read the version off the go.mod diff for that
specific bump instead of assuming one is there: a fluxcd/pkg update may
carry a Kubernetes bump, a module fix, both, or neither, and the same release
window can move k8s.io/* for one controller and not for another.grep the controller for the changed symbol and
for the type that owns it. Example: runtime/client rejecting file
references in kubeconfigs reached kustomize-controller purely through
Impersonator, which kustomize-controller uses and the other controllers do
not — so it changed how .spec.kubeConfig Secrets are accepted with no
kustomize-controller commit behind it.pkg/auth bump) is worth noting for source-controller and
image-reflector-controller, which pull from registries, but not for
image-automation-controller, which only talks to Git.tag..origin/release/....release-vX.Y.Z.go.mod; do not assume the self-API path form.
source-watcher uses github.com/fluxcd/source-watcher/api/v2, so it still
needs the same self-API release bump pattern.main, not the release version bump.git reset --soft HEAD~2, do not git checkout go.mod or
config/manager/kustomization.yaml — that restores them from the index and
silently reverts the bump, leaving a release PR that only touches the
changelog. Rebuild both commits, then check with
git diff origin/release/vX.Y.x --stat that CHANGELOG.md, go.mod and
config/manager/kustomization.yaml are all still modified.git branch -r --list 'origin/release/v*.x' | sort -Vgit tag -l 'v*' | sort -V | tailgit log --oneline <prev-tag>..origin/release/vX.Y.xgh pr view <number> -R fluxcd/<repo> --json number,title,url,baseRefName88fb00c
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.