How the validate-plugins GitHub Action Works: Claude Plugin Validation Pipeline
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 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 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 inmarketplace.jsonor per-file entry directorieschanged-external– External plugin source objects that reference outside repositorieschanged-folders– In-repo plugin directories containing a.claude-plugin/plugin.jsonmanifest
The script assembles a temporary copy of 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:
20-validate-cli-marketplace.sh– Validates the assembledmarketplace.jsonagainst the official Claude plugin JSON schema30-validate-cli-external.sh– Clones each changed external repository (respecting theallowed-hostsinput), synthesizes the manifest, and runs CLI validation with a configurableexternal-timeout-secsper plugin40-validate-cli-local.sh– Runs the CLI validator against each changed local plugin folder containing.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 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, 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:
# 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:
# .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:
- 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.shscript scopes validation to modified entries, external sources, and local plugin folders by diffing against the base ref - Invariant Checks: The
11-validate-invariants.shscript enforces repository policies (I1-I11) including alphabetical ordering and SHA requirements with configurable severity - CLI Validation: Three specialized scripts (
20-validate-cli-marketplace.sh,30-validate-cli-external.sh,40-validate-cli-local.sh) validate schema compliance using the official Claude CLI - Reporting: The
90-report.shscript aggregates results into a markdown report and sets the composite action outputs (resultandreport-path) - Flexibility: Inputs like
skip-external,sha-exempt, andscope-errors-to-changedallow 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, the 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 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.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →