name: Bump Plugin SHAs

# Nightly sweep: for each external entry whose upstream HEAD has moved past
# its pinned SHA, validate at the new SHA with `claude plugin validate`
# inline, then open one PR per bumped plugin on branch `bump/<slug>`.
# Failing entries stay isolated in their own PR; passing bumps merge
# independently.
#
# Bot-free — uses the default GITHUB_TOKEN. PRs opened with GITHUB_TOKEN don't
# trigger on:pull_request workflows, so the required status checks on main
# (`scan` from Scan Plugins, `check` from Check MCP URLs, `validate` from
# Validate Plugins) would never run and the bump PR could never merge.
# workflow_dispatch is exempt from that recursion guard, so we dispatch all
# three ourselves against each per-entry bump branch after its PR is opened.
# Each check run lands on the branch HEAD — the same SHA as the PR head — and
# satisfies the corresponding required check. (Each of those workflows runs
# its job unconditionally on workflow_dispatch, so a dispatch always reports.)
#
# max-bumps caps the per-night work for cost control. Per-entry scans are
# more expensive than a single batched scan, so the cap is conservative.
# The composite action skips entries that already have an open bump PR, so
# re-dispatches don't pile up duplicate work.

on:
  schedule:
    - cron: '23 7 * * *'  # Daily 07:23 UTC
  workflow_dispatch:
    inputs:
      max_bumps:
        description: Cap on plugins bumped this run
        required: false
        default: '30'

permissions:
  contents: write
  pull-requests: write
  actions: write  # gh workflow run {scan-plugins,check-mcp-urls,validate-plugins}.yml per bump branch

concurrency:
  group: bump-plugin-shas

jobs:
  bump:
    runs-on: ubuntu-latest
    # Per-bump cost is ~2s (ls-remote + shallow clone + validate); 30 entries
    # is ~1-2 min. The 60 min ceiling absorbs slow upstreams without letting a
    # pathological run consume the default 360 min budget.
    timeout-minutes: 60
    steps:
      - uses: actions/checkout@v4

      # 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@e2019b2a01f11aa1484c53540b1cfab5eebbc299
        id: bump
        with:
          marketplace-path: .claude-plugin/marketplace.json
          max-bumps: ${{ inputs.max_bumps || '30' }}
          pr-mode: per-entry
          claude-cli-version: latest

      # Per-entry fan-out: dispatch the three required checks against each bump
      # branch. `pr-urls` is a JSON array of {name, old_sha, new_sha, branch,
      # pr_url} entries emitted by the composite action when pr-mode is
      # per-entry. All three (scan / check / validate) are required on main and
      # none fire on the GITHUB_TOKEN-opened PR, so each must be dispatched.
      # A single failed dispatch (transient API error / rate limit) must not
      # strand the remaining branches, so we attempt every dispatch, then fail
      # the step if any failed: a missing required check would otherwise leave
      # its bump PR silently blocked behind a green run, and the composite
      # action skips slugs with an open PR so it would never be retried.
      - name: Dispatch required checks per per-entry PR
        if: steps.bump.outputs.pr-urls != '' && steps.bump.outputs.pr-urls != '[]'
        env:
          GH_TOKEN: ${{ github.token }}
          PR_URLS: ${{ steps.bump.outputs.pr-urls }}
        run: |
          set -euo pipefail
          dispatch_failures="$(mktemp)"
          jq -c '.[]' <<<"$PR_URLS" | while read -r entry; do
            branch=$(jq -r '.branch' <<<"$entry")
            name=$(jq -r '.name' <<<"$entry")
            for wf in scan-plugins check-mcp-urls validate-plugins; do
              echo "Dispatching ${wf}.yml against $branch ($name)"
              if ! gh workflow run "${wf}.yml" --ref "$branch"; then
                echo "::error::Failed to dispatch ${wf}.yml against $branch ($name) — required check will be missing; re-dispatch with: gh workflow run ${wf}.yml --ref $branch"
                echo "${wf} ${branch}" >> "$dispatch_failures"
              fi
            done
          done
          if [ -s "$dispatch_failures" ]; then
            echo "::error::$(wc -l < "$dispatch_failures" | tr -d ' ') required-check dispatch(es) failed; the affected bump PR(s) are blocked until re-dispatched (see annotations above)."
            exit 1
          fi
