Compare commits

...

6 Commits

Author SHA1 Message Date
Jason Jean cd7c0e56c2 fix(repo): remove detached flag from verdaccio spawn in global-setup 2026-03-13 18:48:07 -04:00
Jason Jean 6933b370be Merge branch 'master' into chore/remove-populate-storage-scripts 2026-03-13 17:41:30 -04:00
Jason Jean 94a64642bb chore(repo): assign astro-docs build to extra-large agents 2026-03-13 13:52:52 -04:00
Jason Jean 592265bea3 chore(repo): split nx-release into local and npm targets
Add nx-release-npm target for real npm publishes (--local false).
Keep nx-release for local verdaccio releases via populate-local-registry-storage.
Update publish.yml to use the new target.
2026-03-13 12:40:41 -04:00
Jason Jean 74622cf71e chore(repo): bust cache for populate-local-registry-storage 2026-03-13 11:58:30 -04:00
Jason Jean b9ad9899db chore(repo): remove populate-storage scripts, consolidate into nx-release
Remove the now-redundant populate-storage.js and run-populate-storage.mjs
scripts. The release logic is inlined directly in global-setup.ts, and
the populate-local-registry-storage task is simplified to an orchestration
point that delegates to nx-release via dependsOn.

When running e2e tests outside of Nx (e.g. Jest directly), global-setup
now auto-starts verdaccio if it's not already running.
2026-03-13 11:32:22 -04:00
8 changed files with 94 additions and 117 deletions
+1 -1
View File
@@ -596,7 +596,7 @@ jobs:
echo "Version set to: $VERSION"
echo "DRY_RUN set to: $DRY_RUN"
echo ""
pnpm nx-release --local=false $VERSION $DRY_RUN
pnpm nx nx-release-npm @nx/nx-source -- $VERSION $DRY_RUN
- name: (Stable Release Only) Trigger Docs Release
# Publish docs only on a full release
+1
View File
@@ -78,6 +78,7 @@ assignment-rules:
# These projects should not need to be isolated.
- projects:
- nx-dev
- astro-docs
targets:
- build*
run-on:
+49 -16
View File
@@ -1,10 +1,9 @@
import { Config } from '@jest/types';
import { existsSync, removeSync } from 'fs-extra';
import * as isCI from 'is-ci';
import { exec, execSync } from 'node:child_process';
import { ChildProcess, exec, execSync, spawn } from 'node:child_process';
import { join } from 'node:path';
import { registerTsConfigPaths } from '../../packages/nx/src/plugins/js/utils/register';
import { runLocalRelease } from '../../scripts/local-registry/populate-storage';
export default async function (globalConfig: Config.ConfigGlobals) {
try {
@@ -25,14 +24,23 @@ export default async function (globalConfig: Config.ConfigGlobals) {
const registry = `http://${listenAddress}:${port}`;
const authToken = 'secretVerdaccioToken';
while (true) {
await new Promise((resolve) => setTimeout(resolve, 250));
try {
await assertLocalRegistryIsRunning(registry);
break;
} catch {
console.log(`Waiting for Local registry to start on ${registry}...`);
}
// When running outside of Nx (e.g. Jest directly), start verdaccio ourselves
let verdaccioProcess: ChildProcess | undefined;
if (requiresLocalRelease && !(await isLocalRegistryRunning(registry))) {
console.log(
`Local registry not detected at ${registry}, starting verdaccio...`
);
verdaccioProcess = spawn(
'npx',
[
'verdaccio',
'--config',
'.verdaccio/config.yml',
'--listen',
`${listenAddress}:${port}`,
],
{ stdio: 'ignore' }
);
}
process.env.npm_config_registry = registry;
@@ -54,6 +62,11 @@ export default async function (globalConfig: Config.ConfigGlobals) {
global.e2eTeardown = () => {
// Clean up environment variable instead of npm config command
delete process.env[`npm_config_//${listenAddress}:${port}/:_authToken`];
// Kill verdaccio if we started it
if (verdaccioProcess) {
verdaccioProcess.kill();
verdaccioProcess = undefined;
}
};
/**
@@ -77,8 +90,26 @@ export default async function (globalConfig: Config.ConfigGlobals) {
if (requiresLocalRelease) {
console.log('Publishing packages to local registry');
const publishVersion = process.env.PUBLISHED_VERSION ?? 'major';
// Always show full release logs on CI, they should only happen once via e2e-ci
await runLocalRelease(publishVersion, isCI || isVerbose);
const verbose = isCI || isVerbose;
const releaseCommand = `pnpm nx-release --local ${publishVersion}`;
console.log(`> ${releaseCommand}`);
await new Promise<void>((resolve, reject) => {
const child = exec(releaseCommand, {
maxBuffer: 1024 * 1000000,
windowsHide: false,
});
if (verbose) {
child.stdout?.pipe(process.stdout);
child.stderr?.pipe(process.stderr);
}
child.on('exit', (code) => {
if (code === 0) {
resolve();
} else {
reject(new Error(`Local release failed with exit code ${code}`));
}
});
});
}
}
} catch (err) {
@@ -112,9 +143,11 @@ function getPublishedVersion(): Promise<string | undefined> {
});
}
async function assertLocalRegistryIsRunning(url) {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
async function isLocalRegistryRunning(url: string): Promise<boolean> {
try {
const response = await fetch(url);
return response.ok;
} catch {
return false;
}
}
+1 -1
View File
@@ -343,7 +343,7 @@
"nxCloudId": "62d013ea0852fe0a2df74438",
"nxCloudUrl": "https://staging.nx.app",
"parallel": 1,
"bust": 2,
"bust": 3,
"defaultBase": "master",
"sync": {
"applyChanges": true
+8 -5
View File
@@ -22,9 +22,13 @@
"input": "production",
"projects": ["tag:maven:dev.nx.maven"]
},
"{workspaceRoot}/scripts/local-registry",
"native"
],
"dependsOn": ["local-registry", "nx-release"],
"command": "echo 'Registry storage populated via nx-release dependency'",
"outputs": ["{workspaceRoot}/dist/local-registry/storage"]
},
"nx-release": {
"dependsOn": [
"local-registry",
{
@@ -32,17 +36,16 @@
"projects": ["tag:npm:public"]
}
],
"command": "node ./scripts/local-registry/run-populate-storage.mjs",
"outputs": ["{workspaceRoot}/dist/local-registry/storage"]
"command": "ts-node -P ./scripts/tsconfig.release.json ./scripts/nx-release.ts"
},
"nx-release": {
"nx-release-npm": {
"dependsOn": [
{
"target": "build",
"projects": ["tag:npm:public"]
}
],
"command": "ts-node -P ./scripts/tsconfig.release.json ./scripts/nx-release.ts"
"command": "ts-node -P ./scripts/tsconfig.release.json ./scripts/nx-release.ts --local false"
},
"start-docker-registry": {
"continuous": true,
@@ -1,79 +0,0 @@
// @ts-check
const { exec, execSync } = require('node:child_process');
const {
LARGE_BUFFER,
} = require('nx/src/executors/run-commands/run-commands.impl');
async function populateLocalRegistryStorage() {
const listenAddress = 'localhost';
const port = process.env.NX_LOCAL_REGISTRY_PORT ?? '4873';
const registry = `http://${listenAddress}:${port}`;
const authToken = 'secretVerdaccioToken';
while (true) {
await new Promise((resolve) => setTimeout(resolve, 250));
try {
await assertLocalRegistryIsRunning(registry);
break;
} catch {
console.log(`Waiting for Local registry to start on ${registry}...`);
}
}
process.env.npm_config_registry = registry;
// bun
process.env.BUN_CONFIG_REGISTRY = registry;
process.env.BUN_CONFIG_TOKEN = authToken;
// yarnv1
process.env.YARN_REGISTRY = registry;
// yarnv2
process.env.YARN_NPM_REGISTRY_SERVER = registry;
process.env.YARN_UNSAFE_HTTP_WHITELIST = listenAddress;
try {
const publishVersion = process.env.PUBLISHED_VERSION ?? 'major';
const isVerbose = process.env.NX_VERBOSE_LOGGING === 'true';
console.log('Publishing packages to local registry to populate storage');
await runLocalRelease(publishVersion, isVerbose);
} catch (err) {
console.error('Error:', err);
process.exit(1);
}
}
exports.populateLocalRegistryStorage = populateLocalRegistryStorage;
function runLocalRelease(publishVersion, isVerbose) {
return new Promise((res, rej) => {
const publishProcess = exec(`pnpm nx-release --local ${publishVersion}`, {
env: process.env,
maxBuffer: LARGE_BUFFER,
});
let logs = Buffer.from('');
if (isVerbose) {
publishProcess?.stdout?.pipe(process.stdout);
publishProcess?.stderr?.pipe(process.stderr);
} else {
publishProcess?.stdout?.on('data', (data) => (logs += data));
publishProcess?.stderr?.on('data', (data) => (logs += data));
}
publishProcess.on('exit', (code) => {
if (code && code > 0) {
if (!isVerbose) {
console.log(logs.toString());
}
rej(code);
}
res(undefined);
});
});
}
exports.runLocalRelease = runLocalRelease;
async function assertLocalRegistryIsRunning(url) {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
}
@@ -1,11 +0,0 @@
// @ts-check
import { populateLocalRegistryStorage } from './populate-storage.js';
/**
* This script is primarily intended to run as part of e2e-ci,
* so we want to capture the full logs of the local release.
*/
process.env.NX_VERBOSE_LOGGING = 'true';
await populateLocalRegistryStorage();
+34 -4
View File
@@ -218,6 +218,13 @@ const VALID_AUTHORS_FOR_LATEST = [
hackFixForDevkitPeerDependencies();
if (options.local) {
const port = process.env.NX_LOCAL_REGISTRY_PORT ?? '4873';
const localRegistryUrl = `http://localhost:${port}`;
await waitForLocalRegistry(localRegistryUrl);
process.env.npm_config_registry = localRegistryUrl;
}
// Run with dynamic output-style so that we have more minimal logs by default but still always see errors
let publishCommand = `pnpm nx release publish --registry=${getRegistry()} --tag=${distTag} --output-style=dynamic --parallel=8`;
if (options.dryRun) {
@@ -400,10 +407,6 @@ function parseArgs() {
'Registry is still set to localhost! Run "pnpm local-registry disable" or pass --force'
);
}
} else {
if (!args.force && !registryIsLocalhost) {
throw new Error('--local was passed and registry is not localhost');
}
}
return true;
@@ -476,6 +479,33 @@ function determineDistTag(
return distTag;
}
function waitForLocalRegistry(registryUrl: string): Promise<void> {
console.log(`Waiting for local registry at ${registryUrl}...`);
return new Promise<void>((resolve, reject) => {
const timeout = setTimeout(() => {
clearInterval(interval);
reject(
new Error(
`Local registry at ${registryUrl} did not become available within 60 seconds`
)
);
}, 60_000);
const interval = setInterval(async () => {
try {
const response = await fetch(registryUrl);
if (response.ok) {
clearInterval(interval);
clearTimeout(timeout);
console.log('Local registry is ready.');
resolve();
}
} catch {
// Registry not up yet
}
}, 50);
});
}
//TODO(@Coly010): Remove this after fixing up the release peer dep handling
function hackFixForDevkitPeerDependencies() {
const { readFileSync, writeFileSync } = require('fs');