# How the validate-plugins GitHub Action Works: Claude Plugin Validation Pipeline

> Discover how the validate-plugins GitHub Action uses a three-stage pipeline to ensure Claude plugins meet marketplace standards before merge.

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

---

**The validate-plugins GitHub Action is a composite action that enforces a three-stage validation pipeline—change detection, invariant enforcement, and CLI-based schema validation—to ensure every Claude plugin meets marketplace standards before merge.**

The `validate-plugins` action lives in the `anthropics/claude-plugins-community` repository and serves as the gatekeeper for the Claude plugin marketplace. It triggers on pull requests, pushes to main, and manual workflow dispatches to verify that [`marketplace.json`](https://github.com/anthropics/claude-plugins-community/blob/main/marketplace.json) entries and plugin definitions comply with schema requirements, security policies, and repository-specific invariants.

## Three-Stage Validation Pipeline

The action orchestrates three distinct phases of validation through modular bash scripts located in `.github/actions/validate-plugins/scripts/`.

### Change Detection with 00-detect-changes.sh

The pipeline begins by computing scope. The [`00-detect-changes.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/00-detect-changes.sh) script diffs the current HEAD against a base reference (either the PR base branch or the previous commit) to identify three categories of modifications:

- **`changed-entries`** – Marketplace entries modified in [`marketplace.json`](https://github.com/anthropics/claude-plugins-community/blob/main/marketplace.json) or per-file entry directories
- **`changed-external`** – External plugin source objects that reference outside repositories
- **`changed-folders`** – In-repo plugin directories containing a [`.claude-plugin/plugin.json`](https://github.com/anthropics/claude-plugins-community/blob/main/.claude-plugin/plugin.json) manifest

The script assembles a temporary copy of [`marketplace.json`](https://github.com/anthropics/claude-plugins-community/blob/main/marketplace.json) (either direct or aggregated from per-file entries) and persists the three JSON arrays to `$VALIDATE_TMP/changes.json` for downstream consumption.

### Invariant Enforcement with 11-validate-invariants.sh

Before invoking external tools, the action runs custom policy invariants (I1 through I11) against the gathered marketplace data. These bash-based checks validate:

- Alphabetical ordering of entries
- Duplicate name detection
- SHA presence and validity
- JSON auxiliary file integrity

Configuration inputs control severity: `warn-invariants` converts failures to warnings, `sha-exempt` exempts specific plugins from SHA checks, and `scope-errors-to-changed` restricts error reporting to only the entries modified in the current PR. The script emits results via GitHub Actions logging commands (`::error::` and `::warning::`).

### CLI-Based Validation Steps

The final phase installs the `@anthropic-ai/claude-code` CLI with robust retry logic (up to three attempts with npm cache cleaning) and executes three sequential validation scripts:

1. **[`20-validate-cli-marketplace.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/20-validate-cli-marketplace.sh)** – Validates the assembled [`marketplace.json`](https://github.com/anthropics/claude-plugins-community/blob/main/marketplace.json) against the official Claude plugin JSON schema
2. **[`30-validate-cli-external.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/30-validate-cli-external.sh)** – Clones each changed external repository (respecting the `allowed-hosts` input), synthesizes the manifest, and runs CLI validation with a configurable `external-timeout-secs` per plugin
3. **[`40-validate-cli-local.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/40-validate-cli-local.sh)** – Runs the CLI validator against each changed local plugin folder containing [`.claude-plugin/plugin.json`](https://github.com/anthropics/claude-plugins-community/blob/main/.claude-plugin/plugin.json)

Inputs `skip-external` and `skip-local-folders` selectively disable expensive or irrelevant validation steps when set to `"true"`.

## Composite Action Architecture

The validate-plugins GitHub Action is defined in [`.github/actions/validate-plugins/action.yml`](https://github.com/anthropics/claude-plugins-community/blob/main/.github/actions/validate-plugins/action.yml) as a composite action that exports five outputs: `changed-entries`, `changed-external`, `changed-folders`, `result` (pass/fail), and `report-path`.

### Temporary Workspace Management

All intermediate files live in `$VALIDATE_TMP`, created under the runner’s temporary directory. This workspace houses the assembled [`marketplace.json`](https://github.com/anthropics/claude-plugins-community/blob/main/marketplace.json), change detection results, and per-step status files consumed by the final reporter.

### Robust CLI Installation

The action implements defensive installation logic for the Claude CLI:

```bash

# Simplified representation of the retry logic

for i in {1..3}; do
  npm install -g @anthropic-ai/claude-code --force && break
  npm cache clean --force
  sleep 5
done

```

Network operations are capped with `timeout` to prevent hung jobs from consuming runner minutes indefinitely.

## Implementation Examples

### Basic Workflow Integration

Trigger the action on pull requests affecting plugin definitions:

```yaml

# .github/workflows/validate-plugins.yml

name: Validate Plugins
on:
  pull_request:
    paths:
      - '.claude-plugin/**'
      - '.github/actions/**'
  push:
    branches: [main]
    paths:
      - '.claude-plugin/**'
      - '.github/actions/**'
  workflow_dispatch:

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: ./.github/actions/validate-plugins
        with:
          marketplace-path: .claude-plugin/marketplace.json
          scope-errors-to-changed: "true"

```

### Advanced Configuration

Customize validation strictness and external host policies:

```yaml
- uses: anthropics/claude-plugins-community/.github/actions/validate-plugins@main
  with:
    marketplace-path: .claude-plugin/marketplace.json
    fail-on-warnings: "true"
    sha-exempt: "legacy-plugin beta-plugin"
    allowed-hosts: "github.com gitlab.com bitbucket.org"
    external-timeout-secs: "120"
    skip-local-folders: "false"

```

## Summary

- **Change Detection**: The [`00-detect-changes.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/00-detect-changes.sh) script scopes validation to modified entries, external sources, and local plugin folders by diffing against the base ref
- **Invariant Checks**: The [`11-validate-invariants.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/11-validate-invariants.sh) script enforces repository policies (I1-I11) including alphabetical ordering and SHA requirements with configurable severity
- **CLI Validation**: Three specialized scripts ([`20-validate-cli-marketplace.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/20-validate-cli-marketplace.sh), [`30-validate-cli-external.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/30-validate-cli-external.sh), [`40-validate-cli-local.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/40-validate-cli-local.sh)) validate schema compliance using the official Claude CLI
- **Reporting**: The [`90-report.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/90-report.sh) script aggregates results into a markdown report and sets the composite action outputs (`result` and `report-path`)
- **Flexibility**: Inputs like `skip-external`, `sha-exempt`, and `scope-errors-to-changed` allow downstream repositories to customize the pipeline without modifying the action source

## Frequently Asked Questions

### What triggers the validate-plugins GitHub Action?

The action triggers on pull requests and pushes to the main branch when paths match `.claude-plugin/**` or `.github/actions/**`, plus manual runs via `workflow_dispatch`. This ensures validation runs only when plugin metadata or the validation logic itself changes.

### How does the action handle external plugin repositories?

For external plugins listed in [`marketplace.json`](https://github.com/anthropics/claude-plugins-community/blob/main/marketplace.json), the [`30-validate-cli-external.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/30-validate-cli-external.sh) script clones each repository, checks out the specified SHA (unless exempted), and runs the CLI validator. The `allowed-hosts` input restricts which git hosts the action will fetch from, preventing arbitrary code execution from untrusted domains.

### What are the invariants I1-I11?

The invariants are custom policy checks implemented in [`11-validate-invariants.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/11-validate-invariants.sh) that enforce repository-specific rules such as alphabetical ordering of entries, absence of duplicate plugin names, required SHA fields for external sources, and valid JSON structure for auxiliary files. Many can be downgraded to warnings via the `warn-invariants` input.

### Can I skip certain validation steps?

Yes. Set `skip-external: "true"` to bypass validation of external repositories, or `skip-local-folders: "true"` to skip in-repo plugin folder checks. These flags are useful when iterating on marketplace metadata without triggering expensive external clones or when local plugins are tested through separate CI pipelines.