Files
claude-plugins-official/.github/workflows/scan-plugins.yml
tobin 2b7ecffd4f Make Scan Plugins a viable required check; auto-dispatch on bump PRs
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.
2026-05-11 20:12:56 +00:00

74 lines
2.7 KiB
YAML

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:
workflow_dispatch:
inputs:
scan_all:
description: Scan every external entry (full re-review). Slow.
type: boolean
default: false
permissions:
contents: read
jobs:
scan:
runs-on: ubuntu-latest
timeout-minutes: 360
steps:
- uses: actions/checkout@v4
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.
- 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
fail-on-findings: "true"
scan-all-external: ${{ inputs.scan_all || 'false' }}
claude-cli-version: latest