Skip to content

Commit

Permalink
feat(version): use npmClientArgs in npm install after lerna version (#…
Browse files Browse the repository at this point in the history
…417)

* feat(version): use npmClientArgs in npm install after lerna version
  • Loading branch information
ghiscoding committed Nov 22, 2022
1 parent be2af28 commit 43e5dcd
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 14 deletions.
10 changes: 10 additions & 0 deletions packages/cli/schemas/lerna-schema.json
Expand Up @@ -716,6 +716,9 @@
"npmClient": {
"$ref": "#/$defs/globals/npmClient"
},
"npmClientArgs": {
"$ref": "#/$defs/globals/npmClientArgs"
},
"loglevel": {
"$ref": "#/$defs/globals/loglevel"
},
Expand Down Expand Up @@ -1022,6 +1025,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 @@ -248,6 +248,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 @@ -336,6 +336,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 @@ -726,11 +726,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

0 comments on commit 43e5dcd

Please sign in to comment.