diff --git a/.github/workflows/bump-plugin-shas.yml b/.github/workflows/bump-plugin-shas.yml index a2d965f..77661a5 100644 --- a/.github/workflows/bump-plugin-shas.yml +++ b/.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 diff --git a/.github/workflows/scan-plugins.yml b/.github/workflows/scan-plugins.yml index fc3f571..14bf9b4 100644 --- a/.github/workflows/scan-plugins.yml +++ b/.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