How the `validate-plugins` GitHub Action Works in the Claude Plugins Community

The validate-plugins GitHub Action automatically validates plugin manifests, enforces policy invariants, and tests both internal and external plugins on every pull request or push that touches plugin-related files in the anthropics/claude-plugins-community repository.

This composite action serves as the gatekeeper for the Claude plugin ecosystem, ensuring that every submission meets strict quality and security standards before merging. Built with resilience and selective validation in mind, it leverages the authoritative Claude CLI as its schema validator while implementing custom invariant checks for community-specific policies.

Architecture Overview

The validation system consists of three interconnected layers that work together to provide comprehensive plugin verification.

Component Role Source
Workflow definition Triggers the pipeline on PRs, pushes, and manual dispatches .github/workflows/validate-plugins.yml
Composite action Packages all validation logic into a reusable, configurable step .github/actions/validate-plugins/action.yml
Helper scripts Bash implementations of each validation stage .github/actions/validate-plugins/scripts/*.sh

Workflow Triggers

The workflow defined in .github/workflows/validate-plugins.yml activates on three conditions:

  • Pull requests modifying .claude-plugin/** or .github/actions/**
  • Direct pushes to main affecting those same paths
  • Manual dispatch via workflow_dispatch (used by automated SHA-bumping jobs)

The checkout step uses fetch-depth: 0 to obtain complete Git history, enabling accurate change detection across commits.

Pre-Composite Static Tests

Before invoking the composite action, the workflow runs several lightweight validation scripts:

Script Purpose
test-invariants.sh Repository-level constraints (required files, naming conventions)
test-bump.sh / test-bump-manifest.sh Verify automated SHA-bumping integrity
test-sweep.sh Confirm plugin owners remain reachable
test-external-manifest.sh Validate external plugin references resolve correctly
test-pin-check.sh Ensure pinned versions match expected golden vectors

Composite Action Execution Flow

The core validation logic resides in .github/actions/validate-plugins/action.yml. When invoked, it executes eight sequential stages:

1. Environment Setup

Creates temporary directories, installs jq, configures Node 20, and makes all helper scripts executable.

2. CLI Installation with Resilience

Installs @anthropic-ai/claude-code with robust retry logic and post-install healing to guarantee the claude binary is functional, even when network conditions are unstable.

3. Change Detection (00-detect-changes.sh)

Builds three JSON arrays that drive selective validation:

{
  "changed-entries": ["plugin-name-1", "plugin-name-2"],
  "changed-external": [{"name": "...", "source": "...", "strict": true}],
  "changed-folders": [".claude-plugin/plugins/plugin-name-1"]
}

These arrays enable scoped validation—only testing what actually changed rather than the entire marketplace.

4. Custom Invariant Checking (11-validate-invariants.sh)

Implements eleven policy rules (I1–I11) including:

  • I1: Alphabetical ordering of entries
  • I3: Required field presence
  • I5: SHA pinning requirements (with exemptions via sha-exempt)
  • I8: Naming convention enforcement

Violations can be downgraded to warnings via the warn-invariants input.

5. Marketplace CLI Validation (20-validate-cli-marketplace.sh)

Runs claude plugin validate against the assembled marketplace.json, using the official Claude CLI as the source-of-truth schema validator.

6. External Plugin Validation (30-validate-cli-external.sh)

For each changed external entry (or all entries if validate-all-external: true):

  1. Clones the remote repository
  2. Validates against the CLI schema
  3. Respects per-plugin timeout (external-timeout-secs, default 120 seconds)
  4. Enforces host whitelist (allowed-hosts: github.com gitlab.com bitbucket.org)

7. Local Folder Validation (40-validate-cli-local.sh)

Validates in-repo plugin folders using the same CLI schema, plus 41-validate-aux-files.sh for auxiliary JSON parsing.

8. Report Generation (90-report.sh)

Aggregates all warnings and errors into a markdown report, then sets the composite outputs:

  • result: "pass" or "fail"
  • report-path: location of the detailed report

Key Configuration Options

Inputs

Input Description Default
marketplace-path Path to marketplace.json .claude-plugin/marketplace.json
base-ref Git ref for diff detection PR base sha, github.event.before, or origin/main
warn-invariants Invariants treated as warnings I1 I3 I5 I8
scope-errors-to-changed Downgrade errors on unchanged entries false
skip-external / skip-local-folders Disable validation categories false
fail-on-warnings Treat warnings as failures false
validate-all-external Validate all externals, not just changed false
claude-cli-version CLI version to install latest
external-timeout-secs Per-external-plugin timeout 120
allowed-hosts Whitelisted git hosts github.com gitlab.com bitbucket.org

Outputs

Output Description
changed-entries JSON array of modified marketplace entry names
changed-external JSON array of modified external plugin metadata
changed-folders JSON array of modified in-repo folder paths
result Overall validation status (pass/fail)
report-path Filesystem path to generated markdown report

Practical Usage Example

A typical repository using this action would include:


# .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

      # Static pre-checks

      - run: ./.github/actions/validate-plugins/test-invariants.sh

      # Main validation

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

      - name: Upload report
        uses: actions/upload-artifact@v4
        with:
          name: validation-report
          path: ${{ steps.validation.outputs.report-path }}

When a contributor adds a new plugin folder under .claude-plugin/plugins/my-new-plugin, the action automatically:

  1. Detects the folder change via Git diff
  2. Enforces invariants (naming, ordering, SHA presence)
  3. Validates the plugin manifest against the official Claude CLI schema
  4. Blocks merge if any errors remain

Key Source Files

File Purpose
.github/workflows/validate-plugins.yml Workflow orchestration
.github/actions/validate-plugins/action.yml Composite action definition
.github/actions/validate-plugins/scripts/00-detect-changes.sh Git change detection
.github/actions/validate-plugins/scripts/11-validate-invariants.sh Policy invariant implementation
.github/actions/validate-plugins/scripts/20-validate-cli-marketplace.sh Marketplace CLI validation
.github/actions/validate-plugins/scripts/30-validate-cli-external.sh External plugin cloning and validation
.github/actions/validate-plugins/scripts/40-validate-cli-local.sh In-repo plugin validation
.github/actions/validate-plugins/scripts/90-report.sh Report generation and result synthesis

Summary

  • The validate-plugins action is a composite GitHub Action that consolidates eleven distinct validation stages into a single reusable step
  • It implements selective validation through Git-aware change detection, reducing CI time by testing only modified plugins
  • Custom invariants (I1–I11) enforce community-specific policies beyond the base CLI schema
  • Resilient CLI installation ensures the authoritative Claude validator is always available, even under adverse network conditions
  • Configurable severity allows maintainers to tune invariant enforcement via warn-invariants and fail-on-warnings inputs
  • Security controls include host whitelisting, SHA pinning requirements, and timeout-gated external cloning

Frequently Asked Questions

What triggers the validate-plugins workflow to run?

The workflow triggers on pull requests or pushes that modify files under .claude-plugin/** or .github/actions/**, plus any manual workflow_dispatch event. This path-filtering prevents unnecessary validation runs on unrelated changes.

Why does the action use fetch-depth: 0 during checkout?

Full Git history is required for accurate change detection in 00-detect-changes.sh. Without complete history, the script cannot reliably determine which marketplace entries, external plugins, or local folders changed between the base ref and HEAD.

Can I validate external plugins from hosts other than GitHub?

Yes, but you must update the allowed-hosts input. The default whitelist includes github.com, gitlab.com, and bitbucket.org. Adding custom hosts requires explicit configuration for security—arbitrary external repositories cannot be validated without approval.

What happens if the Claude CLI installation fails?

The action implements retry logic with exponential backoff and post-install healing steps. If the @anthropic-ai/claude-code package installation encounters network issues, it automatically retries before failing the step, ensuring transient errors don't block legitimate plugin submissions.

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 →