From f7b6314fe7e491bb50b20124517e82b1c5eac041 Mon Sep 17 00:00:00 2001 From: Gar Date: Tue, 27 Sep 2022 09:13:10 -0700 Subject: [PATCH 1/2] feat: default access to `public` BREAKING CHANGE: The default value of `access` is now `public` --- workspaces/libnpmpublish/README.md | 4 +- workspaces/libnpmpublish/lib/publish.js | 5 +- workspaces/libnpmpublish/test/publish.js | 62 +++++++++++++++++++++++- 3 files changed, 65 insertions(+), 6 deletions(-) diff --git a/workspaces/libnpmpublish/README.md b/workspaces/libnpmpublish/README.md index 85fb73e52fdbe..9c9c61d4b5965 100644 --- a/workspaces/libnpmpublish/README.md +++ b/workspaces/libnpmpublish/README.md @@ -44,8 +44,8 @@ A couple of options of note: defaults to `latest`. * `opts.access` - tells the registry whether this package should be - published as public or restricted. Only applies to scoped packages, which - default to restricted. + published as `public` or `restricted`. Only applies to scoped + packages. Defaults to `public`. * `opts.token` - can be passed in and will be used as the authentication token for the registry. For other ways to pass in auth details, see the diff --git a/workspaces/libnpmpublish/lib/publish.js b/workspaces/libnpmpublish/lib/publish.js index 75b764c98963f..7d01fabf1f2b4 100644 --- a/workspaces/libnpmpublish/lib/publish.js +++ b/workspaces/libnpmpublish/lib/publish.js @@ -17,10 +17,9 @@ Remove the 'private' field from the package.json to publish it.`), // spec is used to pick the appropriate registry/auth combo const spec = npa.resolve(manifest.name, manifest.version) opts = { - defaultTag: 'latest', - // if scoped, restricted by default - access: spec.scope ? 'restricted' : 'public', + access: 'public', algorithms: ['sha512'], + defaultTag: 'latest', ...opts, spec, } diff --git a/workspaces/libnpmpublish/test/publish.js b/workspaces/libnpmpublish/test/publish.js index fdd20f899430e..c696e82b22544 100644 --- a/workspaces/libnpmpublish/test/publish.js +++ b/workspaces/libnpmpublish/test/publish.js @@ -79,7 +79,66 @@ t.test('basic publish', async t => { t.ok(ret, 'publish succeeded') }) -t.test('scoped publish', async t => { +t.test('scoped publish - default access', async t => { + const manifest = { + name: '@claudiahdz/libnpmpublish', + version: '1.0.0', + description: 'some stuff', + } + + const tarData = await pack(`file:${testDir}`, { ...OPTS }) + const shasum = crypto.createHash('sha1').update(tarData).digest('hex') + const integrity = ssri.fromData(tarData, { algorithms: ['sha512'] }) + const packument = { + _id: '@claudiahdz/libnpmpublish', + name: '@claudiahdz/libnpmpublish', + description: 'some stuff', + 'dist-tags': { + latest: '1.0.0', + }, + versions: { + '1.0.0': { + _id: '@claudiahdz/libnpmpublish@1.0.0', + _nodeVersion: process.versions.node, + _npmVersion: '6.13.7', + name: '@claudiahdz/libnpmpublish', + version: '1.0.0', + description: 'some stuff', + dist: { + shasum, + integrity: integrity.toString(), + tarball: 'http://mock.reg/@claudiahdz/libnpmpublish/' + + '-/@claudiahdz/libnpmpublish-1.0.0.tgz', + }, + }, + }, + access: 'public', + _attachments: { + '@claudiahdz/libnpmpublish-1.0.0.tgz': { + content_type: 'application/octet-stream', + data: tarData.toString('base64'), + length: tarData.length, + }, + }, + } + + const srv = tnock(t, REG) + srv.put('/@claudiahdz%2flibnpmpublish', body => { + t.same(body, packument, 'posted packument matches expectations') + return true + }, { + authorization: 'Bearer deadbeef', + }).reply(201, {}) + + const ret = await publish(manifest, tarData, { + ...OPTS, + npmVersion: '6.13.7', + token: 'deadbeef', + }) + t.ok(ret, 'publish succeeded') +}) + +t.test('scoped publish - restricted access', async t => { const manifest = { name: '@claudiahdz/libnpmpublish', version: '1.0.0', @@ -132,6 +191,7 @@ t.test('scoped publish', async t => { const ret = await publish(manifest, tarData, { ...OPTS, + access: 'restricted', npmVersion: '6.13.7', token: 'deadbeef', }) From dd62db139849df4cee0206a3428d0bad5bea361e Mon Sep 17 00:00:00 2001 From: Gar Date: Wed, 28 Sep 2022 10:56:40 -0700 Subject: [PATCH 2/2] docs: update docs/logging for new --access default --- docs/content/commands/npm-publish.md | 20 ++-- docs/content/using-npm/config.md | 20 ++-- lib/commands/publish.js | 11 +- lib/utils/config/definitions.js | 22 ++-- .../test/lib/commands/publish.js.test.cjs | 100 +++++++++++++++++- .../lib/utils/config/definitions.js.test.cjs | 20 ++-- .../lib/utils/config/describe-all.js.test.cjs | 20 ++-- test/lib/commands/publish.js | 64 ++++++++++- 8 files changed, 217 insertions(+), 60 deletions(-) diff --git a/docs/content/commands/npm-publish.md b/docs/content/commands/npm-publish.md index 536d04988e684..0c1b777c881bb 100644 --- a/docs/content/commands/npm-publish.md +++ b/docs/content/commands/npm-publish.md @@ -118,19 +118,19 @@ tarball that will be compared with the local files by default. #### `access` -* Default: 'restricted' for scoped packages, 'public' for unscoped packages +* Default: 'public' for new packages, existing packages it will not change the + current level * Type: null, "restricted", or "public" -When publishing scoped packages, the access level defaults to `restricted`. -If you want your scoped package to be publicly viewable (and installable) -set `--access=public`. The only valid values for `access` are `public` and -`restricted`. Unscoped packages _always_ have an access level of `public`. +If do not want your scoped package to be publicly viewable (and installable) +set `--access=restricted`. -Note: Using the `--access` flag on the `npm publish` command will only set -the package access level on the initial publish of the package. Any -subsequent `npm publish` commands using the `--access` flag will not have an -effect to the access level. To make changes to the access level after the -initial publish use `npm access`. +Unscoped packages can not be set to `restricted`. + +Note: This defaults to not changing the current access level for existing +packages. Specifying a value of `restricted` or `public` during publish will +change the access for an existing package the same way that `npm access set +status` would. diff --git a/docs/content/using-npm/config.md b/docs/content/using-npm/config.md index 2de35fa2a46c3..e5d9d081feb4a 100644 --- a/docs/content/using-npm/config.md +++ b/docs/content/using-npm/config.md @@ -151,19 +151,19 @@ safer to use a registry-provided authentication bearer token stored in the #### `access` -* Default: 'restricted' for scoped packages, 'public' for unscoped packages +* Default: 'public' for new packages, existing packages it will not change the + current level * Type: null, "restricted", or "public" -When publishing scoped packages, the access level defaults to `restricted`. -If you want your scoped package to be publicly viewable (and installable) -set `--access=public`. The only valid values for `access` are `public` and -`restricted`. Unscoped packages _always_ have an access level of `public`. +If do not want your scoped package to be publicly viewable (and installable) +set `--access=restricted`. -Note: Using the `--access` flag on the `npm publish` command will only set -the package access level on the initial publish of the package. Any -subsequent `npm publish` commands using the `--access` flag will not have an -effect to the access level. To make changes to the access level after the -initial publish use `npm access`. +Unscoped packages can not be set to `restricted`. + +Note: This defaults to not changing the current access level for existing +packages. Specifying a value of `restricted` or `public` during publish will +change the access for an existing package the same way that `npm access set +status` would. diff --git a/lib/commands/publish.js b/lib/commands/publish.js index 64b6dfc513c95..1f281a0bb1d4f 100644 --- a/lib/commands/publish.js +++ b/lib/commands/publish.js @@ -114,10 +114,13 @@ class Publish extends BaseCommand { } } - log.notice( - '', - `Publishing to ${outputRegistry} with tag ${defaultTag}${dryRun ? ' (dry-run)' : ''}` - ) + const access = opts.access === null ? 'default' : opts.access + let msg = `Publishing to ${outputRegistry} with tag ${defaultTag} and ${access} access` + if (dryRun) { + msg = `${msg} (dry-run)` + } + + log.notice('', msg) if (!dryRun) { await otplease(this.npm, opts, opts => libpub(manifest, tarballData, opts)) diff --git a/lib/utils/config/definitions.js b/lib/utils/config/definitions.js index ae38efc32b5ae..5f74eb03b14c8 100644 --- a/lib/utils/config/definitions.js +++ b/lib/utils/config/definitions.js @@ -160,21 +160,19 @@ define('_auth', { define('access', { default: null, defaultDescription: ` - 'restricted' for scoped packages, 'public' for unscoped packages + 'public' for new packages, existing packages it will not change the current level `, type: [null, 'restricted', 'public'], description: ` - When publishing scoped packages, the access level defaults to - \`restricted\`. If you want your scoped package to be publicly viewable - (and installable) set \`--access=public\`. The only valid values for - \`access\` are \`public\` and \`restricted\`. Unscoped packages _always_ - have an access level of \`public\`. - - Note: Using the \`--access\` flag on the \`npm publish\` command will only - set the package access level on the initial publish of the package. Any - subsequent \`npm publish\` commands using the \`--access\` flag will not - have an effect to the access level. To make changes to the access level - after the initial publish use \`npm access\`. + If do not want your scoped package to be publicly viewable (and + installable) set \`--access=restricted\`. + + Unscoped packages can not be set to \`restricted\`. + + Note: This defaults to not changing the current access level for existing + packages. Specifying a value of \`restricted\` or \`public\` during + publish will change the access for an existing package the same way that + \`npm access set status\` would. `, flatten, }) diff --git a/tap-snapshots/test/lib/commands/publish.js.test.cjs b/tap-snapshots/test/lib/commands/publish.js.test.cjs index 3b215960fa37e..28211f7794cba 100644 --- a/tap-snapshots/test/lib/commands/publish.js.test.cjs +++ b/tap-snapshots/test/lib/commands/publish.js.test.cjs @@ -51,7 +51,7 @@ Array [ ], Array [ "", - "Publishing to https://registry.npmjs.org/ with tag latest (dry-run)", + "Publishing to https://registry.npmjs.org/ with tag latest and default access (dry-run)", ], ] ` @@ -72,7 +72,7 @@ exports[`test/lib/commands/publish.js TAP json > must match snapshot 1`] = ` Array [ Array [ "", - "Publishing to https://registry.npmjs.org/ with tag latest", + "Publishing to https://registry.npmjs.org/ with tag latest and default access", ], ] ` @@ -112,6 +112,53 @@ Array [ ] ` +exports[`test/lib/commands/publish.js TAP public access > must match snapshot 1`] = ` +Array [ + Array [ + "", + ], + Array [ + "", + "package: @npm/test-package@1.0.0", + ], + Array [ + "=== Tarball Contents ===", + ], + Array [ + "", + "55B package.json", + ], + Array [ + "=== Tarball Details ===", + ], + Array [ + "", + String( + name: @npm/test-package + version: 1.0.0 + filename: @npm/test-package-1.0.0.tgz + package size: 147 B + unpacked size: 55 B + shasum:{sha} + integrity:{sha} + total files: 1 + ), + ], + Array [ + "", + "", + ], + Array [ + "", + "Publishing to https://registry.npmjs.org/ with tag latest and public access", + ], +] +` + +exports[`test/lib/commands/publish.js TAP public access > new package version 1`] = ` ++ @npm/test-package@1.0.0 +` + exports[`test/lib/commands/publish.js TAP re-loads publishConfig.registry if added during script process > new package version 1`] = ` + test-package@1.0.0 ` @@ -120,6 +167,53 @@ exports[`test/lib/commands/publish.js TAP respects publishConfig.registry, runs ` +exports[`test/lib/commands/publish.js TAP restricted access > must match snapshot 1`] = ` +Array [ + Array [ + "", + ], + Array [ + "", + "package: @npm/test-package@1.0.0", + ], + Array [ + "=== Tarball Contents ===", + ], + Array [ + "", + "55B package.json", + ], + Array [ + "=== Tarball Details ===", + ], + Array [ + "", + String( + name: @npm/test-package + version: 1.0.0 + filename: @npm/test-package-1.0.0.tgz + package size: 147 B + unpacked size: 55 B + shasum:{sha} + integrity:{sha} + total files: 1 + ), + ], + Array [ + "", + "", + ], + Array [ + "", + "Publishing to https://registry.npmjs.org/ with tag latest and restricted access", + ], +] +` + +exports[`test/lib/commands/publish.js TAP restricted access > new package version 1`] = ` ++ @npm/test-package@1.0.0 +` + exports[`test/lib/commands/publish.js TAP scoped _auth config scoped registry > new package version 1`] = ` + @npm/test-package@1.0.0 ` @@ -165,7 +259,7 @@ Array [ ], Array [ "", - "Publishing to https://registry.npmjs.org/ with tag latest", + "Publishing to https://registry.npmjs.org/ with tag latest and default access", ], ] ` diff --git a/tap-snapshots/test/lib/utils/config/definitions.js.test.cjs b/tap-snapshots/test/lib/utils/config/definitions.js.test.cjs index df9313270d056..1005b9e4df112 100644 --- a/tap-snapshots/test/lib/utils/config/definitions.js.test.cjs +++ b/tap-snapshots/test/lib/utils/config/definitions.js.test.cjs @@ -180,19 +180,19 @@ safer to use a registry-provided authentication bearer token stored in the exports[`test/lib/utils/config/definitions.js TAP > config description for access 1`] = ` #### \`access\` -* Default: 'restricted' for scoped packages, 'public' for unscoped packages +* Default: 'public' for new packages, existing packages it will not change the + current level * Type: null, "restricted", or "public" -When publishing scoped packages, the access level defaults to \`restricted\`. -If you want your scoped package to be publicly viewable (and installable) -set \`--access=public\`. The only valid values for \`access\` are \`public\` and -\`restricted\`. Unscoped packages _always_ have an access level of \`public\`. +If do not want your scoped package to be publicly viewable (and installable) +set \`--access=restricted\`. -Note: Using the \`--access\` flag on the \`npm publish\` command will only set -the package access level on the initial publish of the package. Any -subsequent \`npm publish\` commands using the \`--access\` flag will not have an -effect to the access level. To make changes to the access level after the -initial publish use \`npm access\`. +Unscoped packages can not be set to \`restricted\`. + +Note: This defaults to not changing the current access level for existing +packages. Specifying a value of \`restricted\` or \`public\` during publish will +change the access for an existing package the same way that \`npm access set +status\` would. ` exports[`test/lib/utils/config/definitions.js TAP > config description for all 1`] = ` diff --git a/tap-snapshots/test/lib/utils/config/describe-all.js.test.cjs b/tap-snapshots/test/lib/utils/config/describe-all.js.test.cjs index 89ca7c952b182..e14b88464237c 100644 --- a/tap-snapshots/test/lib/utils/config/describe-all.js.test.cjs +++ b/tap-snapshots/test/lib/utils/config/describe-all.js.test.cjs @@ -24,19 +24,19 @@ safer to use a registry-provided authentication bearer token stored in the #### \`access\` -* Default: 'restricted' for scoped packages, 'public' for unscoped packages +* Default: 'public' for new packages, existing packages it will not change the + current level * Type: null, "restricted", or "public" -When publishing scoped packages, the access level defaults to \`restricted\`. -If you want your scoped package to be publicly viewable (and installable) -set \`--access=public\`. The only valid values for \`access\` are \`public\` and -\`restricted\`. Unscoped packages _always_ have an access level of \`public\`. +If do not want your scoped package to be publicly viewable (and installable) +set \`--access=restricted\`. -Note: Using the \`--access\` flag on the \`npm publish\` command will only set -the package access level on the initial publish of the package. Any -subsequent \`npm publish\` commands using the \`--access\` flag will not have an -effect to the access level. To make changes to the access level after the -initial publish use \`npm access\`. +Unscoped packages can not be set to \`restricted\`. + +Note: This defaults to not changing the current access level for existing +packages. Specifying a value of \`restricted\` or \`public\` during publish will +change the access for an existing package the same way that \`npm access set +status\` would. diff --git a/test/lib/commands/publish.js b/test/lib/commands/publish.js index 16b79df532d82..995abff88c2c1 100644 --- a/test/lib/commands/publish.js +++ b/test/lib/commands/publish.js @@ -28,7 +28,7 @@ t.cleanSnapshot = data => { t.test('respects publishConfig.registry, runs appropriate scripts', async t => { const { npm, joinedOutput, prefix } = await loadMockNpm(t, { config: { - loglevel: 'silent', // prevent scripts from leaking to stdout during the test + loglevel: 'silent', [`${alternateRegistry.slice(6)}/:_authToken`]: 'test-other-token', }, prefixDir: { @@ -730,3 +730,65 @@ t.test('scoped _auth config scoped registry', async t => { await npm.exec('publish', []) t.matchSnapshot(joinedOutput(), 'new package version') }) + +t.test('restricted access', async t => { + const spec = npa('@npm/test-package') + const { npm, joinedOutput, logs } = await loadMockNpm(t, { + config: { + ...auth, + access: 'restricted', + }, + prefixDir: { + 'package.json': JSON.stringify({ + name: '@npm/test-package', + version: '1.0.0', + }, null, 2), + }, + globals: ({ prefix }) => ({ + 'process.cwd': () => prefix, + }), + }) + const registry = new MockRegistry({ + tap: t, + registry: npm.config.get('registry'), + authorization: token, + }) + registry.nock.put(`/${spec.escapedName}`, body => { + t.equal(body.access, 'restricted', 'access is explicitly set to restricted') + return true + }).reply(200, {}) + await npm.exec('publish', []) + t.matchSnapshot(joinedOutput(), 'new package version') + t.matchSnapshot(logs.notice) +}) + +t.test('public access', async t => { + const spec = npa('@npm/test-package') + const { npm, joinedOutput, logs } = await loadMockNpm(t, { + config: { + ...auth, + access: 'public', + }, + prefixDir: { + 'package.json': JSON.stringify({ + name: '@npm/test-package', + version: '1.0.0', + }, null, 2), + }, + globals: ({ prefix }) => ({ + 'process.cwd': () => prefix, + }), + }) + const registry = new MockRegistry({ + tap: t, + registry: npm.config.get('registry'), + authorization: token, + }) + registry.nock.put(`/${spec.escapedName}`, body => { + t.equal(body.access, 'public', 'access is explicitly set to public') + return true + }).reply(200, {}) + await npm.exec('publish', []) + t.matchSnapshot(joinedOutput(), 'new package version') + t.matchSnapshot(logs.notice) +})