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 { join } from 'node:path'; import { mkdtempSync } from 'node:fs'; import { tmpdir } from 'node:os'; 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 { // TEMP DIAGNOSTIC (cache-miss hunt): log the exact checkout + any working-tree // drift on the agent. Distinguishes "runs are on different SHAs" from // "same SHA but a task mutated the tree". Safe to remove once resolved. if (process.env.CI) { try { const head = execSync('git rev-parse HEAD').toString().trim(); const porcelain = execSync('git status --porcelain').toString().trim(); console.log( `\n=== [tree-state] target=${process.env.NX_TASK_TARGET_TARGET} HEAD=${head} ===\n` + `git status --porcelain:\n${porcelain || '(clean)'}\n` ); } catch (err) { console.log( `[tree-state] diagnostic failed: ${(err as Error).message}` ); } } const isVerbose: boolean = process.env.NX_VERBOSE_LOGGING === 'true' || !!globalConfig.verbose; /** * For e2e-ci, e2e-local and macos-local-e2e we populate the verdaccio storage up front, but for other workflows we need * to run the full local release process before running tests. */ const prefixes = ['e2e-ci', 'e2e-macos-local', 'e2e-local']; const requiresLocalRelease = !prefixes.some((prefix) => process.env.NX_TASK_TARGET_TARGET?.startsWith(prefix) ); 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; // Use environment variable instead of npm config command to avoid polluting other tests process.env[`npm_config_//${listenAddress}:${port}/:_authToken`] = authToken; // 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; // Use fresh cache directories to avoid serving stale packages when the // same version is republished to the local registry. const e2eCacheDir = mkdtempSync(join(tmpdir(), 'nx-e2e-cache-')); process.env.npm_config_cache = join(e2eCacheDir, 'npm'); // yarnv1 process.env.YARN_CACHE_FOLDER = join(e2eCacheDir, 'yarn'); // yarnv2 process.env.YARN_ENABLE_GLOBAL_CACHE = 'false'; process.env.NX_SKIP_PROVENANCE_CHECK = 'true'; global.e2eTeardown = () => { // Clean up environment variable instead of npm config command delete process.env[`npm_config_//${listenAddress}:${port}/:_authToken`]; }; /** * Set the published version based on what has previously been loaded into the * verdaccio storage. */ if (!requiresLocalRelease) { let publishedVersion = await getPublishedVersion(); console.log(`Testing Published version: Nx ${publishedVersion}`); if (publishedVersion) { process.env.PUBLISHED_VERSION = publishedVersion; } } if ( process.env.NX_E2E_SKIP_GLOBAL_CLEANUP !== 'true' || !existsSync('./build') ) { if (!isCI) { registerTsConfigPaths(join(__dirname, '../../tsconfig.base.json')); const { e2eCwd } = await import('./get-env-info'); removeSync(e2eCwd); } 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); } } } catch (err) { // Clean up registry if possible after setup related errors if (typeof global.e2eTeardown === 'function') { global.e2eTeardown(); console.log('Killed local registry process due to an error during setup'); } throw err; } } function getPublishedVersion(): Promise { execSync(`npm config get registry`, { stdio: 'inherit', }); return new Promise((resolve) => { // Resolve the published nx version from verdaccio exec( 'npm view nx@latest version', { windowsHide: false, }, (error, stdout, stderr) => { if (error) { return resolve(undefined); } return resolve(stdout.trim()); } ); }); } async function assertLocalRegistryIsRunning(url) { const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } }