Skip to content

use private yarn pkg cache, ensure private npm/yarn bin always on path #23372

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 7, 2022
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
3 changes: 2 additions & 1 deletion tests/legacy-cli/e2e/initialize/300-log-environment.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ng } from '../utils/process';
import { exec, ng } from '../utils/process';

export default async function () {
console.log('Environment:');
Expand All @@ -13,5 +13,6 @@ export default async function () {
console.log(` ${envName}: ${process.env[envName]!.replace(/[\n\r]+/g, '\n ')}`);
});

await exec('which', 'ng', 'yarn', 'npm');
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a commit adding this line to ensure the correct ng/yarn/npm are being used. You can omit that commit if you don't think it's useful.

await ng('version');
}
27 changes: 20 additions & 7 deletions tests/legacy-cli/e2e/setup/002-npm-sandbox.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,46 @@
import { mkdir, writeFile } from 'fs/promises';
import { join } from 'path';
import { getGlobalVariable } from '../utils/env';
import { getGlobalVariable, setGlobalVariable } from '../utils/env';

/**
* Configure npm to use a unique sandboxed environment.
*/
export default async function () {
const tempRoot: string = getGlobalVariable('tmp-root');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the configured based on the package manager is used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The packages being tested (cli + yarn + npm) are all installed using npm before running the tests so I think we always need both (see 100-global-cli.ts).

const npmModulesPrefix = join(tempRoot, 'npm-global');
const yarnModulesPrefix = join(tempRoot, 'yarn-global');
const npmRegistry: string = getGlobalVariable('package-registry');
const npmrc = join(tempRoot, '.npmrc');
const yarnrc = join(tempRoot, '.yarnrc');

// Configure npm to use the sandboxed npm globals and rc file
// From this point onward all npm transactions use the "global" npm cache
// isolated within this e2e test invocation.
// Change the npm+yarn userconfig to the sandboxed npmrc to override the default ~
process.env.NPM_CONFIG_USERCONFIG = npmrc;
process.env.NPM_CONFIG_PREFIX = npmModulesPrefix;

// The npm+yarn registry URL
process.env.NPM_CONFIG_REGISTRY = npmRegistry;

// Configure npm+yarn to use a sandboxed bin directory
// From this point onward all yarn/npm bin files/symlinks are put into the prefix directories
process.env.NPM_CONFIG_PREFIX = npmModulesPrefix;
process.env.YARN_CONFIG_PREFIX = yarnModulesPrefix;

// Snapshot builds may contain versions that are not yet released (e.g., RC phase main branch).
// In this case peer dependency ranges may not resolve causing npm 7+ to fail during tests.
// To support this case, legacy peer dependency mode is enabled for snapshot builds.
if (getGlobalVariable('argv')['ng-snapshots']) {
process.env['NPM_CONFIG_legacy_peer_deps'] = 'true';
}

// Configure the registry and prefix used within the test sandbox
// Configure the registry and prefix used within the test sandbox via rc files
await writeFile(npmrc, `registry=${npmRegistry}\nprefix=${npmModulesPrefix}`);
await writeFile(yarnrc, `registry ${npmRegistry}\nprefix ${yarnModulesPrefix}`);

await mkdir(npmModulesPrefix);
await mkdir(yarnModulesPrefix);

setGlobalVariable('npm-global', npmModulesPrefix);
setGlobalVariable('yarn-global', yarnModulesPrefix);

console.log(` Using "${npmModulesPrefix}" as e2e test global npm cache.`);
console.log(` Using "${npmModulesPrefix}" as e2e test global npm bin dir.`);
console.log(` Using "${yarnModulesPrefix}" as e2e test global yarn bin dir.`);
}
51 changes: 26 additions & 25 deletions tests/legacy-cli/e2e/utils/process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ interface ExecOptions {
cwd?: string;
}

const NPM_CONFIG_RE = /^npm_config_/i;
const NPM_CONFIG_RE = /^(npm_config_|yarn_)/i;

let _processes: child_process.ChildProcess[] = [];

Expand All @@ -32,11 +32,19 @@ function _exec(options: ExecOptions, cmd: string, args: string[]): Promise<Proce
let stdout = '';
let stderr = '';
const cwd = options.cwd ?? process.cwd();
const env = options.env;
const env = options.env ?? process.env;
console.log(
`==========================================================================================`,
);

// Ensure the custom npm and yarn global bin is on the PATH
// https://docs.npmjs.com/cli/v8/configuring-npm/folders#executables
const paths = [
join(getGlobalVariable('yarn-global'), 'bin'),
join(getGlobalVariable('npm-global'), process.platform.startsWith('win') ? '' : 'bin'),
env.PATH || process.env['PATH'],
].join(delimiter);

args = args.filter((x) => x !== undefined);
const flags = [
options.silent && 'silent',
Expand All @@ -51,7 +59,7 @@ function _exec(options: ExecOptions, cmd: string, args: string[]): Promise<Proce

const spawnOptions: SpawnOptions = {
cwd,
...(env ? { env } : {}),
env: { ...env, PATH: paths },
};

if (process.platform.startsWith('win')) {
Expand Down Expand Up @@ -106,14 +114,20 @@ function _exec(options: ExecOptions, cmd: string, args: string[]): Promise<Proce

reject(
new Error(
`Running "${cmd} ${args.join(
' ',
)}" returned error. ${error}...\n\nSTDOUT:\n${stdout}\n\nSTDERR:\n${stderr}\n`,
`Running "${cmd} ${args.join(' ')}" returned error. ${error}...\n\nENV:${JSON.stringify(
process.env,
null,
2,
)}\n\nSTDOUT:\n${stdout}\n\nSTDERR:\n${stderr}\n`,
),
);
});
childProcess.on('error', (err) => {
err.message += `${err}...\n\nSTDOUT:\n${stdout}\n\nSTDERR:\n${stderr}\n`;
err.message += `${err}...\n\nENV:${JSON.stringify(
process.env,
null,
2,
)}\n\nSTDOUT:\n${stdout}\n\nSTDERR:\n${stderr}\n`;
reject(err);
});

Expand Down Expand Up @@ -146,15 +160,10 @@ function _exec(options: ExecOptions, cmd: string, args: string[]): Promise<Proce
export function extractNpmEnv() {
return Object.keys(process.env)
.filter((v) => NPM_CONFIG_RE.test(v))
.reduce<NodeJS.ProcessEnv>(
(vars, n) => {
vars[n] = process.env[n];
return vars;
},
{
PATH: process.env.PATH,
},
);
.reduce<NodeJS.ProcessEnv>((vars, n) => {
vars[n] = process.env[n];
return vars;
}, {});
}

export function waitForAnyProcessOutputToMatch(
Expand Down Expand Up @@ -364,20 +373,12 @@ export async function launchTestProcess(entry: string, ...args: any[]) {
};

// Modify the PATH environment variable...
let paths = process.env.PATH!.split(delimiter);
let paths = (env.PATH || process.env.PATH)!.split(delimiter);

// Only include paths within the sandboxed test environment or external
// non angular-cli paths such as /usr/bin for generic commands.
paths = paths.filter((p) => p.startsWith(tempRoot) || !p.includes('angular-cli'));

// Ensure the custom npm global bin is on the PATH
// https://docs.npmjs.com/cli/v8/configuring-npm/folders#executables
if (process.platform.startsWith('win')) {
paths.unshift(env.NPM_CONFIG_PREFIX!);
} else {
paths.unshift(join(env.NPM_CONFIG_PREFIX!, 'bin'));
}

env.PATH = paths.join(delimiter);

return _exec({ env }, process.execPath, [resolve(__dirname, 'run_test_process'), entry, ...args]);
Expand Down