Skip to content
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

feat(version): use npmClientArgs in npm install after lerna version #417

Merged
merged 2 commits into from Nov 22, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 10 additions & 0 deletions packages/cli/schemas/lerna-schema.json
Expand Up @@ -710,6 +710,9 @@
"npmClient": {
"$ref": "#/$defs/globals/npmClient"
},
"npmClientArgs": {
"$ref": "#/$defs/globals/npmClientArgs"
},
"loglevel": {
"$ref": "#/$defs/globals/loglevel"
},
Expand Down Expand Up @@ -1013,6 +1016,13 @@
"default": "npm",
"enum": ["npm", "pnpm", "yarn"]
},
"npmClientArgs": {
"type": "array",
"items": {
"type": "string"
},
"description": "Arguments to pass to the npm client when running commands."
},
"loglevel": {
"type": "string",
"description": "The level of logging to use when running commands. Defaults to 'info' if unspecified. You can see more logs by changing to the next log level: 'silent', 'error', 'warn', 'success', 'info', 'verbose' or 'silly'",
Expand Down
4 changes: 4 additions & 0 deletions packages/cli/src/cli-commands/cli-version-commands.ts
Expand Up @@ -244,6 +244,10 @@ export default {
hidden: true,
type: 'boolean',
},
'npm-client-args': {
describe: "Additional arguments to pass to the npm client when performing 'npm install'.",
type: 'array',
},
'no-sync-workspace-lock': {
describe:
'Do not run `npm install --package-lock-only` or equivalent depending on the package manager defined in `npmClient`.',
Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/models/command-options.ts
Expand Up @@ -329,6 +329,9 @@ export interface VersionCommandOption {
/** Defaults to true when found, update the project root lock file, the lib will internally read/write back to the lock file. */
manuallyUpdateRootLockfile?: boolean;

/** Additional arguments to pass to the npm client when performing 'npm install'. */
npmClientArgs?: string[];

/** Runs `npm install --package-lock-only` or equivalent depending on the package manager defined in `npmClient`. */
syncWorkspaceLock?: boolean;

Expand Down
53 changes: 48 additions & 5 deletions packages/version/src/__tests__/update-lockfile-version.spec.ts
Expand Up @@ -121,7 +121,7 @@ describe('run install lockfile-only', () => {
const execSyncSpy = jest.spyOn(core, 'execSync').mockReturnValue('8.5.0');
const cwd = await initFixture('lockfile-version2');

const lockFileOutput = await runInstallLockFileOnly('npm', cwd);
const lockFileOutput = await runInstallLockFileOnly('npm', cwd, []);

expect(execSyncSpy).toHaveBeenCalled();
expect(execSpy).toHaveBeenCalledWith('npm', ['install', '--package-lock-only'], { cwd });
Expand All @@ -134,21 +134,33 @@ describe('run install lockfile-only', () => {
const execSyncSpy = jest.spyOn(core, 'execSync').mockReturnValue('8.4.0');
const cwd = await initFixture('lockfile-version2');

const lockFileOutput = await runInstallLockFileOnly('npm', cwd);
const lockFileOutput = await runInstallLockFileOnly('npm', cwd, []);

expect(execSyncSpy).toHaveBeenCalled();
expect(execSpy).toHaveBeenCalledWith('npm', ['shrinkwrap', '--package-lock-only'], { cwd });
expect(renameSpy).toHaveBeenCalledWith('npm-shrinkwrap.json', 'package-lock.json');
expect(lockFileOutput).toBe('package-lock.json');
});

it(`should update project root lockfile by calling npm script "npm install --package-lock-only" with extra npm client arguments when provided`, async () => {
const execSpy = jest.spyOn(core, 'exec');
const execSyncSpy = jest.spyOn(core, 'execSync').mockReturnValue('8.5.0');
const cwd = await initFixture('lockfile-version2');

const lockFileOutput = await runInstallLockFileOnly('npm', cwd, ['--legacy-peer-deps']);

expect(execSyncSpy).toHaveBeenCalled();
expect(execSpy).toHaveBeenCalledWith('npm', ['install', '--package-lock-only', '--legacy-peer-deps'], { cwd });
expect(lockFileOutput).toBe('package-lock.json');
});
});

describe('pnpm client', () => {
it('should log an error when lockfile is not located under project root', async () => {
const logSpy = jest.spyOn(npmlog, 'error');
const cwd = await initFixture('lockfile-version2');

const lockFileOutput = await runInstallLockFileOnly('pnpm', cwd);
const lockFileOutput = await runInstallLockFileOnly('pnpm', cwd, []);

expect(logSpy).toHaveBeenCalledWith(
'lock',
Expand All @@ -166,11 +178,29 @@ describe('run install lockfile-only', () => {
const execSpy = jest.spyOn(core, 'exec');
const cwd = await initFixture('lockfile-version2');

const lockFileOutput = await runInstallLockFileOnly('pnpm', cwd);
const lockFileOutput = await runInstallLockFileOnly('pnpm', cwd, []);

expect(execSpy).toHaveBeenCalledWith('pnpm', ['install', '--lockfile-only', '--ignore-scripts'], { cwd });
expect(lockFileOutput).toBe('pnpm-lock.yaml');
});

it(`should update project root lockfile by calling client script "pnpm install --package-lock-only" with extra npm client arguments when provided`, async () => {
jest.spyOn(nodeFs.promises, 'access').mockResolvedValue(true as any);
(nodeFs.renameSync as jest.Mock).mockImplementation(() => true);
(core.exec as jest.Mock).mockImplementation(() => true);
const execSpy = jest.spyOn(core, 'exec');
const cwd = await initFixture('lockfile-version2');

const lockFileOutput = await runInstallLockFileOnly('pnpm', cwd, ['--frozen-lockfile']);

expect(execSpy).toHaveBeenCalled();
expect(execSpy).toHaveBeenCalledWith(
'pnpm',
['install', '--lockfile-only', '--ignore-scripts', '--frozen-lockfile'],
{ cwd }
);
expect(lockFileOutput).toBe('pnpm-lock.yaml');
});
});

describe('yarn client', () => {
Expand All @@ -181,10 +211,23 @@ describe('run install lockfile-only', () => {
const execSpy = jest.spyOn(core, 'exec');
const cwd = await initFixture('lockfile-version2');

const lockFileOutput = await runInstallLockFileOnly('yarn', cwd);
const lockFileOutput = await runInstallLockFileOnly('yarn', cwd, []);

expect(execSpy).toHaveBeenCalledWith('yarn', ['install', '--mode', 'update-lockfile'], { cwd });
expect(lockFileOutput).toBe('yarn.lock');
});

it(`should update project root lockfile by calling client script "yarn install --package-lock-only" with extra npm client arguments when provided`, async () => {
jest.spyOn(nodeFs.promises, 'access').mockResolvedValue(true as any);
(nodeFs.renameSync as jest.Mock).mockImplementation(() => true);
(core.exec as jest.Mock).mockImplementation(() => true);
const execSpy = jest.spyOn(core, 'exec');
const cwd = await initFixture('lockfile-version2');

const lockFileOutput = await runInstallLockFileOnly('yarn', cwd, ['--check-files']);

expect(execSpy).toHaveBeenCalledWith('yarn', ['install', '--mode', 'update-lockfile', '--check-files'], { cwd });
expect(lockFileOutput).toBe('yarn.lock');
});
});
});
11 changes: 6 additions & 5 deletions packages/version/src/lib/update-lockfile-version.ts
Expand Up @@ -126,7 +126,8 @@ export function updateNpmLockFileVersion2(obj: any, pkgName: string, newVersion:
*/
export async function runInstallLockFileOnly(
npmClient: 'npm' | 'pnpm' | 'yarn',
cwd: string
cwd: string,
npmClientArgs: string[]
): Promise<string | undefined> {
let inputLockfileName = '';
let outputLockfileName: string | undefined;
Expand All @@ -136,15 +137,15 @@ export async function runInstallLockFileOnly(
inputLockfileName = 'pnpm-lock.yaml';
if (await validateFileExists(path.join(cwd, inputLockfileName))) {
log.verbose('lock', `updating lock file via "pnpm install --lockfile-only --ignore-scripts"`);
await exec('pnpm', ['install', '--lockfile-only', '--ignore-scripts'], { cwd });
await exec('pnpm', ['install', '--lockfile-only', '--ignore-scripts', ...npmClientArgs], { cwd });
outputLockfileName = inputLockfileName;
}
break;
case 'yarn':
inputLockfileName = 'yarn.lock';
if (await validateFileExists(path.join(cwd, inputLockfileName))) {
log.verbose('lock', `updating lock file via "yarn install --mode update-lockfile"`);
await exec('yarn', ['install', '--mode', 'update-lockfile'], { cwd });
await exec('yarn', ['install', '--mode', 'update-lockfile', ...npmClientArgs], { cwd });
outputLockfileName = inputLockfileName;
}
break;
Expand All @@ -159,7 +160,7 @@ export async function runInstallLockFileOnly(
// however, when lower then we need to call "npm shrinkwrap --package-lock-only" and then rename "npm-shrinkwrap.json" file back to "package-lock.json"
if (semver.gte(localNpmVersion, '8.5.0')) {
log.verbose('lock', `updating lock file via "npm install --package-lock-only"`);
await exec('npm', ['install', '--package-lock-only'], { cwd });
await exec('npm', ['install', '--package-lock-only', ...npmClientArgs], { cwd });
} else {
// TODO: remove this in the next major release
// with npm < 8.5.0, we need to update the lock file in 2 steps
Expand All @@ -169,7 +170,7 @@ export async function runInstallLockFileOnly(
`npm`,
`Your npm version is lower than 8.5.0, we recommend upgrading your npm client to avoid the use of "npm shrinkwrap" instead of the regular (better) "npm install --package-lock-only".`
);
await exec('npm', ['shrinkwrap', '--package-lock-only'], { cwd });
await exec('npm', ['shrinkwrap', '--package-lock-only', ...npmClientArgs], { cwd });

// 2. rename "npm-shrinkwrap.json" back to "package-lock.json"
log.verbose('lock', `renaming "npm-shrinkwrap.json" file back to "package-lock.json"`);
Expand Down
10 changes: 6 additions & 4 deletions packages/version/src/version-command.ts
Expand Up @@ -725,11 +725,13 @@ export class VersionCommand extends Command<VersionCommandOption> {
} else if (this.options.syncWorkspaceLock) {
// update lock file, with npm client defined when `--sync-workspace-lock` is enabled
chain = chain.then(() =>
runInstallLockFileOnly(npmClient, this.project.manifest.location).then((lockfilePath) => {
if (lockfilePath) {
changedFiles.add(lockfilePath);
runInstallLockFileOnly(npmClient, this.project.manifest.location, this.options.npmClientArgs || []).then(
(lockfilePath) => {
if (lockfilePath) {
changedFiles.add(lockfilePath);
}
}
})
)
);
}

Expand Down