bump-plugin-shas.yml 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. name: Bump Plugin SHAs
  2. # Nightly sweep: for each external entry whose upstream HEAD has moved past
  3. # its pinned SHA, validate at the new SHA with `claude plugin validate`
  4. # inline, then open one PR per bumped plugin on branch `bump/<slug>`.
  5. # Failing entries stay isolated in their own PR; passing bumps merge
  6. # independently.
  7. #
  8. # Bot-free — uses the default GITHUB_TOKEN. PRs opened with GITHUB_TOKEN don't
  9. # trigger on:pull_request workflows, so the required status checks on main
  10. # (`scan` from Scan Plugins, `check` from Check MCP URLs, `validate` from
  11. # Validate Plugins) would never run and the bump PR could never merge.
  12. # workflow_dispatch is exempt from that recursion guard, so we dispatch all
  13. # three ourselves against each per-entry bump branch after its PR is opened.
  14. # Each check run lands on the branch HEAD — the same SHA as the PR head — and
  15. # satisfies the corresponding required check. (Each of those workflows runs
  16. # its job unconditionally on workflow_dispatch, so a dispatch always reports.)
  17. #
  18. # max-bumps caps the per-night work for cost control. Per-entry scans are
  19. # more expensive than a single batched scan, so the cap is conservative.
  20. # Raised 30 -> 60 (2026-09): with ~290 external entries and ~15 upstream
  21. # movers a day plus re-bumps of stale merges, 30 was hit on consecutive
  22. # nights and late-alphabet entries went unbumped for days (discovery walks
  23. # the manifest in order and stops at the cap).
  24. # The composite action skips entries that already have an open bump PR, so
  25. # re-dispatches don't pile up duplicate work.
  26. on:
  27. schedule:
  28. - cron: '23 7 * * *' # Daily 07:23 UTC
  29. workflow_dispatch:
  30. inputs:
  31. max_bumps:
  32. description: Cap on plugins bumped this run
  33. required: false
  34. default: '60'
  35. plugin:
  36. description: >-
  37. Bump ONLY this plugin name (exact entry name; empty = all stale). A
  38. frozen/sha-exempt target is still skipped (same as a full run).
  39. required: false
  40. default: ''
  41. permissions:
  42. contents: write
  43. pull-requests: write
  44. actions: write # gh workflow run {scan-plugins,check-mcp-urls,validate-plugins}.yml per bump branch
  45. concurrency:
  46. group: bump-plugin-shas
  47. jobs:
  48. bump:
  49. runs-on: ubuntu-latest
  50. # Per-bump cost is ~2s (ls-remote + shallow clone + validate); 60 entries
  51. # is ~2-4 min. The 60 min ceiling absorbs slow upstreams without letting a
  52. # pathological run consume the default 360 min budget.
  53. timeout-minutes: 60
  54. steps:
  55. - uses: actions/checkout@v4
  56. # createCommitOnBranch-based bump so commits are signed by GitHub and
  57. # satisfy the org-level required_signatures ruleset on main.
  58. - uses: anthropics/claude-plugins-community/.github/actions/bump-plugin-shas@9247660a88cf3f49456a2f6464d66fe4ea4f4a77
  59. id: bump
  60. with:
  61. marketplace-path: .claude-plugin/marketplace.json
  62. max-bumps: ${{ inputs.max_bumps || '60' }}
  63. only: ${{ inputs.plugin }}
  64. pr-mode: per-entry
  65. # Entries listed in .github/bump-tracking.json {"releases-only": [...]}
  66. # bump to the latest published release tag's commit instead of HEAD
  67. # (no release / not-ahead / lookup failure -> pin held; see the action
  68. # README). All other entries HEAD-track exactly as before.
  69. tracking-config: .github/bump-tracking.json
  70. claude-cli-version: latest
  71. # Per-entry fan-out: dispatch the three required checks against each bump
  72. # branch. `pr-urls` is a JSON array of {name, old_sha, new_sha, branch,
  73. # pr_url} entries emitted by the composite action when pr-mode is
  74. # per-entry. All three (scan / check / validate) are required on main and
  75. # none fire on the GITHUB_TOKEN-opened PR, so each must be dispatched.
  76. # A single failed dispatch (transient API error / rate limit) must not
  77. # strand the remaining branches, so we attempt every dispatch, then fail
  78. # the step if any failed: a missing required check would otherwise leave
  79. # its bump PR silently blocked behind a green run, and the composite
  80. # action skips slugs with an open PR so it would never be retried.
  81. - name: Dispatch required checks per per-entry PR
  82. if: steps.bump.outputs.pr-urls != '' && steps.bump.outputs.pr-urls != '[]'
  83. env:
  84. GH_TOKEN: ${{ github.token }}
  85. PR_URLS: ${{ steps.bump.outputs.pr-urls }}
  86. run: |
  87. set -euo pipefail
  88. dispatch_failures="$(mktemp)"
  89. jq -c '.[]' <<<"$PR_URLS" | while read -r entry; do
  90. branch=$(jq -r '.branch' <<<"$entry")
  91. name=$(jq -r '.name' <<<"$entry")
  92. for wf in scan-plugins check-mcp-urls validate-plugins; do
  93. echo "Dispatching ${wf}.yml against $branch ($name)"
  94. if ! gh workflow run "${wf}.yml" --ref "$branch"; then
  95. 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"
  96. echo "${wf} ${branch}" >> "$dispatch_failures"
  97. fi
  98. done
  99. done
  100. if [ -s "$dispatch_failures" ]; then
  101. 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)."
  102. exit 1
  103. fi