Skip to content

Commit bc9afb1

Browse files
isaacsruyadorno
authored andcommitted
Preserve builtin conf when installing npm globally
When a file named 'npmrc' is in the root of the npm module that is currently running, it adds config values that override the defaults (but not the global or user configs). This is a way for a system package installer to tell the npm that it installs to put its globals somewhere other than the default. In order to keep these configs around when users self-update npm with `npm i -g npm`, these config values must be "sticky", and ride along into the newly globally installed npm. This commit restores this behavior, fixing self-updating npm for Windows users, and any other systems that may make use of this functionality. Fixes: #2002 PR-URL: #2184 Credit: @isaacs Close: #2184 Reviewed-by: @ruyadorno
1 parent 4e522fd commit bc9afb1

12 files changed

Lines changed: 173 additions & 52 deletions

File tree

lib/audit.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ const Arborist = require('@npmcli/arborist')
22
const auditReport = require('npm-audit-report')
33
const npm = require('./npm.js')
44
const output = require('./utils/output.js')
5-
const reifyOutput = require('./utils/reify-output.js')
5+
const reifyFinish = require('./utils/reify-finish.js')
66
const auditError = require('./utils/audit-error.js')
77

88
const audit = async args => {
@@ -14,7 +14,7 @@ const audit = async args => {
1414
const fix = args[0] === 'fix'
1515
await arb.audit({ fix })
1616
if (fix)
17-
reifyOutput(arb)
17+
await reifyFinish(arb)
1818
else {
1919
// will throw if there's an error, because this is an audit command
2020
auditError(arb.auditReport)

lib/ci.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const util = require('util')
22
const Arborist = require('@npmcli/arborist')
33
const rimraf = util.promisify(require('rimraf'))
4-
const reifyOutput = require('./utils/reify-output.js')
4+
const reifyFinish = require('./utils/reify-finish.js')
55

66
const log = require('npmlog')
77
const npm = require('./npm.js')
@@ -35,7 +35,7 @@ const ci = async () => {
3535
])
3636
// npm ci should never modify the lockfile or package.json
3737
await arb.reify({ ...npm.flatOptions, save: false })
38-
reifyOutput(arb)
38+
await reifyFinish(arb)
3939
}
4040

4141
module.exports = Object.assign(cmd, { completion, usage })

lib/dedupe.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
const npm = require('./npm.js')
33
const Arborist = require('@npmcli/arborist')
44
const usageUtil = require('./utils/usage.js')
5-
const reifyOutput = require('./utils/reify-output.js')
5+
const reifyFinish = require('./utils/reify-finish.js')
66

77
const usage = usageUtil('dedupe', 'npm dedupe')
88
const completion = require('./utils/completion/none.js')
@@ -18,7 +18,7 @@ const dedupe = async (args) => {
1818
dryRun,
1919
})
2020
await arb.dedupe(npm.flatOptions)
21-
reifyOutput(arb)
21+
await reifyFinish(arb)
2222
}
2323

2424
module.exports = Object.assign(cmd, { usage, completion })

lib/install.js

Lines changed: 30 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ const util = require('util')
66
const readdir = util.promisify(fs.readdir)
77
const npm = require('./npm.js')
88
const usageUtil = require('./utils/usage.js')
9-
const reifyOutput = require('./utils/reify-output.js')
9+
const reifyFinish = require('./utils/reify-finish.js')
1010
const log = require('npmlog')
1111
const { resolve, join } = require('path')
1212
const Arborist = require('@npmcli/arborist')
1313
const runScript = require('@npmcli/run-script')
1414

15-
const install = async (args, cb) => {
15+
const cmd = async (args, cb) => install(args).then(() => cb()).catch(cb)
16+
17+
const install = async args => {
1618
// the /path/to/node_modules/..
1719
const globalTop = resolve(npm.globalDir, '..')
1820
const { ignoreScripts, global: isGlobalInstall } = npm.flatOptions
@@ -34,38 +36,33 @@ const install = async (args, cb) => {
3436
path: where,
3537
})
3638

37-
try {
38-
await arb.reify({
39-
...npm.flatOptions,
40-
add: args,
41-
})
42-
if (!args.length && !isGlobalInstall && !ignoreScripts) {
43-
const { scriptShell } = npm.flatOptions
44-
const scripts = [
45-
'preinstall',
46-
'install',
47-
'postinstall',
48-
'prepublish', // XXX should we remove this finally??
49-
'preprepare',
50-
'prepare',
51-
'postprepare',
52-
]
53-
for (const event of scripts) {
54-
await runScript({
55-
path: where,
56-
args: [],
57-
scriptShell,
58-
stdio: 'inherit',
59-
stdioString: true,
60-
event,
61-
})
62-
}
39+
await arb.reify({
40+
...npm.flatOptions,
41+
add: args,
42+
})
43+
if (!args.length && !isGlobalInstall && !ignoreScripts) {
44+
const { scriptShell } = npm.flatOptions
45+
const scripts = [
46+
'preinstall',
47+
'install',
48+
'postinstall',
49+
'prepublish', // XXX should we remove this finally??
50+
'preprepare',
51+
'prepare',
52+
'postprepare',
53+
]
54+
for (const event of scripts) {
55+
await runScript({
56+
path: where,
57+
args: [],
58+
scriptShell,
59+
stdio: 'inherit',
60+
stdioString: true,
61+
event,
62+
})
6363
}
64-
reifyOutput(arb)
65-
cb()
66-
} catch (er) {
67-
cb(er)
6864
}
65+
await reifyFinish(arb)
6966
}
7067

7168
const usage = usageUtil(
@@ -144,4 +141,4 @@ const completion = async (opts, cb) => {
144141
cb()
145142
}
146143

147-
module.exports = Object.assign(install, { usage, completion })
144+
module.exports = Object.assign(cmd, { usage, completion })

lib/link.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const semver = require('semver')
1010

1111
const npm = require('./npm.js')
1212
const usageUtil = require('./utils/usage.js')
13-
const reifyOutput = require('./utils/reify-output.js')
13+
const reifyFinish = require('./utils/reify-finish.js')
1414

1515
const completion = (opts, cb) => {
1616
const dir = npm.globalDir
@@ -122,7 +122,7 @@ const linkInstall = async args => {
122122
add: names.map(l => `file:${resolve(globalTop, 'node_modules', l)}`),
123123
})
124124

125-
reifyOutput(localArb)
125+
await reifyFinish(localArb)
126126
}
127127

128128
const linkPkg = async () => {
@@ -133,7 +133,7 @@ const linkPkg = async () => {
133133
global: true,
134134
})
135135
await arb.reify({ add: [`file:${npm.prefix}`] })
136-
reifyOutput(arb)
136+
await reifyFinish(arb)
137137
}
138138

139139
module.exports = Object.assign(cmd, { completion, usage })

lib/prune.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const npm = require('./npm.js')
33
const Arborist = require('@npmcli/arborist')
44
const usageUtil = require('./utils/usage.js')
55

6-
const reifyOutput = require('./utils/reify-output.js')
6+
const reifyFinish = require('./utils/reify-finish.js')
77

88
const usage = usageUtil('prune',
99
'npm prune [[<@scope>/]<pkg>...] [--production]'
@@ -19,7 +19,7 @@ const prune = async () => {
1919
path: where,
2020
})
2121
await arb.prune(npm.flatOptions)
22-
reifyOutput(arb)
22+
await reifyFinish(arb)
2323
}
2424

2525
module.exports = Object.assign(cmd, { usage, completion })

lib/uninstall.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const npm = require('./npm.js')
55
const rpj = require('read-package-json-fast')
66
const { resolve } = require('path')
77
const usageUtil = require('./utils/usage.js')
8-
const reifyOutput = require('./utils/reify-output.js')
8+
const reifyFinish = require('./utils/reify-finish.js')
99

1010
const cmd = (args, cb) => rm(args).then(() => cb()).catch(cb)
1111

@@ -32,7 +32,7 @@ const rm = async args => {
3232
...npm.flatOptions,
3333
rm: args,
3434
})
35-
reifyOutput(arb)
35+
await reifyFinish(arb)
3636
}
3737

3838
const usage = usageUtil(

lib/update.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const Arborist = require('@npmcli/arborist')
44
const log = require('npmlog')
55
const npm = require('./npm.js')
66
const usageUtil = require('./utils/usage.js')
7-
const reifyOutput = require('./utils/reify-output.js')
7+
const reifyFinish = require('./utils/reify-finish.js')
88
const completion = require('./utils/completion/installed-deep.js')
99

1010
const usage = usageUtil(
@@ -32,7 +32,7 @@ const update = async args => {
3232
})
3333

3434
await arb.reify({ update })
35-
reifyOutput(arb)
35+
await reifyFinish(arb)
3636
}
3737

3838
module.exports = Object.assign(cmd, { usage, completion })

lib/utils/reify-finish.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
const reifyOutput = require('./reify-output.js')
2+
const npm = require('../npm.js')
3+
const ini = require('ini')
4+
const {writeFile} = require('fs').promises
5+
const {resolve} = require('path')
6+
7+
const reifyFinish = async arb => {
8+
await saveBuiltinConfig(arb)
9+
reifyOutput(arb)
10+
}
11+
12+
const saveBuiltinConfig = async arb => {
13+
const { options: { global }, actualTree } = arb
14+
if (!global)
15+
return
16+
17+
// if we are using a builtin config, and just installed npm as
18+
// a top-level global package, we have to preserve that config.
19+
const npmNode = actualTree.inventory.get('node_modules/npm')
20+
if (!npmNode)
21+
return
22+
23+
const builtinConf = npm.config.data.get('builtin')
24+
if (builtinConf.loadError)
25+
return
26+
27+
const content = ini.stringify(builtinConf.raw).trim() + '\n'
28+
await writeFile(resolve(npmNode.path, 'npmrc'), content)
29+
}
30+
31+
module.exports = reifyFinish
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/* IMPORTANT
2+
* This snapshot file is auto-generated, but designed for humans.
3+
* It should be checked into source control and tracked carefully.
4+
* Re-generate by setting TAP_SNAPSHOT=1 and running tests.
5+
* Make sure to inspect the output below. Do not ignore changes!
6+
*/
7+
'use strict'
8+
exports[`test/lib/utils/reify-finish.js TAP should write if everything above passes > written config 1`] = `
9+
hasBuiltinConfig=true
10+
x=y
11+
12+
[nested]
13+
foo=bar
14+
15+
`

0 commit comments

Comments
 (0)