-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Get correct npm prefix on all Windows unix shells
1. Set the shebang to /usr/bin/env bash instead of /bin/sh (which might be dash or some other shell) 2. Use Unix-style line endings, not Windows-style (Cygwin accepts either, but mingw bash sometimes objects, and WSL bash always does) 3. Test against paths using wslpath if available, but still pass win32 paths to node.exe, since it is a Windows binary that only knows how to handle Windows paths. This makes npm as installed by the Node.js Windows MSI installer behave properly under WSL, Cygwin, MINGW Git Bash, and the internal MINGW Git Bash when posix CLI utilities are exposed to the cmd.exe shell. The test is not quite as comprehensive as I'd like. It runs on the various Windows bash implementations if they are found in their expected locations, skipping any that are not installed. Short of shipping mingw, cygwin, and wsl as test fixtures, I'm not sure how we could do much better, however. At least, we can use this test to assist debug and catch issues on Windows machines (ours or users who report problems). PR-URL: #2789 Credit: @isaacs Close: #2789 Reviewed-by: @nlf
- Loading branch information
Showing
6 changed files
with
198 additions
and
49 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
const t = require('tap') | ||
|
||
if (process.platform !== 'win32') { | ||
t.plan(0, 'test only relevant on windows') | ||
process.exit(0) | ||
} | ||
|
||
const has = path => { | ||
try { | ||
// If WSL is installed, it *has* a bash.exe, but it fails if | ||
// there is no distro installed, so we need to detect that. | ||
const result = spawnSync(path, ['-l', '-c', 'exit 0']) | ||
if (result.status === 0) | ||
return true | ||
else { | ||
// print whatever error we got | ||
throw result.error || Object.assign(new Error(String(result.stderr)), { | ||
code: result.status, | ||
}) | ||
} | ||
} catch (er) { | ||
t.comment(`not installed: ${path}`, er) | ||
return false | ||
} | ||
} | ||
|
||
const { version } = require('../../package.json') | ||
const spawn = require('@npmcli/promise-spawn') | ||
const { spawnSync } = require('child_process') | ||
const { resolve } = require('path') | ||
const { ProgramFiles, SystemRoot } = process.env | ||
const { readFileSync, chmodSync } = require('fs') | ||
const gitBash = resolve(ProgramFiles, 'Git', 'bin', 'bash.exe') | ||
const gitUsrBinBash = resolve(ProgramFiles, 'Git', 'usr', 'bin', 'bash.exe') | ||
const wslBash = resolve(SystemRoot, 'System32', 'bash.exe') | ||
const cygwinBash = resolve(SystemRoot, '/', 'cygwin64', 'bin', 'bash.exe') | ||
|
||
const bashes = Object.entries({ | ||
'wsl bash': wslBash, | ||
'git bash': gitBash, | ||
'git internal bash': gitUsrBinBash, | ||
'cygwin bash': cygwinBash, | ||
}) | ||
|
||
const npmShim = resolve(__dirname, '../../bin/npm') | ||
const npxShim = resolve(__dirname, '../../bin/npx') | ||
|
||
const path = t.testdir({ | ||
'node.exe': readFileSync(process.execPath), | ||
npm: readFileSync(npmShim), | ||
npx: readFileSync(npxShim), | ||
// simulate the state where one version of npm is installed | ||
// with node, but we should load the globally installed one | ||
'global-prefix': { | ||
node_modules: { | ||
npm: t.fixture('symlink', resolve(__dirname, '../..')), | ||
}, | ||
}, | ||
// put in a shim that ONLY prints the intended global prefix, | ||
// and should not be used for anything else. | ||
node_modules: { | ||
npm: { | ||
bin: { | ||
'npx-cli.js': ` | ||
throw new Error('this should not be called') | ||
`, | ||
'npm-cli.js': ` | ||
const assert = require('assert') | ||
const args = process.argv.slice(2) | ||
assert.equal(args[0], 'prefix') | ||
assert.equal(args[1], '-g') | ||
const { resolve } = require('path') | ||
console.log(resolve(__dirname, '../../../global-prefix')) | ||
`, | ||
}, | ||
}, | ||
}, | ||
}) | ||
chmodSync(resolve(path, 'npm'), 0o755) | ||
chmodSync(resolve(path, 'npx'), 0o755) | ||
|
||
for (const [name, bash] of bashes) { | ||
if (!has(bash)) { | ||
t.skip(`${name} not installed`, { bin: bash, diagnostic: true }) | ||
continue | ||
} | ||
|
||
if (bash === cygwinBash && process.env.NYC_CONFIG) { | ||
t.skip('Cygwin does not play nicely with NYC, run without coverage') | ||
continue | ||
} | ||
|
||
t.test(name, async t => { | ||
t.plan(2) | ||
t.test('npm', async t => { | ||
// only cygwin *requires* the -l, but the others are ok with it | ||
// don't hit the registry for the update check | ||
const args = ['-l', 'npm', 'help'] | ||
|
||
const result = await spawn(bash, args, { | ||
env: { PATH: path, npm_config_update_notifier: 'false' }, | ||
cwd: path, | ||
stdioString: true, | ||
}) | ||
t.match(result, { | ||
cmd: bash, | ||
args: ['-l', 'npm', 'help'], | ||
code: 0, | ||
signal: null, | ||
stderr: String, | ||
// should have loaded this instance of npm we symlinked in | ||
stdout: `npm@${version} ${resolve(__dirname, '../..')}`, | ||
}) | ||
}) | ||
|
||
t.test('npx', async t => { | ||
const args = ['-l', 'npx', '--version'] | ||
|
||
const result = await spawn(bash, args, { | ||
env: { PATH: path, npm_config_update_notifier: 'false' }, | ||
cwd: path, | ||
stdioString: true, | ||
}) | ||
t.match(result, { | ||
cmd: bash, | ||
args: ['-l', 'npx', '--version'], | ||
code: 0, | ||
signal: null, | ||
stderr: String, | ||
// should have loaded this instance of npm we symlinked in | ||
stdout: version, | ||
}) | ||
}) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters