Skip to content

ci: gate PRs on scripts/quality.sh - #5

Closed
pluginslab wants to merge 1 commit into
fix/hook-portabilityfrom
ci/quality-gate
Closed

pluginslab wants to merge 1 commit into
fix/hook-portabilityfrom
ci/quality-gate

Conversation

@pluginslab

Copy link
Copy Markdown
Owner

Why

The kit ships a pre-commit hook, a quality script, and a test suite for its own hooks — and none of it ran on a pull request. scripts/quality.sh even carries a header comment claiming CI as a caller:

# - CI (GitHub Actions, etc.)

There was no GitHub Actions. A gate that only fires on the machine of whoever happens to be committing is a convention, not a gate — and this repo's entire pitch is the difference between those two things.

What

One workflow, quality on push: main and every pull_request. It calls ./scripts/quality.sh rather than re-listing the checks, so there is a single definition of "quality" shared by the hook, a local run, and the runner. A check that exists only in CI is a check that surprises you on a PR.

Three decisions worth a look:

Pinned to PHP 8.2. That's what the plugin template's header declares (Requires PHP: 8.2). Gate on the version you claim to support, not on whatever the runner ships. Same reasoning playground-verifier applies when it boots at declared minimums instead of latest. Happy to add a matrix if you'd rather also catch forward-compat breaks — I left it single-version because a green matrix that flakes on a phpunit/PHP-8.4 interaction is worse than an honest single check.

No composer.lock is committed, so the install resolves fresh. That's deliberate rather than an oversight to fix: it means CI catches an upstream release breaking the scaffold before a user hits it on composer install after npm create wp-ai-plugin. The tradeoff is non-hermetic builds — an upstream break turns CI red on an unrelated PR. I think that's the right trade for a scaffold whose job is to work on someone else's fresh machine, but it's a judgement call and reversible.

Read-only permissions, and concurrency cancels superseded runs.

What this deliberately cannot do

Boot WordPress. playground-verifier is the kit's runtime gate and it needs an agent harness plus the wp-playground MCP server, neither of which exists in a GitHub runner. The workflow says so in a comment, because the failure mode here is someone seeing a green check and assuming the plugin runs. It doesn't prove that. v1.0.2 shipped a scaffold that fataled on activation through checks exactly this green.

Static analysis is the floor, not the ceiling.

Verification

Simulated the runner locally rather than pushing and hoping: git archive HEAD into a clean tree with no vendor/, then composer install --prefer-dist --no-progress --no-interaction, then ./scripts/quality.sh. Green, 56 assertions (this branch is off main; #3 takes that to 99 and #4 to 109).

YAML validated.

Note

.github/ is in the CLI's REMOVE_AFTER_EXTRACT list, so this workflow does not ship into scaffolded plugins — it gates the kit only. Whether generated plugins should get a CI workflow of their own is a real question and a bigger one; not touching it here.

Branch protection has to be set in repo settings; I haven't touched those. Once this merges, quality.sh is available as a required check on main.

🤖 Generated with Claude Code

The kit ships a pre-commit hook, a quality script, and a 56-case test
suite for its own hooks — and none of it ran on a pull request. A gate
that only fires on the machine of whoever happens to be committing is a
convention, not a gate.

CI calls scripts/quality.sh rather than re-listing the checks, so there
is one definition of "quality" shared by the hook, a local run, and the
runner. A check that exists only in CI is a check that surprises you on
a PR.

Pinned to PHP 8.2 because that is what the plugin template's header
declares. Same reasoning playground-verifier applies when it boots at the
declared minimum instead of at latest: gate on the version you claim to
support, not on whatever the runner happens to ship.

No composer.lock is committed, so the install resolves fresh. That is
deliberate — it catches an upstream release breaking the scaffold before
a user hits it on `composer install` after `npm create wp-ai-plugin`.

Verified by simulating the run locally: `git archive HEAD` into a clean
tree with no vendor/, then composer install + quality.sh. Green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@pluginslab
pluginslab changed the base branch from main to fix/hook-portability September 16, 2026 21:23
@pluginslab
pluginslab deleted the branch fix/hook-portability September 21, 2026 16:35
@pluginslab pluginslab closed this Sep 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant