-
Notifications
You must be signed in to change notification settings - Fork 220
482 lines (444 loc) · 20.7 KB
/
Copy pathtest-vp-create.yml
File metadata and controls
482 lines (444 loc) · 20.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
name: Test vp create
permissions: {}
on:
workflow_dispatch:
push:
branches:
- main
paths:
- 'packages/cli/src/create/**'
- 'packages/cli/templates/**'
- 'packages/cli/src/migration/**'
- '.github/workflows/test-vp-create.yml'
pull_request:
types: [opened, synchronize, labeled]
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: ${{ github.ref_name != 'main' }}
defaults:
run:
shell: bash
jobs:
detect-changes:
runs-on: namespace-profile-linux-x64-default
permissions:
contents: read
pull-requests: read
outputs:
related-files-changed: ${{ steps.filter.outputs.related-files }}
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: filter
with:
filters: |
related-files:
- 'packages/cli/src/create/**'
- 'packages/cli/templates/**'
- 'packages/cli/src/migration/**'
- .github/workflows/test-vp-create.yml
download-previous-rolldown-binaries:
needs: detect-changes
runs-on: namespace-profile-linux-x64-default
# Run if: not a PR, OR PR has 'test: create-e2e' label, OR PR is from the deps/upstream-update or a renovate/** dependency branch, OR create-related files changed
if: >-
github.event_name != 'pull_request' ||
contains(github.event.pull_request.labels.*.name, 'test: create-e2e') ||
github.head_ref == 'deps/upstream-update' ||
startsWith(github.head_ref, 'renovate/') ||
needs.detect-changes.outputs.related-files-changed == 'true'
permissions:
contents: read
packages: read
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- uses: ./.github/actions/download-rolldown-binaries
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
build:
name: Build vite-plus packages
runs-on: namespace-profile-linux-x64-default
permissions:
contents: read
packages: read
needs:
- download-previous-rolldown-binaries
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- uses: ./.github/actions/clone
- uses: oxc-project/setup-rust@68c3199c5339f965e6e163924c3c450773eba42b # main (pending v1.0.17 — Swatinem/rust-cache v2.9.1 for node24)
with:
save-cache: ${{ github.ref_name == 'main' }}
cache-key: create-e2e-build
- uses: oxc-project/setup-node@4c588e9266bd930b6ddc34307df0659ed511d187 # v1.3.1
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: rolldown-binaries
path: ./rolldown/packages/rolldown/src
merge-multiple: true
- name: Build with upstream
uses: ./.github/actions/build-upstream
with:
target: x86_64-unknown-linux-gnu
- name: Pack packages into tgz
run: |
mkdir -p tmp/tgz
# Every tgz consumer below references fixed `*-0.0.0.tgz` filenames.
# A release commit can leave `packages/{core,cli}` at a published
# version (e.g. 0.1.22), which would make `pnpm pack` emit
# `*-0.1.22.tgz` instead. Pin both to 0.0.0 so the names stay stable.
(cd packages/core && npm pkg set version=0.0.0)
(cd packages/cli && npm pkg set version=0.0.0)
cd packages/core && pnpm pack --pack-destination ../../tmp/tgz && cd ../..
cd packages/cli && pnpm pack --pack-destination ../../tmp/tgz && cd ../..
# Bun is uniquely strict about peer-dep resolution:
# 1. It checks the *resolved target's* package name and version
# against the peer range (vitest 4.1.9 declares peer
# `vite ^6 || ^7 || ^8`).
# 2. A file: override pointing at the vite-plus-core tgz fails
# both the name check (target is `@voidzero-dev/vite-plus-core`,
# not `vite`) and the version check (0.0.0 is outside `^6|^7|^8`).
# pnpm/npm/yarn don't enforce either, and using the same core tgz as
# the file: target for both `vite` and `@voidzero-dev/vite-plus-core`
# is the only configuration they install cleanly. See
# https://github.com/oven-sh/bun/issues/8406.
#
# Generate a sibling vite-7.99.0.tgz: a copy of the core tgz with
# `package.json#name` rewritten to "vite" and `version` to 7.99.0.
# Only the bun matrix entry below points its vite override at this
# alias tgz; pnpm/npm/yarn keep pointing at the real core tgz so
# pnpm's workspace resolver doesn't trip on a "vite@<version>"
# registry lookup (the renamed tgz makes pnpm register the dep as
# vite@7.99.0 and then probe npmjs.org to validate the version).
pnpm exec tool repack-vite-tgz tmp/tgz/voidzero-dev-vite-plus-core-0.0.0.tgz tmp/tgz/vite-7.99.0.tgz vite 7.99.0
# Copy vp binary for test jobs
cp target/x86_64-unknown-linux-gnu/release/vp tmp/tgz/vp
ls -la tmp/tgz
- name: Upload tgz artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: vite-plus-packages
path: tmp/tgz/
retention-days: 1
test-vp-create:
name: vp create ${{ matrix.template.name }} (${{ matrix.package-manager }})
runs-on: namespace-profile-linux-x64-default
permissions:
contents: read
needs:
- build
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
template:
- name: monorepo
create-args: vite:monorepo --directory test-project
template-args: ''
verify-command: vp run ready
verify-migration: 'false'
- name: application
create-args: vite:application --directory test-project
template-args: '-- --template vanilla-ts'
verify-command: vp run build
verify-migration: 'false'
- name: library
create-args: vite:library --directory test-project
template-args: ''
verify-command: |
vp run build
vp run test
verify-migration: 'false'
# Remote template that ships ESLint (+ an eslint.config.js importing
# @eslint/js etc.). Exercises the migrate-before-rewrite reorder in
# `vp create`: after scaffold, ESLint → oxlint and Prettier → oxfmt
# run before the vite-plus rewrite so `.oxlintrc` / `.oxfmtrc` get
# merged into vite.config.ts.
- name: remote-vite-react-ts
create-args: vite@9.0.5
template-args: '-- test-project --template react-ts'
verify-command: vp run build
verify-migration: 'true'
package-manager:
- pnpm
- npm
- yarn
- bun
env:
# Bun's strict peer check requires the `vite` override target's tgz to be
# named "vite" with a version satisfying vitest's `peer vite ^6 || ^7 || ^8`.
# The bun matrix entry uses the masquerade tgz (vite-7.99.0.tgz). pnpm/npm/yarn
# point at the real core tgz — anything else trips a registry lookup for
# vite@<version> when sub-package and override targets are both file: tgz aliases.
VITE_OVERRIDE_TGZ: ${{ matrix.package-manager == 'bun' && 'vite-7.99.0.tgz' || 'voidzero-dev-vite-plus-core-0.0.0.tgz' }}
VP_VERSION: 'file:${{ github.workspace }}/tmp/tgz/vite-plus-0.0.0.tgz'
# Force full dependency rewriting so the library template's existing
# vite-plus dep gets overridden with the local tgz
VP_FORCE_MIGRATE: '1'
# yarn 4 quarantines packages published within `npmMinimalAgeGate`
# (default 1440 min / 24h). When an oxlint bump landed <24h ago, the
# migration step's `vp dlx @oxlint/migrate@<bundled oxlint>` fails with
# `YN0016 ... are quarantined`. The migrate tool is version-pinned to the
# bundled oxlint, so disable the gate for this test (no-op for npm/pnpm/bun).
YARN_NPM_MINIMAL_AGE_GATE: '0'
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 24
- name: Download vite-plus packages
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: vite-plus-packages
path: tmp/tgz
- name: Install vp CLI
run: |
mkdir -p target/release
cp tmp/tgz/vp target/release/vp
chmod +x target/release/vp
node $GITHUB_WORKSPACE/packages/tools/src/install-global-cli.ts --tgz $GITHUB_WORKSPACE/tmp/tgz/vite-plus-0.0.0.tgz
echo "$HOME/.vite-plus/bin" >> $GITHUB_PATH
- name: Verify vp installation
run: |
which vp
vp --version
- name: Run vp create ${{ matrix.template.name }} with ${{ matrix.package-manager }}
working-directory: ${{ runner.temp }}
env:
VP_OVERRIDE_PACKAGES: '{"vite":"file:${{ github.workspace }}/tmp/tgz/${{ env.VITE_OVERRIDE_TGZ }}","@voidzero-dev/vite-plus-core":"file:${{ github.workspace }}/tmp/tgz/voidzero-dev-vite-plus-core-0.0.0.tgz","vitest":"4.1.9","@vitest/expect":"4.1.9","@vitest/runner":"4.1.9","@vitest/snapshot":"4.1.9","@vitest/spy":"4.1.9","@vitest/utils":"4.1.9","@vitest/mocker":"4.1.9","@vitest/pretty-format":"4.1.9","@vitest/coverage-v8":"4.1.9","@vitest/coverage-istanbul":"4.1.9"}'
run: |
vp create ${{ matrix.template.create-args }} \
--no-interactive \
--no-agent \
--package-manager ${{ matrix.package-manager }} \
${{ matrix.template.template-args }}
- name: Verify project structure
working-directory: ${{ runner.temp }}/test-project
run: |
# package.json must exist
test -f package.json
echo "✓ package.json exists"
cat package.json
# List all files for debugging
echo "--- Project root files ---"
ls -la
# Check correct lockfile exists
case "${{ matrix.package-manager }}" in
pnpm)
test -f pnpm-lock.yaml
echo "✓ pnpm-lock.yaml exists"
;;
npm)
test -f package-lock.json
echo "✓ package-lock.json exists"
;;
yarn)
test -f yarn.lock
echo "✓ yarn.lock exists"
;;
bun)
if [ -f bun.lock ]; then
echo "✓ bun.lock exists"
elif [ -f bun.lockb ]; then
echo "✓ bun.lockb exists"
else
echo "✗ No bun lockfile found"
exit 1
fi
;;
esac
# node_modules must exist (vp install ran successfully)
test -d node_modules
echo "✓ node_modules exists"
# Monorepo-specific checks
if [ "${{ matrix.template.name }}" = "monorepo" ]; then
test -d apps/website
echo "✓ apps/website exists"
test -d packages/utils
echo "✓ packages/utils exists"
case "${{ matrix.package-manager }}" in
pnpm)
test -f pnpm-workspace.yaml
echo "✓ pnpm-workspace.yaml exists"
;;
yarn)
test -f .yarnrc.yml
echo "✓ .yarnrc.yml exists"
;;
esac
fi
- name: Verify single dependency instances (pnpm only)
if: matrix.package-manager == 'pnpm'
working-directory: ${{ runner.temp }}/test-project
run: |
# The `vite` override must dedupe vite-plus / vite / vitest to a single
# instance each. When a peer variation splits the graph (e.g. an upstream
# `vite` auto-installed to satisfy vitest's peer in a package without a
# direct `vite` dep), `vp why` reports multiple instances. Detection:
# - vite-plus / vitest: a split prints "Found 1 version, N instances of <pkg>".
# - vite: it is overridden to @voidzero-dev/vite-plus-core, so a clean tree
# only summarises that package; a standalone upstream copy adds a
# "Found <n> version(s) of vite" line.
# Regression guard for voidzero-dev/vite-plus#1932 (the pnpm dedupe fix).
# `-r` checks every workspace package, not just the root importer, so a
# duplicate confined to a sub-package (apps/website, packages/utils) is
# still caught.
fail=0
check() {
pkg="$1"; pattern="$2"
out=$(vp why -r "$pkg" 2>&1)
found=$(echo "$out" | grep '^Found' || true)
echo "[$pkg]"; echo "$found"
if echo "$found" | grep -qE "$pattern"; then
echo "✗ $pkg is not a single instance (override did not dedupe under pnpm)"
echo "----- full \`vp why -r $pkg\` output -----"
echo "$out"
echo "---------------------------------------"
fail=1
else
echo "✓ $pkg single instance"
fi
}
check vite-plus 'instances of vite-plus'
check vitest 'instances of vitest'
check vite 'of vite$'
if [ "$fail" -ne 0 ]; then
echo "Expected vite-plus, vite, and vitest to each resolve to a single instance."
exit 1
fi
echo "✓ vite-plus, vite, vitest are all single instances"
- name: Verify local tgz packages installed
working-directory: ${{ runner.temp }}/test-project
run: |
node -e "
const path = require('path');
const pkg = require(path.resolve('node_modules/vite-plus/package.json'));
if (pkg.version !== '0.0.0') {
console.error('Expected vite-plus@0.0.0, got ' + pkg.version);
process.exit(1);
}
console.log('✓ vite-plus@' + pkg.version + ' installed correctly');
"
- name: Verify monorepo sub-package deps
if: matrix.template.name == 'monorepo'
working-directory: ${{ runner.temp }}/test-project
env:
PACKAGE_MANAGER: ${{ matrix.package-manager }}
run: |
# Issue 1: packages/utils inherits `vite-plus: ^x.y.z` from the
# library template. In catalog-supporting monorepos (pnpm/yarn/bun)
# the migrator must normalize it so siblings don't drift.
# Issue 2: apps/website is scaffolded by create-vite which ships
# `vite` (and sometimes `vitest`) in devDependencies. After
# migration the scripts are rewritten to `vp ...` and `vite-plus`
# brings the runtime in transitively. For npm/yarn/bun those keys
# are dropped (the root overrides/resolutions redirect the
# transitive/peer vite to @voidzero-dev/vite-plus-core regardless).
# For pnpm the aliased `vite` is kept on purpose: pnpm only surfaces
# the pnpm-workspace.yaml `overrides.vite: catalog:` entry through a
# package that directly depends on `vite`, so dropping it would make
# `vp why vite` report upstream vite and the override look ineffective.
node -e "
const fs = require('fs');
const pm = process.env.PACKAGE_MANAGER;
for (const f of ['apps/website/package.json', 'packages/utils/package.json']) {
if (!fs.existsSync(f)) {
console.error('✗ expected ' + f + ' to exist after vp create vite:monorepo');
process.exit(1);
}
}
const app = JSON.parse(fs.readFileSync('apps/website/package.json', 'utf8'));
const utils = JSON.parse(fs.readFileSync('packages/utils/package.json', 'utf8'));
const appDev = app.devDependencies || {};
const utilsDev = utils.devDependencies || {};
if (pm === 'pnpm') {
if (!appDev['vite']) {
console.error('✗ pnpm apps/website should keep aliased vite so the workspace override stays effective');
process.exit(1);
}
console.log('✓ pnpm apps/website keeps aliased vite');
} else {
for (const name of ['vite', 'vitest']) {
if (appDev[name]) {
console.error('✗ apps/website devDependencies still has ' + name + ': ' + appDev[name]);
process.exit(1);
}
}
console.log('✓ apps/website devDependencies has no vite/vitest');
}
if (!appDev['vite-plus']) {
console.error('✗ apps/website missing vite-plus devDependency');
process.exit(1);
}
if (!utilsDev['vite-plus']) {
console.error('✗ packages/utils missing vite-plus devDependency');
process.exit(1);
}
if (pm !== 'npm' && appDev['vite-plus'] !== utilsDev['vite-plus']) {
console.error('✗ vite-plus spec drift: apps/website=' + appDev['vite-plus'] + ' packages/utils=' + utilsDev['vite-plus']);
process.exit(1);
}
console.log('✓ vite-plus consistent across sub-packages: app=' + appDev['vite-plus'] + ' utils=' + utilsDev['vite-plus']);
"
- name: Verify ESLint/Prettier auto-migration
if: matrix.template.verify-migration == 'true'
working-directory: ${{ runner.temp }}/test-project
run: |
# eslint.config.js must be gone (migration deleted it)
test ! -f eslint.config.js
echo "✓ eslint.config.js removed"
# .oxlintrc.json must NOT be loose on disk — it was merged into
# vite.config.ts by the rewrite step that runs after migration.
test ! -f .oxlintrc.json
echo "✓ .oxlintrc.json merged into vite.config.ts"
# vite.config.ts must contain the merged oxlint config.
grep -q '^[[:space:]]*lint:' vite.config.ts
echo "✓ vite.config.ts has merged lint section"
# package.json: eslint devDep removed, vite-plus present, lint script rewritten.
node -e "
const pkg = require('./package.json');
if (pkg.devDependencies && pkg.devDependencies.eslint) {
console.error('✗ eslint devDependency should have been removed');
process.exit(1);
}
if (!pkg.devDependencies || !pkg.devDependencies['vite-plus']) {
console.error('✗ vite-plus devDependency missing');
process.exit(1);
}
if (!pkg.scripts || !pkg.scripts.lint || !pkg.scripts.lint.includes('vp lint')) {
console.error('✗ lint script should invoke vp lint, got: ' + (pkg.scripts && pkg.scripts.lint));
process.exit(1);
}
console.log('✓ package.json migrated (eslint gone, vite-plus added, lint script rewritten)');
"
- name: Run vp check
working-directory: ${{ runner.temp }}/test-project
run: vp check
- name: Verify project builds
working-directory: ${{ runner.temp }}/test-project
run: ${{ matrix.template.verify-command }}
- name: Verify cache (monorepo only)
if: matrix.template.name == 'monorepo'
working-directory: ${{ runner.temp }}/test-project
run: |
# Under npm, `vp run ready` reaches 100% cache hit only on the
# third invocation (#1638): vite-task's directory-listing
# fingerprint and fspy read/write tracking surface false-positive
# misses on run #2 because `packages/utils/node_modules/` is born
# during run #1. pnpm/yarn/bun pre-create per-package
# `node_modules/` at install time and reach 100% on run #2.
# The preceding `Verify project builds` step already invoked
# `vp run ready` once (verify-command for monorepo), so one
# extra warm-up here is enough under npm.
if [ "${{ matrix.package-manager }}" = "npm" ]; then
vp run ready >/dev/null 2>&1
fi
output=$(vp run ready 2>&1)
echo "$output"
if ! echo "$output" | grep -q 'cache hit (100%)'; then
echo "✗ Expected 100% cache hit"
echo "--- vp run --last-details (cache-miss diagnostics) ---"
vp run --last-details || true
exit 1
fi
echo "✓ 100% cache hit verified"