How Plugin SHAs Are Updated for External Plugins in Claude Plugins Community

The anthropics/claude-plugins-community repository automatically updates plugin SHAs through a scheduled GitHub Action called "bump-plugin-shas" that discovers, resolves, validates, and commits new SHA values with cryptographically signed commits.

The Claude Plugins Community repository manages dozens of external plugins contributed by third-party developers. Each plugin entry in marketplace.json pins a specific commit SHA for reproducibility and security. Keeping these pins current—without manual intervention—requires a sophisticated automation pipeline. This article dissects the four-phase workflow implemented in the bump-plugin-shas action, with direct references to the source code in anthropics/claude-plugins-community.

The Four-Phase SHA Update Process

The bump.sh script at .github/actions/bump-plugin-shas/scripts/bump.sh orchestrates the entire pipeline. The workflow triggers on a schedule (typically nightly) and processes every external plugin entry in sequence.

Phase 1: Discovery and Eligibility Filtering

The script iterates through marketplace.json and identifies which plugins need SHA updates. Not every entry qualifies.

At lines 67–84 in bump.sh, a while loop reads each marketplace entry and applies exclusion filters:

  • sha-exempt — Entries marked with this flag skip SHA bumps entirely (lines 86–92)
  • freeze-shas — Pinned entries that must remain at their current SHA (lines 94–101)
  • ALLOWED_HOSTS allow-list — Only trusted Git hosts (typically github.com) are permitted (lines 19–22)

Eligible entries proceed to resolution; skipped entries are logged for audit purposes.

Phase 2: Resolving the Target SHA

For each eligible plugin, the script determines what the new SHA should be. Two resolution modes exist:

HEAD tracking (default):

new_sha="$(git ls-remote -- "$full_url" HEAD 2>/dev/null \
          | awk '{print $1}' | head -1 || true)"

This executes at lines 29–31, fetching the current HEAD commit from the remote repository.

Releases-only tracking (opt-in): Some plugins should only track stable releases, not arbitrary HEAD commits. If the plugin appears in the tracking-config.json "releases-only" list, the script:

  1. Queries the GitHub API for the latest release tag (lines 34–42)
  2. Resolves that tag to its underlying commit SHA (lines 43–61)
ro_resp="$(gh api "repos/$owner_repo/releases/latest" 2>/dev/null)"
latest_tag="$(jq -r '.tag_name // empty' <<<"$ro_resp")"
rc_resp="$(gh api "repos/$owner_repo/commits/$latest_tag" 2>/dev/null)"
new_sha="$(jq -r '.sha // empty' <<<"$rc_resp")"

Phase 3: Validation Before Commit

The new SHA is never committed blindly. At lines 71–78, the script:

  1. Clones the repository at the resolved SHA
  2. Checks out the plugin subdirectory (if specified)
  3. Runs claude plugin validate against the manifest (lines 76–80)

Validation failures abort the bump for that specific plugin while allowing others to proceed. This fail-fast isolation prevents broken plugins from entering the marketplace.

Phase 4: Commit and Pull Request Creation

The action supports two PR modes controlled by the pr-mode input:

Batch mode (pr-mode = batch):

  • Creates or resets a single branch: bump/plugin-shas (lines 70–73)
  • Generates one signed commit containing all SHA updates (lines 66–78)
  • Opens or updates a single PR with a comprehensive changelog

Per-entry mode:

  • Creates a dedicated branch per plugin: bump/<plugin-name> (lines 88–106)
  • Generates individual signed commits with messages like bump(plugin-name): abc1234 → def5678 (lines 96–114)
  • Opens separate PRs for each plugin, enabling selective review and merge

Both modes use cryptographically signed commits via the GraphQL API (lines 35–43 in bump.sh and the create_signed_commit helper at lines 65–85):

create_signed_commit() {
  local branch=$1 base=$2 msg=$3 content_file=$4
  jq -n \
    --rawfile content "$content_file" \
    --arg repo   "$GITHUB_REPOSITORY" \
    --arg branch "$branch" \
    --arg oid    "$base" \
    --arg msg    "$msg" \
    --arg path   "$MARKETPLACE_PATH" \
    '{
      query: "mutation($repo:String!,$branch:String!,$oid:GitObjectID!,$msg:String!,$path:String!,$contents:Base64String!){createCommitOnBranch(input:{branch:{repositoryNameWithOwner:$repo,branchName:$branch},message:{headline:$msg},fileChanges:{additions:[{path:$path,contents:$contents}]},expectedHeadOid:$oid}){commit{oid}}}",
      variables: {repo:$repo,branch:$branch,oid:$oid,msg:$msg,path:$path,contents:($content|@base64)}
    }' \
    | gh api graphql --input - --jq '.data.createCommitOnBranch.commit.oid'
}

PR bodies include a table of old vs. new SHAs and a link to the triggering workflow run (lines 122–128), creating a complete audit trail.

Critical Safety Mechanisms

Source Owner Verification

At lines 30–58, the script verifies that a GitHub repository's owner has not changed since the plugin was first added. This prevents "repo jacking" attacks where a deleted repository is recreated under a different owner's control. The check compares against owner-baseline.json if present.

Subtree Change Detection

For plugins specifying a subdirectory (monorepo patterns), lines 97–115 implement subtree "no-op" suppression. The script fetches the previous SHA and compares whether files in the relevant subdirectory actually changed. If only unrelated files were modified, the bump is skipped—avoiding unnecessary PR noise.

URL and Path Sanitization

Before any network operation, lines 14–27 validate:

  • URL characters against has_unsafe_chars patterns
  • Subdirectory paths for directory traversal attempts (.. components)
  • Absolute vs. relative path conventions

Key Configuration Files

File Path Purpose
action.yml .github/actions/bump-plugin-shas/action.yml Input definitions: sha-exempt, freeze-shas, tracking-config, pr-mode
bump.sh .github/actions/bump-plugin-shas/scripts/bump.sh Core orchestration logic
marketplace.json .claude-plugin/marketplace.json The mutated file containing all source.sha values
tracking-config.json .github/tracking-config.json Optional releases-only plugin list
owner-baseline.json .github/owner-baseline.json Baseline owner IDs for verification

Per-Entry PR Example

When pr-mode is unset or set to per-entry, the following pattern executes for each bumped plugin:

branch="$(branch_for "$name")"
create_or_reset_branch "$branch" "$base_sha"
commit_msg="bump($name): ${old_sha:0:8} → ${new_sha:0:8}"
new_oid=$(create_signed_commit "$branch" "$base_sha" "$commit_msg" "$entry_file")
pr_url=$(gh pr create --base "$BASE_BRANCH" --head "$branch" \
          --title "$commit_msg" --body-file "$body_file")

This creates a dedicated review surface for each change, with abbreviated SHAs in the title for quick visual scanning.

Summary

  • Automated discovery scans marketplace.json nightly, filtering by sha-exempt, freeze-shas, and host allow-lists
  • Dual resolution modes support HEAD tracking for bleeding-edge plugins and releases-only tracking for stability-focused ones
  • Mandatory validation via claude plugin validate ensures only working plugins receive SHA updates
  • Cryptographically signed commits via GraphQL createCommitOnBranch satisfy branch protection policies
  • Flexible PR strategies offer batch consolidation or per-plugin isolation based on organizational needs
  • Defense in depth includes owner verification, subtree change detection, and URL/path sanitization

The workflow ensures that external plugin SHAs in anthropics/claude-plugins-community remain forward-only, validated, and fully auditable without manual maintainer intervention.

Frequently Asked Questions

How often do plugin SHAs get updated?

The bump-plugin-shas workflow runs on a scheduled trigger (typically daily at a quiet UTC hour). Maintainers can also trigger it manually via workflow_dispatch for urgent updates. The schedule is defined in the repository's GitHub Actions workflow file, not in the action itself.

What happens if a plugin fails validation?

Validation failures are isolated per plugin. The bump.sh script logs the failure, skips that specific entry, and continues processing remaining plugins. The failed plugin's SHA remains unchanged in marketplace.json. Audit logs in the workflow run capture the validation error for maintainer review.

Can plugin authors opt out of automatic SHA bumps?

Yes, through two mechanisms: adding the plugin name to the sha-exempt list (for unpinned entries) or the freeze-shas list (for pinned entries that must stay fixed). Both are configured via inputs to the bump-plugin-shas action in the workflow file. Authors should open a PR to modify these lists with justification.

Why are commits signed via GraphQL instead of standard git commands?

Standard git commit operations in GitHub Actions run as the GITHUB_TOKEN actor, which cannot produce verified signatures on protected branches. The GraphQL createCommitOnBranch mutation uses GitHub's internal signing infrastructure, producing commits that display the verified badge and satisfy "require signed commits" branch protection rules.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →