Skip to content

Commit bca8d72

Browse files
committed
test: use verdaccio for e2e init testing (#4100)
1 parent b9c4c3e commit bca8d72

File tree

10 files changed

+1631
-55
lines changed

10 files changed

+1631
-55
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ jobs:
208208
run: |
209209
mkdir -p ./reports/out
210210
yarn test:slow --reporter=default --reporter=junit --outputFile="./reports/out/test_output.xml"
211+
yarn test:verdaccio --reporter=default --reporter=junit --outputFile="./reports/out/test_output_verdaccio.xml"
211212
212213
required-ci:
213214
needs:

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,6 @@ packages/**/typedoc.json
4242
.nx/workspace-data
4343
.cursor/rules/nx-rules.mdc
4444
.github/instructions/nx.instructions.md
45+
46+
tools/verdaccio/storage
47+
tools/verdaccio/htpasswd

.prettierignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ packages/*/*/index.ts
99
packages/*/*/README.md
1010
packages/*/*/tsconfig.json
1111
packages/*/*/typedoc.json
12+
packages/api/core/spec/fixture/api-tester/package.json
1213
packages/api/core/spec/fixture/bad_external_forge_config/bad.js
1314
packages/plugin/webpack/spec/**/.webpack
1415
.links

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"test": "xvfb-maybe vitest run --project fast --project slow",
2323
"test:fast": "xvfb-maybe vitest run --project fast",
2424
"test:slow": "xvfb-maybe vitest run --project slow",
25+
"test:verdaccio": "tsx tools/verdaccio/spawn-verdaccio.ts xvfb-maybe vitest run --project slow-verdaccio",
2526
"test:clear": "ts-node tools/test-clear",
2627
"update:lockfile-fixtures": "ts-node tools/regenerate-lockfile-fixtures.ts",
2728
"postinstall": "husky install && node -e \"try { fs.rmSync('node_modules/.bin/*.ps1', { recursive: true, force: true }) } catch (e) {}\" && ts-node ./tools/gen-tsconfigs.ts && ts-node ./tools/gen-ts-glue.ts"
@@ -123,6 +124,7 @@
123124
"ts-node": "^10.0.0",
124125
"typedoc": "0.25.13",
125126
"typescript": "~5.4.5",
127+
"verdaccio": "^6.2.4",
126128
"vitest": "^3.1.3",
127129
"xvfb-maybe": "^0.2.1",
128130
"yaml-hook": "^1.0.0"

packages/api/core/spec/fixture/api-tester/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,4 @@
3939
"config": {
4040
"forge": "./forge.config.js"
4141
}
42-
}
42+
}
File renamed without changes.

tools/verdaccio/config.yaml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Verdaccio configuration for testing with local packages
2+
# This allows tests to install @electron-forge/* packages from the monorepo
3+
# while proxying everything else to the real npm registry.
4+
5+
storage: ./storage
6+
7+
web:
8+
enable: false
9+
10+
auth:
11+
htpasswd:
12+
file: ./htpasswd
13+
# Allow unlimited users for testing
14+
max_users: -1
15+
16+
# Upstream npm registry
17+
uplinks:
18+
npmjs:
19+
url: https://registry.npmjs.org/
20+
cache: true
21+
22+
packages:
23+
# @electron-forge packages are served locally (no proxy)
24+
'@electron-forge/*':
25+
access: $all
26+
publish: $all
27+
# Don't proxy to npm - only serve local packages
28+
# This ensures we use local versions during tests
29+
30+
# All other scoped packages proxy to npm
31+
'@*/*':
32+
access: $all
33+
publish: $all
34+
proxy: npmjs
35+
36+
# Non-scoped packages proxy to npm
37+
'**':
38+
access: $all
39+
publish: $all
40+
proxy: npmjs
41+
42+
# Server settings
43+
server:
44+
keepAliveTimeout: 60
45+
46+
# Logging
47+
log:
48+
type: stdout
49+
format: pretty
50+
level: warn
51+
52+
# Listen on localhost only
53+
listen: 127.0.0.1:4873

tools/verdaccio/spawn-verdaccio.ts

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
/**
2+
* This script runs any command with a local Verdaccio instance that
3+
* publishes local builds of all `@electron-forge/` packages to the
4+
* proxy registry.
5+
*
6+
* This is useful to test the local build of Electron Forge prior
7+
* to publishing the monorepo, and to wire up `init` tests against
8+
* the latest and greatest.
9+
*
10+
* Usage:
11+
* tsx tools/verdaccio/spawn-verdaccio.ts <command> [args...]
12+
*
13+
* Example:
14+
* tsx tools/verdaccio-spawn-verdaccio.ts yarn test:slow
15+
*/
16+
17+
import { ChildProcess, spawn } from 'node:child_process';
18+
import fs from 'node:fs';
19+
import path from 'node:path';
20+
21+
import { spawn as spawnPromise } from '@malept/cross-spawn-promise';
22+
import debug from 'debug';
23+
24+
const FORGE_ROOT_DIR = path.resolve(__dirname, '../..');
25+
/**
26+
* Path to the Verdaccio configuration file.
27+
* The below constants are derived from settings in the YAML.
28+
*/
29+
const CONFIG_PATH = path.resolve(__dirname, 'config.yaml');
30+
31+
const LOCALHOST = '127.0.0.1';
32+
const VERDACCIO_PORT = 4873;
33+
const VERDACCIO_URL = `http://${LOCALHOST}:${VERDACCIO_PORT}`;
34+
const STORAGE_PATH = path.resolve(__dirname, 'storage');
35+
36+
const d = debug('electron-forge:verdaccio');
37+
38+
let verdaccioProcess: ChildProcess | null = null;
39+
40+
/**
41+
* Starts the Verdaccio server.
42+
*/
43+
async function startVerdaccio(): Promise<void> {
44+
console.log('🚀 Starting Verdaccio...');
45+
46+
// Clean up old storage
47+
await fs.promises.rm(STORAGE_PATH, { recursive: true, force: true });
48+
await fs.promises.mkdir(STORAGE_PATH);
49+
50+
return new Promise((resolve, reject) => {
51+
verdaccioProcess = spawn('yarn', ['verdaccio', '--config', CONFIG_PATH], {
52+
cwd: FORGE_ROOT_DIR,
53+
// On Windows, detaching the child process will cause the Promise to hang
54+
// On UNIX-based platforms, detatching it is necessary to successfully kill the Verdaccio server
55+
detached: process.platform !== 'win32',
56+
shell: process.platform === 'win32',
57+
});
58+
59+
let started = false;
60+
61+
verdaccioProcess.stdout?.on('data', (data: Buffer) => {
62+
const output = data.toString();
63+
d(output);
64+
if (output.includes('http address') && !started) {
65+
started = true;
66+
// Give it a moment to be fully ready
67+
setTimeout(resolve, 500);
68+
}
69+
});
70+
71+
verdaccioProcess.stderr?.on('data', (data: Buffer) => {
72+
const output = data.toString();
73+
console.error('[verdaccio]', output);
74+
});
75+
76+
verdaccioProcess.on('error', reject);
77+
verdaccioProcess.on('close', (code) => {
78+
if (!started || code !== 0) {
79+
reject(new Error(`Verdaccio exited with code ${code}`));
80+
}
81+
});
82+
});
83+
}
84+
85+
/**
86+
* Kills the local Verdaccio instance.
87+
*/
88+
function stopVerdaccio(): void {
89+
if (verdaccioProcess && verdaccioProcess.pid) {
90+
console.log('🛑 Stopping Verdaccio...');
91+
// Kill the entire process group (negative PID) to ensure all child processes are terminated
92+
try {
93+
process.kill(-verdaccioProcess.pid, 'SIGTERM');
94+
} catch {
95+
// Process may have already exited
96+
verdaccioProcess.kill('SIGTERM');
97+
}
98+
verdaccioProcess = null;
99+
}
100+
}
101+
102+
/**
103+
* Publishes all `@electron-forge/` packages to the localhost Verdaccio registry.
104+
*/
105+
async function publishPackages(): Promise<void> {
106+
console.log('📦 Publishing monorepo packages to Verdaccio registry...');
107+
108+
try {
109+
await spawnPromise(
110+
`yarn`,
111+
[
112+
'lerna',
113+
'publish',
114+
'from-package',
115+
'--registry',
116+
VERDACCIO_URL,
117+
'--yes',
118+
'--no-git-tag-version',
119+
'--no-push',
120+
],
121+
{
122+
cwd: FORGE_ROOT_DIR,
123+
stdio: 'inherit',
124+
},
125+
);
126+
console.log('✅ All packages published to Verdaccio registry');
127+
} catch (error) {
128+
const errorMessage = error instanceof Error ? error.message : String(error);
129+
console.error('❌ Failed to publish packages:', errorMessage);
130+
throw error;
131+
}
132+
}
133+
134+
async function runCommand(args: string[]) {
135+
console.log(`🏃 Running: ${args.join(' ')}`);
136+
console.log(` Using registry: ${VERDACCIO_URL}`);
137+
138+
await spawnPromise(args[0], args.slice(1), {
139+
cwd: FORGE_ROOT_DIR,
140+
stdio: 'inherit',
141+
env: {
142+
...process.env,
143+
// https://docs.npmjs.com/cli/v9/using-npm/config#registry
144+
// https://pnpm.io/settings#registry
145+
NPM_CONFIG_REGISTRY: VERDACCIO_URL,
146+
// https://yarnpkg.com/configuration/yarnrc#npmRegistryServer
147+
YARN_NPM_REGISTRY_SERVER: VERDACCIO_URL,
148+
// https://yarnpkg.com/configuration/yarnrc#unsafeHttpWhitelist
149+
YARN_UNSAFE_HTTP_WHITELIST: LOCALHOST,
150+
},
151+
});
152+
}
153+
154+
async function main(): Promise<void> {
155+
const args = process.argv.slice(2);
156+
157+
if (args.length === 0) {
158+
console.error(
159+
'Usage: tsx tools/verdaccio/spawn-verdaccio.ts <command> [args...]',
160+
);
161+
console.error(
162+
'Example: tsx tools/verdaccio/spawn-verdaccio.ts yarn test:slow',
163+
);
164+
process.exit(1);
165+
}
166+
167+
// Handle signals
168+
process.on('SIGINT', () => {
169+
stopVerdaccio();
170+
process.exit(1);
171+
});
172+
process.on('SIGTERM', () => {
173+
stopVerdaccio();
174+
process.exit(1);
175+
});
176+
177+
try {
178+
await startVerdaccio();
179+
await publishPackages();
180+
await runCommand(args);
181+
stopVerdaccio();
182+
process.exit(0);
183+
} catch (error) {
184+
console.error('❌ Error:', error);
185+
stopVerdaccio();
186+
process.exit(1);
187+
}
188+
}
189+
190+
main();

vitest.workspace.mts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ export default defineWorkspace([
55
extends: './vitest.config.mts',
66
test: {
77
include: ['**/spec/**/*.spec.ts'],
8-
exclude: ['**/spec/**/*.slow.spec.ts'],
8+
exclude: [
9+
'**/spec/**/*.slow.spec.ts',
10+
'**/spec/**/*.slow.verdaccio.spec.ts',
11+
],
912
name: 'fast',
1013
},
1114
},
@@ -18,4 +21,13 @@ export default defineWorkspace([
1821
testTimeout: 160000,
1922
},
2023
},
24+
{
25+
extends: './vitest.config.mts',
26+
test: {
27+
include: ['**/spec/**/*.slow.verdaccio.spec.ts'],
28+
name: 'slow',
29+
hookTimeout: 160000,
30+
testTimeout: 160000,
31+
},
32+
},
2133
]);

0 commit comments

Comments
 (0)