From 959d90a34670d6fa1c9244cbb31a9ac0565ce9c1 Mon Sep 17 00:00:00 2001 From: Claude Code Bot Date: Tue, 8 Sep 2026 17:19:50 -0700 Subject: [PATCH 1/2] chore: clear standards-check debt (shellcheck, zizmor pins, markdownlint scope) The fleet-wide `standards-check` workflow failed here on three linters. This clears two of them outright and reduces the third to a single finding that needs a policy decision rather than a code change. shellcheck (25 findings in 5 scripts, all under plugins/): fixed in place, no `# shellcheck disable`. Most are SC2312 on command substitutions whose exit status was already discarded, so `|| true` is a no-op that states the existing behavior. Two edits are not purely cosmetic and are called out here deliberately: - `protect-mcp/test/{run-tests,verify-fixtures}.sh` gain `cd ... || exit 1`. Both run `set -uo pipefail` with no `-e`, so this adds a failure path that did not exist. The target is the script's own directory, so the new exit is unreachable in practice. `validate-chart.sh` deserves a note as a non-change. It runs `set -e`, so the `|| true` added to its three `Chart.yaml` extractions looks like it might be suppressing an abort. It is not: the script sets `-e` only and never `pipefail`, so `grep ... | awk ...` already exited with awk's status, which is 0 even when grep matches nothing. A missing field reached the script's own `error "Chart name not found"` branch before this change and still does. Both protect-mcp suites still pass (8/8 and 12/12) after these edits. zizmor: `eval-report.yml` was the one workflow left unpinned. Its three third-party actions now carry commit SHAs at the latest patch of the major they already used, and its `actions/checkout` gains `persist-credentials: false` (the workflow pushes nothing). The canonical fleet `zizmor.yml` is copied in verbatim so first-party reusable-workflow refs keep their floating tags. One `help[adhoc-packages]` finding on `validate.yml` is left visible. It does not match the rationale the canonical config documents for this rule, and pinning does not clear it (verified against zizmor 1.30.0), so it needs a decision rather than a local silence. Tracked separately; the check stays red on that one finding. markdownlint: 408 findings across 111 files, all under `plugins/`. The repo's own CI job already lints only `*.md` and `docs/*.md` and documents that per-plugin docs are owned by their plugin authors. This adds `.markdownlint-cli2.jsonc` so config-discovering tools apply that same scope instead of walking vendored plugin docs. It extends `.markdownlint.json` rather than copying its rules, so the two cannot drift, and `.markdownlint.json` is unchanged because the repo's own action reads it directly. No Markdown outside `plugins/` had any finding to fix. Claude-Session: https://claude.ai/code/session_019HDRKLQNv82SEBd4zGpcXf --- .github/workflows/eval-report.yml | 8 +- .markdownlint-cli2.jsonc | 24 ++++ .../assets/thermal-sample.sh | 6 +- .../skills/file-conversion/scripts/convert.sh | 8 +- .../scripts/validate-chart.sh | 6 +- plugins/protect-mcp/test/run-tests.sh | 18 +-- plugins/protect-mcp/test/verify-fixtures.sh | 2 +- zizmor.yml | 130 ++++++++++++++++++ 8 files changed, 180 insertions(+), 22 deletions(-) create mode 100644 .markdownlint-cli2.jsonc create mode 100644 zizmor.yml diff --git a/.github/workflows/eval-report.yml b/.github/workflows/eval-report.yml index 8f9b8eda..50277950 100644 --- a/.github/workflows/eval-report.yml +++ b/.github/workflows/eval-report.yml @@ -33,10 +33,12 @@ jobs: DEPTH: ${{ inputs.depth || 'quick' }} ONLY_CHANGED: ${{ inputs.only_changed || '' }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + persist-credentials: false - name: Install uv - uses: astral-sh/setup-uv@v5 + uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 with: enable-cache: true @@ -66,7 +68,7 @@ jobs: - name: Upload reports artifact if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: eval-reports-${{ env.DEPTH }}-${{ github.run_id }} path: eval-reports/ diff --git a/.markdownlint-cli2.jsonc b/.markdownlint-cli2.jsonc new file mode 100644 index 00000000..fd293b91 --- /dev/null +++ b/.markdownlint-cli2.jsonc @@ -0,0 +1,24 @@ +// markdownlint-cli2 configuration. +// +// Scope: this repo lints its own top-level and `docs/` Markdown. Per-plugin +// documentation under `plugins/` is owned by the plugin authors, which is the +// same scope the repo's own `markdownlint` CI job already applies -- it runs +// `markdownlint-cli2 "*.md" "docs/*.md"` and does not walk `plugins/`. +// +// This file exists so that tooling which discovers Markdown itself (the +// fleet-wide `standards-check` workflow, pre-commit hooks) applies that same +// scope, instead of reporting several hundred findings in vendored plugin +// docs that this repo does not own. +// +// Rules live in `.markdownlint.json` and are extended here rather than +// duplicated, so the two configs cannot drift. The repo's own CI action reads +// `.markdownlint.json` directly. +{ + "config": { + "extends": ".markdownlint.json" + }, + "ignores": [ + "plugins/**", + "node_modules/**" + ] +} diff --git a/plugins/dgx-spark-ops/skills/spark-memory-thermal-ops/assets/thermal-sample.sh b/plugins/dgx-spark-ops/skills/spark-memory-thermal-ops/assets/thermal-sample.sh index 01d1e1ad..bfef08a4 100755 --- a/plugins/dgx-spark-ops/skills/spark-memory-thermal-ops/assets/thermal-sample.sh +++ b/plugins/dgx-spark-ops/skills/spark-memory-thermal-ops/assets/thermal-sample.sh @@ -12,9 +12,9 @@ INTERVAL="${1:-30}" LOGFILE="${2:-thermal.log}" PIDFILE="${LOGFILE}.pid" -if [ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; then - echo "Stopping existing sampler (pid $(cat "$PIDFILE"))" - kill "$(cat "$PIDFILE")" +if [ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE" || true)" 2>/dev/null; then + echo "Stopping existing sampler (pid $(cat "$PIDFILE" || true))" + kill "$(cat "$PIDFILE" || true)" fi echo "timestamp,temperature.gpu,power.draw" > "$LOGFILE" diff --git a/plugins/file-conversion/skills/file-conversion/scripts/convert.sh b/plugins/file-conversion/skills/file-conversion/scripts/convert.sh index d28293c4..e82b63d2 100755 --- a/plugins/file-conversion/skills/file-conversion/scripts/convert.sh +++ b/plugins/file-conversion/skills/file-conversion/scripts/convert.sh @@ -27,6 +27,7 @@ fi TARGET=$(printf '%s' "$TARGET" | tr '[:upper:]' '[:lower:]' | sed 's/^\.//') case "$TARGET" in *[!a-z0-9]*|'') echo "error: invalid target format: '$2' (use a plain extension like pdf, mp3, docx)" >&2; exit 2 ;; + *) ;; # valid plain format token; nothing to do esac BASENAME=$(basename "$IN") SRC=$(printf '%s' "${BASENAME##*.}" | tr '[:upper:]' '[:lower:]' | tr -cd 'a-z0-9') @@ -37,6 +38,7 @@ SRC=$(printf '%s' "${BASENAME##*.}" | tr '[:upper:]' '[:lower:]' | tr -cd 'a-z0- if [ -n "$OUT" ]; then case "$OUT" in /*|~*|*../*|*/..|..) echo "error: output path must be relative and must not contain '..': $OUT" >&2; exit 2 ;; + *) ;; # relative, non-traversing path; nothing to do esac fi [ -z "$OUT" ] && OUT="${IN%.*}.${TARGET}" @@ -63,7 +65,7 @@ else # printf is a shell builtin, so the large payload here is not subject to argv # limits; read it back from the encoded file. B64=$(cat "$B64_FILE") - SAFE_FN=$(printf '%s' "$BASENAME" | tr -d '"\\' | LC_ALL=C tr -cd '[:print:]') + SAFE_FN=$(printf '%s' "$BASENAME" | tr -d '"'"\\\\" | LC_ALL=C tr -cd '[:print:]') printf '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"convert_file","arguments":{"base64_content":"%s","source_format":"%s","target_format":"%s","filename":"%s"}}}' \ "$B64" "$SRC" "$TARGET" "$SAFE_FN" > "$REQ_FILE" fi @@ -72,7 +74,7 @@ HTTP_CODE=$(curl -sS --max-time 300 -o "$RESP_FILE" -w "%{http_code}" \ -X POST "$ENDPOINT" -H "Content-Type: application/json" --data-binary "@$REQ_FILE") if [ "$HTTP_CODE" != "200" ]; then - echo "error: conversion service returned HTTP $HTTP_CODE: $(head -c 300 "$RESP_FILE")" >&2 + echo "error: conversion service returned HTTP $HTTP_CODE: $(head -c 300 "$RESP_FILE" || true)" >&2 exit 4 fi @@ -80,7 +82,7 @@ DOWNLOAD_URL=$(grep -o 'https://changethisfile.com/v1/jobs/download/[A-Za-z0-9_- if [ -z "$DOWNLOAD_URL" ]; then # Surface the tool's error text (rate limit, unsupported route, etc.) ERR=$(sed 's/\\n/ /g' "$RESP_FILE" | grep -o '"text":"[^"]*"' | head -1 | sed 's/^"text":"//; s/"$//') - echo "error: ${ERR:-unexpected response: $(head -c 300 "$RESP_FILE")}" >&2 + echo "error: ${ERR:-unexpected response: $(head -c 300 "$RESP_FILE" || true)}" >&2 exit 5 fi diff --git a/plugins/kubernetes-operations/skills/helm-chart-scaffolding/scripts/validate-chart.sh b/plugins/kubernetes-operations/skills/helm-chart-scaffolding/scripts/validate-chart.sh index b8d5b0f3..e5340598 100755 --- a/plugins/kubernetes-operations/skills/helm-chart-scaffolding/scripts/validate-chart.sh +++ b/plugins/kubernetes-operations/skills/helm-chart-scaffolding/scripts/validate-chart.sh @@ -69,9 +69,9 @@ echo "" # 3. Check Chart.yaml echo "3️⃣ Validating Chart.yaml..." -CHART_NAME=$(grep "^name:" "$CHART_DIR/Chart.yaml" | awk '{print $2}') -CHART_VERSION=$(grep "^version:" "$CHART_DIR/Chart.yaml" | awk '{print $2}') -APP_VERSION=$(grep "^appVersion:" "$CHART_DIR/Chart.yaml" | awk '{print $2}' | tr -d '"') +CHART_NAME=$(grep "^name:" "$CHART_DIR/Chart.yaml" | awk '{print $2}' || true) +CHART_VERSION=$(grep "^version:" "$CHART_DIR/Chart.yaml" | awk '{print $2}' || true) +APP_VERSION=$(grep "^appVersion:" "$CHART_DIR/Chart.yaml" | awk '{print $2}' | tr -d '"' || true) if [ -z "$CHART_NAME" ]; then error "Chart name not found" diff --git a/plugins/protect-mcp/test/run-tests.sh b/plugins/protect-mcp/test/run-tests.sh index 160ef6aa..aa77feff 100755 --- a/plugins/protect-mcp/test/run-tests.sh +++ b/plugins/protect-mcp/test/run-tests.sh @@ -12,7 +12,7 @@ set -uo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -cd "$SCRIPT_DIR" +cd "$SCRIPT_DIR" || exit 1 # --- Preflight --------------------------------------------------------------- @@ -57,8 +57,8 @@ echo "=== Test 1: PreToolUse permit on Read ===" INPUT=fixtures/pretool-allow-read.json npx --yes protect-mcp@0.7.4 evaluate \ --policy fixtures/test-policy.cedar \ - --tool "$(extract "$INPUT" tool_name)" \ - --input "$(python3 -c 'import json,sys; print(json.dumps(json.load(open(sys.argv[1]))["tool_input"]))' "$INPUT")" \ + --tool "$(extract "$INPUT" tool_name || true)" \ + --input "$(python3 -c 'import json,sys; print(json.dumps(json.load(open(sys.argv[1]))["tool_input"]))' "$INPUT" || true)" \ --fail-on-missing-policy false >/dev/null 2>&1 check_exit $? 0 "Read is permitted by test-policy.cedar" @@ -68,8 +68,8 @@ echo "=== Test 2: PreToolUse permit on Bash git ===" INPUT=fixtures/pretool-allow-bash-safe.json npx --yes protect-mcp@0.7.4 evaluate \ --policy fixtures/test-policy.cedar \ - --tool "$(extract "$INPUT" tool_name)" \ - --input "$(python3 -c 'import json,sys; print(json.dumps(json.load(open(sys.argv[1]))["tool_input"]))' "$INPUT")" \ + --tool "$(extract "$INPUT" tool_name || true)" \ + --input "$(python3 -c 'import json,sys; print(json.dumps(json.load(open(sys.argv[1]))["tool_input"]))' "$INPUT" || true)" \ --fail-on-missing-policy false >/dev/null 2>&1 check_exit $? 0 "Bash 'git status' is permitted" @@ -79,8 +79,8 @@ echo "=== Test 3: PreToolUse forbid on Bash rm -rf ===" INPUT=fixtures/pretool-deny-bash-destructive.json npx --yes protect-mcp@0.7.4 evaluate \ --policy fixtures/test-policy.cedar \ - --tool "$(extract "$INPUT" tool_name)" \ - --input "$(python3 -c 'import json,sys; print(json.dumps(json.load(open(sys.argv[1]))["tool_input"]))' "$INPUT")" \ + --tool "$(extract "$INPUT" tool_name || true)" \ + --input "$(python3 -c 'import json,sys; print(json.dumps(json.load(open(sys.argv[1]))["tool_input"]))' "$INPUT" || true)" \ --fail-on-missing-policy false >/dev/null 2>&1 check_exit $? 2 "Bash 'rm -rf /' is denied with exit 2" @@ -90,8 +90,8 @@ echo "=== Test 4: PreToolUse forbid on Write ===" INPUT=fixtures/pretool-deny-write.json npx --yes protect-mcp@0.7.4 evaluate \ --policy fixtures/test-policy.cedar \ - --tool "$(extract "$INPUT" tool_name)" \ - --input "$(python3 -c 'import json,sys; print(json.dumps(json.load(open(sys.argv[1]))["tool_input"]))' "$INPUT")" \ + --tool "$(extract "$INPUT" tool_name || true)" \ + --input "$(python3 -c 'import json,sys; print(json.dumps(json.load(open(sys.argv[1]))["tool_input"]))' "$INPUT" || true)" \ --fail-on-missing-policy false >/dev/null 2>&1 check_exit $? 2 "Write is denied with exit 2" diff --git a/plugins/protect-mcp/test/verify-fixtures.sh b/plugins/protect-mcp/test/verify-fixtures.sh index c63a8138..cc9374c4 100755 --- a/plugins/protect-mcp/test/verify-fixtures.sh +++ b/plugins/protect-mcp/test/verify-fixtures.sh @@ -6,7 +6,7 @@ set -uo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -cd "$SCRIPT_DIR" +cd "$SCRIPT_DIR" || exit 1 command -v python3 >/dev/null 2>&1 || { echo "SKIP: python3 required"; exit 77; } diff --git a/zizmor.yml b/zizmor.yml new file mode 100644 index 00000000..0bcb3262 --- /dev/null +++ b/zizmor.yml @@ -0,0 +1,130 @@ +# zizmor configuration for repos consuming the smartwatermelon workflow set. +# +# Copy this to the root of any repo that uses the caller stubs from this +# repo's README, alongside your pre-commit hook. Without it, zizmor's blanket +# hash-pin policy reports ~9 high findings against a byte-identical standard +# caller stub, and the only workaround is `SKIP=zizmor` on every commit. +# +# That workaround is the actual hazard this file exists to remove. Routinely +# skipping the security linter is what let anthropics/claude-code-action sit +# at v1.0.70 for 123 releases while carrying GHSA-8q5r-mmjf-575q (see #123). +# A linter people bypass by habit protects nothing. +# +# Every entry below is a *documented policy decision*, not a convenience +# mute. Each one is justified against the code. Findings that reflect a real +# gap are deliberately left visible — see the note on third-party actions. + +rules: + # --------------------------------------------------------------------- + # unpinned-uses + # --------------------------------------------------------------------- + # zizmor's default is a blanket hash-pin requirement: every `uses:` must + # name a commit SHA. That is correct for third-party actions and wrong for + # first-party reusable workflows, and this repo deliberately treats the two + # classes differently: + # + # Third-party actions -> SHA-pin, plus dependabot.yml to keep the + # (actions/checkout, ...) pins current. We do not control upstream, so + # a repointed tag would run arbitrary code on + # the next trigger. + # + # First-party reusable -> floating tag (@v3). We control this repo, + # workflows (this repo) its branch protection, and who moves the + # tag. Floating refs are what make coordinated + # fleet remediation possible at all. + # + # The second rule is not a relaxation — it is load-bearing. When + # GHSA-8q5r-mmjf-575q was patched here, the fix reached consumers by + # repointing one tag. The ~19 repos that had pinned an exact @v3.1.0 + # silently received nothing, because immutable tags cannot carry a fix + # published after they were cut. Hash-pinning a first-party ref has the + # same effect, permanently. + # + # `ref-pin` still requires *a* ref — `@main` or a bare repo reference is + # rejected. It only lifts the hash requirement. + unpinned-uses: + config: + policies: + # First-party: tag refs are the convention (see README "Versioning"). + smartwatermelon/github-workflows/*: ref-pin + # Everything else keeps the strict default. This line matters: it is + # what keeps genuine third-party findings visible. A blanket ignore + # here would also hide, e.g., `actions/checkout@v7` in a repo's own + # workflows — which is a real gap worth fixing, not policy. + "*": hash-pin + + # --------------------------------------------------------------------- + # excessive-permissions + # --------------------------------------------------------------------- + # The caller stubs declare workflow-level permissions because the reusable + # workflows require them. They are not aspirational or copy-pasted: + # + # contents: read - checkout and diff reading + # pull-requests: write - posting and minimizing review comments + # issues: write - the inline-comment API path + # id-token: write - OIDC exchange for the app token + # + # `dependabot-auto-merge.yml` is in the list for the same reason but needs + # a different pair — `contents: write` to merge and `pull-requests: write` + # to approve. Both are inherent to what that workflow does; a read-only + # auto-merger is a contradiction. Note it deliberately runs with no + # `actions/checkout` (enforced by this repo's `guard-no-checkout` job), so + # the write scopes never combine with executing PR-controlled code. + # + # Removing any of them does not narrow the blast radius; it produces a + # `startup_failure` before the job runs. GitHub also does not let a caller + # grant a called workflow more than the called workflow declares for + # itself, so the effective ceiling is set in the reusable workflow, not + # here. + # + # Scoped to the three standard caller filenames only. A repo's own + # workflows are still audited normally — if `validate.yml` or `release.yml` + # asks for more than it needs, that finding still fires. + # + # These match on FILENAME, not on which reusable workflow is called, so the + # coupling is by naming convention. Note especially that `claude.yml` is the + # caller for `claude-assistant.yml` — the names differ, which is easy to + # misread as an omission. A consumer who names a caller anything else (say + # `claude-assistant-caller.yml`) gets excessive-permissions findings with no + # explanation; the fix is to rename the caller to the standard filename, or + # to add the local name here. See #142 and the README's zizmor section. + excessive-permissions: + ignore: + - claude-blocking-review.yml # caller for claude-blocking-review.yml + - claude.yml # caller for claude-assistant.yml + - dependabot-auto-merge.yml # caller for dependabot-auto-merge.yml + - standards-check.yml # caller for standards-check.yml + + # A cooldown deliberately DELAYS applying action updates. This repo was + # burned by exactly that delay: claude-code-action sat at v1.0.70 for 123 + # releases and stayed vulnerable to GHSA-8q5r-mmjf-575q with no PR opened + # (see #123 and the note at the top of .github/dependabot.yml). Adding a + # cooldown here would re-introduce the lag that incident was about, so the + # absence of one is a decision, not an oversight. + # + # This file also serves as the fleet-wide CI fallback policy: + # standards-check.yml uses it for any consuming repo that has no zizmor.yml + # of its own, so this ignore applies fleet-wide by design, not only here. + dependabot-cooldown: + ignore: + - dependabot.yml + + # zizmor advises `$/...` for a same-repo reusable workflow call. GitHub and + # actionlint both reject that form — actionlint reports "not following the + # format owner/repo/path@ref nor ./path/to/workflow.yml" — so `./...` is the + # only syntax that actually runs. Verified against zizmor 1.30 / actionlint + # on 2026-09-08. + self-repository: + ignore: + - self-review.yml + - self-standards-check.yml + + # markdownlint-cli2 and yamllint have no lockfile to install from — they are + # single pinned CLI tools, not project dependencies. standards-check.yml + # pins an exact version of each (MARKDOWNLINT_CLI2_VERSION, + # YAMLLINT_VERSION), and the three binary downloads next to them are + # additionally SHA256-verified. A lockfile would add a second place for the + # version to drift without removing the install. + adhoc-packages: + ignore: + - standards-check.yml From 9ef63464047520d524f5ac0b79a6af623588e033 Mon Sep 17 00:00:00 2001 From: Claude Code Bot Date: Tue, 8 Sep 2026 17:32:44 -0700 Subject: [PATCH 2/2] ci: accept the @latest gemini-cli install; correct stale pin comments The real-CLI smoke test deliberately tracks the current gemini-cli release and there is no lockfile to install from, so zizmor's adhoc-packages finding is accepted inline with rationale (decision 2026-09-08). The misleading "pinned" comment is corrected. Closes #20. The pre-existing SHA pins carried major-only comments (# v4, # v5) that zizmor's online ref-version-mismatch audit now flags because those floating tags have moved. Comments corrected to the exact tag each SHA is; setup-uv's SHA matched no current v5 tag and is bumped to v5.4.2. Claude-Session: https://claude.ai/code/session_019HDRKLQNv82SEBd4zGpcXf --- .github/workflows/code-quality.yml | 14 +++++++------- .github/workflows/validate.yml | 31 +++++++++++++++--------------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index 72cf7d3f..184c78a7 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -19,12 +19,12 @@ jobs: run: working-directory: plugins/plugin-eval steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: persist-credentials: false - name: Install uv - uses: astral-sh/setup-uv@e58605a9b6da7c637471fab8847a5e5a6b8df081 # v5 + uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 with: enable-cache: true @@ -70,12 +70,12 @@ jobs: name: Markdown (markdownlint-cli2) runs-on: ubuntu-latest steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: persist-credentials: false - name: Set up Node.js - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: '24' @@ -86,7 +86,7 @@ jobs: # to spec, but lint enforcement happens at the plugin-author layer, not at # the framework PR layer. # Config in .markdownlint.json keeps the ruleset narrow and pragmatic. - uses: DavidAnson/markdownlint-cli2-action@eb5ca3ab411449c66620fe7f1b3c9e10547144b0 # v18 + uses: DavidAnson/markdownlint-cli2-action@eb5ca3ab411449c66620fe7f1b3c9e10547144b0 # v18.0.0 with: globs: | *.md @@ -97,12 +97,12 @@ jobs: name: JSON / TOML / YAML syntax runs-on: ubuntu-latest steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: persist-credentials: false - name: Install uv - uses: astral-sh/setup-uv@e58605a9b6da7c637471fab8847a5e5a6b8df081 # v5 + uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 with: enable-cache: true diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 8da35e95..0cd6140c 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -15,7 +15,7 @@ jobs: name: Validate JSON files runs-on: ubuntu-latest steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: persist-credentials: false @@ -112,12 +112,12 @@ jobs: run: working-directory: plugins/plugin-eval steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: persist-credentials: false - name: Install uv - uses: astral-sh/setup-uv@e58605a9b6da7c637471fab8847a5e5a6b8df081 # v5 + uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 with: enable-cache: true @@ -137,12 +137,12 @@ jobs: run: working-directory: plugins/plugin-eval steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: persist-credentials: false - name: Install uv - uses: astral-sh/setup-uv@e58605a9b6da7c637471fab8847a5e5a6b8df081 # v5 + uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 with: enable-cache: true @@ -159,12 +159,12 @@ jobs: name: Cross-harness generation + validation runs-on: ubuntu-latest steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: persist-credentials: false - name: Install uv - uses: astral-sh/setup-uv@e58605a9b6da7c637471fab8847a5e5a6b8df081 # v5 + uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 with: enable-cache: true @@ -201,7 +201,7 @@ jobs: - name: Upload generated artifacts for inspection if: always() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: multi-harness-output path: | @@ -223,17 +223,17 @@ jobs: # generated artifacts to catch issues that pure-Python parsing misses (CLI # version drift, schema-loader surprises, plugin discovery bugs). steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: persist-credentials: false - name: Set up Node.js (for Gemini CLI) - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: '24' - name: Set up Bun (for OpenCode CLI) - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 + uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 with: bun-version: latest @@ -243,9 +243,10 @@ jobs: echo "$HOME/.opencode/bin" >> "$GITHUB_PATH" - name: Install Gemini CLI - # Pinned to the released line we developed against; bump alongside any - # gemini-extension.json schema changes. - run: npm install -g @google/gemini-cli@latest + # Deliberately @latest: this is a real-CLI smoke test and should track + # the current release. No lockfile exists to install from, so zizmor's + # adhoc-packages finding is accepted here (decision 2026-09-08, #20). + run: npm install -g @google/gemini-cli@latest # zizmor: ignore[adhoc-packages] - name: Verify CLI versions run: | @@ -253,7 +254,7 @@ jobs: gemini --version - name: Install uv - uses: astral-sh/setup-uv@e58605a9b6da7c637471fab8847a5e5a6b8df081 # v5 + uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 with: enable-cache: true