diff --git a/packages/core/src/child-process.ts b/packages/core/src/child-process.ts index c8b4dc3d..759b80f8 100644 --- a/packages/core/src/child-process.ts +++ b/packages/core/src/child-process.ts @@ -22,6 +22,7 @@ let currentColor = 0; * @param {string} command * @param {string[]} args * @param {import("execa").Options} [opts] + * @param {boolean} [dryRun] */ export function exec(command: string, args: string[], opts?: ExecaOptions & { pkg?: Package }, dryRun = false): Promise { const options = Object.assign({ stdio: 'pipe' }, opts); @@ -35,6 +36,7 @@ export function exec(command: string, args: string[], opts?: ExecaOptions & { pk * @param {string} command * @param {string[]} args * @param {import("execa").SyncOptions} [opts] + * @param {boolean} [dryRun] */ export function execSync(command: string, args?: string[], opts?: ExacaSyncOptions, dryRun = false) { // prettier-ignore @@ -48,6 +50,7 @@ export function execSync(command: string, args?: string[], opts?: ExacaSyncOptio * @param {string} command * @param {string[]} args * @param {import("execa").Options} [opts] + * @param {boolean} [dryRun] */ export function spawn(command: string, args: string[], opts?: ExecaOptions & { pkg?: Package }, dryRun = false): Promise { const options = Object.assign({}, opts, { stdio: 'inherit' }); @@ -62,6 +65,7 @@ export function spawn(command: string, args: string[], opts?: ExecaOptions & { p * @param {string[]} args * @param {import("execa").Options} [opts] * @param {string} [prefix] + * @param {boolean} [dryRun] */ /* c8 ignore next */ export function spawnStreaming( @@ -124,6 +128,7 @@ export function getExitCode(result: any) { * @param {string} command * @param {string[]} args * @param {import("execa").Options} opts + * @param {boolean} [dryRun] */ export function spawnProcess(command: string, args: string[], opts: ExecaOptions & { pkg?: Package }, dryRun = false) { if (dryRun) { diff --git a/packages/core/src/corepack/exec-package-manager.spec.ts b/packages/core/src/corepack/exec-package-manager.spec.ts new file mode 100644 index 00000000..a8b8df46 --- /dev/null +++ b/packages/core/src/corepack/exec-package-manager.spec.ts @@ -0,0 +1,121 @@ +import chalk from 'chalk'; +import npmlog from 'npmlog'; +import { beforeEach, describe, expect, it, Mock, vi } from 'vitest'; + +import { execPackageManager, execPackageManagerSync } from './exec-package-manager'; +import { exec, execSync, getChildProcessCount } from '../child-process'; +import { Package } from '../package'; + +vi.mock('../child-process', async () => ({ + ...(await vi.importActual('../child-process')), + exec: vi.fn(), + execSync: vi.fn(), + getChildProcessCount: (await vi.importActual('../child-process')).getChildProcessCount, +})); + +const execActual = (await vi.importActual('../child-process')).exec; +const execSyncActual = (await vi.importActual('../child-process')).execSync; + +describe('.execPackageManagerSync()', () => { + beforeEach(() => { + process.env = {}; + }); + + describe('mock child processes', () => { + it('calls execSync without corepack when disabled', () => { + execPackageManagerSync('echo', ['execPackageManagerSync']); + + expect(execSync).toHaveBeenCalledWith('echo', ['execPackageManagerSync'], undefined, false); + }); + + it('calls execSync with corepack when enabled', () => { + Object.assign({}, process.env); + process.env.COREPACK_ROOT = 'pnpm'; + + execPackageManagerSync('echo', ['execPackageManagerSync']); + + expect(execSync).toHaveBeenCalledWith('corepack', ['echo', 'execPackageManagerSync'], undefined, false); + }); + }); + + describe('import actual child processes', () => { + beforeEach(() => { + (execSync as Mock).mockImplementationOnce(execSyncActual); + }); + + it('should execute a command in a child process and return the result', () => { + expect(execPackageManagerSync('echo', ['execPackageManagerSync'])).toContain(`execPackageManagerSync`); + }); + + it('should execute a command in dry-run and log the command', () => { + const logSpy = vi.spyOn(npmlog, 'info'); + execPackageManagerSync('echo', ['execPackageManagerSync'], undefined, true); + expect(logSpy).toHaveBeenCalledWith(chalk.bold.magenta('[dry-run] >'), 'echo execPackageManagerSync'); + }); + + it('does not error when stdout is ignored', () => { + expect(() => execPackageManagerSync('echo', ['ignored'], { stdio: 'ignore' })).not.toThrow(); + }); + }); +}); + +describe('.execPackageManager()', () => { + beforeEach(() => { + process.env = {}; + }); + + describe('mock child processes', () => { + it('calls exec without corepack when disabled', () => { + execPackageManager('echo', ['execPackageManager']); + + expect(exec).toHaveBeenCalledWith('echo', ['execPackageManager'], undefined, false); + }); + + it('calls exec with corepack when enabled', () => { + Object.assign({}, process.env); + process.env.COREPACK_ROOT = 'pnpm'; + + execPackageManager('echo', ['execPackageManager']); + + expect(exec).toHaveBeenCalledWith('corepack', ['echo', 'execPackageManager'], undefined, false); + }); + }); + + describe('import actual child processes', () => { + beforeEach(() => { + (exec as Mock).mockImplementationOnce(execActual); + }); + + it('returns an execa Promise', async () => { + const { stderr, stdout } = (await execPackageManager('echo', ['foo'])) as any; + + expect(stderr).toBe(''); + expect(stdout).toContain(`foo`); + }); + + it('should execute a command in dry-run and log the command', () => { + const logSpy = vi.spyOn(npmlog, 'info'); + execPackageManager('echo', ['exec'], undefined, true); + expect(logSpy).toHaveBeenCalledWith(chalk.bold.magenta('[dry-run] >'), 'echo exec'); + }); + + it('rejects on undefined command', async () => { + const result = execPackageManager('nowImTheModelOfAModernMajorGeneral', undefined as any); + + await expect(result).rejects.toThrow(/\bnowImTheModelOfAModernMajorGeneral\b/); + expect(getChildProcessCount()).toBe(0); + }); + + it('decorates opts.pkg on error if caught', async () => { + const result = execPackageManager('theVeneratedVirginianVeteranWhoseMenAreAll', ['liningUpToPutMeUpOnAPedestal'], { + pkg: { name: 'hamilton' } as Package, + }); + + await expect(result).rejects.toThrow( + expect.objectContaining({ + pkg: { name: 'hamilton' }, + }) + ); + }); + }); +}); diff --git a/packages/core/src/corepack/exec-package-manager.ts b/packages/core/src/corepack/exec-package-manager.ts new file mode 100644 index 00000000..fe2dd400 --- /dev/null +++ b/packages/core/src/corepack/exec-package-manager.ts @@ -0,0 +1,28 @@ +import type { Options as ExecaOptions, SyncOptions as ExacaSyncOptions } from 'execa'; + +import { exec, execSync } from '../child-process'; +import { isCorepackEnabled } from './is-corepack-enabled'; +import type { Package } from '../package'; + +function createCommandAndArgs(npmClient: string, args: string[]) { + let command = npmClient; + const commandArgs = args === undefined ? [] : [...args]; + + if (isCorepackEnabled()) { + commandArgs.unshift(command); + command = 'corepack'; + } + + return { command, commandArgs }; +} + +// prettier-ignore +export function execPackageManager(npmClient: string, args: string[], opts?: ExecaOptions & { pkg?: Package }, dryRun = false): Promise { + const { command, commandArgs } = createCommandAndArgs(npmClient, args); + return exec(command, commandArgs, opts, dryRun); +} + +export function execPackageManagerSync(npmClient: string, args: string[], opts?: ExacaSyncOptions, dryRun = false): string { + const { command, commandArgs } = createCommandAndArgs(npmClient, args); + return execSync(command, commandArgs, opts, dryRun); +} diff --git a/packages/core/src/corepack/index.ts b/packages/core/src/corepack/index.ts new file mode 100644 index 00000000..3c92e3b5 --- /dev/null +++ b/packages/core/src/corepack/index.ts @@ -0,0 +1,2 @@ +export * from './exec-package-manager'; +export * from './is-corepack-enabled'; diff --git a/packages/core/src/corepack/is-corepack-enabled.ts b/packages/core/src/corepack/is-corepack-enabled.ts new file mode 100644 index 00000000..19363523 --- /dev/null +++ b/packages/core/src/corepack/is-corepack-enabled.ts @@ -0,0 +1,5 @@ +export function isCorepackEnabled() { + // https://github.com/nodejs/corepack#environment-variables + // The COREPACK_ROOT environment variable is specifically set by Corepack to indicate that it is running. + return process.env['COREPACK_ROOT'] !== undefined; +} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index a9835def..69b9f200 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,4 +1,5 @@ // folders +export * from './corepack/index.js'; export * from './models/index.js'; export * from './package-graph/index.js'; export * from './project/index.js'; diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json index ef90abdd..53b5e23d 100644 --- a/packages/core/tsconfig.json +++ b/packages/core/tsconfig.json @@ -2,6 +2,7 @@ "extends": "../../tsconfig.base.json", "compileOnSave": false, "compilerOptions": { + "module": "ESNext", "rootDir": "src", "outDir": "dist", "types": ["node"] diff --git a/packages/run/src/lib/__tests__/npm-run-script.spec.ts b/packages/run/src/lib/__tests__/npm-run-script.spec.ts index 066d8b47..d4049af9 100644 --- a/packages/run/src/lib/__tests__/npm-run-script.spec.ts +++ b/packages/run/src/lib/__tests__/npm-run-script.spec.ts @@ -3,14 +3,14 @@ import { describe, expect, it, Mock, vi } from 'vitest'; // mocked modules vi.mock('@lerna-lite/core'); -import { exec, spawnStreaming } from '@lerna-lite/core'; +import { execPackageManager, spawnStreaming } from '@lerna-lite/core'; import { RunScriptOption, ScriptStreamingOption } from '../../models'; // file under test import { npmRunScript, npmRunScriptStreaming } from '../npm-run-script'; describe('npm-run-script', () => { - (exec as Mock).mockResolvedValue(null); + (execPackageManager as Mock).mockResolvedValue(null); (spawnStreaming as Mock).mockResolvedValue(null); describe('npmRunScript()', () => { @@ -26,7 +26,7 @@ describe('npm-run-script', () => { await npmRunScript(script, config); - expect(exec).toHaveBeenLastCalledWith( + expect(execPackageManager).toHaveBeenLastCalledWith( 'npm', ['run', script, '--bar', 'baz'], { @@ -52,7 +52,7 @@ describe('npm-run-script', () => { await npmRunScript(script, config, true); - expect(exec).toHaveBeenLastCalledWith( + expect(execPackageManager).toHaveBeenLastCalledWith( 'npm', ['run', script, '--bar', 'baz'], { @@ -79,7 +79,7 @@ describe('npm-run-script', () => { await npmRunScript(script, config); - expect(exec).toHaveBeenLastCalledWith( + expect(execPackageManager).toHaveBeenLastCalledWith( 'npm', ['run', script], { @@ -105,7 +105,7 @@ describe('npm-run-script', () => { await npmRunScript(script, config); - expect(exec).toHaveBeenLastCalledWith( + expect(execPackageManager).toHaveBeenLastCalledWith( 'yarn', ['run', script, '--bar', 'baz'], { diff --git a/packages/run/src/lib/npm-run-script.ts b/packages/run/src/lib/npm-run-script.ts index 9736aeeb..0b5a0061 100644 --- a/packages/run/src/lib/npm-run-script.ts +++ b/packages/run/src/lib/npm-run-script.ts @@ -1,5 +1,5 @@ import log from 'npmlog'; -import { exec, Package, spawnStreaming } from '@lerna-lite/core'; +import { execPackageManager, Package, spawnStreaming } from '@lerna-lite/core'; import { getNpmExecOpts } from './get-npm-exec-opts.js'; import { RunScriptOption, ScriptStreamingOption } from '../models/index.js'; @@ -10,7 +10,7 @@ export function npmRunScript(script: string, { args, npmClient, pkg, reject = tr const argv = ['run', script, ...args]; const opts = makeOpts(pkg, reject); - return exec(npmClient, argv, opts, dryRun); + return execPackageManager(npmClient, argv, opts, dryRun); } export function npmRunScriptStreaming( diff --git a/packages/version/src/__tests__/update-lockfile-version.spec.ts b/packages/version/src/__tests__/update-lockfile-version.spec.ts index 813a68a5..185db52e 100644 --- a/packages/version/src/__tests__/update-lockfile-version.spec.ts +++ b/packages/version/src/__tests__/update-lockfile-version.spec.ts @@ -2,11 +2,11 @@ import { beforeEach, describe, expect, it, Mock, test, vi } from 'vitest'; vi.mock('load-json-file', async () => await vi.importActual('../lib/__mocks__/load-json-file')); vi.mock('@lerna-lite/core', async () => { - const { exec, execSync } = await vi.importActual('@lerna-lite/core'); + const { execPackageManager, execPackageManagerSync } = await vi.importActual('@lerna-lite/core'); return { ...(await vi.importActual('@lerna-lite/core')), - exec: vi.fn(exec), - execSync: vi.fn(execSync), + execPackageManager: vi.fn(execPackageManager), + execPackageManagerSync: vi.fn(execPackageManagerSync), }; }); @@ -23,7 +23,7 @@ import { loadJsonFile } from 'load-json-file'; // helpers const __filename = fileURLToPath(import.meta.url); const __dirname = pathDirname(__filename); -import { exec, execSync, Project } from '@lerna-lite/core'; +import { execPackageManager, execPackageManagerSync, Project } from '@lerna-lite/core'; import { initFixtureFactory } from '@lerna-test/helpers'; const initFixture = initFixtureFactory(__dirname); @@ -180,7 +180,7 @@ describe('validateFileExists() method', () => { describe('pnpm client', () => { it('should log an error when lockfile is not located under project root', async () => { - (exec as Mock).mockImplementationOnce(() => false); + (execPackageManager as Mock).mockImplementationOnce(() => false); const logSpy = vi.spyOn(npmlog, 'error'); const cwd = await initFixture('lockfile-version2'); @@ -195,24 +195,24 @@ describe('pnpm client', () => { it(`should update project root lockfile by calling client script "pnpm install --package-lock-only"`, async () => { vi.spyOn(fsPromises, 'access').mockResolvedValue(true as any); - (exec as Mock).mockImplementationOnce(() => true); + (execPackageManager as Mock).mockImplementationOnce(() => true); const cwd = await initFixture('lockfile-version2'); const lockFileOutput = await runInstallLockFileOnly('pnpm', cwd, []); - expect(exec).toHaveBeenCalledWith('pnpm', ['install', '--lockfile-only', '--ignore-scripts'], { cwd }); + expect(execPackageManager).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 () => { vi.spyOn(fsPromises, 'access').mockResolvedValue(true as any); - (exec as Mock).mockImplementationOnce(() => true); + (execPackageManager as Mock).mockImplementationOnce(() => true); const cwd = await initFixture('lockfile-version2'); const lockFileOutput = await runInstallLockFileOnly('pnpm', cwd, ['--frozen-lockfile']); - expect(exec).toHaveBeenCalled(); - expect(exec).toHaveBeenCalledWith('pnpm', ['install', '--lockfile-only', '--ignore-scripts', '--frozen-lockfile'], { cwd }); + expect(execPackageManager).toHaveBeenCalled(); + expect(execPackageManager).toHaveBeenCalledWith('pnpm', ['install', '--lockfile-only', '--ignore-scripts', '--frozen-lockfile'], { cwd }); expect(lockFileOutput).toBe('pnpm-lock.yaml'); }); }); @@ -220,28 +220,28 @@ describe('pnpm client', () => { describe('run install lockfile-only', () => { describe('npm client', () => { it(`should update project root lockfile by calling npm script "npm install --package-lock-only" when npm version is >= 8.5.0`, async () => { - (execSync as any).mockReturnValueOnce('8.5.0'); + (execPackageManagerSync as any).mockReturnValueOnce('8.5.0'); vi.spyOn(fsPromises, 'access').mockResolvedValue(true as any); - (exec as Mock).mockImplementationOnce(() => true); + (execPackageManager as Mock).mockImplementationOnce(() => true); const cwd = await initFixture('lockfile-version2'); const lockFileOutput = await runInstallLockFileOnly('npm', cwd, []); - expect(execSync).toHaveBeenCalledWith('npm', ['--version']); - expect(exec).toHaveBeenCalledWith('npm', ['install', '--package-lock-only'], { cwd }); + expect(execPackageManagerSync).toHaveBeenCalledWith('npm', ['--version']); + expect(execPackageManager).toHaveBeenCalledWith('npm', ['install', '--package-lock-only'], { cwd }); expect(lockFileOutput).toBe('package-lock.json'); }); it(`should display a log error when npm version is below 8.5.0 and not actually sync anything`, async () => { - (execSync as any).mockReturnValueOnce('8.4.0'); + (execPackageManagerSync as any).mockReturnValueOnce('8.4.0'); vi.spyOn(fsPromises, 'access').mockResolvedValue(true as any); - (exec as Mock).mockImplementationOnce(() => true); + (execPackageManager as Mock).mockImplementationOnce(() => true); const cwd = await initFixture('lockfile-version2'); const logSpy = vi.spyOn(npmlog, 'error'); await runInstallLockFileOnly('npm', cwd, []); - expect(execSync).toHaveBeenCalledWith('npm', ['--version']); + expect(execPackageManagerSync).toHaveBeenCalledWith('npm', ['--version']); expect(logSpy).toHaveBeenCalledWith( 'lock', expect.stringContaining('your npm version is lower than 8.5.0 which is the minimum requirement to use `--sync-workspace-lock`') @@ -249,26 +249,26 @@ describe('run install lockfile-only', () => { }); it(`should update project root lockfile by calling npm script "npm install --package-lock-only" with extra npm client arguments when provided`, async () => { - (execSync as any).mockReturnValueOnce('8.5.0'); + (execPackageManagerSync as any).mockReturnValueOnce('8.5.0'); vi.spyOn(fsPromises, 'access').mockResolvedValue(true as any); - (exec as Mock).mockImplementationOnce(() => true); + (execPackageManager as Mock).mockImplementationOnce(() => true); const cwd = await initFixture('lockfile-version2'); const lockFileOutput = await runInstallLockFileOnly('npm', cwd, ['--legacy-peer-deps']); - expect(execSync).toHaveBeenCalledWith('npm', ['--version']); - expect(exec).toHaveBeenCalledWith('npm', ['install', '--package-lock-only', '--legacy-peer-deps'], { cwd }); + expect(execPackageManagerSync).toHaveBeenCalledWith('npm', ['--version']); + expect(execPackageManager).toHaveBeenCalledWith('npm', ['install', '--package-lock-only', '--legacy-peer-deps'], { cwd }); expect(lockFileOutput).toBe('package-lock.json'); }); it(`should update project root lockfile by calling npm script "npm install --legacy-peer-deps,--force" with multiple npm client arguments provided as CSV`, async () => { - (execSync as any).mockReturnValueOnce('8.5.0'); + (execPackageManagerSync as any).mockReturnValueOnce('8.5.0'); const cwd = await initFixture('lockfile-version2'); const lockFileOutput = await runInstallLockFileOnly('npm', cwd, ['--legacy-peer-deps,--force']); - expect(execSync).toHaveBeenCalledWith('npm', ['--version']); - expect(exec).toHaveBeenCalledWith('npm', ['install', '--package-lock-only', '--legacy-peer-deps', '--force'], { + expect(execPackageManagerSync).toHaveBeenCalledWith('npm', ['--version']); + expect(execPackageManager).toHaveBeenCalledWith('npm', ['install', '--package-lock-only', '--legacy-peer-deps', '--force'], { cwd, }); expect(lockFileOutput).toBe('package-lock.json'); @@ -281,40 +281,52 @@ describe('run install lockfile-only', () => { }); it(`should NOT update project root lockfile when yarn version is 1.0.0 and is below 2.0.0`, async () => { - (execSync as any).mockReturnValueOnce('1.0.0'); + (execPackageManagerSync as any).mockReturnValueOnce('1.0.0'); vi.spyOn(fsPromises, 'access').mockResolvedValue(true as any); - (exec as Mock).mockImplementationOnce(() => true); + (execPackageManager as Mock).mockImplementationOnce(() => true); const cwd = await initFixture('lockfile-version2'); await runInstallLockFileOnly('yarn', cwd, []); - expect(execSync).toHaveBeenCalledWith('yarn', ['--version']); - expect(exec).not.toHaveBeenCalled(); + expect(execPackageManagerSync).toHaveBeenCalledWith('yarn', ['--version']); + expect(execPackageManager).not.toHaveBeenCalled(); }); it(`should update project root lockfile by calling client script "yarn install --package-lock-only"`, async () => { - (execSync as any).mockReturnValueOnce('3.0.0'); + (execPackageManagerSync as any).mockReturnValueOnce('3.0.0'); vi.spyOn(fsPromises, 'access').mockResolvedValue(true as any); - (exec as Mock).mockImplementationOnce(() => true); + (execPackageManager as Mock).mockImplementationOnce(() => true); const cwd = await initFixture('lockfile-version2'); const lockFileOutput = await runInstallLockFileOnly('yarn', cwd, []); - expect(execSync).toHaveBeenCalledWith('yarn', ['--version']); - expect(exec).toHaveBeenCalledWith('yarn', ['install', '--mode', 'update-lockfile'], { cwd }); + expect(execPackageManagerSync).toHaveBeenCalledWith('yarn', ['--version']); + expect(execPackageManager).toHaveBeenCalledWith('yarn', ['install', '--mode', 'update-lockfile'], { + cwd, + env: { + ...process.env, + YARN_ENABLE_SCRIPTS: 'false', + }, + }); 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 () => { - (execSync as any).mockReturnValueOnce('4.0.0'); + (execPackageManagerSync as any).mockReturnValueOnce('4.0.0'); vi.spyOn(fsPromises, 'access').mockResolvedValue(true as any); - (exec as Mock).mockImplementationOnce(() => true); + (execPackageManager as Mock).mockImplementationOnce(() => true); const cwd = await initFixture('lockfile-version2'); const lockFileOutput = await runInstallLockFileOnly('yarn', cwd, ['--check-files']); - expect(execSync).toHaveBeenCalledWith('yarn', ['--version']); - expect(exec).toHaveBeenCalledWith('yarn', ['install', '--mode', 'update-lockfile', '--check-files'], { cwd }); + expect(execPackageManagerSync).toHaveBeenCalledWith('yarn', ['--version']); + expect(execPackageManager).toHaveBeenCalledWith('yarn', ['install', '--mode', 'update-lockfile', '--check-files'], { + cwd, + env: { + ...process.env, + YARN_ENABLE_SCRIPTS: 'false', + }, + }); expect(lockFileOutput).toBe('yarn.lock'); }); }); diff --git a/packages/version/src/lib/update-lockfile-version.ts b/packages/version/src/lib/update-lockfile-version.ts index 59b5a8d4..09705fa5 100644 --- a/packages/version/src/lib/update-lockfile-version.ts +++ b/packages/version/src/lib/update-lockfile-version.ts @@ -5,7 +5,7 @@ import { join } from 'node:path'; import log from 'npmlog'; import semver from 'semver'; import { writeJsonFile } from 'write-json-file'; -import { exec, execSync, Package } from '@lerna-lite/core'; +import { execPackageManager, execPackageManagerSync, Package } from '@lerna-lite/core'; /** * From a folder path provided, try to load a `package-lock.json` file if it exists. @@ -139,16 +139,22 @@ export async function runInstallLockFileOnly( inputLockfileName = 'pnpm-lock.yaml'; if (await validateFileExists(join(cwd, inputLockfileName))) { log.verbose('lock', `updating lock file via "pnpm install --lockfile-only --ignore-scripts"`); - await exec('pnpm', ['install', '--lockfile-only', '--ignore-scripts', ...npmClientArgs], { cwd }); + await execPackageManager('pnpm', ['install', '--lockfile-only', '--ignore-scripts', ...npmClientArgs], { cwd }); outputLockfileName = inputLockfileName; } break; case 'yarn': inputLockfileName = 'yarn.lock'; - const yarnVersion = execSync('yarn', ['--version']); + const yarnVersion = execPackageManagerSync('yarn', ['--version']); if (semver.gte(yarnVersion, '2.0.0') && (await validateFileExists(join(cwd, inputLockfileName)))) { log.verbose('lock', `updating lock file via "yarn install --mode update-lockfile"`); - await exec('yarn', ['install', '--mode', 'update-lockfile', ...npmClientArgs], { cwd }); + await execPackageManager('yarn', ['install', '--mode', 'update-lockfile', ...npmClientArgs], { + cwd, + env: { + ...process.env, + YARN_ENABLE_SCRIPTS: 'false', + }, + }); outputLockfileName = inputLockfileName; } break; @@ -156,13 +162,13 @@ export async function runInstallLockFileOnly( default: inputLockfileName = 'package-lock.json'; if (await validateFileExists(join(cwd, inputLockfileName))) { - const localNpmVersion = execSync('npm', ['--version']); + const localNpmVersion = execPackageManagerSync('npm', ['--version']); log.silly(`npm`, `current local npm version is "${localNpmVersion}"`); // with npm version >=8.5.0, we can simply call "npm install --package-lock-only" 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', ...npmClientArgs], { cwd }); + await execPackageManager('npm', ['install', '--package-lock-only', ...npmClientArgs], { cwd }); } else { log.error( 'lock',