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

> Learn how plugin SHAs update automatically for external plugins in the Claude Plugins Community. Discover the automated GitHub Action that handles SHA discovery, validation, and commits.

- Repository: [Anthropic/claude-plugins-community](https://github.com/anthropics/claude-plugins-community)
- Tags: how-to-guide
- Published: 2026-08-31

---

**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`](https://github.com/anthropics/claude-plugins-community/blob/main/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`](https://github.com/anthropics/claude-plugins-community/blob/main/bump.sh)** script at [`.github/actions/bump-plugin-shas/scripts/bump.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/.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`](https://github.com/anthropics/claude-plugins-community/blob/main/marketplace.json) and identifies which plugins need SHA updates. Not every entry qualifies.

At **lines 67–84** in [`bump.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/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):

```bash
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`](https://github.com/anthropics/claude-plugins-community/blob/main/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)

```bash
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`](https://github.com/anthropics/claude-plugins-community/blob/main/bump.sh) and the `create_signed_commit` helper at lines 65–85):

```bash
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`](https://github.com/anthropics/claude-plugins-community/blob/main/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`](https://github.com/anthropics/claude-plugins-community/blob/main/action.yml)** | [`.github/actions/bump-plugin-shas/action.yml`](https://github.com/anthropics/claude-plugins-community/blob/main/.github/actions/bump-plugin-shas/action.yml) | Input definitions: `sha-exempt`, `freeze-shas`, `tracking-config`, `pr-mode` |
| **[`bump.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/bump.sh)** | [`.github/actions/bump-plugin-shas/scripts/bump.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/.github/actions/bump-plugin-shas/scripts/bump.sh) | Core orchestration logic |
| **[`marketplace.json`](https://github.com/anthropics/claude-plugins-community/blob/main/marketplace.json)** | [`.claude-plugin/marketplace.json`](https://github.com/anthropics/claude-plugins-community/blob/main/.claude-plugin/marketplace.json) | The mutated file containing all `source.sha` values |
| **[`tracking-config.json`](https://github.com/anthropics/claude-plugins-community/blob/main/tracking-config.json)** | [`.github/tracking-config.json`](https://github.com/anthropics/claude-plugins-community/blob/main/.github/tracking-config.json) | Optional releases-only plugin list |
| **[`owner-baseline.json`](https://github.com/anthropics/claude-plugins-community/blob/main/owner-baseline.json)** | [`.github/owner-baseline.json`](https://github.com/anthropics/claude-plugins-community/blob/main/.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:

```bash
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`](https://github.com/anthropics/claude-plugins-community/blob/main/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`](https://github.com/anthropics/claude-plugins-community/blob/main/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`](https://github.com/anthropics/claude-plugins-community/blob/main/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.