From d482518ebafcdd729baa55276fc39679d2f7d018 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 26 Aug 2026 09:12:21 +0000 Subject: [PATCH 1/3] ci: update pinned GitHub Actions to their current majors Every action except trivy-action was at least one major behind, and the codeql-action digest had gone stale against the v4 tag it comments. actions/checkout v6.1.0 -> v7.0.1 actions/setup-java v5.7.0 -> v6.0.0 actions/upload-artifact v4.6.2 -> v7.0.1 github/codeql-action v4 -> v4.37.8 (digest refresh) docker/setup-qemu-action v3.7.0 -> v4.2.0 docker/setup-buildx-action v3.12.0 -> v4.3.0 docker/build-push-action v6.19.2 -> v7.3.0 docker/login-action v3.7.0 -> v4.6.0 aquasecurity/trivy-action is already at v0.36.0, the current release. Each digest is the commit the major moving tag resolves to, which is also the latest release in that line, so the `# vN` comments Renovate reads keep tracking the major exactly as before. The majors were checked input by input rather than taken on trust: - setup-java v6 renames server-username/server-password to server-username-env-var/server-password-env-var. The old names survive as deprecated aliases and the GITHUB_ACTOR/GITHUB_TOKEN defaults moved from action.yml into the action's own code, so maven-publish.yml, which passes neither and relied on those defaults, still authenticates. central-publish.yml passes both explicitly and moves to the new names so it is not building on a deprecation. - setup-buildx-action v4 drops the config, config-inline and install inputs; docker.yml passes none of them. - upload-artifact v4 -> v7 is additive on inputs (`archive`, defaulting to the previous behaviour) and outputs are unchanged. - The rest of the majors are runtime moves to node24 with no input changes. AGENTS.md quotes a checkout pin as its worked example of SHA pinning, so that digest moves with the workflows. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_012EbVuqbqEztVsS892PpQ5i --- .github/workflows/central-publish.yml | 8 ++++---- .github/workflows/codeql.yml | 10 +++++----- .github/workflows/coverage.yml | 6 +++--- .github/workflows/docker.yml | 14 +++++++------- .github/workflows/integration-tests.yml | 4 ++-- .github/workflows/maven-publish.yml | 4 ++-- .github/workflows/maven-windows.yml | 4 ++-- .github/workflows/maven.yml | 4 ++-- .github/workflows/pitest.yml | 6 +++--- .github/workflows/spotbugs.yml | 8 ++++---- AGENTS.md | 2 +- 11 files changed, 35 insertions(+), 35 deletions(-) diff --git a/.github/workflows/central-publish.yml b/.github/workflows/central-publish.yml index 7fcab095..a8563dc3 100644 --- a/.github/workflows/central-publish.yml +++ b/.github/workflows/central-publish.yml @@ -62,7 +62,7 @@ jobs: fi echo "All required Maven Central secrets are present." - - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - name: Derive the reproducible-build timestamp # The pom declares no project.build.outputTimestamp on purpose, so that no @@ -80,7 +80,7 @@ jobs: echo "BUILD_OUTPUT_TIMESTAMP=${timestamp}" >> "$GITHUB_ENV" - name: Set up JDK 25 - uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6 with: java-version: '25' distribution: 'temurin' @@ -88,8 +88,8 @@ jobs: # Creates a settings.xml server entry with id "central" whose # username/password come from the env vars below. server-id: central - server-username: MAVEN_CENTRAL_USERNAME - server-password: MAVEN_CENTRAL_PASSWORD + server-username-env-var: MAVEN_CENTRAL_USERNAME + server-password-env-var: MAVEN_CENTRAL_PASSWORD - name: Bootstrap CLAUDE.md enforcer rule # The custom enforcer rule is consumed as a plugin dependency by the diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index b9dc5279..d2baa562 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -42,17 +42,17 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - name: Setup java - uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6 with: java-version: 25 distribution: 'temurin' # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@988661ebb5e81487b3fb31b2185d2856c0a10679 # v4 + uses: github/codeql-action/init@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -66,7 +66,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@988661ebb5e81487b3fb31b2185d2856c0a10679 # v4 + uses: github/codeql-action/autobuild@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4 # ā„¹ļø Command-line programs to run using the OS shell. # šŸ“š See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -80,6 +80,6 @@ jobs: - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@988661ebb5e81487b3fb31b2185d2856c0a10679 # v4 + uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 079494f3..23ab1d39 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -21,9 +21,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - name: Set up JDK 25 - uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6 with: java-version: '25' distribution: 'temurin' @@ -31,7 +31,7 @@ jobs: - name: Run tests with JaCoCo run: mvn -B -ntp verify -Pcoverage --file pom.xml - name: Upload coverage reports - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: name: jacoco-reports path: '**/target/site/jacoco/' diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 5b1287f0..cb583876 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -35,10 +35,10 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - name: Set up JDK 25 - uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6 with: java-version: '25' distribution: 'temurin' @@ -78,17 +78,17 @@ jobs: } >> "$GITHUB_OUTPUT" - name: Set up QEMU - uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3 + uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 + uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4 # A single-arch build first, loaded into the local daemon, so the image is # actually executed before anything is published. Buildx cannot --load a # multi-platform image, hence the two builds; the GHA layer cache makes the # later multi-arch build reuse this work for linux/amd64. - name: Build the image (linux/amd64) - uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7 with: context: . file: assembly/Dockerfile @@ -132,7 +132,7 @@ jobs: - name: Log in to GitHub Container Registry if: github.event_name == 'release' - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4 with: registry: ghcr.io username: ${{ github.actor }} @@ -143,7 +143,7 @@ jobs: # architectures the trimmed DuckDB driver still carries natives for. - name: Push the image to GHCR if: github.event_name == 'release' - uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7 with: context: . file: assembly/Dockerfile diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 1f204ba8..50c5b363 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -21,9 +21,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - name: Set up JDK 25 - uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6 with: java-version: '25' distribution: 'temurin' diff --git a/.github/workflows/maven-publish.yml b/.github/workflows/maven-publish.yml index 0c748312..85775f8c 100644 --- a/.github/workflows/maven-publish.yml +++ b/.github/workflows/maven-publish.yml @@ -25,7 +25,7 @@ jobs: packages: write steps: - - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - name: Derive the reproducible-build timestamp # The pom declares no project.build.outputTimestamp on purpose, so that no @@ -43,7 +43,7 @@ jobs: echo "BUILD_OUTPUT_TIMESTAMP=${timestamp}" >> "$GITHUB_ENV" - name: Set up JDK 25 - uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6 with: java-version: '25' distribution: 'temurin' diff --git a/.github/workflows/maven-windows.yml b/.github/workflows/maven-windows.yml index cdaa5772..999f1991 100644 --- a/.github/workflows/maven-windows.yml +++ b/.github/workflows/maven-windows.yml @@ -26,9 +26,9 @@ jobs: runs-on: windows-latest steps: - - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - name: Set up JDK 25 - uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6 with: java-version: '25' distribution: 'temurin' diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 28acac71..986802d1 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -35,9 +35,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - name: Set up JDK 25 - uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6 with: java-version: '25' distribution: 'temurin' diff --git a/.github/workflows/pitest.yml b/.github/workflows/pitest.yml index cdae309a..8db8aeb8 100644 --- a/.github/workflows/pitest.yml +++ b/.github/workflows/pitest.yml @@ -23,9 +23,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - name: Set up JDK 25 - uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6 with: java-version: '25' distribution: 'temurin' @@ -40,7 +40,7 @@ jobs: # bound to the `test` phase, so it still runs on every module. run: mvn -B -ntp install -Ppitest --file pom.xml - name: Upload mutation testing reports - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: name: pitest-reports path: '**/target/pit-reports/' diff --git a/.github/workflows/spotbugs.yml b/.github/workflows/spotbugs.yml index 6a4967f2..f75c3d8c 100644 --- a/.github/workflows/spotbugs.yml +++ b/.github/workflows/spotbugs.yml @@ -29,9 +29,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - name: Set up JDK 25 - uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5 + uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6 with: java-version: '25' distribution: 'temurin' @@ -66,11 +66,11 @@ jobs: # No category input: it would only apply to runs that have no # automationDetails.id of their own, and the step above gives every run # the per-module id that keeps the delivery's runs distinct. - uses: github/codeql-action/upload-sarif@988661ebb5e81487b3fb31b2185d2856c0a10679 # v4 + uses: github/codeql-action/upload-sarif@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4 with: sarif_file: target/sarif - name: Upload SpotBugs reports - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: name: spotbugs-reports path: | diff --git a/AGENTS.md b/AGENTS.md index 57788079..fffb393c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -832,7 +832,7 @@ across all ten, and a new workflow is expected to keep them: Everywhere else it is `false` — a scheduled run is not superseded by a newer commit, and a publish interrupted half-way can leave a partial release behind. - **Actions pinned to a commit SHA**, with the tag kept in a trailing comment - (`uses: actions/checkout@d23441a… # v6`). A tag is mutable, so an unpinned + (`uses: actions/checkout@3d3c42e… # v7`). A tag is mutable, so an unpinned action is a third party's ability to change what CI runs without a commit here — and CI is what produces the released artifacts the supply-chain posture of [ADR 0002](docs/adr/0002-security-policy-and-supply-chain-posture.md) rests From 94b0d466662838e78ad8c20f6964d93f3457a966 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 26 Aug 2026 09:20:49 +0000 Subject: [PATCH 2/3] docs: record the major-only convention for pinned action tags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The workflows already comment their SHA pins with the major alone, but nothing said so, which is how the convention drifts back to exact versions the next time someone bumps an action by hand. The rule: pin the digest that the moving major tag resolves to, and comment it `# v7`, never `# v7.0.1`. The SHA is the real pin, so the comment only says which line the pin belongs to — and Renovate reads it as the current version, so a major-only comment makes every patch and minor release a digest refresh and reserves a comment change for an actual major. The grouped actions PR is then reviewed by looking for a changed `# vN` instead of re-reading nine version strings that moved for no behavioural reason. aquasecurity/trivy-action is the one exception, and is documented as such: it publishes no moving major tag at all — all 75 of its tags are exact `v0.x.y` releases — so it keeps `# v0.36.0` and Renovate raises its minor bumps normally. Recorded in AGENTS.md as the workflow convention it belongs to (plus the consequence for reviewing Renovate's grouped PR), summarised in CLAUDE.md beside the CI notes, and given its rationale in ADR 0005, since the comment granularity is what shapes the updates Renovate proposes. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_012EbVuqbqEztVsS892PpQ5i --- AGENTS.md | 14 +++++++++++++- CLAUDE.md | 11 ++++++++--- docs/adr/0005-renovate-dependency-updates.md | 11 +++++++++++ 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index fffb393c..8601e5f8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -838,6 +838,16 @@ across all ten, and a new workflow is expected to keep them: of [ADR 0002](docs/adr/0002-security-policy-and-supply-chain-posture.md) rests on. `assembly/Dockerfile` pins its `eclipse-temurin` base images by digest for the same reason. Renovate refreshes both. +- **That trailing comment names the major alone** — `# v7`, never `# v7.0.1`. + The SHA is what pins the action, so the comment exists only to say which line + the pin belongs to, and Renovate reads it as the current version: a new `v7.x` + release is then a digest refresh that leaves the comment untouched, and only a + genuine `v8` rewrites it. That makes the version text in a diff the signal that + release notes need reading, instead of churning on every patch. Pin the digest + the **moving major tag** resolves to and the two never disagree, since that tag + tracks the newest release in its line. `aquasecurity/trivy-action` is the one + exception: it publishes no moving major tag — all 75 of its tags are exact + `v0.x.y` releases — so it keeps `# v0.36.0`. ### Dependency updates @@ -862,7 +872,9 @@ configuration is `.github/renovate.json`: inside the reactor at `${revision}`. - `pinDigests` is on for the **github-actions** and **dockerfile** managers, so the SHA pins above are refreshed rather than left to rot; the action bumps - arrive as one grouped PR. + arrive as one grouped PR. Because those pins comment the major alone, most of + that PR is digest churn; a comment that changes from `# v7` to `# v8` is the + part to stop and read. - A **major** bump of the Maven API artifacts or of Spring Boot needs dependency dashboard approval — a Maven 4 API is wired in on purpose while the build is pinned to 3.9.x, and the framework the MCP servers boot on deserves a review. diff --git a/CLAUDE.md b/CLAUDE.md index c148709f..9c12c54e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -236,9 +236,14 @@ the whole matrix passes: GHCR; `maven-publish.yml` (GitHub Packages); `central-publish.yml` (Maven Central). -Every workflow builds on JDK 25 (Temurin). Use clear, conventional commit -messages — the `git-commit` skill writes them with this repository's real module -scopes — keep changes focused, and add or update tests alongside the code. +Every workflow builds on JDK 25 (Temurin). Every action is pinned to a commit +SHA whose trailing comment names the **major only** (`# v7`, never `# v7.0.1`): +pin the digest the moving major tag resolves to. `aquasecurity/trivy-action` +publishes no such tag and is the documented exception. + +Use clear, conventional commit messages — the `git-commit` skill writes them with +this repository's real module scopes — keep changes focused, and add or update +tests alongside the code. **Do not open a pull request unless explicitly asked.** ## Agent configuration diff --git a/docs/adr/0005-renovate-dependency-updates.md b/docs/adr/0005-renovate-dependency-updates.md index c66b3f88..ef06d5e9 100644 --- a/docs/adr/0005-renovate-dependency-updates.md +++ b/docs/adr/0005-renovate-dependency-updates.md @@ -74,6 +74,17 @@ repository's shape calls for: dashboard rather than arriving unannounced. - **This project's own `io.github.adamw7` modules disabled** — they resolve inside the reactor at `${revision}`, so there is no release for Renovate to raise. +- **`pinDigests` on the github-actions and dockerfile managers, with action + comments naming the major alone** (`# v7`, not `# v7.0.1`). The digest is the + real pin; Renovate reads the comment as the current version, so a major-only + comment turns every patch and minor release into a digest refresh and reserves + a comment change for an actual major. Reviewing the grouped actions PR is then + a matter of looking for a changed `# vN`, rather than re-reading nine version + strings that moved for no behavioural reason. The digest pinned is the one the + moving major tag resolves to, which is also the newest release in that line. + `aquasecurity/trivy-action` cannot follow this: it ships no moving major tag, + only exact `v0.x.y` releases, so it stays pinned with an exact comment and + Renovate raises its minor bumps normally. Validate a change to that file with `npx --package renovate renovate-config-validator` before committing it. From 8fb4e67449a5e3d71b6440c8d79384e8af8e1407 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 26 Aug 2026 09:35:01 +0000 Subject: [PATCH 3/3] ci: reference actions by major tag instead of pinning digests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces every action's commit-SHA pin with the major tag its version comment already named, so a workflow line states what it runs instead of hiding it behind a digest, and an action's own patch and security releases reach CI without a commit here. actions/checkout@v7 actions/setup-java@v6 actions/upload-artifact@v7 github/codeql-action/*@v4 docker/setup-qemu-action@v4 docker/setup-buildx-action@v4 docker/build-push-action@v7 docker/login-action@v4 aquasecurity/trivy-action cannot follow the rule: it publishes no major tag at all — every one of its 75 tags is an exact v0.x.y release — so docker.yml names @v0.36.0 and Renovate bumps it like any other dependency. Every tag above was resolved against its upstream repository before committing, so no workflow references a ref that does not exist. Renovate would otherwise have converted all of this straight back to digests on its next run, so .github/renovate.json changes with it: the helpers:pinGitHubActionDigests preset is dropped and the github-actions rule sets pinDigests false. The dockerfile manager keeps pinDigests true — that case is genuinely different, since an image tag is re-pushed in place and those base images end up inside the released artifacts — and its rule description no longer says "for the same reason" now that the reason it referred to is gone. Validated with renovate-config-validator. The trade is recorded rather than glossed: a re-pointed tag changes what CI runs without a commit here. ADR 0005 carries it as an explicit negative consequence, with what makes it acceptable and what would make it worth revisiting. AGENTS.md and CLAUDE.md drop the SHA-pinning convention for this one and keep the Dockerfile's digest pinning distinct from it. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_012EbVuqbqEztVsS892PpQ5i --- .github/renovate.json | 9 ++-- .github/workflows/central-publish.yml | 4 +- .github/workflows/codeql.yml | 10 ++--- .github/workflows/coverage.yml | 6 +-- .github/workflows/docker.yml | 16 +++---- .github/workflows/integration-tests.yml | 4 +- .github/workflows/maven-publish.yml | 4 +- .github/workflows/maven-windows.yml | 4 +- .github/workflows/maven.yml | 4 +- .github/workflows/pitest.yml | 6 +-- .github/workflows/spotbugs.yml | 8 ++-- AGENTS.md | 46 ++++++++++---------- CLAUDE.md | 9 ++-- docs/adr/0005-renovate-dependency-updates.md | 29 +++++++----- 14 files changed, 84 insertions(+), 75 deletions(-) diff --git a/.github/renovate.json b/.github/renovate.json index 3c91c383..35140430 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -3,8 +3,7 @@ "extends": [ "config:recommended", ":semanticCommits", - ":dependencyDashboard", - "helpers:pinGitHubActionDigests" + ":dependencyDashboard" ], "timezone": "UTC", "schedule": [ @@ -19,15 +18,15 @@ "osvVulnerabilityAlerts": false, "packageRules": [ { - "description": "The workflows pin every action to a commit SHA, so a floating tag cannot change what CI runs; keep those digests fresh in one PR.", + "description": "The workflows reference actions by their major tag (v7), so the tag itself is what moves and there is no digest to refresh; raise the major bumps in one PR.", "matchManagers": [ "github-actions" ], "groupName": "github actions", - "pinDigests": true + "pinDigests": false }, { - "description": "assembly/Dockerfile pins its base images by digest for the same reason, and a Temurin rebuild is what carries the base OS's security fixes in.", + "description": "assembly/Dockerfile pins its base images by digest, because an image tag is re-pushed in place and the released artifacts are built from it; a Temurin rebuild is what carries the base OS's security fixes in.", "matchManagers": [ "dockerfile" ], diff --git a/.github/workflows/central-publish.yml b/.github/workflows/central-publish.yml index a8563dc3..f5a0165a 100644 --- a/.github/workflows/central-publish.yml +++ b/.github/workflows/central-publish.yml @@ -62,7 +62,7 @@ jobs: fi echo "All required Maven Central secrets are present." - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: actions/checkout@v7 - name: Derive the reproducible-build timestamp # The pom declares no project.build.outputTimestamp on purpose, so that no @@ -80,7 +80,7 @@ jobs: echo "BUILD_OUTPUT_TIMESTAMP=${timestamp}" >> "$GITHUB_ENV" - name: Set up JDK 25 - uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6 + uses: actions/setup-java@v6 with: java-version: '25' distribution: 'temurin' diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index d2baa562..eb5df91f 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -42,17 +42,17 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + uses: actions/checkout@v7 - name: Setup java - uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6 + uses: actions/setup-java@v6 with: java-version: 25 distribution: 'temurin' # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4 + uses: github/codeql-action/init@v4 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -66,7 +66,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4 + uses: github/codeql-action/autobuild@v4 # ā„¹ļø Command-line programs to run using the OS shell. # šŸ“š See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -80,6 +80,6 @@ jobs: - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4 + uses: github/codeql-action/analyze@v4 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 23ab1d39..45f42b2f 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -21,9 +21,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: actions/checkout@v7 - name: Set up JDK 25 - uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6 + uses: actions/setup-java@v6 with: java-version: '25' distribution: 'temurin' @@ -31,7 +31,7 @@ jobs: - name: Run tests with JaCoCo run: mvn -B -ntp verify -Pcoverage --file pom.xml - name: Upload coverage reports - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + uses: actions/upload-artifact@v7 with: name: jacoco-reports path: '**/target/site/jacoco/' diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index cb583876..17ba866c 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -35,10 +35,10 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + uses: actions/checkout@v7 - name: Set up JDK 25 - uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6 + uses: actions/setup-java@v6 with: java-version: '25' distribution: 'temurin' @@ -78,17 +78,17 @@ jobs: } >> "$GITHUB_OUTPUT" - name: Set up QEMU - uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4 + uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4 + uses: docker/setup-buildx-action@v4 # A single-arch build first, loaded into the local daemon, so the image is # actually executed before anything is published. Buildx cannot --load a # multi-platform image, hence the two builds; the GHA layer cache makes the # later multi-arch build reuse this work for linux/amd64. - name: Build the image (linux/amd64) - uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7 + uses: docker/build-push-action@v7 with: context: . file: assembly/Dockerfile @@ -122,7 +122,7 @@ jobs: # caught before the push rather than after — and, on the weekly run, while # there is still time to fix it rather than at the release. - name: Scan the image for vulnerabilities - uses: aquasecurity/trivy-action@a9c7b0f06e461e9d4b4d1711f154ee024b8d7ab8 # v0.36.0 + uses: aquasecurity/trivy-action@v0.36.0 with: image-ref: tools:ci format: table @@ -132,7 +132,7 @@ jobs: - name: Log in to GitHub Container Registry if: github.event_name == 'release' - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4 + uses: docker/login-action@v4 with: registry: ghcr.io username: ${{ github.actor }} @@ -143,7 +143,7 @@ jobs: # architectures the trimmed DuckDB driver still carries natives for. - name: Push the image to GHCR if: github.event_name == 'release' - uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7 + uses: docker/build-push-action@v7 with: context: . file: assembly/Dockerfile diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 50c5b363..85a6110a 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -21,9 +21,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: actions/checkout@v7 - name: Set up JDK 25 - uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6 + uses: actions/setup-java@v6 with: java-version: '25' distribution: 'temurin' diff --git a/.github/workflows/maven-publish.yml b/.github/workflows/maven-publish.yml index 85775f8c..f1ef4e12 100644 --- a/.github/workflows/maven-publish.yml +++ b/.github/workflows/maven-publish.yml @@ -25,7 +25,7 @@ jobs: packages: write steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: actions/checkout@v7 - name: Derive the reproducible-build timestamp # The pom declares no project.build.outputTimestamp on purpose, so that no @@ -43,7 +43,7 @@ jobs: echo "BUILD_OUTPUT_TIMESTAMP=${timestamp}" >> "$GITHUB_ENV" - name: Set up JDK 25 - uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6 + uses: actions/setup-java@v6 with: java-version: '25' distribution: 'temurin' diff --git a/.github/workflows/maven-windows.yml b/.github/workflows/maven-windows.yml index 999f1991..51f11030 100644 --- a/.github/workflows/maven-windows.yml +++ b/.github/workflows/maven-windows.yml @@ -26,9 +26,9 @@ jobs: runs-on: windows-latest steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: actions/checkout@v7 - name: Set up JDK 25 - uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6 + uses: actions/setup-java@v6 with: java-version: '25' distribution: 'temurin' diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 986802d1..1ac72e6e 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -35,9 +35,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: actions/checkout@v7 - name: Set up JDK 25 - uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6 + uses: actions/setup-java@v6 with: java-version: '25' distribution: 'temurin' diff --git a/.github/workflows/pitest.yml b/.github/workflows/pitest.yml index 8db8aeb8..99b256b7 100644 --- a/.github/workflows/pitest.yml +++ b/.github/workflows/pitest.yml @@ -23,9 +23,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: actions/checkout@v7 - name: Set up JDK 25 - uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6 + uses: actions/setup-java@v6 with: java-version: '25' distribution: 'temurin' @@ -40,7 +40,7 @@ jobs: # bound to the `test` phase, so it still runs on every module. run: mvn -B -ntp install -Ppitest --file pom.xml - name: Upload mutation testing reports - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + uses: actions/upload-artifact@v7 with: name: pitest-reports path: '**/target/pit-reports/' diff --git a/.github/workflows/spotbugs.yml b/.github/workflows/spotbugs.yml index f75c3d8c..d34ba630 100644 --- a/.github/workflows/spotbugs.yml +++ b/.github/workflows/spotbugs.yml @@ -29,9 +29,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: actions/checkout@v7 - name: Set up JDK 25 - uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6 + uses: actions/setup-java@v6 with: java-version: '25' distribution: 'temurin' @@ -66,11 +66,11 @@ jobs: # No category input: it would only apply to runs that have no # automationDetails.id of their own, and the step above gives every run # the per-module id that keeps the delivery's runs distinct. - uses: github/codeql-action/upload-sarif@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4 + uses: github/codeql-action/upload-sarif@v4 with: sarif_file: target/sarif - name: Upload SpotBugs reports - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + uses: actions/upload-artifact@v7 with: name: spotbugs-reports path: | diff --git a/AGENTS.md b/AGENTS.md index 8601e5f8..b3e8cd86 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -831,23 +831,24 @@ across all ten, and a new workflow is expected to keep them: itself at 120 s has no business finishing an answer nobody is waiting for. Everywhere else it is `false` — a scheduled run is not superseded by a newer commit, and a publish interrupted half-way can leave a partial release behind. -- **Actions pinned to a commit SHA**, with the tag kept in a trailing comment - (`uses: actions/checkout@3d3c42e… # v7`). A tag is mutable, so an unpinned - action is a third party's ability to change what CI runs without a commit - here — and CI is what produces the released artifacts the supply-chain posture - of [ADR 0002](docs/adr/0002-security-policy-and-supply-chain-posture.md) rests - on. `assembly/Dockerfile` pins its `eclipse-temurin` base images by digest for - the same reason. Renovate refreshes both. -- **That trailing comment names the major alone** — `# v7`, never `# v7.0.1`. - The SHA is what pins the action, so the comment exists only to say which line - the pin belongs to, and Renovate reads it as the current version: a new `v7.x` - release is then a digest refresh that leaves the comment untouched, and only a - genuine `v8` rewrites it. That makes the version text in a diff the signal that - release notes need reading, instead of churning on every patch. Pin the digest - the **moving major tag** resolves to and the two never disagree, since that tag - tracks the newest release in its line. `aquasecurity/trivy-action` is the one - exception: it publishes no moving major tag — all 75 of its tags are exact - `v0.x.y` releases — so it keeps `# v0.36.0`. +- **Actions referenced by their major tag alone** — `uses: actions/setup-java@v6`, + never a commit SHA and never `@v6.0.0`. The major tag is the one each action's + maintainers move, so a patch or a security fix inside an action reaches CI + without a commit here, and the workflow says in the line itself what it runs + rather than hiding it behind a digest. The trade is deliberate: a tag is + mutable, so this trusts each action's owner not to re-point it — see the + trade-off recorded in + [ADR 0005](docs/adr/0005-renovate-dependency-updates.md). + `aquasecurity/trivy-action` is the one action that cannot follow the rule: it + publishes no major tag at all — every one of its 75 tags is an exact `v0.x.y` + release — so `docker.yml` names `@v0.36.0` and Renovate bumps it like any other + dependency. +- **`assembly/Dockerfile` is the opposite case and stays pinned by digest.** An + image tag is re-pushed in place by whoever owns it, and those `eclipse-temurin` + base images end up *inside* the released artifacts, which is the part of the + supply-chain posture of + [ADR 0002](docs/adr/0002-security-policy-and-supply-chain-posture.md) that a + mutable base image would undo. Renovate keeps those digests fresh. ### Dependency updates @@ -870,11 +871,12 @@ configuration is `.github/renovate.json`: once already. - This project's own `io.github.adamw7:**` modules are **disabled**: they resolve inside the reactor at `${revision}`. -- `pinDigests` is on for the **github-actions** and **dockerfile** managers, so - the SHA pins above are refreshed rather than left to rot; the action bumps - arrive as one grouped PR. Because those pins comment the major alone, most of - that PR is digest churn; a comment that changes from `# v7` to `# v8` is the - part to stop and read. +- `pinDigests` is **off** for the **github-actions** manager and **on** for + **dockerfile**, matching the two conventions above: the workflows track major + tags, so there is no action digest to refresh, while the Dockerfile's base + images stay pinned and current. What Renovate still raises for actions is the + major move — `@v7` to `@v8` — as one grouped PR, and that is the one to read + release notes for. - A **major** bump of the Maven API artifacts or of Spring Boot needs dependency dashboard approval — a Maven 4 API is wired in on purpose while the build is pinned to 3.9.x, and the framework the MCP servers boot on deserves a review. diff --git a/CLAUDE.md b/CLAUDE.md index 9c12c54e..39bcd970 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -236,10 +236,11 @@ the whole matrix passes: GHCR; `maven-publish.yml` (GitHub Packages); `central-publish.yml` (Maven Central). -Every workflow builds on JDK 25 (Temurin). Every action is pinned to a commit -SHA whose trailing comment names the **major only** (`# v7`, never `# v7.0.1`): -pin the digest the moving major tag resolves to. `aquasecurity/trivy-action` -publishes no such tag and is the documented exception. +Every workflow builds on JDK 25 (Temurin). Actions are referenced by their +**major tag alone** (`uses: actions/setup-java@v6`) — not a commit SHA, not +`@v6.0.0` — so an action's own patches reach CI without a commit here. +`aquasecurity/trivy-action` publishes no major tag and is the exception, at +`@v0.36.0`. `assembly/Dockerfile` still pins its base images by digest. Use clear, conventional commit messages — the `git-commit` skill writes them with this repository's real module scopes — keep changes focused, and add or update diff --git a/docs/adr/0005-renovate-dependency-updates.md b/docs/adr/0005-renovate-dependency-updates.md index ef06d5e9..050f2b07 100644 --- a/docs/adr/0005-renovate-dependency-updates.md +++ b/docs/adr/0005-renovate-dependency-updates.md @@ -74,17 +74,16 @@ repository's shape calls for: dashboard rather than arriving unannounced. - **This project's own `io.github.adamw7` modules disabled** — they resolve inside the reactor at `${revision}`, so there is no release for Renovate to raise. -- **`pinDigests` on the github-actions and dockerfile managers, with action - comments naming the major alone** (`# v7`, not `# v7.0.1`). The digest is the - real pin; Renovate reads the comment as the current version, so a major-only - comment turns every patch and minor release into a digest refresh and reserves - a comment change for an actual major. Reviewing the grouped actions PR is then - a matter of looking for a changed `# vN`, rather than re-reading nine version - strings that moved for no behavioural reason. The digest pinned is the one the - moving major tag resolves to, which is also the newest release in that line. - `aquasecurity/trivy-action` cannot follow this: it ships no moving major tag, - only exact `v0.x.y` releases, so it stays pinned with an exact comment and - Renovate raises its minor bumps normally. +- **`pinDigests` off for github-actions, on for dockerfile.** The workflows + reference each action by its major tag (`actions/setup-java@v6`), so the tag is + what moves and there is no digest for Renovate to refresh; what it raises is + the major bump, `@v6` to `@v7`, in the grouped actions pull request — the one + place release notes are worth reading. The Dockerfile is the opposite case and + stays pinned: an image tag is re-pushed in place by whoever owns it, and those + base images end up inside the released artifacts. + `aquasecurity/trivy-action` publishes no major tag at all — every one of its 75 + tags is an exact `v0.x.y` release — so it is referenced exactly and Renovate + raises its minor bumps like any other dependency. Validate a change to that file with `npx --package renovate renovate-config-validator` before committing it. @@ -104,3 +103,11 @@ Validate a change to that file with - Requires the Renovate App to be installed and configured on the repository. - Care is needed so Renovate and Dependabot do not both open a PR for the same security-driven bump; the role split in ADR 0002 exists to prevent that. +- Referencing actions by a major tag rather than a commit SHA means a tag that is + re-pointed — by a compromised account or by its owner — changes what CI runs + without a commit in this repository. That is accepted knowingly: it is what + lets an action's own patch and security releases reach CI unattended, the + actions in use are first-party GitHub and Docker ones plus Trivy, and the + artifacts a release actually ships are protected at the layer that survives + this — `assembly/Dockerfile`'s digest-pinned base images. Revisit it if an + action in use changes hands.