Skip to content

Commit c4d9a09

Browse files
authored
test(e2e): add more projects to ecosystem CI tests (#366)
<!-- CURSOR_SUMMARY --> > [!NOTE] > Broadens ecosystem CI coverage and makes migration/install flows compatible with local tgz builds. > > - Adds `frm-stack` and `vue-mini` to E2E matrix; updates commands; keeps `skeleton` commented; adjusts `vibe-dashboard` steps and retains `rollipop` > - Replaces `pnpm install` with `vite install` in workflow; installs Playwright where needed > - Simplifies `ecosystem-ci/patch-project.ts` to a single `vite migrate` path using `file:` tgz overrides; removes project-specific patchers > - Updates `ecosystem-ci/repo.json` (new repos, updated hashes) and `.gitignore` > - Improves migrator logic (`packages/global/src/migration/migrator.ts`) to: > - Use `catalog:` only when versions aren’t `file:`; write `file:` directly to overrides/devDeps > - Skip catalog entries for `file:` versions and safely delete existing catalog items > - Correct vite-plus version selection in monorepos vs npm > - Respect `file:` when deciding `catalog:` usage in overrides and vite-plus injection > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 3f08039. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent afbb297 commit c4d9a09

5 files changed

Lines changed: 71 additions & 227 deletions

File tree

.github/workflows/e2e-test.yml

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,24 +47,47 @@ jobs:
4747
project:
4848
- name: vibe-dashboard
4949
node-version: 24
50-
command: vite run ready
51-
- name: skeleton
52-
node-version: 22
5350
command: |
54-
pnpm run --filter="@skeletonlabs/*" --filter="!*skeleton.dev" --sequential build
55-
pnpm test
51+
pnpm playwright install --with-deps
52+
# FIXME: Failed to load JS plugin: ./plugins/debugger.js
53+
# vite run ready
54+
vite fmt
55+
vite test
56+
vite run build
57+
# FIXME: TypeError: Failed to fetch dynamically imported module
58+
# - name: skeleton
59+
# node-version: 24
60+
# command: |
61+
# vite run format
62+
# vite run lint:check
63+
# vite run check
64+
# pnpm exec playwright install chromium
65+
# vite run test
5666
- name: rollipop
5767
node-version: 22
58-
skip-install: true
5968
command: |
60-
vite install --no-frozen-lockfile
6169
vite run -r build
6270
vite run lint
6371
vite run -r typecheck
6472
vite run format
6573
vite run @rollipop/common#test
6674
vite run @rollipop/core#test
6775
vite run @rollipop/dev-server#test
76+
- name: frm-stack
77+
node-version: 24
78+
command: |
79+
vite run lint:check
80+
vite run format:check
81+
vite run typecheck
82+
vite run @yourcompany/api#test
83+
vite run @yourcompany/backend-core#test
84+
- name: vue-mini
85+
node-version: 24
86+
command: |
87+
vite run format
88+
vite run lint
89+
vite run type
90+
vite run test -- --coverage
6891
6992
steps:
7093
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
@@ -116,11 +139,7 @@ jobs:
116139
# avoid the vite migration using the wrong ignore file
117140
rm -f ../.gitignore
118141
node ../patch-project.ts ${{ matrix.project.name }}
119-
if [ -n "${{ matrix.project.skip-install }}" ]; then
120-
exit 0
121-
fi
122-
pnpm install --no-frozen-lockfile
123-
pnpm playwright install --with-deps
142+
vite install --no-frozen-lockfile
124143
125144
- name: Run vite-plus commands in ${{ matrix.project.name }}
126145
working-directory: ecosystem-ci/${{ matrix.project.name }}

ecosystem-ci/.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
vibe-dashboard
22
skeleton
3-
rollipop
3+
rollipop
4+
frm-stack
5+
vue-mini

ecosystem-ci/patch-project.ts

Lines changed: 1 addition & 206 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import { execSync } from 'node:child_process';
2-
import fs from 'node:fs';
32
import { dirname, join } from 'node:path';
43
import { fileURLToPath } from 'node:url';
54

6-
import vitestPackageJson from '../packages/test/package.json' with { type: 'json' };
75
import repos from './repo.json' with { type: 'json' };
86

97
const projectDir = dirname(fileURLToPath(import.meta.url));
@@ -19,196 +17,6 @@ if (!projects.includes(project)) {
1917

2018
const tgzPath = join(projectDir, '..', 'tmp', 'tgz');
2119

22-
async function patchVibeDashboard() {
23-
const pnpmWorkspacePath = join(projectDir, 'vibe-dashboard', 'pnpm-workspace.yaml');
24-
const pnpmWorkspaceFile = fs
25-
.readFileSync(pnpmWorkspacePath, 'utf8')
26-
.replace(
27-
'"vite": "npm:@voidzero-dev/vite-plus-core"',
28-
`"vite": "file:${tgzPath}/voidzero-dev-vite-plus-core-0.0.0.tgz"`,
29-
)
30-
.replace(
31-
'"vitest": "npm:@voidzero-dev/vite-plus-test"',
32-
`"vitest": "file:${tgzPath}/voidzero-dev-vite-plus-test-0.0.0.tgz"
33-
"@vitest/browser": "file:${tgzPath}/voidzero-dev-vite-plus-test-0.0.0.tgz"
34-
"@vitest/browser-playwright": "file:${tgzPath}/voidzero-dev-vite-plus-test-0.0.0.tgz"
35-
"@voidzero-dev/vite-plus": "file:${tgzPath}/voidzero-dev-vite-plus-0.0.0.tgz"
36-
"@voidzero-dev/vite-plus-core": "file:${tgzPath}/voidzero-dev-vite-plus-core-0.0.0.tgz"
37-
"@voidzero-dev/vite-plus-test": "file:${tgzPath}/voidzero-dev-vite-plus-test-0.0.0.tgz"`,
38-
);
39-
fs.writeFileSync(pnpmWorkspacePath, pnpmWorkspaceFile);
40-
41-
// Remove @vitest/* packages from apps/dashboard/package.json
42-
// These are bundled into our vitest package and shouldn't be installed separately
43-
const dashboardPackageJsonPath = join(
44-
projectDir,
45-
'vibe-dashboard',
46-
'apps',
47-
'dashboard',
48-
'package.json',
49-
);
50-
const dashboardPackageJson = JSON.parse(fs.readFileSync(dashboardPackageJsonPath, 'utf8'));
51-
if (dashboardPackageJson.devDependencies) {
52-
// Remove @vitest/browser, @vitest/ui, and @vitest/browser-playwright
53-
// They're all bundled in our vitest package now
54-
const vitestPackagesToRemove = ['@vitest/browser', '@vitest/ui', '@vitest/browser-playwright'];
55-
for (const pkg of vitestPackagesToRemove) {
56-
delete dashboardPackageJson.devDependencies[pkg];
57-
}
58-
}
59-
60-
// Note: @vitest/* packages are now bundled into our vitest package, so we don't need
61-
// to add them as separate devDependencies anymore.
62-
63-
// Write the updated package.json
64-
fs.writeFileSync(dashboardPackageJsonPath, JSON.stringify(dashboardPackageJson, null, 2) + '\n');
65-
66-
// Update vite.config.ts to import from vitest/browser-playwright instead of @vitest/browser-playwright
67-
// This is needed because pnpm overrides don't affect Node.js module resolution at config load time
68-
const viteConfigPath = join(projectDir, 'vibe-dashboard', 'apps', 'dashboard', 'vite.config.ts');
69-
const viteConfigContent = fs
70-
.readFileSync(viteConfigPath, 'utf8')
71-
.replace('from "@vitest/browser-playwright"', 'from "vitest/browser-playwright"');
72-
fs.writeFileSync(viteConfigPath, viteConfigContent);
73-
74-
// Add pnpm overrides to ensure @vitest/* packages are installed at matching versions
75-
const vitestVersion = vitestPackageJson.devDependencies['@vitest/runner'];
76-
const vitestOverrides = [
77-
'@vitest/runner',
78-
'@vitest/utils',
79-
'@vitest/spy',
80-
'@vitest/expect',
81-
'@vitest/snapshot',
82-
'@vitest/mocker',
83-
'@vitest/pretty-format',
84-
];
85-
86-
const pnpmWorkspaceContent = fs.readFileSync(pnpmWorkspacePath, 'utf8');
87-
if (!pnpmWorkspaceContent.includes('"@vitest/runner":')) {
88-
const overridesStr = vitestOverrides.map((pkg) => ` "${pkg}": "${vitestVersion}"`).join('\n');
89-
const updatedContent = pnpmWorkspaceContent.replace(
90-
/^overrides:\n/m,
91-
`overrides:\n${overridesStr}\n`,
92-
);
93-
fs.writeFileSync(pnpmWorkspacePath, updatedContent);
94-
}
95-
}
96-
97-
async function patchSkeleton() {
98-
const packageJsonPath = join(projectDir, 'skeleton', 'package.json');
99-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
100-
101-
// Change test command from "vitest run" to "vite test"
102-
packageJson.scripts.test = 'vite test';
103-
104-
// Add pnpm overrides with tgz files
105-
// Include @vitest/browser and @vitest/browser-playwright to use bundled versions
106-
packageJson.pnpm = packageJson.pnpm || {};
107-
packageJson.pnpm.overrides = {
108-
...packageJson.pnpm.overrides,
109-
vite: `file:${tgzPath}/voidzero-dev-vite-plus-core-0.0.0.tgz`,
110-
'rolldown-vite': `file:${tgzPath}/voidzero-dev-vite-plus-core-0.0.0.tgz`,
111-
vitest: `file:${tgzPath}/voidzero-dev-vite-plus-test-0.0.0.tgz`,
112-
'@vitest/browser': `file:${tgzPath}/voidzero-dev-vite-plus-test-0.0.0.tgz`,
113-
'@vitest/browser-playwright': `file:${tgzPath}/voidzero-dev-vite-plus-test-0.0.0.tgz`,
114-
'@voidzero-dev/vite-plus': `file:${tgzPath}/voidzero-dev-vite-plus-0.0.0.tgz`,
115-
'@voidzero-dev/vite-plus-core': `file:${tgzPath}/voidzero-dev-vite-plus-core-0.0.0.tgz`,
116-
'@voidzero-dev/vite-plus-test': `file:${tgzPath}/voidzero-dev-vite-plus-test-0.0.0.tgz`,
117-
};
118-
119-
packageJson.devDependencies = {
120-
...packageJson.devDependencies,
121-
'@voidzero-dev/vite-plus': `latest`,
122-
playwright: `catalog:`,
123-
};
124-
125-
// Relax engine constraints to support broader node versions
126-
if (packageJson.engines?.node) {
127-
packageJson.engines.node = '>=22.0.0';
128-
}
129-
130-
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
131-
132-
const vitestVersion = vitestPackageJson.devDependencies['@vitest/expect'];
133-
134-
// Patch pnpm-workspace.yaml
135-
const pnpmWorkspacePath = join(projectDir, 'skeleton', 'pnpm-workspace.yaml');
136-
let pnpmWorkspaceContent = fs
137-
.readFileSync(pnpmWorkspacePath, 'utf8')
138-
.replace(`trustPolicy: no-downgrade`, '\n')
139-
.replace(
140-
/'@vitest\/browser-playwright': [\d.]+/,
141-
`'@vitest/browser-playwright': ${vitestVersion}`,
142-
);
143-
144-
// Add entries to existing minimumReleaseAgeExclude if it exists, otherwise append new section
145-
const newExcludes = ["'@voidzero-dev/*'", "'@vitest/*'", 'oxlint', 'oxfmt', 'oxlint-tsgolint'];
146-
if (pnpmWorkspaceContent.includes('minimumReleaseAgeExclude:')) {
147-
// Find the minimumReleaseAgeExclude section and add new entries
148-
pnpmWorkspaceContent = pnpmWorkspaceContent.replace(
149-
/minimumReleaseAgeExclude:\n((?: - .+\n)+)/,
150-
(_, existingEntries) => {
151-
const newEntriesStr = newExcludes.map((e) => ` - ${e}\n`).join('');
152-
return `minimumReleaseAgeExclude:\n${existingEntries}${newEntriesStr}`;
153-
},
154-
);
155-
} else {
156-
pnpmWorkspaceContent += `\nminimumReleaseAgeExclude:\n${newExcludes.map((e) => ` - ${e}`).join('\n')}\n`;
157-
}
158-
159-
// Add peerDependencyRules if not present
160-
if (!pnpmWorkspaceContent.includes('peerDependencyRules:')) {
161-
pnpmWorkspaceContent += `
162-
peerDependencyRules:
163-
allowAny:
164-
- vite
165-
- vitest
166-
`;
167-
}
168-
169-
fs.writeFileSync(pnpmWorkspacePath, pnpmWorkspaceContent);
170-
171-
// Update vite.config.ts files to import from vitest/browser-playwright instead of @vitest/browser-playwright
172-
// This is needed because pnpm overrides don't affect Node.js module resolution at config load time
173-
const skeletonReactConfigPath = join(
174-
projectDir,
175-
'skeleton',
176-
'packages',
177-
'skeleton-react',
178-
'vite.config.ts',
179-
);
180-
const skeletonSvelteConfigPath = join(
181-
projectDir,
182-
'skeleton',
183-
'packages',
184-
'skeleton-svelte',
185-
'vite.config.ts',
186-
);
187-
188-
for (const configPath of [skeletonReactConfigPath, skeletonSvelteConfigPath]) {
189-
const content = fs
190-
.readFileSync(configPath, 'utf8')
191-
// Handle both single and double quotes
192-
.replace(/from ['"]@vitest\/browser-playwright['"]/, 'from "vitest/browser-playwright"');
193-
fs.writeFileSync(configPath, content);
194-
}
195-
196-
// Remove @vitest/browser-playwright from package devDependencies
197-
// These are bundled in our vitest package now
198-
const packagesToUpdate = [
199-
join(projectDir, 'skeleton', 'packages', 'skeleton-react', 'package.json'),
200-
join(projectDir, 'skeleton', 'packages', 'skeleton-svelte', 'package.json'),
201-
];
202-
203-
for (const pkgPath of packagesToUpdate) {
204-
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
205-
if (pkg.devDependencies?.['@vitest/browser-playwright']) {
206-
delete pkg.devDependencies['@vitest/browser-playwright'];
207-
}
208-
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, '\t') + '\n');
209-
}
210-
}
211-
21220
async function migrateProject(project: string) {
21321
const repoRoot = join(projectDir, project);
21422
// run vite migrate
@@ -228,17 +36,4 @@ async function migrateProject(project: string) {
22836
});
22937
}
23038

231-
switch (project) {
232-
case 'vibe-dashboard':
233-
await patchVibeDashboard();
234-
break;
235-
case 'skeleton':
236-
await patchSkeleton();
237-
break;
238-
case 'rollipop':
239-
await migrateProject(project);
240-
break;
241-
default:
242-
console.error(`Project ${project} is not supported`);
243-
process.exit(1);
244-
}
39+
await migrateProject(project);

ecosystem-ci/repo.json

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,26 @@
22
"skeleton": {
33
"repository": "https://github.com/skeletonlabs/skeleton.git",
44
"branch": "main",
5-
"hash": "6f613fe3326ed483885e4acc6f49e2044aa9ab79"
5+
"hash": "6d57f29b823275c6e3fb267c6834da5d39558fb6"
66
},
77
"vibe-dashboard": {
88
"repository": "https://github.com/voidzero-dev/vibe-dashboard.git",
99
"branch": "main",
10-
"hash": "30c68c9ad65c1315abf4c69366b21bc63b5d0fb4"
10+
"hash": "f780e8769539feff8beb5e9bf44de6f5c5abad1c"
1111
},
1212
"rollipop": {
1313
"repository": "https://github.com/leegeunhyeok/rollipop.git",
1414
"branch": "main",
1515
"hash": "9beb8dd8fb70ef298b3a18703a831d6d4d3c01a1"
16+
},
17+
"frm-stack": {
18+
"repository": "https://github.com/Nikola-Milovic/frm-stack.git",
19+
"branch": "main",
20+
"hash": "e9e344125d8476ed6f34880036c0b1aef8dc0bb5"
21+
},
22+
"vue-mini": {
23+
"repository": "https://github.com/vue-mini/vue-mini.git",
24+
"branch": "master",
25+
"hash": "c51332662993dde44f665822bdea94cd0abf368b"
1626
}
1727
}

packages/global/src/migration/migrator.ts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,11 @@ function rewritePnpmWorkspaceYaml(projectPath: string): void {
218218

219219
// overrides
220220
for (const key of Object.keys(VITE_PLUS_OVERRIDE_PACKAGES)) {
221-
doc.setIn(['overrides', scalarString(key)], scalarString('catalog:'));
221+
let version = VITE_PLUS_OVERRIDE_PACKAGES[key];
222+
if (!version.startsWith('file:')) {
223+
version = 'catalog:';
224+
}
225+
doc.setIn(['overrides', scalarString(key)], scalarString(version));
222226
}
223227
// remove dependency selector from vite, e.g. "vite-plugin-svgr>vite": "npm:[email protected]"
224228
const overrides = doc.getIn(['overrides']) as YAMLMap<Scalar<string>, Scalar<string>>;
@@ -323,11 +327,21 @@ function rewriteYarnrcYml(projectPath: string): void {
323327
*/
324328
function rewriteCatalog(doc: YamlDocument): void {
325329
for (const [key, value] of Object.entries(VITE_PLUS_OVERRIDE_PACKAGES)) {
330+
// ERR_PNPM_CATALOG_IN_OVERRIDES  Could not resolve a catalog in the overrides: The entry for 'vite' in catalog 'default' declares a dependency using the 'file' protocol
331+
// ignore setting catalog if value starts with 'file:'
332+
if (value.startsWith('file:')) {
333+
continue;
334+
}
326335
doc.setIn(['catalog', key], scalarString(value));
327336
}
328-
doc.setIn(['catalog', VITE_PLUS_NAME], scalarString(VITE_PLUS_VERSION));
337+
if (!VITE_PLUS_VERSION.startsWith('file:')) {
338+
doc.setIn(['catalog', VITE_PLUS_NAME], scalarString(VITE_PLUS_VERSION));
339+
}
329340
for (const name of REMOVE_PACKAGES) {
330-
doc.deleteIn(['catalog', name]);
341+
const path = ['catalog', name];
342+
if (doc.hasIn(path)) {
343+
doc.deleteIn(path);
344+
}
331345
}
332346

333347
// TODO: rewrite `catalogs` when OVERRIDE_PACKAGES exists in catalog
@@ -394,7 +408,10 @@ function rewriteRootWorkspacePackageJson(
394408
if (!pkg.devDependencies?.[VITE_PLUS_NAME]) {
395409
pkg.devDependencies = {
396410
...pkg.devDependencies,
397-
[VITE_PLUS_NAME]: packageManager === PackageManager.npm ? VITE_PLUS_VERSION : 'catalog:',
411+
[VITE_PLUS_NAME]:
412+
packageManager === PackageManager.npm || VITE_PLUS_VERSION.startsWith('file:')
413+
? VITE_PLUS_VERSION
414+
: 'catalog:',
398415
};
399416
}
400417
return pkg;
@@ -437,7 +454,7 @@ export function rewritePackageJson(
437454
const supportCatalog = isMonorepo && packageManager !== PackageManager.npm;
438455
let needVitePlus = false;
439456
for (const [key, version] of Object.entries(VITE_PLUS_OVERRIDE_PACKAGES)) {
440-
const value = supportCatalog ? 'catalog:' : version;
457+
const value = supportCatalog && !version.startsWith('file:') ? 'catalog:' : version;
441458
if (pkg.devDependencies?.[key]) {
442459
pkg.devDependencies[key] = value;
443460
needVitePlus = true;
@@ -460,7 +477,8 @@ export function rewritePackageJson(
460477
}
461478
if (needVitePlus) {
462479
// add vite-plus to devDependencies
463-
const version = supportCatalog ? 'catalog:' : VITE_PLUS_VERSION;
480+
const version =
481+
supportCatalog && !VITE_PLUS_VERSION.startsWith('file:') ? 'catalog:' : VITE_PLUS_VERSION;
464482
pkg.devDependencies = {
465483
...pkg.devDependencies,
466484
[VITE_PLUS_NAME]: version,

0 commit comments

Comments
 (0)