validate-plugins.yml 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. name: Validate Plugins
  2. on:
  3. pull_request:
  4. paths:
  5. - '.claude-plugin/**'
  6. - '*/.claude-plugin/**'
  7. - '*/agents/**'
  8. - '*/skills/**'
  9. - '*/commands/**'
  10. # `validate` is a required status check, so a PR that touches ONLY workflow
  11. # files (e.g. an action-SHA re-pin) would otherwise never trigger validate
  12. # and sit "Expected — Waiting for status to be reported" forever (workflow_dispatch
  13. # check runs aren't associated with the PR, so they don't satisfy it). Run
  14. # validate on workflow changes too so those PRs can clear the gate in-context.
  15. - '.github/workflows/**'
  16. push:
  17. branches: [main]
  18. paths:
  19. - '.claude-plugin/**'
  20. # `validate` is a required status check on main. Bump PRs are opened with
  21. # GITHUB_TOKEN, which doesn't fire on:pull_request (recursion guard), so the
  22. # path-filtered trigger above never reports on them and the PR would be
  23. # blocked forever. The bump workflow dispatches this against each per-entry
  24. # bump branch instead; the check run lands on the branch HEAD (= PR head)
  25. # and satisfies the required check. The validate job runs unconditionally,
  26. # so a dispatch always reports.
  27. workflow_dispatch:
  28. permissions:
  29. contents: read
  30. jobs:
  31. validate:
  32. runs-on: ubuntu-latest
  33. steps:
  34. - uses: actions/checkout@v4
  35. with:
  36. fetch-depth: 0
  37. - name: Check for renamed plugins without renames entry
  38. run: |
  39. set -euo pipefail
  40. base="${{ github.event.pull_request.base.sha || 'origin/main' }}"
  41. # Names removed from plugins[] in this diff
  42. removed=$(comm -23 \
  43. <(git show "$base:.claude-plugin/marketplace.json" | jq -r '.plugins[].name' | sort) \
  44. <(jq -r '.plugins[].name' .claude-plugin/marketplace.json | sort))
  45. [ -z "$removed" ] && { echo "No plugin names removed."; exit 0; }
  46. # Renames keys present in HEAD
  47. rename_keys=$(jq -r '.renames // {} | keys[]' .claude-plugin/marketplace.json | sort)
  48. missing=$(comm -23 <(echo "$removed" | sort) <(echo "$rename_keys"))
  49. if [ -n "$missing" ]; then
  50. echo "::error::Plugin name(s) removed without a renames entry: $missing"
  51. echo "Add to .claude-plugin/marketplace.json top-level \"renames\": {\"<old>\": \"<new>\"} (or null if intentionally removed)."
  52. exit 1
  53. fi
  54. echo "All removed names have renames entries."
  55. - uses: anthropics/claude-plugins-community/.github/actions/validate-plugins@426e469f322952061102b286b378c0c9733a0934
  56. with:
  57. marketplace-path: .claude-plugin/marketplace.json
  58. # Official curated marketplace: SHA-pin (I5) is a HARD error.
  59. # I8/I11 are warnings until the 15 known vendored-path/name issues
  60. # are cleaned up (see PR body); tighten to "I1 I3" after.
  61. warn-invariants: "I1 I3 I8 I11"
  62. claude-cli-version: latest