From 66ee21a5a0593fbe913e416f94eba663c3e8db4c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 06:39:41 +0000 Subject: [PATCH 1/2] deps(actions)(deps): bump the github-actions group with 3 updates Bumps the github-actions group with 3 updates: [actions/checkout](https://github.com/actions/checkout), [actions/setup-java](https://github.com/actions/setup-java) and [softprops/action-gh-release](https://github.com/softprops/action-gh-release). Updates `actions/checkout` from 6.0.3 to 7.0.1 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/df4cb1c069e1874edd31b4311f1884172cec0e10...3d3c42e5aac5ba805825da76410c181273ba90b1) Updates `actions/setup-java` from 5.4.0 to 5.6.0 - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/1bcf9fb12cf4aa7d266a90ae39939e61372fe520...03ad4de0992f5dab5e18fcb136590ce7c4a0ac95) Updates `softprops/action-gh-release` from 2.6.2 to 3.0.2 - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/3bb12739c298aeb8a4eeaf626c5b8d85266b0e65...3d0d9888cb7fd7b750713d6e236d1fcb99157228) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 7.0.1 dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: actions/setup-java dependency-version: 5.6.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions - dependency-name: softprops/action-gh-release dependency-version: 3.0.2 dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 4 ++-- .github/workflows/release.yml | 6 +++--- .github/workflows/security.yml | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1381709..8960aac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,13 +21,13 @@ jobs: # GitHub-hosted, change this line to: runs-on: ubuntu-latest runs-on: [self-hosted, orion] steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6 # JDK 21: the io.nextflow.nextflow-plugin toolchain builds with a JDK 21 # toolchain (sources still target Java 17). Provide 21 so Gradle uses it # rather than downloading one. - name: Setup JDK 21 - uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5 + uses: actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # v5 with: distribution: temurin java-version: '21' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a94da19..8b5cb23 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,11 +15,11 @@ jobs: name: Build and publish plugin runs-on: [self-hosted, orion] steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6 # JDK 21 — see ci.yml (the nextflow-plugin toolchain builds on JDK 21). - name: Setup JDK 21 - uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5 + uses: actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # v5 with: distribution: temurin java-version: '21' @@ -46,7 +46,7 @@ jobs: # Use the action rather than the gh CLI — the self-hosted runner container # has no gh installed. - name: Publish release - uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2 + uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2 with: files: build/distributions/nf-spawn-*.zip generate_release_notes: true diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 0552a10..e662a95 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -18,7 +18,7 @@ jobs: name: Secret Scan (gitleaks) runs-on: ubuntu-latest steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6 with: fetch-depth: 0 # full history so leaked-then-removed secrets are caught # Run the gitleaks BINARY directly (MIT-licensed, free for orgs). The @@ -37,7 +37,7 @@ jobs: name: Trivy Security Scan runs-on: ubuntu-latest steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6 - name: Trivy filesystem scan (vulns + secrets) uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 with: @@ -60,7 +60,7 @@ jobs: container: image: semgrep/semgrep steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6 # Enforcing: fails the build on findings. Triaged false-positives are # annotated inline with `# nosemgrep: -- ` (#368). - name: Semgrep scan From 9f7b7b3ee3a696168c1ac2455c527ec1909e6d22 Mon Sep 17 00:00:00 2001 From: scttfrdmn <3011922+scttfrdmn@users.noreply.github.com> Date: Mon, 3 Aug 2026 09:37:42 -0700 Subject: [PATCH 2/2] fix pin labels in the actions bump, and make a false label fail CI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dependabot's bump moved actions/checkout to 3d3c42e while writing `# v6` on all five refs. That SHA is v7.0.1. A three-major jump was labelled as a same-line bump, and the label is the only human-readable part of a pin — a wrong one is worse than none, because it makes the diff look routine. Labels corrected to the tags the SHAs actually carry: actions/checkout 3d3c42e # v6 -> # v7.0.1 actions/setup-java 03ad4de # v5 -> # v5.6.0 gradle/actions/setup-gradle 48b5f21 # v4 -> # v4.4.3 (pre-existing on main) SpawnCiHygieneTest passed the mislabelled bump because it only required that SOME `# vN` be present. It now requires an exact vX.Y.Z: a bare major can keep meaning something new as the pin moves, an exact version either matches or is a visible lie. That is the strongest form of the claim checkable offline, so the Spock suite stays hermetic. scripts/verify-pins.sh does the network half — resolve each SHA against the tag its comment claims — and is wired into ci.yml, so it gates PRs and catches the next mislabelled bump before merge, not after. Both checks accept annotated-tag pins. setup-gradle pins v4.4.3's tag OBJECT (48b5f21) rather than the commit (ed40850); GitHub resolves either. An earlier draft compared against one form only and reported a correct pin as mislabelled, printing "comment says v4.4.3 ... SHA is actually v4.4.3". The majors are safe here, checked rather than assumed: - action-gh-release@v3 moves to the Node 24 runtime. orion runs runner 2.336.0 and checkout@v6.0.3 was already `using: node24` and passing, so the runtime is present. - checkout@v7's only behavior change blocks fork checkouts under pull_request_target / workflow_run. Neither trigger appears in this repo. checkout@v7.0.1 was already exercised by this PR on both runners: ci.yml on self-hosted orion and security.yml on ubuntu-latest. Only release.yml is unexercised until the next tag. Verified: ./gradlew test green (96 tests, 5 hygiene features); verify-pins.sh OK on all 12 pins; shellcheck clean. Mutation-tested — bare-major label, wrong label, nonexistent tag and altered SHA each fail, workflows byte-identical after. build.gradle version untouched (0.10.0). --- .github/workflows/ci.yml | 17 +++- .github/workflows/release.yml | 6 +- .github/workflows/security.yml | 6 +- CHANGELOG.md | 21 +++++ scripts/verify-pins.sh | 87 +++++++++++++++++++ .../nextflow/spawn/SpawnCiHygieneTest.groovy | 29 +++++++ 6 files changed, 157 insertions(+), 9 deletions(-) create mode 100755 scripts/verify-pins.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8960aac..c206084 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,19 +21,19 @@ jobs: # GitHub-hosted, change this line to: runs-on: ubuntu-latest runs-on: [self-hosted, orion] steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # JDK 21: the io.nextflow.nextflow-plugin toolchain builds with a JDK 21 # toolchain (sources still target Java 17). Provide 21 so Gradle uses it # rather than downloading one. - name: Setup JDK 21 - uses: actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # v5 + uses: actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # v5.6.0 with: distribution: temurin java-version: '21' - name: Setup Gradle - uses: gradle/actions/setup-gradle@48b5f213c81028ace310571dc5ec0fbbca0b2947 # v4 + uses: gradle/actions/setup-gradle@48b5f213c81028ace310571dc5ec0fbbca0b2947 # v4.4.3 - name: Build run: ./gradlew assemble --no-daemon @@ -43,3 +43,14 @@ jobs: # main safety net and were previously never executed in CI. - name: Test run: ./gradlew test --no-daemon + + # The half of pin hygiene that needs the network, so it can't live in the + # (hermetic) Spock suite: does each pin's `# vX.Y.Z` comment actually name + # the tag its SHA carries? A wrong label is worse than none — it makes a + # major-version jump read as a routine same-line bump, which is exactly how + # a checkout v6 -> v7.0.1 bump nearly landed labelled `# v6`. + # + # Read-only (git ls-remote), no auth. Runs on PRs, so a Dependabot bump that + # mislabels a pin is caught before merge rather than after. + - name: Verify pin comments match their SHAs + run: ./scripts/verify-pins.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8b5cb23..33d4f11 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,17 +15,17 @@ jobs: name: Build and publish plugin runs-on: [self-hosted, orion] steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # JDK 21 — see ci.yml (the nextflow-plugin toolchain builds on JDK 21). - name: Setup JDK 21 - uses: actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # v5 + uses: actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # v5.6.0 with: distribution: temurin java-version: '21' - name: Setup Gradle - uses: gradle/actions/setup-gradle@48b5f213c81028ace310571dc5ec0fbbca0b2947 # v4 + uses: gradle/actions/setup-gradle@48b5f213c81028ace310571dc5ec0fbbca0b2947 # v4.4.3 # Sanity-check the tag matches build.gradle's version, so the artifact and # the release tag never drift. diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index e662a95..d3219ca 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -18,7 +18,7 @@ jobs: name: Secret Scan (gitleaks) runs-on: ubuntu-latest steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 # full history so leaked-then-removed secrets are caught # Run the gitleaks BINARY directly (MIT-licensed, free for orgs). The @@ -37,7 +37,7 @@ jobs: name: Trivy Security Scan runs-on: ubuntu-latest steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Trivy filesystem scan (vulns + secrets) uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 with: @@ -60,7 +60,7 @@ jobs: container: image: semgrep/semgrep steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # Enforcing: fails the build on findings. Triaged false-positives are # annotated inline with `# nosemgrep: -- ` (#368). - name: Semgrep scan diff --git a/CHANGELOG.md b/CHANGELOG.md index c1a3aa5..ad9f8f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `nextflow-plugin` toolchain, already a release behind. A new `SpawnCiHygieneTest` fails the build if a pin is reverted or an entry dropped. +### Changed +- **Actions bumped, and every pin's version comment now states the truth (#83).** + `actions/checkout` → v7.0.1, `actions/setup-java` → v5.6.0, + `softprops/action-gh-release` → v3.0.2. The bumps arrived from Dependabot + labelled `# v6` on all five `checkout` refs — but the SHA it moved to + (`3d3c42e`) is **v7.0.1**, so a three-major jump read as a routine same-line + bump; `gradle/actions/setup-gradle` had likewise carried a bare `# v4` while + pinning v4.4.3. Comments now name exact versions. Nothing in the majors affects + this repo: `action-gh-release@v3` requires the Node 24 runtime, which the orion + runner (2.336.0) provides and `checkout@v6.0.3` was already using, and + `checkout@v7`'s one behavior change only blocks fork checkouts under + `pull_request_target`/`workflow_run`, neither of which appears here. + ### Fixed +- **A pin's version comment can no longer silently misstate what CI runs (#83).** + `SpawnCiHygieneTest` required only that *some* `# vN` comment be present, so the + mislabelled `checkout` bump above passed it. It now requires an exact `vX.Y.Z`, + and a new `scripts/verify-pins.sh` — wired into CI, so it gates PRs — resolves + each SHA against the tag its comment claims and fails if they disagree. The + network half lives in the script to keep the Spock suite hermetic. Both accept + annotated-tag pins (the tag object *or* the commit it points at); an earlier + draft compared against only one form and called a correct pin mislabelled. - **`./gradlew test` no longer reports success without running the tests when only CI config changed (#80).** Gradle's up-to-date check considered just the Groovy sources, so a commit touching only `.github/` or `build.gradle` would skip diff --git a/scripts/verify-pins.sh b/scripts/verify-pins.sh new file mode 100755 index 0000000..37db8ad --- /dev/null +++ b/scripts/verify-pins.sh @@ -0,0 +1,87 @@ +#!/usr/bin/env bash +# Verify every action pin's `# vX.Y.Z` comment actually names the tag its SHA +# carries. +# +# This is the half of pin hygiene that needs the network, so it is NOT in the +# Spock suite (which must stay offline and hermetic). SpawnCiHygieneTest enforces +# the offline half: every pin is a 40-hex SHA with an exact vX.Y.Z comment. +# +# Why it exists: a pin's comment can be silently false. Dependabot bumped +# actions/checkout to 3d3c42e — which is v7.0.1 — while leaving `# v6` on all +# five refs, and a stale `# v4` on gradle/actions/setup-gradle (really v4.4.3) +# had sat on main for months. Both read as fine. A wrong label is worse than no +# label: it makes a major-version jump look like a routine same-line bump. +# +# Read-only: git ls-remote only. No auth, no writes, no side effects. +# +# Usage: scripts/verify-pins.sh +set -euo pipefail + +cd "$(dirname "$0")/.." + +fail=0 +found=0 + +while IFS= read -r line; do + # file:lineno: ... uses: owner/action@ # vX.Y.Z + loc=${line%%:*} + rest=${line#*:} + lineno=${rest%%:*} + + ref=$(printf '%s\n' "$line" | sed -nE 's/.*uses:[[:space:]]*([^[:space:]]+@[0-9a-f]{40}).*/\1/p') + ver=$(printf '%s\n' "$line" | sed -nE 's/.*@[0-9a-f]{40}[[:space:]]*#[[:space:]]*(v[0-9][^[:space:]]*).*/\1/p') + [ -n "$ref" ] && [ -n "$ver" ] || continue + + action=${ref%@*} + sha=${ref#*@} + # gradle/actions/setup-gradle is a subdirectory action: the repo is the first + # two path segments, so trim anything deeper before querying. + repo=$(printf '%s\n' "$action" | cut -d/ -f1,2) + + found=$((found + 1)) + + # Resolve the claimed tag to BOTH acceptable SHAs and accept either. + # + # For an annotated tag, `refs/tags/vX` is the tag OBJECT and `refs/tags/vX^{}` + # the commit it points at, and the two differ. Both are legitimate pins — + # gradle/actions/setup-gradle@48b5f21 is v4.4.3's tag object, actions/checkout + # pins the commit — and GitHub resolves either. Comparing against just one form + # reports a correct pin as mislabeled (which this script did on its first run, + # absurdly printing "comment says v4.4.3 ... SHA is actually v4.4.3"). + claimed=$(git ls-remote "https://github.com/$repo" \ + "refs/tags/$ver" "refs/tags/$ver^{}" 2>/dev/null | awk '{print $1}') + + if [ -z "$claimed" ]; then + printf ' MISSING TAG %s:%s %s @ %.12s — tag %s does not exist in %s\n' \ + "$loc" "$lineno" "$action" "$sha" "$ver" "$repo" + fail=1 + elif ! printf '%s\n' "$claimed" | grep -qFx "$sha"; then + # Name what the SHA really is, so the fix is obvious rather than a puzzle. + actual=$(git ls-remote --tags "https://github.com/$repo" 2>/dev/null \ + | awk -v s="$sha" '$1==s {gsub("refs/tags/",""); gsub(/\^\{\}$/,"",$2); print $2}' \ + | sort -u | paste -sd, -) + printf ' MISLABELED %s:%s %s @ %.12s — comment says %s (that tag is %.12s); SHA is actually %s\n' \ + "$loc" "$lineno" "$action" "$sha" "$ver" "$(printf '%s' "$claimed" | head -1)" \ + "${actual:-untagged}" + fail=1 + else + printf ' ok %s:%s %s %s\n' "$loc" "$lineno" "$action" "$ver" + fi +done < <(grep -rn 'uses:.*@[0-9a-f]\{40\}' .github/workflows/) + +# Anti-vacuous: a grep that stops matching would otherwise report success forever. +if [ "$found" -eq 0 ]; then + echo "verify-pins: found no SHA-pinned actions under .github/workflows — the check" >&2 + echo "matched nothing, which is a parser problem, not a clean bill of health." >&2 + exit 1 +fi + +if [ "$fail" -ne 0 ]; then + echo + echo "verify-pins: FAIL — a pin's version comment does not match its SHA." >&2 + echo "Fix the comment to name the tag the SHA really carries (or move the SHA)." >&2 + exit 1 +fi + +echo +echo "verify-pins: OK — all $found pins' comments match their SHAs." diff --git a/src/test/groovy/io/nextflow/spawn/SpawnCiHygieneTest.groovy b/src/test/groovy/io/nextflow/spawn/SpawnCiHygieneTest.groovy index 694b0af..2c63e45 100644 --- a/src/test/groovy/io/nextflow/spawn/SpawnCiHygieneTest.groovy +++ b/src/test/groovy/io/nextflow/spawn/SpawnCiHygieneTest.groovy @@ -83,6 +83,35 @@ class SpawnCiHygieneTest extends Specification { '\nUse: uses: owner/action@<40-hex-sha> # vX.Y.Z' } + def 'every version comment names an exact patch version, not a bare major'() { + // The comment is the only human-readable part of a pin, and a bare `# v6` + // is not just vague — it can be actively false, which is worse than absent. + // Dependabot bumped checkout to 3d3c42e (which is v7.0.1) while leaving the + // comment reading `# v6` on all five refs. The pin was correct; the label + // said it was a major version behind what it actually ran, and the previous + // spec passed because it only required SOME `# vN` to be present. A stale + // `# v4` on setup-gradle (really v4.4.3) had sat on main the same way. + // + // Requiring X.Y.Z is the strongest version of this claim checkable offline: + // a bare major can silently keep meaning something new as the pin moves, + // whereas an exact version either matches the SHA or is a visible lie, and + // reviewers can resolve it. Verifying comment-vs-tag needs the network, so + // it stays out of the unit suite — see scripts/verify-pins.sh. + given: + def exact = Pattern.compile(/^[^@\s]+@[0-9a-f]{40}\s+#\s*v\d+\.\d+\.\d+\s*$/) + def refs = usesRefs() + + expect: 'anti-vacuous — if the parser stops matching, this would pass forever' + !refs.isEmpty() + + and: + def vague = refs.findAll { !exact.matcher(it[2] as String).matches() } + assert vague.isEmpty(), 'these pins do not name an exact vX.Y.Z, so the comment ' + + 'cannot be checked against the SHA and may silently misstate what CI runs:\n' + + vague.collect { " ${it[0]}:${it[1]} ${it[2]}" }.join('\n') + + '\nResolve the SHA to its tag and write that: # vX.Y.Z' + } + def 'Dependabot has a github-actions entry whose group patterns cover every action'() { // The other half of pinning. A SHA never moves, including past a security // fix, so pinning without Dependabot trades a mutable-tag hole for a