Skip to content

Commit 197c641

Browse files
committed
Merge remote-tracking branch 'origin' into oidc-default-provenance
2 parents 3c730e3 + b7758d7 commit 197c641

File tree

6 files changed

+100
-68
lines changed

6 files changed

+100
-68
lines changed

CONTRIBUTING.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ When submitting a new bug report, please first [search](https://github.com/npm/c
1313
**1. Clone this repository...**
1414

1515
```bash
16-
$ git clone git@github.com:npm/cli.git npm
16+
git clone git@github.com:npm/cli.git npm
1717
```
1818

1919
**2. Navigate into project & install development-specific dependencies...**
2020

2121
```bash
22-
$ cd ./npm && node ./scripts/resetdeps.js
22+
cd ./npm && node ./scripts/resetdeps.js
2323
```
2424

2525
**3. Write some code &/or add some tests...**
@@ -30,7 +30,7 @@ $ cd ./npm && node ./scripts/resetdeps.js
3030

3131
**4. Run tests & ensure they pass...**
3232
```
33-
$ node . run test
33+
node . run test
3434
```
3535

3636
**5. Open a [Pull Request](https://github.com/npm/cli/pulls) for your work & become the newest contributor to `npm`! 🎉**
@@ -61,9 +61,9 @@ node . exec
6161
```
6262

6363
For example instead of:
64-
```bash
64+
```bash
6565
npm exec -- <package>
66-
```
66+
```
6767
Use:
6868
```bash
6969
node . exec -- <package>

docs/lib/content/configuring-npm/install.md

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,3 @@ installers:
6969

7070
Or see [this page](https://nodejs.org/en/download/package-manager/) to
7171
install npm for Linux in the way many Linux developers prefer.
72-
73-
#### Less-common operating systems
74-
75-
For more information on installing Node.js on a variety of operating
76-
systems, see [this page][pkg-mgr].
77-
78-
[pkg-mgr]: https://nodejs.org/en/download/package-manager/

tap-snapshots/test/lib/docs.js.test.cjs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1290,7 +1290,8 @@ a semver. Like the \`rc\` in \`1.2.0-rc.8\`.
12901290
12911291
#### \`progress\`
12921292
1293-
* Default: \`true\` unless running in a known CI system
1293+
* Default: \`true\` when not in CI and both stderr and stdout are TTYs and not
1294+
in a dumb terminal
12941295
* Type: Boolean
12951296
12961297
When set to \`true\`, npm will display a progress bar during time intensive

workspaces/arborist/lib/audit-report.js

Lines changed: 35 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
// an object representing the set of vulnerabilities in a tree
2-
/* eslint camelcase: "off" */
32

43
const localeCompare = require('@isaacs/string-locale-compare')('en')
54
const npa = require('npm-package-arg')
@@ -8,16 +7,15 @@ const pickManifest = require('npm-pick-manifest')
87
const Vuln = require('./vuln.js')
98
const Calculator = require('@npmcli/metavuln-calculator')
109

11-
const _getReport = Symbol('getReport')
12-
const _fixAvailable = Symbol('fixAvailable')
13-
const _checkTopNode = Symbol('checkTopNode')
14-
const _init = Symbol('init')
15-
const _omit = Symbol('omit')
1610
const { log, time } = require('proc-log')
1711

1812
const npmFetch = require('npm-registry-fetch')
1913

2014
class AuditReport extends Map {
15+
#omit
16+
error = null
17+
topVulns = new Map()
18+
2119
static load (tree, opts) {
2220
return new AuditReport(tree, opts).run()
2321
}
@@ -91,22 +89,18 @@ class AuditReport extends Map {
9189

9290
constructor (tree, opts = {}) {
9391
super()
94-
const { omit } = opts
95-
this[_omit] = new Set(omit || [])
96-
this.topVulns = new Map()
97-
92+
this.#omit = new Set(opts.omit || [])
9893
this.calculator = new Calculator(opts)
99-
this.error = null
10094
this.options = opts
10195
this.tree = tree
10296
this.filterSet = opts.filterSet
10397
}
10498

10599
async run () {
106-
this.report = await this[_getReport]()
100+
this.report = await this.#getReport()
107101
log.silly('audit report', this.report)
108102
if (this.report) {
109-
await this[_init]()
103+
await this.#init()
110104
}
111105
return this
112106
}
@@ -116,7 +110,7 @@ class AuditReport extends Map {
116110
return !!(vuln && vuln.isVulnerable(node))
117111
}
118112

119-
async [_init] () {
113+
async #init () {
120114
const timeEnd = time.start('auditReport:init')
121115

122116
const promises = []
@@ -171,7 +165,15 @@ class AuditReport extends Map {
171165
vuln.nodes.add(node)
172166
for (const { from: dep, spec } of node.edgesIn) {
173167
if (dep.isTop && !vuln.topNodes.has(dep)) {
174-
this[_checkTopNode](dep, vuln, spec)
168+
vuln.fixAvailable = this.#fixAvailable(vuln, spec)
169+
if (vuln.fixAvailable !== true) {
170+
// now we know the top node is vulnerable, and cannot be
171+
// upgraded out of the bad place without --force. But, there's
172+
// no need to add it to the actual vulns list, because nothing
173+
// depends on root.
174+
this.topVulns.set(vuln.name, vuln)
175+
vuln.topNodes.add(dep)
176+
}
175177
} else {
176178
// calculate a metavuln, if necessary
177179
const calc = this.calculator.calculate(dep.packageName, advisory)
@@ -214,33 +216,14 @@ class AuditReport extends Map {
214216
timeEnd()
215217
}
216218

217-
[_checkTopNode] (topNode, vuln, spec) {
218-
vuln.fixAvailable = this[_fixAvailable](topNode, vuln, spec)
219-
220-
if (vuln.fixAvailable !== true) {
221-
// now we know the top node is vulnerable, and cannot be
222-
// upgraded out of the bad place without --force. But, there's
223-
// no need to add it to the actual vulns list, because nothing
224-
// depends on root.
225-
this.topVulns.set(vuln.name, vuln)
226-
vuln.topNodes.add(topNode)
227-
}
228-
}
229-
230-
// check whether the top node is vulnerable.
231-
// check whether we can get out of the bad place with --force, and if
232-
// so, whether that update is SemVer Major
233-
[_fixAvailable] (topNode, vuln, spec) {
234-
// this will always be set to at least {name, versions:{}}
235-
const paku = vuln.packument
236-
219+
// given the spec, see if there is a fix available at all, and note whether or not it's a semver major fix or not (i.e. will need --force)
220+
#fixAvailable (vuln, spec) {
221+
// TODO we return true, false, OR an object here. this is probably a bad pattern.
237222
if (!vuln.testSpec(spec)) {
238223
return true
239224
}
240225

241-
// similarly, even if we HAVE a packument, but we're looking for it
242-
// somewhere other than the registry, and we got something vulnerable,
243-
// then we're stuck with it.
226+
// even if we HAVE a packument, if we're looking for it somewhere other than the registry and we have something vulnerable then we're stuck with it.
244227
const specObj = npa(spec)
245228
if (!specObj.registry) {
246229
return false
@@ -250,15 +233,13 @@ class AuditReport extends Map {
250233
spec = specObj.subSpec.rawSpec
251234
}
252235

253-
// We don't provide fixes for top nodes other than root, but we
254-
// still check to see if the node is fixable with a different version,
255-
// and if that is a semver major bump.
236+
// we don't provide fixes for top nodes other than root, but we still check to see if the node is fixable with a different version, and note if that is a semver major bump.
256237
try {
257238
const {
258239
_isSemVerMajor: isSemVerMajor,
259240
version,
260241
name,
261-
} = pickManifest(paku, spec, {
242+
} = pickManifest(vuln.packument, spec, {
262243
...this.options,
263244
before: null,
264245
avoid: vuln.range,
@@ -274,7 +255,7 @@ class AuditReport extends Map {
274255
throw new Error('do not call AuditReport.set() directly')
275256
}
276257

277-
async [_getReport] () {
258+
async #getReport () {
278259
// if we're not auditing, just return false
279260
if (this.options.audit === false || this.options.offline === true || this.tree.inventory.size === 1) {
280261
return null
@@ -312,11 +293,17 @@ class AuditReport extends Map {
312293

313294
// return true if we should audit this one
314295
shouldAudit (node) {
315-
return !node.version ? false
316-
: node.isRoot ? false
317-
: this.filterSet && this.filterSet.size !== 0 && !this.filterSet.has(node) ? false
318-
: this[_omit].size === 0 ? true
319-
: !node.shouldOmit(this[_omit])
296+
if (
297+
!node.version ||
298+
node.isRoot ||
299+
(this.filterSet && this.filterSet?.size !== 0 && !this.filterSet?.has(node))
300+
) {
301+
return false
302+
}
303+
if (this.#omit.size === 0) {
304+
return true
305+
}
306+
return !node.shouldOmit(this.#omit)
320307
}
321308

322309
prepareBulkData () {

workspaces/config/lib/definitions/definitions.js

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1571,9 +1571,9 @@ const definitions = {
15711571
},
15721572
}),
15731573
progress: new Definition('progress', {
1574-
default: !ciInfo.isCI,
1574+
default: !(ciInfo.isCI || !process.stderr.isTTY || !process.stdout.isTTY || process.env.TERM === 'dumb'),
15751575
defaultDescription: `
1576-
\`true\` unless running in a known CI system
1576+
\`true\` when not in CI and both stderr and stdout are TTYs and not in a dumb terminal
15771577
`,
15781578
type: Boolean,
15791579
description: `
@@ -1583,11 +1583,8 @@ const definitions = {
15831583
Set to \`false\` to suppress the progress bar.
15841584
`,
15851585
flatten (key, obj, flatOptions) {
1586-
flatOptions.progress = !obj.progress ? false
1587-
// progress is only written to stderr but we disable it unless stdout is a tty
1588-
// also. This prevents the progress from appearing when piping output to another
1589-
// command which doesn't break anything, but does look very odd to users.
1590-
: !!process.stderr.isTTY && !!process.stdout.isTTY && process.env.TERM !== 'dumb'
1586+
// Only show progress if explicitly enabled AND we have proper TTY environment
1587+
flatOptions.progress = !!obj.progress && !!process.stderr.isTTY && !!process.stdout.isTTY && process.env.TERM !== 'dumb'
15911588
},
15921589
}),
15931590
provenance: new Definition('provenance', {

workspaces/config/test/definitions/definitions.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,7 @@ t.test('progress', t => {
402402

403403
const flat = {}
404404

405+
// Test flatten function behavior
405406
mockDefs().progress.flatten('progress', {}, flat)
406407
t.strictSame(flat, { progress: false })
407408

@@ -417,6 +418,59 @@ t.test('progress', t => {
417418
mockDefs().progress.flatten('progress', { progress: true }, flat)
418419
t.strictSame(flat, { progress: false })
419420

421+
// Ensures consistency between default and flatOptions behavior
422+
t.test('default value consistency', t => {
423+
// Test case 1: Both TTYs, normal terminal, not CI
424+
setEnv({ tty: true, term: 'xterm' })
425+
const def1 = mockDefs({ 'ci-info': { isCI: false, name: null } }).progress
426+
t.equal(def1.default, true, 'default should be true when both TTYs, normal terminal, and not CI')
427+
428+
// Test case 2: No TTYs, not CI
429+
setEnv({ tty: false, term: 'xterm' })
430+
const def2 = mockDefs({ 'ci-info': { isCI: false, name: null } }).progress
431+
t.equal(def2.default, false, 'default should be false when no TTYs')
432+
433+
// Test case 3: Both TTYs but dumb terminal, not CI
434+
setEnv({ tty: true, term: 'dumb' })
435+
const def3 = mockDefs({ 'ci-info': { isCI: false, name: null } }).progress
436+
t.equal(def3.default, false, 'default should be false in dumb terminal')
437+
438+
// Test case 4: Mixed TTY states, not CI
439+
mockGlobals(t, {
440+
'process.stderr.isTTY': true,
441+
'process.stdout.isTTY': false,
442+
'process.env.TERM': 'xterm',
443+
})
444+
const def4 = mockDefs({ 'ci-info': { isCI: false, name: null } }).progress
445+
t.equal(def4.default, false, 'default should be false when only one TTY')
446+
447+
// Test case 5: Good TTY environment but in CI
448+
setEnv({ tty: true, term: 'xterm' })
449+
const def5 = mockDefs({ 'ci-info': { isCI: true, name: 'github-actions' } }).progress
450+
t.equal(def5.default, false, 'default should be false in CI even with good TTY environment')
451+
452+
t.end()
453+
})
454+
455+
// Test that flatten behavior is independent of CI detection
456+
t.test('flatten function ignores CI detection', t => {
457+
const flatObj = {}
458+
459+
// Test that CI doesn't affect flatten behavior when user explicitly enables
460+
setEnv({ tty: true, term: 'xterm' })
461+
const defsCI = mockDefs({ 'ci-info': { isCI: true, name: 'github-actions' } })
462+
defsCI.progress.flatten('progress', { progress: true }, flatObj)
463+
t.equal(flatObj.progress, true, 'flatten should enable progress in CI if user explicitly sets true and TTY is available')
464+
465+
// Test that non-CI doesn't guarantee flatten success if TTY is bad
466+
setEnv({ tty: false, term: 'xterm' })
467+
const defsNoCI = mockDefs({ 'ci-info': { isCI: false, name: null } })
468+
defsNoCI.progress.flatten('progress', { progress: true }, flatObj)
469+
t.equal(flatObj.progress, false, 'flatten should disable progress outside CI if TTY is not available')
470+
471+
t.end()
472+
})
473+
420474
t.end()
421475
})
422476

0 commit comments

Comments
 (0)