Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add --replace-registry-host=<npmjs|always|never> #4860

Merged
merged 1 commit into from Aug 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 18 additions & 0 deletions docs/content/using-npm/config.md
Expand Up @@ -1393,6 +1393,24 @@ The base URL of the npm registry.
<!-- automatically generated, do not edit manually -->
<!-- see lib/utils/config/definitions.js -->

#### `replace-registry-host`

* Default: "npmjs"
* Type: "npmjs", "never", "always", or String

Defines behavior for replacing the registry host in a lockfile with the
configured registry.

The default behavior is to replace package dist URLs from the default
registry (https://registry.npmjs.org) to the configured registry. If set to
"never", then use the registry value. If set to "always", then replace the
registry host with the configured host every time.

You may also specify a bare hostname (e.g., "registry.npmjs.org").

<!-- automatically generated, do not edit manually -->
<!-- see lib/utils/config/definitions.js -->

#### `save`

* Default: `true` unless when using `npm update` where it defaults to `false`
Expand Down
18 changes: 18 additions & 0 deletions lib/utils/config/definitions.js
Expand Up @@ -1649,6 +1649,24 @@ define('registry', {
flatten,
})

define('replace-registry-host', {
default: 'npmjs',
hint: '<npmjs|never|always> | hostname',
type: ['npmjs', 'never', 'always', String],
description: `
Defines behavior for replacing the registry host in a lockfile with the
configured registry.

The default behavior is to replace package dist URLs from the default
registry (https://registry.npmjs.org) to the configured registry. If set to
"never", then use the registry value. If set to "always", then replace the
registry host with the configured host every time.

You may also specify a bare hostname (e.g., "registry.npmjs.org").
`,
flatten,
})

define('save', {
default: true,
defaultDescription: `\`true\` unless when using \`npm update\` where it
Expand Down
2 changes: 2 additions & 0 deletions tap-snapshots/test/lib/commands/config.js.test.cjs
Expand Up @@ -121,6 +121,7 @@ exports[`test/lib/commands/config.js TAP config list --json > output matches sna
"read-only": false,
"rebuild-bundle": true,
"registry": "https://registry.npmjs.org/",
"replace-registry-host": "npmjs",
"save": true,
"save-bundle": false,
"save-dev": false,
Expand Down Expand Up @@ -277,6 +278,7 @@ proxy = null
read-only = false
rebuild-bundle = true
registry = "https://registry.npmjs.org/"
replace-registry-host = "npmjs"
save = true
save-bundle = false
save-dev = false
Expand Down
18 changes: 18 additions & 0 deletions tap-snapshots/test/lib/utils/config/definitions.js.test.cjs
Expand Up @@ -116,6 +116,7 @@ Array [
"read-only",
"rebuild-bundle",
"registry",
"replace-registry-host",
"save",
"save-bundle",
"save-dev",
Expand Down Expand Up @@ -1460,6 +1461,23 @@ exports[`test/lib/utils/config/definitions.js TAP > config description for regis
The base URL of the npm registry.
`

exports[`test/lib/utils/config/definitions.js TAP > config description for replace-registry-host 1`] = `
#### \`replace-registry-host\`

* Default: "npmjs"
* Type: "npmjs", "never", "always", or String

Defines behavior for replacing the registry host in a lockfile with the
configured registry.

The default behavior is to replace package dist URLs from the default
registry (https://registry.npmjs.org) to the configured registry. If set to
"never", then use the registry value. If set to "always", then replace the
registry host with the configured host every time.

You may also specify a bare hostname (e.g., "registry.npmjs.org").
`

exports[`test/lib/utils/config/definitions.js TAP > config description for save 1`] = `
#### \`save\`

Expand Down
18 changes: 18 additions & 0 deletions tap-snapshots/test/lib/utils/config/describe-all.js.test.cjs
Expand Up @@ -1266,6 +1266,24 @@ The base URL of the npm registry.
<!-- automatically generated, do not edit manually -->
<!-- see lib/utils/config/definitions.js -->
#### \`replace-registry-host\`
* Default: "npmjs"
* Type: "npmjs", "never", "always", or String
Defines behavior for replacing the registry host in a lockfile with the
configured registry.
The default behavior is to replace package dist URLs from the default
registry (https://registry.npmjs.org) to the configured registry. If set to
"never", then use the registry value. If set to "always", then replace the
registry host with the configured host every time.
You may also specify a bare hostname (e.g., "registry.npmjs.org").
<!-- automatically generated, do not edit manually -->
<!-- see lib/utils/config/definitions.js -->
#### \`save\`
* Default: \`true\` unless when using \`npm update\` where it defaults to \`false\`
Expand Down
4 changes: 4 additions & 0 deletions workspaces/arborist/lib/arborist/index.js
Expand Up @@ -74,8 +74,12 @@ class Arborist extends Base {
cache: options.cache || `${homedir()}/.npm/_cacache`,
packumentCache: options.packumentCache || new Map(),
workspacesEnabled: options.workspacesEnabled !== false,
replaceRegistryHost: options.replaceRegistryHost,
lockfileVersion: lockfileVersion(options.lockfileVersion),
}
this.replaceRegistryHost = this.options.replaceRegistryHost =
(!this.options.replaceRegistryHost || this.options.replaceRegistryHost === 'npmjs') ?
'registry.npmjs.org' : this.options.replaceRegistryHost

this[_workspacesEnabled] = this.options.workspacesEnabled

Expand Down
10 changes: 8 additions & 2 deletions workspaces/arborist/lib/arborist/reify.js
Expand Up @@ -712,13 +712,19 @@ module.exports = cls => class Reifier extends cls {
[_registryResolved] (resolved) {
// the default registry url is a magic value meaning "the currently
// configured registry".
// `resolved` must never be falsey.
//
// XXX: use a magic string that isn't also a valid value, like
// ${REGISTRY} or something. This has to be threaded through the
// Shrinkwrap and Node classes carefully, so for now, just treat
// the default reg as the magical animal that it has been.
return resolved && resolved
.replace(/^https?:\/\/registry\.npmjs\.org\//, this.registry)
const resolvedURL = new URL(resolved)
fritzy marked this conversation as resolved.
Show resolved Hide resolved
if ((this.options.replaceRegistryHost === resolvedURL.hostname)
|| this.options.replaceRegistryHost === 'always') {
// this.registry always has a trailing slash
resolved = `${this.registry.slice(0, -1)}${resolvedURL.pathname}${resolvedURL.searchParams}`
}
return resolved
}

// bundles are *sort of* like shrinkwraps, in that the branch is defined
Expand Down
9 changes: 9 additions & 0 deletions workspaces/arborist/test/arborist/index.js
Expand Up @@ -236,3 +236,12 @@ t.test('lockfileVersion config validation', async t => {
message: 'Invalid lockfileVersion config: banana',
})
})

t.test('valid replaceRegistryHost values', t => {
t.equal(new Arborist({ replaceRegistryHost: 'registry.garbage.com' }).options.replaceRegistryHost, 'registry.garbage.com')
t.equal(new Arborist({ replaceRegistryHost: 'npmjs' }).options.replaceRegistryHost, 'registry.npmjs.org')
t.equal(new Arborist({ replaceRegistryHost: undefined }).options.replaceRegistryHost, 'registry.npmjs.org')
t.equal(new Arborist({ replaceRegistryHost: 'always' }).options.replaceRegistryHost, 'always')
t.equal(new Arborist({ replaceRegistryHost: 'never' }).options.replaceRegistryHost, 'never')
t.end()
})
130 changes: 130 additions & 0 deletions workspaces/arborist/test/arborist/reify.js
Expand Up @@ -2,6 +2,7 @@ const { join, resolve, basename } = require('path')
const t = require('tap')
const runScript = require('@npmcli/run-script')
const localeCompare = require('@isaacs/string-locale-compare')('en')
const tnock = require('../fixtures/tnock')

// mock rimraf so we can make it fail in rollback tests
const realRimraf = require('rimraf')
Expand Down Expand Up @@ -2923,3 +2924,132 @@ t.test('installLinks', (t) => {

t.end()
})

t.only('should preserve exact ranges, missing actual tree', async (t) => {
const Arborist = require('../../lib/index.js')
const abbrev = resolve(__dirname,
'../fixtures/registry-mocks/content/abbrev/-/abbrev-1.1.1.tgz')
const abbrevTGZ = fs.readFileSync(abbrev)

const abbrevPackument = JSON.stringify({
_id: 'abbrev',
_rev: 'lkjadflkjasdf',
name: 'abbrev',
'dist-tags': { latest: '1.1.1' },
versions: {
'1.1.1': {
name: 'abbrev',
version: '1.1.1',
dist: {
tarball: 'https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz',
},
},
},
})

const abbrevPackument2 = JSON.stringify({
_id: 'abbrev',
_rev: 'lkjadflkjasdf',
name: 'abbrev',
'dist-tags': { latest: '1.1.1' },
versions: {
'1.1.1': {
name: 'abbrev',
version: '1.1.1',
dist: {
tarball: 'https://registry.garbage.org/abbrev/-/abbrev-1.1.1.tgz',
},
},
},
})

t.only('host should not be replaced replaceRegistryHost=never', async (t) => {
const testdir = t.testdir({
project: {
'package.json': JSON.stringify({
name: 'myproject',
version: '1.0.0',
dependencies: {
abbrev: '1.1.1',
},
}),
},
})

tnock(t, 'https://registry.github.com')
.get('/abbrev')
.reply(200, abbrevPackument)

tnock(t, 'https://registry.npmjs.org')
.get('/abbrev/-/abbrev-1.1.1.tgz')
.reply(200, abbrevTGZ)

const arb = new Arborist({
path: resolve(testdir, 'project'),
registry: 'https://registry.github.com',
cache: resolve(testdir, 'cache'),
replaceRegistryHost: 'never',
})
await arb.reify()
})

t.only('host should be replaced replaceRegistryHost=npmjs', async (t) => {
const testdir = t.testdir({
project: {
'package.json': JSON.stringify({
name: 'myproject',
version: '1.0.0',
dependencies: {
abbrev: '1.1.1',
},
}),
},
})

tnock(t, 'https://registry.github.com')
.get('/abbrev')
.reply(200, abbrevPackument)

tnock(t, 'https://registry.github.com')
.get('/abbrev/-/abbrev-1.1.1.tgz')
.reply(200, abbrevTGZ)

const arb = new Arborist({
path: resolve(testdir, 'project'),
registry: 'https://registry.github.com',
cache: resolve(testdir, 'cache'),
replaceRegistryHost: 'npmjs',
})
await arb.reify()
})

t.only('host should be always replaceRegistryHost=always', async (t) => {
const testdir = t.testdir({
project: {
'package.json': JSON.stringify({
name: 'myproject',
version: '1.0.0',
dependencies: {
abbrev: '1.1.1',
},
}),
},
})

tnock(t, 'https://registry.github.com')
.get('/abbrev')
.reply(200, abbrevPackument2)

tnock(t, 'https://registry.github.com')
.get('/abbrev/-/abbrev-1.1.1.tgz')
.reply(200, abbrevTGZ)

const arb = new Arborist({
path: resolve(testdir, 'project'),
registry: 'https://registry.github.com',
cache: resolve(testdir, 'cache'),
replaceRegistryHost: 'always',
})
await arb.reify()
})
})
14 changes: 14 additions & 0 deletions workspaces/arborist/test/fixtures/tnock.js
@@ -0,0 +1,14 @@
'use strict'

const nock = require('nock')

module.exports = tnock
function tnock (t, host) {
const server = nock(host)
nock.disableNetConnect()
t.teardown(function () {
nock.enableNetConnect()
server.done()
})
return server
}