فهرست منبع

Make Scan Plugins a viable required check; auto-dispatch on bump PRs (#1815)

Scan Plugins is meant to gate every change to marketplace.json, but two
gaps made that unenforceable:

1. The bump workflow opens PRs with GITHUB_TOKEN, which GitHub exempts
   from on:pull_request triggers. Weekly bump PRs (e.g. #1809) get no
   scan check at all.
2. The workflow had a paths filter, so a required-check ruleset for
   `scan` would block every PR that doesn't touch marketplace.json
   (no check run = pending forever).

Fixes:

scan-plugins.yml
- Drop the paths filter; replace with a step-level `git diff --quiet`
  early-exit on the same paths. The check now reports on every PR,
  which makes it safe to require.
- Fail closed when ANTHROPIC_API_KEY is unset and a scan is needed.
  The shared action no-ops gracefully in that case (right default for
  community repos), but a required check that silently does nothing is
  a rubber stamp.

bump-plugin-shas.yml
- After the action opens the bump PR, `gh workflow run scan-plugins.yml
  --ref bump/plugin-shas`. workflow_dispatch is exempt from the
  GITHUB_TOKEN recursion guard, and the resulting check run lands on
  the branch HEAD (= PR head), so it satisfies the required check.
- Add `actions: write` so the dispatch is allowed.

Follow-up: add a repo ruleset on main requiring the `scan` check
(integration: github-actions) once this merges.
Tobin South 3 ماه پیش
والد
کامیت
45896c8f2f
2فایلهای تغییر یافته به همراه60 افزوده شده و 7 حذف شده
  1. 18 3
      .github/workflows/bump-plugin-shas.yml
  2. 42 4
      .github/workflows/scan-plugins.yml

+ 18 - 3
.github/workflows/bump-plugin-shas.yml

@@ -4,9 +4,13 @@ name: Bump Plugin SHAs
 # its pinned SHA, validate at the new SHA with `claude plugin validate`
 # inline, then open one PR with all passing bumps.
 #
-# Bot-free — uses the default GITHUB_TOKEN. Because GITHUB_TOKEN-opened PRs
-# don't trigger on:pull_request workflows, validation runs in this workflow
-# before the PR is opened; the PR body links back here as the CI evidence.
+# Bot-free — uses the default GITHUB_TOKEN. PRs opened with GITHUB_TOKEN don't
+# trigger on:pull_request workflows, so the policy scan (`Scan Plugins`, a
+# required status check on main) would never run and the bump PR could never
+# merge. workflow_dispatch is exempt from that recursion guard, so we dispatch
+# the scan ourselves on the bump branch after the PR is opened. The check run
+# lands on the branch HEAD — the same SHA as the PR head — and satisfies the
+# required check.
 
 on:
   schedule:
@@ -21,6 +25,7 @@ on:
 permissions:
   contents: write
   pull-requests: write
+  actions: write  # gh workflow run scan-plugins.yml on the bump branch
 
 concurrency:
   group: bump-plugin-shas
@@ -34,7 +39,17 @@ jobs:
       # createCommitOnBranch-based bump so commits are signed by GitHub and
       # satisfy the org-level required_signatures ruleset on main.
       - uses: anthropics/claude-plugins-community/.github/actions/bump-plugin-shas@c41c6911de0afffd2bc5cd8b21fb1e06444ee13b
+        id: bump
         with:
           marketplace-path: .claude-plugin/marketplace.json
           max-bumps: ${{ inputs.max_bumps || '20' }}
           claude-cli-version: latest
+
+      # `bump/plugin-shas` is the action's default `pr-branch`. The scan diffs
+      # the branch against origin/main (the action's base-ref fallback when
+      # there's no pull_request event) and scans only the bumped entries.
+      - name: Dispatch policy scan on bump branch
+        if: steps.bump.outputs.pr-url != ''
+        env:
+          GH_TOKEN: ${{ github.token }}
+        run: gh workflow run scan-plugins.yml --ref bump/plugin-shas

+ 42 - 4
.github/workflows/scan-plugins.yml

@@ -1,10 +1,15 @@
 name: Scan Plugins
 
+# Claude policy scan of changed external marketplace entries.
+#
+# `scan` is a required status check on main. A path-filtered workflow never
+# reports a check run when its paths don't match, which would leave unrelated
+# PRs blocked forever — so this workflow runs on every PR and skips the heavy
+# scan setup at the step level when nothing scan-relevant changed. The check
+# always reports.
+
 on:
   pull_request:
-    paths:
-      - '.claude-plugin/marketplace.json'
-      - '.github/policy/**'
   workflow_dispatch:
     inputs:
       scan_all:
@@ -24,9 +29,42 @@ jobs:
         with:
           fetch-depth: 0
 
+      # Same paths the workflow-level filter used to gate on. workflow_dispatch
+      # always runs the scan (no PR diff to inspect).
+      - name: Check for scan-relevant changes
+        id: changes
+        env:
+          EVENT_NAME: ${{ github.event_name }}
+          BASE_SHA: ${{ github.event.pull_request.base.sha }}
+        run: |
+          if [[ "$EVENT_NAME" == "workflow_dispatch" ]]; then
+            echo "relevant=true" >> "$GITHUB_OUTPUT"
+            exit 0
+          fi
+          if git diff --quiet "$BASE_SHA" HEAD -- .claude-plugin/marketplace.json .github/policy/; then
+            echo "relevant=false" >> "$GITHUB_OUTPUT"
+            echo "::notice::No changes to marketplace.json or policy/ — skipping policy scan."
+          else
+            echo "relevant=true" >> "$GITHUB_OUTPUT"
+          fi
+
+      # The shared action no-ops gracefully when ANTHROPIC_API_KEY is unset
+      # (sensible default for community repos). Here `scan` is a required
+      # check, so a silent no-op would make it a rubber stamp — fail closed.
+      - name: Require ANTHROPIC_API_KEY when a scan is needed
+        if: steps.changes.outputs.relevant == 'true'
+        env:
+          API_KEY_SET: ${{ secrets.ANTHROPIC_API_KEY != '' }}
+        run: |
+          if [[ "$API_KEY_SET" != "true" ]]; then
+            echo "::error::ANTHROPIC_API_KEY is not configured; refusing to skip a required policy scan."
+            exit 1
+          fi
+
       # Blocking: policy failures fail the job. Loosen by removing
       # fail-on-findings if the false-positive rate is too high.
-      - uses: anthropics/claude-plugins-community/.github/actions/scan-plugins@b277757588871fe55b2620de8c6dfda470e2e9d8
+      - if: steps.changes.outputs.relevant == 'true'
+        uses: anthropics/claude-plugins-community/.github/actions/scan-plugins@b277757588871fe55b2620de8c6dfda470e2e9d8
         with:
           anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
           policy-prompt: .github/policy/prompt.md