201 lines
5.4 KiB
TypeScript
201 lines
5.4 KiB
TypeScript
import { readJsonFile, workspaceRoot } from '@nx/devkit';
|
|
import { existsSync } from 'fs-extra';
|
|
import { execSync } from 'node:child_process';
|
|
import { join } from 'path';
|
|
import { gte } from 'semver';
|
|
import { dirSync } from 'tmp';
|
|
|
|
import * as isCI from 'is-ci';
|
|
import { PackageManager } from 'nx/src/utils/package-manager';
|
|
import { tmpProjPath } from './create-project-utils';
|
|
import { e2eConsoleLogger } from './log-utils';
|
|
|
|
export const isWindows = require('is-windows');
|
|
|
|
export function getPublishedVersion(): string {
|
|
process.env.PUBLISHED_VERSION =
|
|
process.env.PUBLISHED_VERSION ||
|
|
// read version of built nx package
|
|
readJsonFile(join(workspaceRoot, `./dist/packages/nx/package.json`))
|
|
.version ||
|
|
// fallback to latest if built nx package is missing
|
|
'latest';
|
|
return process.env.PUBLISHED_VERSION as string;
|
|
}
|
|
|
|
export function detectPackageManager(dir: string = ''): PackageManager {
|
|
return existsSync(join(dir, 'bun.lockb')) || existsSync(join(dir, 'bun.lock'))
|
|
? 'bun'
|
|
: existsSync(join(dir, 'yarn.lock'))
|
|
? 'yarn'
|
|
: existsSync(join(dir, 'pnpm-lock.yaml')) ||
|
|
existsSync(join(dir, 'pnpm-workspace.yaml'))
|
|
? 'pnpm'
|
|
: 'npm';
|
|
}
|
|
|
|
export function isNotWindows() {
|
|
return !isWindows();
|
|
}
|
|
|
|
export function isOSX() {
|
|
return process.platform === 'darwin';
|
|
}
|
|
|
|
export function isAndroid() {
|
|
return (
|
|
process.platform === 'linux' &&
|
|
process.env.ANDROID_HOME &&
|
|
process.env.ANDROID_SDK_ROOT
|
|
);
|
|
}
|
|
|
|
export const e2eRoot = isCI
|
|
? dirSync({ prefix: 'nx-e2e-' }).name
|
|
: '/tmp/nx-e2e';
|
|
|
|
export function isVerbose() {
|
|
return (
|
|
process.env.NX_VERBOSE_LOGGING === 'true' ||
|
|
process.argv.includes('--verbose')
|
|
);
|
|
}
|
|
|
|
export function isVerboseE2ERun() {
|
|
return process.env.NX_E2E_VERBOSE_LOGGING === 'true' || isVerbose();
|
|
}
|
|
|
|
export const e2eCwd = `${e2eRoot}/nx`;
|
|
|
|
export function getSelectedPackageManager(): 'npm' | 'yarn' | 'pnpm' | 'bun' {
|
|
return (process.env.SELECTED_PM as 'npm' | 'yarn' | 'pnpm' | 'bun') || 'npm';
|
|
}
|
|
|
|
export function getNpmMajorVersion(): string | undefined {
|
|
try {
|
|
const [npmMajorVersion] = execSync(`npm -v`).toString().split('.');
|
|
return npmMajorVersion;
|
|
} catch {
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
export function getYarnMajorVersion(path: string): string | undefined {
|
|
try {
|
|
// this fails if path is not yet created
|
|
const [yarnMajorVersion] = execSync(`yarn -v`, {
|
|
cwd: path,
|
|
encoding: 'utf-8',
|
|
}).split('.');
|
|
return yarnMajorVersion;
|
|
} catch {
|
|
try {
|
|
const [yarnMajorVersion] = execSync(`yarn -v`, {
|
|
encoding: 'utf-8',
|
|
}).split('.');
|
|
return yarnMajorVersion;
|
|
} catch {
|
|
return undefined;
|
|
}
|
|
}
|
|
}
|
|
|
|
export function getPnpmVersion(): string | undefined {
|
|
try {
|
|
const pnpmVersion = execSync(`pnpm -v`, {
|
|
encoding: 'utf-8',
|
|
}).trim();
|
|
return pnpmVersion;
|
|
} catch {
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
export function getLatestLernaVersion(): string {
|
|
const lernaVersion = execSync(`npm view lerna version`, {
|
|
encoding: 'utf-8',
|
|
}).trim();
|
|
return lernaVersion;
|
|
}
|
|
|
|
export const packageManagerLockFile = {
|
|
npm: 'package-lock.json',
|
|
yarn: 'yarn.lock',
|
|
pnpm: 'pnpm-lock.yaml',
|
|
bun: (() => {
|
|
try {
|
|
// In version 1.2.0, bun switched to a text based lockfile format by default
|
|
return gte(execSync('bun --version').toString().trim(), '1.2.0')
|
|
? 'bun.lock'
|
|
: 'bun.lockb';
|
|
} catch {
|
|
return 'bun.lockb';
|
|
}
|
|
})(),
|
|
};
|
|
|
|
// Re-export browser installation utilities from ensure-browser-installation.ts
|
|
export {
|
|
ensureCypressInstallation,
|
|
ensurePlaywrightBrowsersInstallation,
|
|
} from './ensure-browser-installation';
|
|
|
|
export function getStrippedEnvironmentVariables() {
|
|
return Object.fromEntries(
|
|
Object.entries(process.env).filter(([key]) => {
|
|
if (key.startsWith('NX_E2E_')) {
|
|
return true;
|
|
}
|
|
|
|
const allowedKeys = [
|
|
'NX_ADD_PLUGINS',
|
|
'NX_ISOLATE_PLUGINS',
|
|
'NX_VERBOSE_LOGGING',
|
|
'NX_NATIVE_LOGGING',
|
|
'NX_USE_LOCAL',
|
|
];
|
|
|
|
if (key.startsWith('NX_') && !allowedKeys.includes(key)) {
|
|
return false;
|
|
}
|
|
|
|
if (key === 'JEST_WORKER_ID') {
|
|
return false;
|
|
}
|
|
|
|
// Remove NODE_PATH to prevent module resolution conflicts with original workspace.
|
|
// NODE_PATH is inherited from Jest (which runs from the original workspace) and contains
|
|
// pnpm paths that cause require.resolve() to find workspace packages instead of e2e test versions.
|
|
if (key === 'NODE_PATH') {
|
|
return false;
|
|
}
|
|
|
|
// Remove GITHUB_STEP_SUMMARY so e2e subprocesses don't append to the real CI job
|
|
// summary. The stripper drops NX_TASK_TARGET_PROJECT, so a child nx looks top-level
|
|
// and would otherwise write its performance report (once per nx command) into the
|
|
// runner's summary. GITHUB_ACTIONS is kept so log grouping still gets exercised.
|
|
if (key === 'GITHUB_STEP_SUMMARY') {
|
|
return false;
|
|
}
|
|
|
|
// Remove AI agent detection env vars to prevent the test runner's
|
|
// environment (e.g., running inside Claude Code) from leaking into
|
|
// e2e test subprocesses. Tests that need these pass them explicitly.
|
|
const aiAgentEnvVars = [
|
|
'CLAUDECODE',
|
|
'CLAUDE_CODE',
|
|
'OPENCODE',
|
|
'GEMINI_CLI',
|
|
'CURSOR_TRACE_ID',
|
|
'COMPOSER_NO_INTERACTION',
|
|
'REPL_ID',
|
|
];
|
|
if (aiAgentEnvVars.includes(key)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
})
|
|
);
|
|
}
|