Skip to content

Add corpus regeneration diff CI - #284

Open
sirreal wants to merge 11 commits into
masterfrom
add-corpus-diff-ci
Open

Add corpus regeneration diff CI#284
sirreal wants to merge 11 commits into
masterfrom
add-corpus-diff-ci

Conversation

@sirreal

@sirreal sirreal commented Aug 18, 2026

Copy link
Copy Markdown
Member

Add a CI check that diffs the parser's output over a fixed corpus of WordPress source, so every PR shows exactly what it changes.

What it does

  • Parses the PHP of WordPress 7.0.4 (whole release, 1880 files) with the parser at the base branch and with the PR merged into it, and diffs the two JSON exports.
  • Posts one comment on the PR, updated on every push: hunk and line counts, the diff in a collapsed block, and a link to the corpus.diff artifact. Oversize diffs show the first hunks that fit and say so.
  • Same report goes to the job summary.

Why

Decisions

  • Non-blocking. A non-empty diff is a review artifact, not a failure; it may be the point of the PR. Make it blocking once the signal has earned it.
  • Raw exports are diffed, not prep-diff.php output. Both sides share the corpus, the PHP binary, and the exporter, so output is already deterministic. prep-diff.php zeroes line numbers, strips namespace prefixes, and sorts collections: on real PRs that relocated changed records and would hide a line-number regression. It stays for cross-environment comparisons.
  • The head checkout's tools/export-corpus.php exports both sides, so a tooling change never reads as a parser change.
  • Pinned to 7.0.4. 7.1 is out; the pin stays so Keep the pinned WordPress corpus current automatically #286 can be validated against a real bump after this lands.
  • PHP 8.4. The version is not part of the comparison.
  • Fork and Dependabot PRs get a read-only token and receive the artifact and summary only. A workflow_run relay for them is a follow-up.

Whole job: 45 s. Demo of a dirty diff: #289.

7.0.4 is the latest WordPress release. Pinning the point release rather
than the 7.0 major tag tracks the patches to wp-includes, so the corpus
matches what is shipped.

WordPress 7.0 raised core's minimum PHP to 7.4, which is exactly this
job's PHP floor.

Verified against wp-includes@7.0.4: 1039 PHP files, all of them present
in the export, no parse errors, ~49 MB of JSON. Both of the job's guards
(>= 500 files, >= 1 MB of JSON) still hold.
Records what the corpus-diff check is, how to run it by hand with the
repo's own tools/export-corpus.php and prep-diff.php, and the policy the
workflow already states: the head checkout's tooling drives both sides.

@sirreal sirreal left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

This is an agentic review, generated by Claude Code at the repository owner's request.

Ready to land. Recommend landing this one first, so the parser-output changes queued behind it (#277, #282) get corpus evidence automatically.

Verified

  • CI green: test-php (7.4), test-php (8.4), and the new WordPress corpus regeneration diff job. Mergeable, clean, based on master.
  • The corpus-diff job really ran end to end (run 32167418014, 40s): corpus verified at 1039 PHP files, base.json and head.json both 48,979,143 bytes — byte-identical, as expected for a PR that changes only tooling — and the uploaded corpus.diff artifact is 138 bytes, i.e. the zipped empty file. 0 hunks confirmed, and both size guards (>= 500 files, >= 1 MB) are exercised rather than nominal. This matches the numbers in the PR body.
  • prep-diff.php exists on master, so the head-drives-both-sides invocation resolves. tools/export-corpus.php is 7.4-clean (no arrow functions, match, nullsafe, named args); $argc/$argv and ini_set( 'display_errors', 'stderr' ) are valid under the CLI SAPI.
  • diff ... || [ $? -eq 1 ] is correct — $? on the right-hand side of || is still diff's status, with no intervening command to clobber it. Under the default bash -e shell the || is what keeps a non-empty diff from failing the job.
  • fetch-depth: 0 makes actions/checkout fetch +refs/heads/*:refs/remotes/origin/*, so origin/${{ github.base_ref }} exists for the merge-base step. Action pinning (SHA for setup-php, major tag for the actions/* ones) matches the existing unit-test.yml convention.
  • README procedure matches what the workflow actually does, including the tooling policy.

Worth noting

  • The step is better than its name. On pull_request, checkout gives you refs/pull/N/merge, whose parents are the base tip and the PR head — so git merge-base origin/<base> HEAD resolves to the base branch tip, not the historical branch point. That is the more useful comparison (base-as-it-is vs. base-plus-this-PR, always exactly one PR apart, no false hunks from other work landed since branching), but the step name and the header comment both say "merge base". Consider rewording so the next reader doesn't "fix" it.
  • "The job always succeeds" (PR body) is imprecise. Several steps fail deliberately: corpus < 500 files, either export < 1 MB, diff exit 2, composer install failure. The workflow header states it correctly — "the job succeeds even when the diff is non-empty" — which is the property that actually matters. Only the PR body overstates it.
  • Stale text in the PR body. The opening paragraph still says "the WordPress 6.8 tag"; the Update section and WP_CORPUS_TAG say 7.0.4. Cosmetic, but worth a squash-message fix so the merged history isn't self-contradicting.
  • export-corpus.php's if ( ! is_array( $files ) ) guard is effectively dead: get_wp_files() signals failure by returning new \WP_Error( ... ), and WP_Error does not exist outside WordPress, so that path fatals before it can return. Harmless (only reachable on an unreadable directory) but the guard does not do what it appears to.
  • No concurrency group, so every push to a PR queues another full run. At ~40s that is cheap; a cancel-in-progress group would still be tidy.

Follow-ups already anticipated

  • #286 stacks on this branch (add-corpus-diff-ci) to keep the pin current — it cannot land until this merges.
  • Making the job blocking, and adding wp-admin/includes to the corpus, are both correctly deferred.

- Name the comparison for what it is: on pull_request HEAD is the PR merged
  into the base branch, so the merge base resolves to the base branch tip.
  Rename the step, explain it in the workflow, and say the same in the
  README, including how to reproduce that comparison locally.
- Drop the unreachable is_array() guard in tools/export-corpus.php:
  get_wp_files() returns a WP_Error on failure, which does not exist under
  plain PHP, so that path fatals with a non-zero exit before it can return.
- Add a concurrency group so a new push cancels the in-flight run for the
  same PR.
Both sides run under the same binary, so the PHP version is not part of
the comparison; use the newest runtime in the unit-test matrix.
Both sides share the corpus, the PHP binary, and the exporter, so the
export is already deterministic. prep-diff.php reconciles exports from
different environments: it zeroes line numbers, strips global-namespace
prefixes, and sorts collections. Measured against three open PRs, the
raw diff was equal or smaller in every case and kept each change at its
source location, while the collection sort relocated changed records
and the erasures hid classes of real change. Diff the exports as
emitted.
tools/corpus-diff-comment.sh renders the diff as Markdown: hunk and
line counts, the compared commits, a download link, and the diff in a
collapsed diff block when the body fits under GitHub's comment limit;
otherwise the counts and the link alone. The workflow writes the same
report to the job summary and creates or updates a single PR comment,
found by a marker on its first line.

pull_request runs for forks and Dependabot get a read-only token, so
the comment step is skipped for them.
@github-actions

github-actions Bot commented Aug 21, 2026

Copy link
Copy Markdown

Corpus diff

0 hunks. No behavior change over WordPress 7.0.4, parser at eee4b8b (base) vs e74611e (this PR merged).

Instead of dropping the diff from the comment when it exceeds the size
budget, keep the leading whole hunks that fit, and say how many of the
total are shown. The download link still has the full diff.
wp-admin, the root files, and the bundled themes and plugins join
wp-includes: 1880 PHP files instead of 1039, 64 MB of JSON instead of
49 MB, about 1.4 seconds more per export locally. More shapes of real
code mean more regressions caught, and every hunk is still a parser
behavior change. Only PHP files are kept in the cache; the cache key
carries a layout suffix so the old wp-includes-only entry is not reused.
@sirreal

sirreal commented Aug 21, 2026

Copy link
Copy Markdown
Member Author

This is quite useful, I plan to merge soon.

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