feat(repo): support dynamic package manager for e2e tests

This commit is contained in:
Tasos Bekos 2021-01-16 15:44:10 +02:00
parent 659eb12047
commit c92f4ba96b
4 changed files with 106 additions and 53 deletions

View File

@ -12,6 +12,7 @@ import {
removeSync, removeSync,
} from 'fs-extra'; } from 'fs-extra';
import * as path from 'path'; import * as path from 'path';
import { detectPackageManager } from '@nrwl/tao/src/shared/package-manager';
interface RunCmdOpts { interface RunCmdOpts {
silenceError?: boolean; silenceError?: boolean;
@ -26,11 +27,6 @@ export function currentCli() {
let projName: string; let projName: string;
function setCurrentProjName(name: string) {
projName = name;
return name;
}
export function uniq(prefix: string) { export function uniq(prefix: string) {
return `${prefix}${Math.floor(Math.random() * 10000000)}`; return `${prefix}${Math.floor(Math.random() * 10000000)}`;
} }
@ -54,18 +50,18 @@ export function runCreateWorkspace(
appName?: string; appName?: string;
style?: string; style?: string;
base?: string; base?: string;
packageManager?: string; packageManager?: 'npm' | 'yarn' | 'pnpm';
cli?: string; cli?: string;
extraArgs?: string; extraArgs?: string;
} }
) { ) {
setCurrentProjName(name); projName = name;
const pm = getPackageManagerCommand({ packageManager });
const linterArg = const linterArg =
preset === 'angular' || preset === 'angular-nest' ? ' --linter=tslint' : ''; preset === 'angular' || preset === 'angular-nest' ? ' --linter=tslint' : '';
let command = `npx create-nx-workspace@${ let command = `${pm.createWorkspace} ${name} --cli=${
process.env.PUBLISHED_VERSION
} ${name} --cli=${
cli || currentCli() cli || currentCli()
} --preset=${preset} ${linterArg} --no-nxCloud --no-interactive`; } --preset=${preset} ${linterArg} --no-nxCloud --no-interactive`;
if (appName) { if (appName) {
@ -97,7 +93,8 @@ export function runCreateWorkspace(
export function packageInstall(pkg: string, projName?: string) { export function packageInstall(pkg: string, projName?: string) {
const cwd = projName ? `./tmp/${currentCli()}/${projName}` : tmpProjPath(); const cwd = projName ? `./tmp/${currentCli()}/${projName}` : tmpProjPath();
const install = execSync(`npm i ${pkg}`, { const pm = getPackageManagerCommand({ path: cwd });
const install = execSync(`${pm.add} ${pkg}`, {
cwd, cwd,
// ...{ stdio: ['pipe', 'pipe', 'pipe'] }, // ...{ stdio: ['pipe', 'pipe', 'pipe'] },
...{ stdio: [0, 1, 2] }, ...{ stdio: [0, 1, 2] },
@ -118,9 +115,15 @@ export function runNgNew(): string {
* for the currently selected CLI. * for the currently selected CLI.
*/ */
export function newProject(): string { export function newProject(): string {
const packageManager = process.env.SELECTED_PM as any;
try { try {
if (!directoryExists(tmpBackupProjPath())) { const newProjName = uniq('proj');
runCreateWorkspace('proj', { preset: 'empty' }); const useBackupProject = packageManager !== 'pnpm';
const projScope = useBackupProject ? 'proj' : newProjName;
if (!useBackupProject || !directoryExists(tmpBackupProjPath())) {
runCreateWorkspace(projScope, { preset: 'empty', packageManager });
const packages = [ const packages = [
`@nrwl/angular`, `@nrwl/angular`,
`@nrwl/express`, `@nrwl/express`,
@ -131,12 +134,16 @@ export function newProject(): string {
`@nrwl/nx-plugin`, `@nrwl/nx-plugin`,
`@nrwl/eslint-plugin-nx`, `@nrwl/eslint-plugin-nx`,
]; ];
packageInstall(packages.join(` `), 'proj'); packageInstall(packages.join(` `), projScope);
moveSync(`./tmp/${currentCli()}/proj`, `${tmpBackupProjPath()}`); if (useBackupProject) {
moveSync(`./tmp/${currentCli()}/proj`, `${tmpBackupProjPath()}`);
}
} }
projName = uniq('proj'); projName = newProjName;
copySync(`${tmpBackupProjPath()}`, `${tmpProjPath()}`); if (useBackupProject) {
return 'proj'; copySync(`${tmpBackupProjPath()}`, `${tmpProjPath()}`);
}
return projScope;
} catch (e) { } catch (e) {
console.log(`Failed to set up project for e2e tests.`); console.log(`Failed to set up project for e2e tests.`);
console.log(e.message); console.log(e.message);
@ -178,7 +185,8 @@ export function runCommandUntil(
criteria: (output: string) => boolean, criteria: (output: string) => boolean,
{ kill = true } = {} { kill = true } = {}
): Promise<{ process: ChildProcess }> { ): Promise<{ process: ChildProcess }> {
const p = exec(`npm run nx --scripts-prepend-node-path -- ${command}`, { const pm = getPackageManagerCommand();
const p = exec(`${pm.runNx} ${command}`, {
cwd: tmpProjPath(), cwd: tmpProjPath(),
env: { ...process.env, FORCE_COLOR: 'false' }, env: { ...process.env, FORCE_COLOR: 'false' },
}); });
@ -217,10 +225,9 @@ export function runCLIAsync(
silent: false, silent: false,
} }
): Promise<{ stdout: string; stderr: string; combinedOutput: string }> { ): Promise<{ stdout: string; stderr: string; combinedOutput: string }> {
const pm = getPackageManagerCommand();
return runCommandAsync( return runCommandAsync(
`npm run nx ${ `${opts.silent ? pm.runNxSilent : pm.runNx} ${command}`,
opts.silent ? '--silent' : ''
} --scripts-prepend-node-path -- ${command}`,
opts opts
); );
} }
@ -265,9 +272,10 @@ export function runCLI(
} }
): string { ): string {
try { try {
let r = execSync(`npm run nx --scripts-prepend-node-path -- ${command}`, { const pm = getPackageManagerCommand();
let r = execSync(`${pm.runNx} ${command}`, {
cwd: opts.cwd || tmpProjPath(), cwd: opts.cwd || tmpProjPath(),
env: opts.env as any, env: opts.env,
}).toString(); }).toString();
r = r.replace( r = r.replace(
/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,
@ -448,3 +456,40 @@ function tmpBackupProjPath(path?: string) {
? `./tmp/${currentCli()}/proj-backup/${path}` ? `./tmp/${currentCli()}/proj-backup/${path}`
: `./tmp/${currentCli()}/proj-backup`; : `./tmp/${currentCli()}/proj-backup`;
} }
export function getPackageManagerCommand({
path = tmpProjPath(),
packageManager = detectPackageManager(path),
scriptsPrependNodePath = true,
} = {}): {
createWorkspace: string;
runNx: string;
runNxSilent: string;
add: string;
} {
const scriptsPrependNodePathFlag = scriptsPrependNodePath
? ' --scripts-prepend-node-path '
: '';
return {
npm: {
createWorkspace: `npx create-nx-workspace@${process.env.PUBLISHED_VERSION}`,
runNx: `npm run nx${scriptsPrependNodePathFlag} --`,
runNxSilent: `npm run nx --silent${scriptsPrependNodePathFlag} --`,
add: `npm install`,
},
yarn: {
// `yarn create nx-workspace` is failing due to wrong global path
createWorkspace: `yarn global add create-nx-workspace@${process.env.PUBLISHED_VERSION} && create-nx-workspace`,
runNx: `yarn nx`,
runNxSilent: `yarn --silent nx`,
add: `yarn add`,
},
pnpm: {
createWorkspace: `pnpx create-nx-workspace@${process.env.PUBLISHED_VERSION}`,
runNx: `pnpm run nx --`,
runNxSilent: `pnpm run nx --silent --`,
add: `pnpm add`,
},
}[packageManager];
}

View File

@ -1,5 +1,6 @@
import { NxJson } from '@nrwl/workspace'; import { NxJson } from '@nrwl/workspace';
import { import {
getPackageManagerCommand,
listFiles, listFiles,
newProject, newProject,
readFile, readFile,
@ -194,7 +195,7 @@ describe('run-many', () => {
}); });
describe('affected:*', () => { describe('affected:*', () => {
it('should print, build, and test affected apps', () => { it('should print, build, and test affected apps', async () => {
const proj = newProject(); const proj = newProject();
const myapp = uniq('myapp'); const myapp = uniq('myapp');
const myapp2 = uniq('myapp2'); const myapp2 = uniq('myapp2');
@ -232,9 +233,12 @@ describe('affected:*', () => {
` `
); );
expect( expect(
runCLI( (
`affected:apps --files="libs/${mylib}/src/index.ts" --plain` await runCLIAsync(
).split('\n')[4] `affected:apps --files="libs/${mylib}/src/index.ts" --plain`,
{ silent: true }
)
).stdout.trim()
).toEqual(myapp); ).toEqual(myapp);
const affectedApps = runCLI( const affectedApps = runCLI(
@ -255,9 +259,12 @@ describe('affected:*', () => {
expect(noAffectedApps).not.toContain(myapp2); expect(noAffectedApps).not.toContain(myapp2);
expect( expect(
runCLI( (
`affected:libs --files="libs/${mylib}/src/index.ts" --plain` await runCLIAsync(
).split('\n')[4] `affected:libs --files="libs/${mylib}/src/index.ts" --plain`,
{ silent: true }
)
).stdout.trim()
).toEqual(`${mylib} ${mypublishablelib}`); ).toEqual(`${mylib} ${mypublishablelib}`);
const affectedLibs = runCLI( const affectedLibs = runCLI(
@ -341,7 +348,7 @@ describe('affected:*', () => {
expect(isolatedTests).toContain(`- ${myapp}`); expect(isolatedTests).toContain(`- ${myapp}`);
const interpolatedTests = runCLI( const interpolatedTests = runCLI(
`affected --target test --files="libs/${mylib}/src/index.ts" -- --jest-config {project.root}/jest.config.js` `affected --target test --files="libs/${mylib}/src/index.ts" --jest-config {project.root}/jest.config.js`
); );
expect(interpolatedTests).toContain(`Running target \"test\" succeeded`); expect(interpolatedTests).toContain(`Running target \"test\" succeeded`);
}, 1000000); }, 1000000);
@ -480,6 +487,9 @@ describe('print-affected', () => {
).stdout.trim() ).stdout.trim()
); );
const { runNx } = getPackageManagerCommand({
scriptsPrependNodePath: false,
});
expect(resWithTarget.tasks[0]).toMatchObject({ expect(resWithTarget.tasks[0]).toMatchObject({
id: `${myapp}:test`, id: `${myapp}:test`,
overrides: {}, overrides: {},
@ -487,7 +497,7 @@ describe('print-affected', () => {
project: myapp, project: myapp,
target: 'test', target: 'test',
}, },
command: `npm run nx -- test ${myapp}`, command: `${runNx} test ${myapp}`,
outputs: [`coverage/apps/${myapp}`], outputs: [`coverage/apps/${myapp}`],
}); });
expect(resWithTarget.tasks[0].hash).toBeDefined(); expect(resWithTarget.tasks[0].hash).toBeDefined();
@ -509,7 +519,7 @@ describe('print-affected', () => {
project: myapp, project: myapp,
target: 'build', target: 'build',
}, },
command: `npm run nx -- build ${myapp}`, command: `${runNx} build ${myapp}`,
outputs: [`dist/apps/${myapp}`], outputs: [`dist/apps/${myapp}`],
}); });
expect(resWithDeps.tasks[0].hash).toBeDefined(); expect(resWithDeps.tasks[0].hash).toBeDefined();
@ -521,7 +531,7 @@ describe('print-affected', () => {
project: mypublishablelib, project: mypublishablelib,
target: 'build', target: 'build',
}, },
command: `npm run nx -- build ${mypublishablelib}`, command: `${runNx} build ${mypublishablelib}`,
outputs: [`dist/libs/${mypublishablelib}`], outputs: [`dist/libs/${mypublishablelib}`],
}); });
expect(resWithDeps.tasks[1].hash).toBeDefined(); expect(resWithDeps.tasks[1].hash).toBeDefined();

View File

@ -1,9 +1,10 @@
import { existsSync } from 'fs'; import { existsSync } from 'fs';
import { join } from 'path';
export function detectPackageManager() { export function detectPackageManager(dir = '') {
return existsSync('yarn.lock') return existsSync(join(dir, 'yarn.lock'))
? 'yarn' ? 'yarn'
: existsSync('pnpm-lock.yaml') : existsSync(join(dir, 'pnpm-lock.yaml'))
? 'pnpm' ? 'pnpm'
: 'npm'; : 'npm';
} }

View File

@ -3,36 +3,33 @@ storage: ../../tmp/local-registry/storage
auth: auth:
htpasswd: htpasswd:
file: ./htpasswd file: ./htpasswd
max_users: 1
# a list of other known repositories we can talk to # a list of other known repositories we can talk to
uplinks: uplinks:
npmjs: npmjs:
url: https://registry.npmjs.org/ url: https://registry.npmjs.org/
cache: true max_fails: 100
yarn: maxage: 30m
url: https://registry.yarnpkg.com fail_timeout: 10m
cache: true timeout: 600s
agent_options:
keepAlive: true
maxSockets: 40
maxFreeSockets: 10
cache: false
packages: packages:
'@*/*': '@nrwl/*':
# scoped packages
access: $all access: $all
publish: $all publish: $all
unpublish: $all
proxy: npmjs
'**': '**':
# allow all users (including non-authenticated users) to read and
# publish all packages
access: $all access: $all
# allow all users (including non-authenticated users) to publish/publish packages
publish: $all publish: $all
unpublish: $all unpublish: $all
# if package is not available locally, proxy requests to 'yarn' registry
proxy: npmjs proxy: npmjs
# log settings # log settings
logs: logs:
- { type: stdout, format: pretty, level: error } - { type: stdout, format: pretty, level: warn }