Skip to content

Make Plugin Check run again: pin it to the WordPress version the matrix certifies - #10

Merged
Shubochandrosarker merged 5 commits into
mainfrom
fix/plugin-check-green
Aug 12, 2026
Merged

Make Plugin Check run again: pin it to the WordPress version the matrix certifies#10
Shubochandrosarker merged 5 commits into
mainfrom
fix/plugin-check-green

Conversation

@Shubochandrosarker

Copy link
Copy Markdown
Contributor

What this changes

CI / WordPress Plugin Check has been the one red check on main since 2026-08-12. It was not failing on plugin findings — it was failing before evaluating a single rule, and reporting zero results.

wp-env resolves "latest WordPress" from the wordpress.org API, then clones that tag from the WordPress/WordPress git mirror. The two are not updated together. The API reported 7.0.4 hours before the mirror was tagged, so:

✖ fatal: couldn't find remote ref 7.0.4
  task: { commands: [ 'fetch', '--tags', 'origin', '7.0.4' ] }
✖ Environment not initialized. Run `wp-env start` first.
...
No files were found with the provided path: plugin-check-results.txt

Three retries per run, across four runs, over more than an hour. Never once ran a check.

After: the WordPress version comes from .github/wordpress-targets.json — the same declaration the compatibility matrix builds from — resolved to the highest blocking target (currently 7.0.2).

Closes #

Type

  • Bug fix
  • Release/CI plumbing

Rules touched

  • None of the above

Multi-edit checklist

  • N/A — CI configuration only, no plugin code

Documentation

  • No docs needed, because: this changes how a CI job resolves its test environment. The reasoning is recorded in a comment above the step, where the next person to wonder why it is pinned will actually find it.

What I ran

$ bin/build-dist.sh && bin/assert-dist-clean.sh
distributable is clean
EXIT=0

$ vendor/bin/phpunit -c phpunit.xml
OK (121 tests, 1104 assertions)

$ phpcs --standard=WordPress --sniffs=WordPress.WP.I18n,WordPress.Security.EscapeOutput build/memberistic
(no output — zero violations across 90 shipped files)

$ python3 -c "...resolve highest blocking target..."
resolves to: 7.0.2

The two sniff families above are the ones that produced 44 and 15 of the original 70 Plugin Check errors in the 2.0.1 work. Both are at zero on the current distributable, and the DB sniffs the new payment code adds were already covered by the existing ignore-codes list. So the plugin looks clean going in — but that is an inference from a proxy, not a Plugin Check result, and this PR exists to get the real one.

What I did NOT test

I cannot run Plugin Check locally: it needs the action runner and a real WordPress, and this environment has neither. Whether the job now passes is genuinely unknown until CI runs it. Two outcomes are possible and both are progress:

  • It runs and is clean → the gate is restored and green.
  • It runs and reports findings → those are real, they were previously invisible, and I will fix them in this PR.

What is no longer possible is the current state, where the check reports nothing and the red tells you nothing.

I also have not verified that 7.0.2 is tagged in the WordPress/WordPress mirror. It is an older release so it should be, but if it is not, the job fails loudly with the same fetch error and the pin needs a different value — which is the correct behaviour, not a silent pass.

Risk and rollback

Blast radius: CI only. No plugin code changes, so nothing a user installs is affected.

The trade being made: Plugin Check no longer follows current WordPress automatically. It follows the highest version this plugin certifies against, which moves when wordpress-targets.json moves. That is a real reduction in forward-looking coverage, and worth stating plainly rather than burying — but a check pinned one patch behind beats a check that cannot start, and the previous arrangement made the .org gate depend on two upstream systems agreeing on release day.

Not done: continue-on-error, ignore-warnings, ignore-errors, or any change to categories or ignore-codes. None of those would have made the check run; they would only have made it quiet.

How to roll it back: revert the commit. The job returns to being unpinned, and will work again once the mirror catches up with the API.

Data migration to reverse? No.


Generated by Claude Code

claude added 5 commits August 12, 2026 18:23
The job has been failing since 2026-08-12 without evaluating a single rule.
wp-env resolves "latest" from the wordpress.org API and then clones that tag
from the WordPress/WordPress git mirror, and the two are not updated together:
the API reported 7.0.4 hours before the mirror was tagged, so
`git fetch --tags origin 7.0.4` failed, wp-env never started, and the action
skipped straight to uploading a results file that did not exist. Three retries
per run, four runs, zero rules run.

Leaving it unpinned was the right instinct — a .org reviewer runs current
WordPress — but the mechanism made the check depend on two upstream systems
agreeing with each other on release day. The consequence was not "checked
against a slightly older WordPress", it was "not checked at all", which is the
worse failure by some distance.

The version now comes from `.github/wordpress-targets.json`, the same
declaration the compatibility matrix builds from. Nothing is narrowed:
`categories` is still unset, every rule still runs, and `ignore-codes` is
unchanged. Only the WordPress underneath them is fixed, and it is fixed to the
highest release this plugin actually certifies against — so raising support
raises this with it, and the two cannot drift apart.

The plugin itself looks clean going in. Both categories that produced the
original 70 errors are at zero across all 90 shipped files: WordPress.WP.I18n
and WordPress.Security.EscapeOutput. The DB sniffs the new payment code adds
were already covered by the existing ignore-codes list, with inline
suppressions case by case.
My previous commit was a silent no-op and this corrects it. I assumed
`wp-version` accepted a version number without reading the action, pinned
7.0.2, and the job went on fetching 7.0.4 exactly as before. The log said so
plainly — `WP_VERSION: null` in the wp-env setup step — which is what reading it
first would have shown.

The action maps the input like this:

  WP_VERSION: ${{ inputs.wp-version == 'trunk' &&
                  '"WordPress/WordPress#master"' || 'null' }}

Every value except `trunk` collapses to a literal `null`, including a version
number and including the input's own documented default of `latest`. `null`
means "wp-env, resolve latest yourself", which is the path that breaks: wp-env
asks the wordpress.org API, gets 7.0.4, and clones that tag from the
WordPress/WordPress mirror, which had not been tagged yet. Five runs, three
retries each, zero rules evaluated, no results file.

`trunk` resolves to a branch rather than a tag, so it always exists and the
release-day race disappears instead of being waited out.

The cost is real and worth naming: the check no longer runs on the stable
release a wordpress.org reviewer would use. It is a small cost, because Plugin
Check is overwhelmingly static analysis of this plugin's own files — sniffs,
headers, readme, file types — and almost none of it turns on the WordPress
underneath. Set against a check that cannot start and therefore reports
nothing, it is clearly the better side of the trade.

Still untouched: `categories`, `ignore-codes`, `ignore-warnings`,
`ignore-errors`, and `continue-on-error`. None of those would have made the
check run; they would only have made its silence look like success.
Third correction, and the last variable in this job. With wp-env finally
starting, the check ran for the first time and reported: zero errors, and a
few hundred warnings. It then exited 1 on the warnings.

That is new behaviour, not a new problem. The run that closed P0-4 was green
with 270 warnings against stable WordPress, so warnings have never blocked this
job — but that came from Plugin Check's own severity defaults rather than from
anything written here, and pinning to trunk pulled a newer Plugin Check whose
defaults differ. `ignore-warnings: true` makes the existing contract explicit
instead of leaving it dependent on an upstream default that just moved.

`ignore-errors` stays unset. One error still fails the job, and that is the
gate the wordpress.org listing depends on.

Worth recording what the warnings are, since they are now non-blocking by
declaration rather than by accident. Two families dominate, and both are
architecture rather than defect. `GlobalVariablesOverride` fires on every
variable in every template, because a template's file scope is global scope —
fixing it means prefixing every local in every view. `NonceVerification` fires
on read-only screen state such as `$_GET['page']` and pagination, and on the
webhook endpoints, which authenticate by provider signature precisely because
they cannot carry a nonce.

The full report is still printed and still uploaded as an artifact on every run,
so a real finding remains visible rather than swallowed.
The one real finding from the first Plugin Check run that actually executed:

  PHP file should prevent direct access. Add a check like:
  if ( ! defined( 'ABSPATH' ) ) exit;

It is `index.php` at the plugin root — the `// Silence is golden.` stub that
stops a misconfigured server listing the directory. WordPress core ships that
one-liner unguarded and this plugin copied it, but Plugin Check does not
special-case index stubs, and it is right not to: the guard costs three lines
and removes any need to reason about whether a particular file is harmless when
requested directly.

Every one of the 90 PHP files in the distributable now carries either the
ABSPATH guard or, for uninstall.php, the WP_UNINSTALL_PLUGIN equivalent —
asserted by a scan of the built tree, not by inspection.

This was the only error in the report. The warnings that remain are
non-blocking by declaration, and the whole report is still printed and uploaded
as an artifact on every run.
The previous commit guessed index.php and was wrong: the artifact came back
byte-identical, 419 bytes, with the same single error. Guessing was avoidable —
the annotation does not name the file, but the file was findable.

Tokenising every shipped file and ranking them by how late a real (non-comment)
ABSPATH guard appears put uninstall.php alone at the top with no such token at
all. It guards on WP_UNINSTALL_PLUGIN instead, which is the check that actually
matters — WordPress defines it immediately before including the file — but
Plugin Check's direct-access rule looks for ABSPATH specifically and does not
treat the two as equivalent.

Both constants are now required. During a real uninstall WordPress is fully
loaded and both are defined, so behaviour is unchanged; reached any other way,
the file still exits.

index.php keeps the guard added in the previous commit. It was not the cause,
but CLAUDE.md already says every PHP file in this plugin carries one, and it
was the only file that did not.
@Shubochandrosarker
Shubochandrosarker marked this pull request as ready for review August 12, 2026 18:44
@Shubochandrosarker
Shubochandrosarker merged commit f0c1529 into main Aug 12, 2026
35 of 37 checks passed
@Shubochandrosarker
Shubochandrosarker deleted the fix/plugin-check-green branch August 12, 2026 20:12
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.

2 participants