Skip to content

CI: Cut lint run time by caching dependencies and skipping the build - #3692

Merged
obenland merged 4 commits into
trunkfrom
update/faster-lint-workflow
Oct 7, 2026
Merged

obenland merged 4 commits into
trunkfrom
update/faster-lint-workflow

Conversation

@obenland

@obenland obenland commented Oct 7, 2026 •

Copy link
Copy Markdown
Member

What

The Lint job took ~2m09s, of which only 21s was linting (see this run). The rest was setup it didn't need:

Step Time Needed for lint?
Install svn (apt) 27s Only for two wordpress-meta/* dev packages that phpcs excludes. The Ubuntu mirror served the 2.3 MB package at 25–43 kB/s, so this step ranged from 27s to 97s across runs.
yarn install 39s setup-node only caches yarn's download cache, so node_modules was linked from scratch every run
yarn build 13s No. Nothing lints built output
setup-node + yarn cache restore 14s Partly

How

Follows the patterns in WordPress/wordpress.org, wporg-main-2022 and wporg-mu-plugins.

  • Two path-filtered workflows replace linters.yml: lint-php.yml runs only when PHP, phpcs.xml.dist or composer files change; lint-frontend.yml runs only when JS/SCSS/CSS, package manifests, lint configs, yarn.lock, .nvmrc or composer.lock change (the ESLint/Stylelint configs come from wporg-repo-tools). A PHP-only PR never spins up Node, and a docs-only PR runs nothing.
  • Composite action .github/actions/setup-composer restores vendor/ from a cache keyed on composer.lock. svn install, composer install and the git reset only run on a cache miss. composer.lock has changed twice since 2024, so misses are rare.
  • node_modules cache (root + three workspaces) keyed on yarn.lock + .nvmrc. On an exact hit yarn install is skipped entirely. Yarn 1 does not trust a restored tree: with node_modules fully restored it still re-fetched and re-linked everything (82s), so skipping is the only way to benefit from the cache. No restore-keys, since a stale node_modules is only safe on an exact match.
  • phpcs runs on 4 processes via <arg name="parallel" value="4" /> in phpcs.xml.dist, matching the runner's core count. 10.7s → 5s. The I18n text_domain list is passed as <element> nodes, which removes the PHPCS 3.3 deprecation notice and is the syntax PHPCS 4 requires.
  • Dropped yarn build and the no-op yarn setup:tools from the PHP side.
  • Concurrency: a newer push cancels a PR's in-flight run. Trunk runs are never cancelled, so they always populate the caches PR runs read from.
  • timeout-minutes lowered to 10.

Action pinning and hardening (all workflows)

  • Every uses: across .github/workflows and .github/actions is pinned to the commit SHA of its latest release, with the version as a trailing comment: checkout v7.0.1, setup-node v7.0.0, cache v6.1.0, github-script v9.0.0, setup-php 2.37.2, actions-js/push v1.6, create-or-update-comment v5.0.0. The inputs these workflows use are unchanged across those bumps.
  • takanome-dev/assign-issue-action is SHA-pinned but stays on v2.0.0. v3 renames every input, adds a per-user cap of 3 open assignments that cannot be disabled (0 and empty both fall back to 3), turns days_until_unassign: 0 into 14, and enables auto-suggestion replies, newcomer welcome text, reminders and block-after-unassign by default. Moving to it is a behavior decision for the team, not a mechanical upgrade.
  • .github/dependabot.yml keeps the pins current with one grouped weekly PR.
  • Every checkout sets persist-credentials: false. The lint jobs are read-only; build.yml and i18n.yml push through the token they hand actions-js/push, not the one checkout would leave in .git/config.
  • review-instructions.yml is gated at the job level, so a runner no longer starts for every issue comment.

Measured on this PR

Before After (cache hit)
Lint PHP part of one 2m09s job 10–14s (phpcs 3–5s)
Lint JavaScript and Styles part of one 2m09s job 27–31s (node_modules restore 7s, eslint 6s, stylelint 2s)
Wall clock, push → all green ~2m14s ~30s

First run after a composer.lock or yarn.lock change is a cache miss and takes 1–2.5 minutes depending on apt mirror speed. That happens once per lockfile change on trunk; PR branches read trunk's caches.

🤖 Generated with Claude Code

The lint job spent about 2 minutes on setup for 21 seconds of actual
linting. It installed svn via apt, ran a full composer install, linked
node_modules from scratch, and ran a webpack build whose output nothing
lints.

Split linting into two parallel jobs (PHP, and JS/CSS) that share a
composite action for the composer side. vendor/ is cached on
composer.lock, so svn and composer install only run on a miss.
node_modules is cached on yarn.lock and .nvmrc, so yarn install is a
no-op on a hit. The build step is dropped, and a concurrency group
cancels superseded runs on the same ref.

Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 7, 2026 19:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

obenland and others added 3 commits October 7, 2026 14:48
…e hits

Each lint workflow now only runs when files it checks (or its tooling)
change, so PHP-only PRs skip the Node job entirely and vice versa.

On an exact node_modules cache hit, yarn install is skipped outright:
yarn 1 re-fetched and re-linked everything (82s) even with node_modules
restored. The composer setup action no longer copies the repo-tools
configs, since only the front-end job needs them.

phpcs scans on 4 processes, matching the runner core count. Trunk runs
are no longer cancelled by a newer push, so they always populate the
caches that PR runs read from.

Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
All actions now reference the commit SHA of their latest release, with
the version as a trailing comment, so a moved tag cannot change what
runs. A Dependabot config keeps the pins current. Every checkout sets
persist-credentials: false; the two workflows that push use the token
the push action is given, not the one checkout would leave in .git.

The assign-issue action stays on v2.0.0, pinned: v3 renames its inputs,
adds a per-user assignment cap that cannot be disabled, and turns
days_until_unassign 0 into 14, so moving to it is a behavior decision.

The review-instructions job is gated at the job level, so a runner no
longer starts for every issue comment. phpcs.xml.dist passes the I18n
text domains as element nodes, the syntax PHPCS 4 requires.

Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
Dependabot scans the composite action's directory too, since "/" only
covers workflows. apt-get update runs before the svn install on a cache
miss, so a stale package index cannot fail the install. The PHP job
calls composer directly instead of going through yarn. The concurrency
comment says what GitHub actually does with a queued trunk run.

Co-Authored-By: Claude Mythos 5.1 <noreply@anthropic.com>
@obenland
obenland merged commit b8f81c3 into trunk Oct 7, 2026
3 checks passed
@obenland
obenland deleted the update/faster-lint-workflow branch October 7, 2026 20:30
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