diff --git a/lib/commands/exec.js b/lib/commands/exec.js index 7b7d8ad30fcd5..930a68de0baae 100644 --- a/lib/commands/exec.js +++ b/lib/commands/exec.js @@ -88,8 +88,6 @@ class Exec extends BaseCommand { return libexec({ ...flatOptions, allowScripts: allowScriptsPolicy, - // we explicitly set packageLockOnly to false because if it's true when we try to install a missing package, we won't actually install it - packageLockOnly: false, // what the user asked to run args[0] is run by default args: [...args], // copy args so they don't get mutated // specify a custom command to be run instead of args[0] diff --git a/test/lib/commands/exec.js b/test/lib/commands/exec.js index 87422d1519a44..7abd17e31a8e6 100644 --- a/test/lib/commands/exec.js +++ b/test/lib/commands/exec.js @@ -52,6 +52,42 @@ t.test('registry package', async t => { t.ok(exists.isFile(), 'bin ran, creating file') }) +t.test('registry package with dry-run set', async t => { + const registry = new MockRegistry({ + tap: t, + registry: 'https://registry.npmjs.org/', + }) + + const manifest = registry.manifest({ name: '@npmcli/npx-dry-run-test' }) + manifest.versions['1.0.0'].bin = { 'npx-dry-run-test': 'index.js' } + + const { npm } = await loadMockNpm(t, { + config: { + audit: false, + yes: true, + 'dry-run': true, + }, + prefixDir: { + 'npm-exec-test': { + 'package.json': JSON.stringify(manifest), + 'index.js': `#!/usr/bin/env node + require('fs').writeFileSync('npm-exec-test-success', '')`, + }, + }, + }) + + await registry.package({ + times: 2, + manifest, + tarballs: { + '1.0.0': path.join(npm.prefix, 'npm-exec-test'), + } }) + + await npm.exec('exec', ['@npmcli/npx-dry-run-test']) + const exists = await fs.stat(path.join(npm.prefix, 'npm-exec-test-success')) + t.ok(exists.isFile(), 'package was installed and bin ran, creating file') +}) + t.test('--prefix', async t => { const registry = new MockRegistry({ tap: t, diff --git a/workspaces/libnpmexec/lib/index.js b/workspaces/libnpmexec/lib/index.js index 4d7c126654689..56529693a51b6 100644 --- a/workspaces/libnpmexec/lib/index.js +++ b/workspaces/libnpmexec/lib/index.js @@ -263,6 +263,10 @@ const exec = async (opts) => { await mkdir(installDir, { recursive: true }) const npxArb = new Arborist({ ...flatOptions, + // a missing package must actually be installed to the npx cache, so it can be run, + // even when these are set: eg, a lifecycle script inherits npm_config_dry_run from `npm publish --dry-run` + dryRun: false, + packageLockOnly: false, path: installDir, }) const lockPath = join(installDir, 'concurrency.lock') diff --git a/workspaces/libnpmexec/test/registry.js b/workspaces/libnpmexec/test/registry.js index adbf4116107b4..b2ea492f4359f 100644 --- a/workspaces/libnpmexec/test/registry.js +++ b/workspaces/libnpmexec/test/registry.js @@ -328,3 +328,29 @@ t.test('override save to true when installing to npx cache', async t => { value: 'packages-2.0.0', }) }) + +t.test('install to npx cache when dryRun or packageLockOnly is set', async t => { + for (const flag of ['dryRun', 'packageLockOnly']) { + t.test(flag, async t => { + const { fixtures, package } = createPkg({ versions: ['2.0.0'] }) + + const { exec, path, registry, readOutput } = setup(t, { + testdir: merge(fixtures, { + global: {}, + }), + }) + + await package({ registry, path }) + + await exec({ + args: ['@npmcli/create-index'], + globalPath: resolve(path, 'global'), + [flag]: true, + }) + + t.match(await readOutput('@npmcli-create-index'), { + value: 'packages-2.0.0', + }, 'package was installed and its bin ran') + }) + } +})