Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 5 additions & 12 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,16 +91,12 @@ jobs:
with:
python-version: 3.11

- name: Set Yarn global folder for Windows
if: runner.os == 'Windows'
shell: bash
run: yarn config set globalFolder "D:\\yarn-data"

- name: Set TEMP for Windows
- name: Use D:\\ drive for Windows
if: runner.os == 'Windows'
shell: bash
run: |
mkdir -p /d/fast-temp
echo "YARN_GLOBAL_FOLDER=D:\\yarn-data" >> $GITHUB_ENV
echo "TEMP=D:\\fast-temp" >> $GITHUB_ENV

- name: Install dependencies
Expand Down Expand Up @@ -178,16 +174,12 @@ jobs:
with:
python-version: 3.11

- name: Set Yarn global folder for Windows
if: runner.os == 'Windows'
shell: bash
run: yarn config set globalFolder "D:\\yarn-data"

- name: Set TEMP for Windows
- name: Use D:\\ drive for Windows
if: runner.os == 'Windows'
shell: bash
run: |
mkdir -p /d/fast-temp
echo "YARN_GLOBAL_FOLDER=D:\\yarn-data" >> $GITHUB_ENV
echo "TEMP=D:\\fast-temp" >> $GITHUB_ENV

- name: Install dependencies
Expand Down Expand Up @@ -228,6 +220,7 @@ jobs:
run: |
mkdir -p ./reports/out
yarn test:slow --reporter=default --reporter=junit --outputFile="./reports/out/test_output.xml"
yarn test:verdaccio --reporter=default --reporter=junit --outputFile="./reports/out/test_output_verdaccio.xml"

required-ci:
needs:
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,6 @@ packages/**/typedoc.json
.nx/workspace-data
.cursor/rules/nx-rules.mdc
.github/instructions/nx.instructions.md

tools/verdaccio/storage
tools/verdaccio/htpasswd
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ packages/*/*/index.ts
packages/*/*/README.md
packages/*/*/tsconfig.json
packages/*/*/typedoc.json
packages/api/core/spec/fixture/api-tester/package.json
packages/api/core/spec/fixture/bad_external_forge_config/bad.js
packages/plugin/webpack/spec/**/.webpack
.links
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"test": "xvfb-maybe vitest run --project fast --project slow",
"test:fast": "xvfb-maybe vitest run --project fast",
"test:slow": "xvfb-maybe vitest run --project slow",
"test:verdaccio": "tsx tools/verdaccio/spawn-verdaccio.ts xvfb-maybe vitest run --project slow-verdaccio",
"test:clear": "tsx tools/test-clear",
"update:lockfile-fixtures": "tsx tools/regenerate-lockfile-fixtures.ts",
"postinstall": "husky install && node -e \"try { fs.rmSync('node_modules/.bin/*.ps1', { recursive: true, force: true }) } catch (e) {}\" && tsx ./tools/gen-tsconfigs.ts && tsx ./tools/gen-ts-glue.ts"
Expand Down Expand Up @@ -122,6 +123,7 @@
"ref-napi": "^3.0.3",
"typedoc": "0.25.13",
"typescript": "~5.4.5",
"verdaccio": "^6.2.4",
"vitest": "^4.0.14",
"xvfb-maybe": "^0.2.1",
"yaml-hook": "^1.0.0"
Expand Down
2 changes: 1 addition & 1 deletion packages/api/core/spec/fixture/api-tester/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,4 @@
"config": {
"forge": "./forge.config.js"
}
}
}
53 changes: 53 additions & 0 deletions tools/verdaccio/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Verdaccio configuration for testing with local packages
# This allows tests to install @electron-forge/* packages from the monorepo
# while proxying everything else to the real npm registry.

storage: ./storage

web:
enable: false

auth:
htpasswd:
file: ./htpasswd
# Allow unlimited users for testing
max_users: -1

# Upstream npm registry
uplinks:
npmjs:
url: https://registry.npmjs.org/
cache: true

packages:
# @electron-forge packages are served locally (no proxy)
'@electron-forge/*':
access: $all
publish: $all
# Don't proxy to npm - only serve local packages
# This ensures we use local versions during tests

# All other scoped packages proxy to npm
'@*/*':
access: $all
publish: $all
proxy: npmjs

# Non-scoped packages proxy to npm
'**':
access: $all
publish: $all
proxy: npmjs

# Server settings
server:
keepAliveTimeout: 60

# Logging
log:
type: stdout
format: pretty
level: warn

# Listen on localhost only
listen: 127.0.0.1:4873
190 changes: 190 additions & 0 deletions tools/verdaccio/spawn-verdaccio.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
/**
* This script runs any command with a local Verdaccio instance that
* publishes local builds of all `@electron-forge/` packages to the
* proxy registry.
*
* This is useful to test the local build of Electron Forge prior
* to publishing the monorepo, and to wire up `init` tests against
* the latest and greatest.
*
* Usage:
* tsx tools/verdaccio/spawn-verdaccio.ts <command> [args...]
*
* Example:
* tsx tools/verdaccio-spawn-verdaccio.ts yarn test:slow
*/

import { ChildProcess, spawn } from 'node:child_process';
import fs from 'node:fs';
import path from 'node:path';

import { spawn as spawnPromise } from '@malept/cross-spawn-promise';
import debug from 'debug';

const FORGE_ROOT_DIR = path.resolve(__dirname, '../..');
/**
* Path to the Verdaccio configuration file.
* The below constants are derived from settings in the YAML.
*/
const CONFIG_PATH = path.resolve(__dirname, 'config.yaml');

const LOCALHOST = '127.0.0.1';
const VERDACCIO_PORT = 4873;
const VERDACCIO_URL = `http://${LOCALHOST}:${VERDACCIO_PORT}`;
const STORAGE_PATH = path.resolve(__dirname, 'storage');

const d = debug('electron-forge:verdaccio');

let verdaccioProcess: ChildProcess | null = null;

/**
* Starts the Verdaccio server.
*/
async function startVerdaccio(): Promise<void> {
console.log('🚀 Starting Verdaccio...');

// Clean up old storage
await fs.promises.rm(STORAGE_PATH, { recursive: true, force: true });
await fs.promises.mkdir(STORAGE_PATH);

return new Promise((resolve, reject) => {
verdaccioProcess = spawn('yarn', ['verdaccio', '--config', CONFIG_PATH], {
cwd: FORGE_ROOT_DIR,
// On Windows, detaching the child process will cause the Promise to hang
// On UNIX-based platforms, detatching it is necessary to successfully kill the Verdaccio server
detached: process.platform !== 'win32',
shell: process.platform === 'win32',
});

let started = false;

verdaccioProcess.stdout?.on('data', (data: Buffer) => {
const output = data.toString();
d(output);
if (output.includes('http address') && !started) {
started = true;
// Give it a moment to be fully ready
setTimeout(resolve, 500);
}
});

verdaccioProcess.stderr?.on('data', (data: Buffer) => {
const output = data.toString();
console.error('[verdaccio]', output);
});

verdaccioProcess.on('error', reject);
verdaccioProcess.on('close', (code) => {
if (!started || code !== 0) {
reject(new Error(`Verdaccio exited with code ${code}`));
}
});
});
}

/**
* Kills the local Verdaccio instance.
*/
function stopVerdaccio(): void {
if (verdaccioProcess && verdaccioProcess.pid) {
console.log('🛑 Stopping Verdaccio...');
// Kill the entire process group (negative PID) to ensure all child processes are terminated
try {
process.kill(-verdaccioProcess.pid, 'SIGTERM');
} catch {
// Process may have already exited
verdaccioProcess.kill('SIGTERM');
}
verdaccioProcess = null;
}
}

/**
* Publishes all `@electron-forge/` packages to the localhost Verdaccio registry.
*/
async function publishPackages(): Promise<void> {
console.log('📦 Publishing monorepo packages to Verdaccio registry...');

try {
await spawnPromise(
`yarn`,
[
'lerna',
'publish',
'from-package',
'--registry',
VERDACCIO_URL,
'--yes',
'--no-git-tag-version',
'--no-push',
],
{
cwd: FORGE_ROOT_DIR,
stdio: 'inherit',
},
);
console.log('✅ All packages published to Verdaccio registry');
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
console.error('❌ Failed to publish packages:', errorMessage);
throw error;
}
}

async function runCommand(args: string[]) {
console.log(`🏃 Running: ${args.join(' ')}`);
console.log(` Using registry: ${VERDACCIO_URL}`);

await spawnPromise(args[0], args.slice(1), {
cwd: FORGE_ROOT_DIR,
stdio: 'inherit',
env: {
...process.env,
// https://docs.npmjs.com/cli/v9/using-npm/config#registry
// https://pnpm.io/settings#registry
NPM_CONFIG_REGISTRY: VERDACCIO_URL,
// https://yarnpkg.com/configuration/yarnrc#npmRegistryServer
YARN_NPM_REGISTRY_SERVER: VERDACCIO_URL,
// https://yarnpkg.com/configuration/yarnrc#unsafeHttpWhitelist
YARN_UNSAFE_HTTP_WHITELIST: LOCALHOST,
},
});
}

async function main(): Promise<void> {
const args = process.argv.slice(2);

if (args.length === 0) {
console.error(
'Usage: tsx tools/verdaccio/spawn-verdaccio.ts <command> [args...]',
);
console.error(
'Example: tsx tools/verdaccio/spawn-verdaccio.ts yarn test:slow',
);
process.exit(1);
}

// Handle signals
process.on('SIGINT', () => {
stopVerdaccio();
process.exit(1);
});
process.on('SIGTERM', () => {
stopVerdaccio();
process.exit(1);
});

try {
await startVerdaccio();
await publishPackages();
await runCommand(args);
stopVerdaccio();
process.exit(0);
} catch (error) {
console.error('❌ Error:', error);
stopVerdaccio();
process.exit(1);
}
}

main();
14 changes: 13 additions & 1 deletion vitest.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ export default defineConfig({
extends: './vitest.config.mts',
test: {
include: ['**/spec/**/*.spec.ts'],
exclude: ['**/spec/**/*.slow.spec.ts'],
exclude: [
'**/spec/**/*.slow.spec.ts',
'**/spec/**/*.slow.verdaccio.spec.ts',
],
name: 'fast',
},
},
Expand All @@ -24,6 +27,15 @@ export default defineConfig({
testTimeout: 240000,
},
},
{
extends: './vitest.config.mts',
test: {
include: ['**/spec/**/*.slow.verdaccio.spec.ts'],
name: 'slow-verdaccio',
hookTimeout: 240000,
testTimeout: 240000,
},
},
],
},
});
Loading
Loading