From afb6ada768ce7a2daccd5bd6ada28a5778887b96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20F=C3=B8rde=20Lindhagen?= Date: Tue, 9 Jun 2026 17:00:02 +0200 Subject: [PATCH] Bump third-party workflows --- .../has-application-changed/action.yml | 2 +- .github/actions/tools/gradle/action.yml | 2 +- .github/actions/tools/pnpm/action.yml | 2 +- .github/actions/tools/terraform/action.yml | 2 +- .github/workflows/build.gradle.yml | 2 +- .github/workflows/build.node.yml | 2 +- .github/workflows/build.python.yml | 6 +- .../deployment.preview-cleanup-schedule.yml | 2 +- .../workflows/helpers.build-railyard-docs.yml | 4 +- .../helpers.find-changes.terraform.yml | 4 +- .github/workflows/helpers.terraform-plan.yml | 4 +- .../helpers.upload-open-api-spec.yml | 6 +- .github/workflows/lint.terraform.yml | 4 +- .github/workflows/package.docker.yml | 2 +- .github/workflows/package.s3.jar.yml | 4 +- .github/workflows/package.s3.yml | 2 +- .github/workflows/test.node.yml | 2 +- .github/workflows/test.python.yml | 4 +- .../skills/update-github-actions/SKILL.md | 10 + .../references/workflow.md | 287 ++++++++++++++++++ 20 files changed, 325 insertions(+), 28 deletions(-) create mode 100644 .opencode/skills/update-github-actions/SKILL.md create mode 100644 .opencode/skills/update-github-actions/references/workflow.md diff --git a/.github/actions/helpers/has-application-changed/action.yml b/.github/actions/helpers/has-application-changed/action.yml index 89f7f106..6e77046e 100644 --- a/.github/actions/helpers/has-application-changed/action.yml +++ b/.github/actions/helpers/has-application-changed/action.yml @@ -11,7 +11,7 @@ runs: steps: - name: Get all changed application files id: changed-application-files - uses: tj-actions/changed-files@ed68ef82c095e0d48ec87eccea555d944a631a4c # v46.0.5 + uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6 with: since_last_remote_commit: true files: | diff --git a/.github/actions/tools/gradle/action.yml b/.github/actions/tools/gradle/action.yml index 18a1e6c2..bf1a56e2 100644 --- a/.github/actions/tools/gradle/action.yml +++ b/.github/actions/tools/gradle/action.yml @@ -53,7 +53,7 @@ runs: echo "Java version: ${{ inputs.java-version }}" - name: Set Up Java - uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0 + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 with: java-version: ${{ inputs.java-version }} distribution: ${{ inputs.java-distribution }} diff --git a/.github/actions/tools/pnpm/action.yml b/.github/actions/tools/pnpm/action.yml index 046dc35d..d69e4708 100644 --- a/.github/actions/tools/pnpm/action.yml +++ b/.github/actions/tools/pnpm/action.yml @@ -31,7 +31,7 @@ runs: using: composite steps: - name: Install pnpm - uses: pnpm/action-setup@8b2eead6074fefa43a68bf4c9e0f03ea4126b5ba # v6.0.3 + uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 with: version: ${{ inputs.pnpm-version }} run_install: false diff --git a/.github/actions/tools/terraform/action.yml b/.github/actions/tools/terraform/action.yml index 11abac94..a18f70fb 100644 --- a/.github/actions/tools/terraform/action.yml +++ b/.github/actions/tools/terraform/action.yml @@ -93,7 +93,7 @@ runs: exit 1 - name: Setup Terraform - uses: hashicorp/setup-terraform@5e8dbf3c6d9deaf4193ca7a8fb23f2ac83bb6c85 # v4 + uses: hashicorp/setup-terraform@dfe3c3f87815947d99a8997f908cb6525fc44e9e # v4.0.1 with: terraform_version: ${{ env.TERRAFORM_VERSION }} diff --git a/.github/workflows/build.gradle.yml b/.github/workflows/build.gradle.yml index 05154b6b..2c1ce469 100644 --- a/.github/workflows/build.gradle.yml +++ b/.github/workflows/build.gradle.yml @@ -76,7 +76,7 @@ jobs: GRADLE_CACHE_ENCRYPTION_KEY: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }} - name: Store built JAR as Artifact - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: ${{ inputs.artifact-name }} path: ${{inputs.working-directory}}/${{ inputs.artifact-path }} diff --git a/.github/workflows/build.node.yml b/.github/workflows/build.node.yml index 8f4be1d1..cb8e84ef 100644 --- a/.github/workflows/build.node.yml +++ b/.github/workflows/build.node.yml @@ -98,7 +98,7 @@ jobs: pnpm-version: ${{ inputs.pnpm-version }} - name: Store build as Artifact - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: ${{ inputs.artifact-name }} path: ${{inputs.working-directory}}/${{ inputs.artifact-path }} diff --git a/.github/workflows/build.python.yml b/.github/workflows/build.python.yml index 3aa33cf7..dabd4d44 100644 --- a/.github/workflows/build.python.yml +++ b/.github/workflows/build.python.yml @@ -44,12 +44,12 @@ jobs: ref: ${{ inputs.git-sha }} - name: Install uv - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 + uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 with: enable-cache: true cache-dependency-glob: "${{ inputs.working-directory }}/uv.lock" - - uses: actions/setup-python@v6 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ inputs.python_version }} @@ -79,7 +79,7 @@ jobs: INPUT_NAME: ${{ inputs.artifact-name }} - name: Store build as Artifact - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: ${{ steps.artifact-name.outputs.artifact-name }} path: ${{ inputs.working-directory }}/out diff --git a/.github/workflows/deployment.preview-cleanup-schedule.yml b/.github/workflows/deployment.preview-cleanup-schedule.yml index 7a72db13..d1b1579a 100644 --- a/.github/workflows/deployment.preview-cleanup-schedule.yml +++ b/.github/workflows/deployment.preview-cleanup-schedule.yml @@ -375,7 +375,7 @@ jobs: environment: Test steps: - name: Authenticate with AWS - uses: aws-actions/configure-aws-credentials@ec61189d14ec14c8efccab744f656cffd0e33f37 # v6.1.0 + uses: aws-actions/configure-aws-credentials@v6 with: aws-region: ${{ vars.AWS_REGION }} role-to-assume: "arn:aws:iam::${{ vars.AWS_ACCOUNT_ID }}:role/${{ vars.AWS_DEPLOYMENT_ROLE_NAME }}" diff --git a/.github/workflows/helpers.build-railyard-docs.yml b/.github/workflows/helpers.build-railyard-docs.yml index d80942ea..fb1ff066 100644 --- a/.github/workflows/helpers.build-railyard-docs.yml +++ b/.github/workflows/helpers.build-railyard-docs.yml @@ -56,7 +56,7 @@ jobs: contents: read steps: - name: Set up Python 3.12 - uses: actions/setup-python@v6 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: '3.12' - name: Install mkdocs and plugins @@ -106,7 +106,7 @@ jobs: contents: read steps: - name: Download build artifact - uses: actions/download-artifact@v7 + uses: actions/download-artifact@v8 with: name: ${{ inputs.artifact-name }} path: artifact-download diff --git a/.github/workflows/helpers.find-changes.terraform.yml b/.github/workflows/helpers.find-changes.terraform.yml index 9499de77..85dd5c34 100644 --- a/.github/workflows/helpers.find-changes.terraform.yml +++ b/.github/workflows/helpers.find-changes.terraform.yml @@ -127,7 +127,7 @@ jobs: echo "working-directory=$NAME_WITHOUT_SLASHES" >> $GITHUB_OUTPUT - name: Upload matrix outputs - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: ${{ steps.without-slashes.outputs.working-directory }}-matrix-outputs-${{ matrix.environment }} path: ./matrix-outputs @@ -151,7 +151,7 @@ jobs: echo "working-directory=$NAME_WITHOUT_SLASHES" >> $GITHUB_OUTPUT - name: Download matrix outputs - uses: actions/download-artifact@v7 + uses: actions/download-artifact@v8 with: pattern: ${{ steps.without-slashes.outputs.working-directory }}-matrix-outputs-* merge-multiple: true diff --git a/.github/workflows/helpers.terraform-plan.yml b/.github/workflows/helpers.terraform-plan.yml index 840a47f4..590a89db 100644 --- a/.github/workflows/helpers.terraform-plan.yml +++ b/.github/workflows/helpers.terraform-plan.yml @@ -142,7 +142,7 @@ jobs: - name: Upload plan artifact if: steps.changed-files.outcome == 'success' - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: terraform-plan-${{ matrix.environment }} path: temp-plans/${{ matrix.environment }}.md @@ -160,7 +160,7 @@ jobs: uses: actions/checkout@v6 - name: Download all plan artifacts - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v8 continue-on-error: true with: pattern: terraform-plan-* diff --git a/.github/workflows/helpers.upload-open-api-spec.yml b/.github/workflows/helpers.upload-open-api-spec.yml index d4b9b447..6013ffbc 100644 --- a/.github/workflows/helpers.upload-open-api-spec.yml +++ b/.github/workflows/helpers.upload-open-api-spec.yml @@ -38,12 +38,12 @@ jobs: - uses: actions/checkout@v6 - name: Convert from YAML to JSON - uses: mikefarah/yq@v4.40.5 + uses: mikefarah/yq@1b9b4ac5187171d2e5e3129be0cfa827c7f9d53d # v4.53.3 with: cmd: yq -o=json '.' '${{ inputs.open-api-spec-path }}' > 'converted.json' - name: Persist file between jobs - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: 'open-api-spec' path: 'converted.json' @@ -62,7 +62,7 @@ jobs: role-to-assume: "arn:aws:iam::${{ vars.AWS_ACCOUNT_ID }}:role/${{ vars.AWS_DEPLOYMENT_ROLE_NAME }}" - name: Download API Spec - uses: actions/download-artifact@v7 + uses: actions/download-artifact@v8 with: name: "open-api-spec" diff --git a/.github/workflows/lint.terraform.yml b/.github/workflows/lint.terraform.yml index edb40886..62526d30 100644 --- a/.github/workflows/lint.terraform.yml +++ b/.github/workflows/lint.terraform.yml @@ -39,7 +39,7 @@ jobs: steps: - uses: actions/checkout@v6 - - uses: hashicorp/setup-terraform@5e8dbf3c6d9deaf4193ca7a8fb23f2ac83bb6c85 # v4.0 + - uses: hashicorp/setup-terraform@dfe3c3f87815947d99a8997f908cb6525fc44e9e # v4.0.1 with: terraform_version: ${{ inputs.terraform-version }} @@ -57,7 +57,7 @@ jobs: steps: - uses: actions/checkout@v6 - - uses: hashicorp/setup-terraform@5e8dbf3c6d9deaf4193ca7a8fb23f2ac83bb6c85 # v4.0 + - uses: hashicorp/setup-terraform@dfe3c3f87815947d99a8997f908cb6525fc44e9e # v4.0.1 with: terraform_version: ${{ inputs.terraform-version }} diff --git a/.github/workflows/package.docker.yml b/.github/workflows/package.docker.yml index cfe3c166..51f21c8e 100644 --- a/.github/workflows/package.docker.yml +++ b/.github/workflows/package.docker.yml @@ -62,7 +62,7 @@ jobs: - name: Download Artifact if: inputs.artifact-name != '' - uses: actions/download-artifact@v7 + uses: actions/download-artifact@v8 with: name: ${{ inputs.artifact-name }} path: ${{inputs.working-directory}}/${{ inputs.artifact-path }} diff --git a/.github/workflows/package.s3.jar.yml b/.github/workflows/package.s3.jar.yml index 46f36e2d..39e73f2f 100644 --- a/.github/workflows/package.s3.jar.yml +++ b/.github/workflows/package.s3.jar.yml @@ -55,13 +55,13 @@ jobs: - name: Download artifacts id: download-artifacts - uses: actions/download-artifact@v7 + uses: actions/download-artifact@v8 with: name: ${{ inputs.artifact-name }} path: "./temp-artifact" - name: Authenticate with AWS - uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0 + uses: aws-actions/configure-aws-credentials@v6 with: aws-region: ${{ inputs.aws-region }} role-to-assume: "arn:aws:iam::${{ vars.AWS_ACCOUNT_ID }}:role/${{ vars.AWS_DEPLOYMENT_ROLE_NAME }}" diff --git a/.github/workflows/package.s3.yml b/.github/workflows/package.s3.yml index 92df6d1a..5451ce1c 100644 --- a/.github/workflows/package.s3.yml +++ b/.github/workflows/package.s3.yml @@ -51,7 +51,7 @@ jobs: - name: Download artifacts if specified if: inputs.artifact-name != '' - uses: actions/download-artifact@v7 + uses: actions/download-artifact@v8 with: name: ${{ inputs.artifact-name }} path: "./temp-artifact" diff --git a/.github/workflows/test.node.yml b/.github/workflows/test.node.yml index ef439172..0875a07a 100644 --- a/.github/workflows/test.node.yml +++ b/.github/workflows/test.node.yml @@ -54,7 +54,7 @@ jobs: - name: Download Artifact if: inputs.artifact-name != '' - uses: actions/download-artifact@v7 + uses: actions/download-artifact@v8 with: name: ${{ inputs.artifact-name }} path: ${{ inputs.artifact-path }} diff --git a/.github/workflows/test.python.yml b/.github/workflows/test.python.yml index 5a1d770c..d10a63ee 100644 --- a/.github/workflows/test.python.yml +++ b/.github/workflows/test.python.yml @@ -30,12 +30,12 @@ jobs: - uses: actions/checkout@v6 - name: Install uv - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 + uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 with: enable-cache: true cache-dependency-glob: "${{ inputs.working-directory }}/uv.lock" - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ inputs.python-version }} diff --git a/.opencode/skills/update-github-actions/SKILL.md b/.opencode/skills/update-github-actions/SKILL.md new file mode 100644 index 00000000..65ed5701 --- /dev/null +++ b/.opencode/skills/update-github-actions/SKILL.md @@ -0,0 +1,10 @@ +--- +name: update-github-actions +description: Update external GitHub Actions to their latest versions. Use when someone asks to "update github actions", "bump action versions", "update action dependencies", "upgrade workflow dependencies", or similar. Fetches the latest releases from GitHub, checks for known vulnerabilities, reviews breaking changes, and updates workflow files with correct version pins. +--- + +# Update GitHub Actions + +Updates external GitHub Actions dependencies across all workflow files in `.github/workflows/` (and any `action.yml` files under `.github/actions/` that call external actions). + +Read [references/workflow.md](references/workflow.md) **fully before proceeding** — it contains the complete step-by-step process: pinning rules, how to discover actions, how to fetch the latest release and commit SHA, vulnerability checks, breaking change analysis, and how to apply updates. diff --git a/.opencode/skills/update-github-actions/references/workflow.md b/.opencode/skills/update-github-actions/references/workflow.md new file mode 100644 index 00000000..a991c91e --- /dev/null +++ b/.opencode/skills/update-github-actions/references/workflow.md @@ -0,0 +1,287 @@ +# Update GitHub Actions Workflow + +## Pinning rules + +The pin method depends on the action owner. Apply these rules consistently: + +| Action category | Pin method | Example | +|---|---|---| +| `actions/checkout` | Floating major version tag | `actions/checkout@v4` | +| `actions/upload-artifact` | Floating major version tag | `actions/upload-artifact@v7` | +| `actions/download-artifact` | Floating major version tag | `actions/download-artifact@v8` | +| `actions/setup-java` | Full commit SHA + version comment | `actions/setup-java@be666c2f... # v5.2.0` | +| `actions/setup-python` | Full commit SHA + version comment | `actions/setup-python@a309ff8b... # v6.2.0` | +| `github/*` | Full commit SHA + version comment | `github/branch-deploy@ddf8ca48... # v11.1.4` | +| `aws-actions/*` | Floating major version tag | `aws-actions/configure-aws-credentials@v4` | +| `docker/*` | Floating major version tag | `docker/build-push-action@v6` | +| `nsbno/platform-actions/*` | **Skip** — internal, not updated here | — | +| All other third-party | Full commit SHA + version comment | `astral-sh/setup-uv@abc123def... # v8.1.0` | + +> `github/*`, `actions/setup-java`, and `actions/setup-python` are SHA-pinned (not floating) because they touch sensitive runtime environments or deployment logic and an unreviewed tag move would be high-impact. + +**Why SHA pinning?** A floating tag like `@v8` can be silently redirected to a different commit by the action owner (intentionally or after a supply-chain compromise). SHA pinning freezes the exact code that runs. The human-readable version comment preserves discoverability. + +**Version comment format:** Add the version as an inline comment immediately after the SHA: +```yaml +uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 +``` + +--- + +## 1. Discover all external actions + +Read every `.github/workflows/*.yml` file (and any `.github/actions/**/action.yml` that calls external actions). Extract every `uses:` line. Collect the unique set of external actions, skipping: +- `./` paths (local composite actions) +- `nsbno/platform-actions/*` (internal — owned by this repo) + +Build a table: + +| Action | Current pin | Pin type | +|---|---|---| +| `actions/checkout` | `@v6` | Floating | +| `astral-sh/setup-uv` | `@08807647...` | SHA (v8.1.0) | +| … | … | … | + +If the same action is pinned inconsistently across files (e.g., `@v6` in one, `@a309ff8b...` in another), note the inconsistency — it should be resolved to a single version. + +--- + +## 2. Fetch the latest release for each action + +Use `gh release view` or the GitHub API. Always fetch from the official source. + +### Check if a newer version exists + +```bash +# Latest release tag and body +gh release view --repo {owner}/{repo} --json tagName,name,publishedAt,body + +# List recent releases (useful to see the full version history) +gh release list --repo {owner}/{repo} --limit 10 --json tagName,publishedAt,isLatest,isPreRelease +``` + +Filter out pre-releases and drafts unless the current pin is also a pre-release. + +### Determine the update type + +Compare the current version to the latest: + +| Update type | Example | Caution level | +|---|---|---| +| **Patch** | `v8.1.0` → `v8.1.1` | Low — apply by default | +| **Minor** | `v8.0.0` → `v8.1.0` | Medium — check release notes | +| **Major** | `v8.x.x` → `v9.0.0` | High — read §5 carefully before updating | + +For actions using floating major tags (`actions/*`, `aws-actions/*`, `docker/*`), a new major version means changing `@v8` to `@v9`. Treat this the same as a major update. For `github/*` (SHA-pinned), fetch the new commit SHA and update accordingly. + +--- + +## 3. Get the commit SHA for a release tag (SHA-pinned actions only) + +The GitHub tags API always returns the actual commit SHA (it dereferences annotated tags automatically): + +```bash +OWNER="astral-sh" +REPO="setup-uv" +TAG="v8.1.0" + +SHA=$(gh api "repos/$OWNER/$REPO/tags" --paginate \ + --jq ".[] | select(.name == \"$TAG\") | .commit.sha") + +echo "$SHA" +``` + +If the `--paginate` call is slow (repo has many tags), use the git refs API and dereference manually: + +```bash +REF=$(gh api "repos/$OWNER/$REPO/git/refs/tags/$TAG") +OBJ_TYPE=$(echo "$REF" | jq -r '.object.type') +OBJ_SHA=$(echo "$REF" | jq -r '.object.sha') + +if [ "$OBJ_TYPE" = "tag" ]; then + # Annotated tag — dereference to get the actual commit SHA + SHA=$(gh api "repos/$OWNER/$REPO/git/tags/$OBJ_SHA" | jq -r '.object.sha') +else + # Lightweight tag — already a commit SHA + SHA="$OBJ_SHA" +fi + +echo "$TAG @ $SHA" +``` + +Verify the SHA is 40 characters long. A short SHA is a sign something went wrong. + +--- + +## 4. Check for known vulnerabilities + +Check the OSV database (no auth required) for each action: + +```bash +OWNER="astral-sh" +REPO="setup-uv" + +curl -s "https://api.osv.dev/v1/query" \ + -H "Content-Type: application/json" \ + -d "{\"package\": {\"name\": \"$OWNER/$REPO\", \"ecosystem\": \"GitHub Actions\"}}" \ + | jq '.vulns // [] | length' +``` + +If any vulnerabilities are returned: + +```bash +curl -s "https://api.osv.dev/v1/query" \ + -H "Content-Type: application/json" \ + -d "{\"package\": {\"name\": \"$OWNER/$REPO\", \"ecosystem\": \"GitHub Actions\"}}" \ + | jq '.vulns[] | {id, summary, affected: .affected[].ranges}' +``` + +**Decision rules:** +- Vulnerability affects the current version **and** is fixed in the target version → update immediately, flag in the summary. +- Vulnerability affects the target version too → **do not update**, flag as blocked, report to the user. +- No vulnerabilities → proceed. + +Also check if the action repository has been archived, renamed, or transferred — these are risk signals. Visit `https://github.com/{owner}/{repo}` or run: + +```bash +gh api "repos/$OWNER/$REPO" --jq '{archived: .archived, disabled: .disabled, visibility: .visibility}' +``` + +--- + +## 5. Review release notes for breaking changes + +Always read the release notes before updating. Focus on what can impact this repository. + +```bash +# Show full body of the latest release +gh release view --repo {owner}/{repo} --json tagName,body --jq '.body' + +# For a range of releases (e.g., going from v8.0.0 to v8.2.0) +gh release list --repo {owner}/{repo} --limit 20 --json tagName,publishedAt \ + | jq '.[] | .tagName' +# Then fetch each release body between the two versions +``` + +### Patch and minor updates + +Look for: +- Deprecated inputs/outputs that we currently use +- Changed default values for inputs we rely on +- Environment or runner version requirements that changed + +If the release notes are clean and there are no deprecations affecting our usage → update. + +### Major version updates + +A major version bump is a **breaking change boundary**. Before updating, check the full migration guide. Look for: + +1. **Removed or renamed inputs** — check every `with:` block in our workflows that uses this action. If we pass an input that was removed, the workflow will fail. +2. **Changed output names** — check every `${{ steps..outputs. }}` reference after this action. +3. **Changed environment requirements** — e.g., minimum runner OS, Node.js version bundled, new required permissions. +4. **Changed behavior for existing inputs** — a default value change can silently alter pipeline behavior. +5. **Removed runner support** — e.g., dropping `ubuntu-20.04` support. + +For a major update, list every workflow file and specific `with:` argument we use for this action, then cross-reference against the breaking changes list. Only proceed if none of our usages are affected, or if you can update all affected call sites in the same change. + +If the impact is unclear, **skip the major update** and note it in the summary with a link to the migration guide. + +--- + +## 6. Apply the updates + +### Updating floating-version actions (`actions/*`, `aws-actions/*`, `docker/*`) + +Replace only the version suffix. Preserve the rest of the line exactly. + +```yaml +# Before +- uses: actions/checkout@v5 + +# After (major bump) +- uses: actions/checkout@v6 +``` + +### Updating SHA-pinned actions + +Replace the full `@{sha}` and the version comment. The SHA must be the full 40-character commit hash. + +```yaml +# Before +- uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 + +# After +- uses: astral-sh/setup-uv@a39d71779d1b28ac4e2e5c2cc02f53124ac0f82b # v8.2.0 +``` + +If an action is currently pinned with a floating tag but should be SHA-pinned (per the pinning rules above), convert it in the same change: + +```yaml +# Before (incorrect — community third-party should be SHA-pinned) +- uses: mikefarah/yq@v4.40.5 + +# After (correct) +- uses: mikefarah/yq@a2a52a81514b56b3816abd4c08ac8ca18a1d7e5d # v4.44.3 +``` + +### Resolving inconsistent pins + +If the same action is pinned differently across multiple files (e.g., `@v6` in one and `@a309ff8b...` in another), unify all occurrences to the latest correct pin in the same PR. + +--- + +## 7. Verification + +After making changes, run the validate-github-actions skill (if available) to check that all workflow files remain syntactically valid and that no callers are broken. + +At minimum, verify manually: +- No `uses:` line was partially updated (e.g., SHA without comment, or mixed old/new versions) +- The SHA is 40 characters for all SHA-pinned actions +- All occurrences of each action across all workflow files are updated, not just the first one + +--- + +## 8. Summary report + +After completing the updates, produce a concise summary: + +``` +## GitHub Actions Update Summary + +### Updated +| Action | Old | New | Type | +|---|---|---|---| +| `astral-sh/setup-uv` | `v8.1.0` | `v8.2.0` | Patch | +| `actions/checkout` | `@v5` | `@v6` | Major | + +### Skipped (breaking changes or vulnerabilities in target) +| Action | Current | Latest | Reason | +|---|---|---|---| +| `docker/build-push-action` | `@v6` | `@v7` | Major — input `provenance` changed default; review required | + +### Already up to date +- `hashicorp/setup-terraform` (v4.0) +- `github/branch-deploy` (v11) +``` + +If no updates were needed, say so clearly. + +--- + +## Quick reference: all external actions in this repo + +| Action | Should be pinned? | Official releases page | +|---|---|---| +| `actions/checkout` | Floating (`@vN`) | https://github.com/actions/checkout/releases | +| `actions/upload-artifact` | Floating (`@vN`) | https://github.com/actions/upload-artifact/releases | +| `actions/download-artifact` | Floating (`@vN`) | https://github.com/actions/download-artifact/releases | +| `actions/setup-java` | SHA | https://github.com/actions/setup-java/releases | +| `actions/setup-python` | SHA | https://github.com/actions/setup-python/releases | +| `github/branch-deploy` | SHA | https://github.com/github/branch-deploy/releases | +| `aws-actions/configure-aws-credentials` | Floating (`@vN`) | https://github.com/aws-actions/configure-aws-credentials/releases | +| `aws-actions/amazon-ecr-login` | Floating (`@vN`) | https://github.com/aws-actions/amazon-ecr-login/releases | +| `docker/setup-buildx-action` | Floating (`@vN`) | https://github.com/docker/setup-buildx-action/releases | +| `docker/build-push-action` | Floating (`@vN`) | https://github.com/docker/build-push-action/releases | +| `astral-sh/setup-uv` | SHA | https://github.com/astral-sh/setup-uv/releases | +| `hashicorp/setup-terraform` | SHA | https://github.com/hashicorp/setup-terraform/releases | +| `mikefarah/yq` | SHA | https://github.com/mikefarah/yq/releases |