name: workflow-tool-versions
description: Bump pinned CLI tool versions in GitHub Actions workflows on the Fission repo (helm, kind, skaffold, cosign, golangci-lint, goreleaser, etc.) — primarily the *_VERSION: env vars and the # vX.Y.Z comments next to SHA-pinned uses: actions. Use when the user asks to update workflow tool versions, do a CI tool sweep, check what's outdated in .github/workflows/, or process Dependabot's grouped github-actions PR. Use when this capability is needed.
metadata:
author: fission
Workflow tool-version sweep (Fission)
Playbook for refreshing the CLI tool versions that the Fission GitHub Actions workflows pin. Optimised for isolating regressions: each tool lands as its own commit so CI failure attribution is one click on git blame.
When to invoke
Trigger phrases: "update workflow tool versions", "bump skaffold/kind/cosign/helm", "what's outdated in our workflows", "process the dependabot github-actions PR", "CI tool refresh".
Skip if the user names a single tool and a single version — just edit and commit.
Scope (in order of priority)
- Primary —
env:block*_VERSION:vars. These are the versions the workflow downloads at runtime. This is the user's main scope. - Secondary —
# vX.Y.Zcomments next to SHA-pinneduses:actions. Dependabot already does these on a weekly schedule (.github/dependabot.ymlhas agithub-actionsgroup), so a manual sweep is mostly catch-up if Dependabot is behind. - Tertiary — matrix
kindversion:(k8s node images for kind). Tied to kind release support windows; bump only when the underlying kind binary supports newer node images. Verify againstkindrelease notes — kind v0.X.Y publishes a list of supportedkindest/nodetags.
Out of scope:
with: version: "~> v2"(floating semver constraints — by design, no pin to bump).- Tool versions referenced in
Makefile,hack/,Dockerfiles, orgoreleaserconfig — those live with the build code, not the workflow runners.
Known inventory (snapshot for orientation)
The skill should re-discover dynamically (see Phase 1) — but a recent snapshot for orientation:
| Workflow | Var | Tool | Upstream repo |
|---|---|---|---|
| push_pr.yaml | HELM_VERSION | helm | helm/helm |
| push_pr.yaml | KIND_VERSION | kind | kubernetes-sigs/kind |
| push_pr.yaml | SKAFFOLD_VERSION | skaffold | GoogleContainerTools/skaffold |
| upgrade_test.yaml | HELM_VERSION | helm | helm/helm |
| upgrade_test.yaml | KIND_VERSION | kind | kubernetes-sigs/kind |
| release.yaml | KIND_VERSION | kind | kubernetes-sigs/kind |
| release.yaml | COSIGN_VERSION | cosign | sigstore/cosign |
| lint.yaml | GOLANGCI_LINT_VERSION | golangci-lint | golangci/golangci-lint |
SHA-pinned actions usually present:
Azure/setup-helm,goreleaser/goreleaser-action,sigstore/cosign-installer,aquasecurity/trivy-action,helm/kind-action,step-security/harden-runner,actions/checkout,actions/setup-go.
Phase 0 — Baseline
- Create branch off
main. Check for a stale collision first — last month's branch is often still present locally and onoriginafter its PR merged, sogit checkout -b ci/tool-versions-<YYYY-MM>fails with "branch already exists". If it exists, rungh pr list --head <branch> --state all; a MERGED PR means it's leftover (its bumps are already inmain) — don't reuse it. Use a date-stamped name:git checkout main && git checkout -b ci/tool-versions-<YYYY-MM-DD>. After a recent merged sweep, expect most*_VERSION:vars to already be at latest — that's normal, not a discovery failure. - Inventory dynamically — do not trust the snapshot above:
Then for SHA-pinned actions with version comments:grep -rnE "^\s*[A-Z_]+_VERSION:" .github/workflows/grep -rnE "uses:.*@[a-f0-9]{40} # v" .github/workflows/ - Resolve each pinned tool to its upstream repo (see table) and fetch latest:
for repo in helm/helm kubernetes-sigs/kind GoogleContainerTools/skaffold sigstore/cosign golangci/golangci-lint; do latest=$(gh api "repos/$repo/releases/latest" --jq '.tag_name') echo "$repo -> $latest" donegh apialready authenticates; no token wrangling needed.
Phase 1 — Decide what to bump
Per tool, decide based on the size of the jump:
- Patch (vX.Y.Z → vX.Y.Z+n): take it unconditionally. Read the release notes only if the version skips many patches.
- Minor (vX.Y → vX.Y+1): read upstream release notes for breaking-ish changes — golangci-lint and skaffold periodically rename or drop linters/profiles. Helm minor bumps are usually safe; kind minor bumps may drop support for older
kindest/nodetags, so cross-check against the kindversion matrix. - Major (vX → vX+1): don't lump into a sweep. Open a separate branch.
Flag deliberate stale pins:
upgrade_test.yamlhistorically pins Helm v3.x whilepush_pr.yamlpins v4.x —upgrade_testsimulates the v3-user upgrade path, so the v3 pin is intentional. Leave it unless the user says otherwise.kindversion:matrix entries that intentionally lag (testing k8s 1.28 LTS for backwards compat) — leave them unless the kind binary no longer ships akindest/node:v1.28.ximage.
Phase 2 — Per-tool workflow
For each tool to bump:
# Edit the *_VERSION: line(s). If the var is used in >1 workflow,
# bump them in the same commit IFF the workflows share intent
# (push_pr + release both want "latest stable kind"). Keep
# upgrade_test separate when it has its own intent.
$EDITOR .github/workflows/push_pr.yaml .github/workflows/release.yaml
# Sanity-check the YAML still parses. PyYAML is NOT installed on the dev macOS
# box (python3 -c "import yaml" -> ModuleNotFoundError), so prefer ruby, which ships
# with macOS and has YAML in stdlib. (For a pure value-only edit on a pre-valid line,
# YAML validity is preserved by construction — but validate anyway when in doubt.)
ruby -ryaml -e 'ARGV.each { |f| YAML.load_file(f) }; puts "YAML OK"' \
.github/workflows/push_pr.yaml .github/workflows/release.yaml
# Fallback if ruby is absent: python3 -c "import yaml,sys; [yaml.safe_load(open(f)) for f in sys.argv[1:]]" <files>
git add .github/workflows/
git commit -m "Bump <tool> v<old> -> v<new>"
Commit message convention (matches Fission's existing dep-bump style):
Bump <tool> v<old> -> v<new>
<One line on why if it closes a CVE or unblocks a known issue.>
If the tool is in multiple files with the same intent (e.g. kind across push_pr and release), bump them in one commit. If intents differ (helm in push_pr-target vs upgrade_test-source), separate commits.
Phase 3 — Verification
There is no local equivalent for "did this workflow still pass" — CI is the test.
git log --oneline main..HEADto review the per-tool commits.git push -u origin <branch>and let CI run on the PR.- If a single tool's commit breaks CI, you can revert that one commit without affecting the rest. That's the point of the per-tool structure.
Pitfalls
Helm CLI version drift between push_pr and upgrade_test
upgrade_test.yaml pinning Helm v3.x while push_pr.yaml is on v4.x looks like a bug; it's deliberate (testing the "user is on Helm v3, upgrading their Fission install" path). Don't "fix" this by aligning them.
kindversion matrix is NOT the kind binary
KIND_VERSION is the kind CLI binary (kubernetes-sigs/kind). matrix.kindversion is the k8s node image tag (kindest/node:vX.Y.Z). They version separately. Updating one does not require updating the other, but the kind binary must still support the requested node-image tag — check the kind release notes' "Supported Kubernetes versions" table.
Skaffold breaking config changes
Skaffold occasionally renames or removes profile keys. The Fission skaffold.yaml uses kind, kind-ci, kind-debug, kind-opentelemetry profiles. After a skaffold bump, grep for apiVersion and kind: Config in skaffold.yaml and verify they match the new skaffold's expected schema. Most safe: download the new skaffold locally and run skaffold diagnose --profile kind-ci against the repo.
golangci-lint config schema migrations
v2.0.0 and v2.x.0 minors have repeatedly tightened the .golangci.yaml schema. If you bump and CI fails with a config-validation error, run golangci-lint config verify against the new binary locally. Sometimes the fix is to migrate a deprecated linter name; don't --no-config your way out.
cosign across two workflow jobs in release.yaml
release.yaml installs cosign twice (once in the release job, once in image-provenance-verification-with-cosign). Both read the same COSIGN_VERSION env, so a single bump covers both — but verify both still reference the env, not a hardcoded version, after editing.
Dependabot's grouped github-actions PR
Dependabot already groups action version bumps weekly (.github/dependabot.yml declares a github-actions group). The *_VERSION: env vars are NOT touched by Dependabot — those are runtime downloads, not action versions. So this skill's primary scope (env vars) does not overlap with Dependabot's; the SHA-pinned uses: bumps usually arrive via Dependabot, so manually doing them is catch-up if Dependabot is paused or behind.
Before deciding a SHA-pinned action is "behind," check for an open Dependabot PR: gh pr list --author "app/dependabot" --state open. If there's an open github-actions group PR (e.g. it bumps harden-runner/codeql-action/codecov-action/goreleaser-action), that PR already owns those SHA bumps — don't duplicate it in a sweep; flag it in the summary instead.
Bumping a SHA-pinned action when the user explicitly asks
The user may name a SHA-pinned action directly ("can we bump goreleaser too") even though it's normally Dependabot's domain. Do it, but:
- Resolve the new tag to its commit SHA (the pin is a 40-hex SHA, not the tag):
gh api repos/<owner>/<repo>/commits/<tag> --jq '.sha'. This dereferences annotated tags to the commit. Update both the@<sha>and the trailing# vX.Y.Zcomment. - One action lives in multiple files — e.g.
goreleaser/goreleaser-actionis pinned inpush_pr.yaml,upgrade_test.yaml, andrelease.yaml(×2). Replace every occurrence with the identical new@<sha> # vX.Y.Zand commit together.Editwithreplace_all: trueper file is reliable; aperl -pi -e "s{...}{...}"one-liner with the#/spaces in shell-expanded vars silently no-ops, so don't trust it without agrepre-check. - Flag the Dependabot overlap in the PR/summary if an open
github-actionsgroup PR also bumps it (it usually does), so the user can reconcile (whichever merges first, the other rebases/drops the entry).
Out of scope
- Tools versioned in
Makefile,hack/,Dockerfile, or goreleaser config. - Floating semver ranges (
~> v2). - Major version bumps — do those on a dedicated branch with their own validation plan.
Canonical execution (copy-paste outline for the model)
1. TaskCreate: baseline + one task per tool to bump + push/CI
2. git checkout -b ci/tool-versions-<YYYY-MM>
3. Discover pinned versions via grep on .github/workflows/
4. For each *_VERSION env var, gh api repos/<owner>/<repo>/releases/latest
5. For each tool to bump (patch/minor; defer majors):
a. Edit the *_VERSION line(s); group same-intent files together
b. Validate YAML parses (python3 yaml.safe_load)
c. Commit per tool
6. git push and let CI verify
7. Summarize: commits on branch, versions bumped, deferred majors, flagged
intentional stale pins (Helm v3 in upgrade_test).
Source: fission/fission — distributed by TomeVault.