1111 runs-on : ubuntu-latest
1212 strategy :
1313 matrix :
14- php-version : [ '7.4', '8.3 ' ]
14+ php-version : [ '7.4', '8.5 ' ]
1515
1616 steps :
1717 - name : Checkout repository
2525 - name : Setup Node.js
2626 uses : actions/setup-node@v4
2727 with :
28- node-version : ' 22 '
28+ node-version-file : ' .nvmrc '
2929 cache : ' npm'
3030
3131 - name : Cache Composer
5555 - name : Setup PHP
5656 uses : shivammathur/setup-php@v2
5757 with :
58- php-version : ' 8.3 '
58+ php-version : ' 8.5 '
5959
6060 - name : Cache Composer
6161 uses : actions/cache@v4
@@ -80,30 +80,140 @@ jobs:
8080 - name : Run PHPStan
8181 run : composer phpstan
8282
83+ unit :
84+ name : Unit (PHPUnit)
85+ runs-on : ubuntu-latest
86+ steps :
87+ - name : Checkout repository
88+ uses : actions/checkout@v4
89+
90+ - name : Setup PHP
91+ uses : shivammathur/setup-php@v2
92+ with :
93+ php-version : ' 8.2'
94+
95+ - name : Setup Node.js
96+ uses : actions/setup-node@v4
97+ with :
98+ node-version-file : ' .nvmrc'
99+
100+ - name : Cache Composer
101+ uses : actions/cache@v4
102+ with :
103+ path : ~/.composer/cache
104+ key : ${{ runner.os }}-composer-unit-${{ hashFiles('**/composer.lock') }}
105+ restore-keys : |
106+ ${{ runner.os }}-composer-unit-
107+
108+ # This job only needs vendor/bin/phpunit (from Composer) and the wp-env
109+ # CLI. A full `npm ci` pulls ~2,200 packages and has taken anywhere from
110+ # 36s to 7 minutes on hosted runners; @wordpress/env alone is ~400
111+ # packages and installs in ~30s. It is installed into a scratch prefix
112+ # outside the repo so npm does not reconcile against package-lock.json
113+ # and pull the whole tree anyway. The version is read from the lockfile
114+ # so it cannot drift from what developers run locally.
115+ - name : Install Composer dependencies
116+ run : composer install --no-interaction --no-progress
117+
118+ - name : Install wp-env
119+ run : |
120+ version=$(node -p "require('./package-lock.json').packages['node_modules/@wordpress/env'].version")
121+ echo "Installing @wordpress/env@$version"
122+ npm install --prefix "$RUNNER_TEMP/wp-env" --no-audit --no-fund "@wordpress/env@$version"
123+ echo "$RUNNER_TEMP/wp-env/node_modules/.bin" >> "$GITHUB_PATH"
124+
125+ # The wp-env sources directory is deliberately not cached. A restored
126+ # ~/.wp-env carries the previous run's install state, which skipped the
127+ # plugin's activation hook and left the relationships table missing.
128+ - name : Start wp-env
129+ run : wp-env start
130+
131+ - name : Run unit tests
132+ run : wp-env run tests-cli --env-cwd="wp-content/plugins/$(basename "$PWD")" vendor/bin/phpunit
133+
134+ - name : Stop wp-env
135+ if : always()
136+ run : wp-env stop
137+
83138 e2e :
84139 name : E2E (Playwright)
85140 runs-on : ubuntu-latest
86141 steps :
87142 - name : Checkout repository
88143 uses : actions/checkout@v4
89144
145+ # Not needed by Playwright, but every job that has this step completes
146+ # `npm ci` in ~35s while this job, without it, took 2.5 to 4 minutes on
147+ # the same runs (same npm, same cache hit). setup-php also installs
148+ # Composer, which the postinstall hook calls.
149+ - name : Setup PHP
150+ uses : shivammathur/setup-php@v2
151+ with :
152+ php-version : ' 8.2'
153+
90154 - name : Setup Node.js
91155 uses : actions/setup-node@v4
92156 with :
93- node-version : ' 22 '
157+ node-version-file : ' .nvmrc '
94158 cache : ' npm'
95159
160+ - name : Cache Composer
161+ uses : actions/cache@v4
162+ with :
163+ path : ~/.composer/cache
164+ key : ${{ runner.os }}-composer-e2e-${{ hashFiles('**/composer.lock') }}
165+ restore-keys : |
166+ ${{ runner.os }}-composer-e2e-
167+
168+ # Deliberately not `--ignore-scripts`: with the npm 10 that ships with
169+ # the pinned Node, that flag made this step take 4 to 7 minutes instead
170+ # of ~36s (a known npm 10 reify stall around lifecycle-script nodes).
96171 - name : Install dependencies
97172 run : npm ci
98173
99- - name : Install Playwright browsers
100- run : npx playwright install --with-deps chromium
174+ - name : Get Playwright version
175+ id : playwright-version
176+ run : echo "version=$(node -p "require('@playwright/test/package.json').version")" >> "$GITHUB_OUTPUT"
101177
102- - name : Build assets
103- run : npm run build
104-
105- - name : Start wp-env
106- run : npm run env:start
178+ - name : Cache Playwright browsers
179+ id : playwright-cache
180+ uses : actions/cache@v4
181+ with :
182+ path : ~/.cache/ms-playwright
183+ key : ${{ runner.os }}-playwright-${{ steps.playwright-version.outputs.version }}
184+
185+ # wp-env spends most of its time pulling Docker images and installing
186+ # WordPress, none of which depends on the Node-side steps. Run it in the
187+ # background while the browser install and asset build proceed, then
188+ # wait for it. All of this has to live in one step because `wait` only
189+ # sees children of the same shell. wp-env output goes to a file and is
190+ # printed afterwards so the interleaved log stays readable.
191+ - name : Start wp-env, install browsers, build assets
192+ env :
193+ PLAYWRIGHT_CACHE_HIT : ${{ steps.playwright-cache.outputs.cache-hit }}
194+ run : |
195+ npm run env:start > wp-env-start.log 2>&1 &
196+ wp_env_pid=$!
197+
198+ if [ "$PLAYWRIGHT_CACHE_HIT" = "true" ]; then
199+ # Browser binaries came from cache; only the apt packages they
200+ # need are missing on a fresh runner.
201+ npx playwright install-deps chromium
202+ else
203+ npx playwright install --with-deps chromium
204+ fi
205+
206+ npm run build
207+
208+ echo "::group::wp-env start"
209+ if wait "$wp_env_pid"; then
210+ cat wp-env-start.log
211+ echo "::endgroup::"
212+ else
213+ cat wp-env-start.log
214+ echo "::endgroup::"
215+ exit 1
216+ fi
107217
108218 - name : Run E2E tests
109219 env :
0 commit comments