diff --git a/README.md b/README.md index a4fe257a..56511bab 100644 --- a/README.md +++ b/README.md @@ -47,13 +47,8 @@ Both the [token](https://docs.npmjs.com/getting-started/working_with_tokens) and | Variable | Description | | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------- | | `NPM_TOKEN` | Npm token created via [npm token create](https://docs.npmjs.com/getting-started/working_with_tokens#how-to-create-new-tokens) | -| `NPM_USERNAME` | Npm username created via [npm adduser](https://docs.npmjs.com/cli/adduser) or on [npmjs.com](https://www.npmjs.com) | -| `NPM_PASSWORD` | Password of the npm user. | -| `NPM_EMAIL` | Email address associated with the npm user | | `NPM_CONFIG_USERCONFIG` | Path to non-default .npmrc file | -Use either `NPM_TOKEN` for token authentication or `NPM_USERNAME`, `NPM_PASSWORD` and `NPM_EMAIL` for legacy authentication - ### Options | Options | Description | Default | diff --git a/index.js b/index.js index a46b0f79..178d132e 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,6 @@ import { castArray, defaultTo } from "lodash-es"; import AggregateError from "aggregate-error"; import { temporaryFile } from "tempy"; -import setLegacyToken from "./lib/set-legacy-token.js"; import getPkg from "./lib/get-pkg.js"; import verifyNpmConfig from "./lib/verify-config.js"; import verifyNpmAuth from "./lib/verify-auth.js"; @@ -26,8 +25,6 @@ export async function verifyConditions(pluginConfig, context) { const errors = verifyNpmConfig(pluginConfig); - setLegacyToken(context); - try { const pkg = await getPkg(pluginConfig, context); @@ -49,8 +46,6 @@ export async function verifyConditions(pluginConfig, context) { export async function prepare(pluginConfig, context) { const errors = verified ? [] : verifyNpmConfig(pluginConfig); - setLegacyToken(context); - try { // Reload package.json in case a previous external step updated it const pkg = await getPkg(pluginConfig, context); @@ -73,8 +68,6 @@ export async function publish(pluginConfig, context) { let pkg; const errors = verified ? [] : verifyNpmConfig(pluginConfig); - setLegacyToken(context); - try { // Reload package.json in case a previous external step updated it pkg = await getPkg(pluginConfig, context); @@ -100,8 +93,6 @@ export async function addChannel(pluginConfig, context) { let pkg; const errors = verified ? [] : verifyNpmConfig(pluginConfig); - setLegacyToken(context); - try { // Reload package.json in case a previous external step updated it pkg = await getPkg(pluginConfig, context); diff --git a/lib/set-legacy-token.js b/lib/set-legacy-token.js deleted file mode 100644 index 703fe615..00000000 --- a/lib/set-legacy-token.js +++ /dev/null @@ -1,6 +0,0 @@ -export default function ({ env }) { - // Set the environment variable `LEGACY_TOKEN` when user use the legacy auth, so it can be resolved by npm CLI - if (env.NPM_USERNAME && env.NPM_PASSWORD && env.NPM_EMAIL) { - env.LEGACY_TOKEN = Buffer.from(`${env.NPM_USERNAME}:${env.NPM_PASSWORD}`, "utf8").toString("base64"); - } -} diff --git a/lib/set-npmrc-auth.js b/lib/set-npmrc-auth.js index e00a20b0..6e92d080 100644 --- a/lib/set-npmrc-auth.js +++ b/lib/set-npmrc-auth.js @@ -6,11 +6,7 @@ import nerfDart from "nerf-dart"; import AggregateError from "aggregate-error"; import getError from "./get-error.js"; -export default async function ( - npmrc, - registry, - { cwd, env: { NPM_TOKEN, NPM_CONFIG_USERCONFIG, NPM_USERNAME, NPM_PASSWORD, NPM_EMAIL }, logger } -) { +export default async function (npmrc, registry, { cwd, env: { NPM_TOKEN, NPM_CONFIG_USERCONFIG }, logger }) { logger.log("Verify authentication for registry %s", registry); const { configs, ...rcConfig } = rc( "npm", @@ -29,13 +25,7 @@ export default async function ( return; } - if (NPM_USERNAME && NPM_PASSWORD && NPM_EMAIL) { - await fs.outputFile( - npmrc, - `${currentConfig ? `${currentConfig}\n` : ""}_auth = \${LEGACY_TOKEN}\nemail = \${NPM_EMAIL}` - ); - logger.log(`Wrote NPM_USERNAME, NPM_PASSWORD and NPM_EMAIL to ${npmrc}`); - } else if (NPM_TOKEN) { + if (NPM_TOKEN) { await fs.outputFile( npmrc, `${currentConfig ? `${currentConfig}\n` : ""}${nerfDart(registry)}:_authToken = \${NPM_TOKEN}` diff --git a/test/helpers/npm-registry.js b/test/helpers/npm-registry.js index 5d28b90b..99ad4d98 100644 --- a/test/helpers/npm-registry.js +++ b/test/helpers/npm-registry.js @@ -7,7 +7,7 @@ import path from "path"; import delay from "delay"; import pRetry from "p-retry"; -const IMAGE = "verdaccio/verdaccio:4"; +const IMAGE = "verdaccio/verdaccio:5"; const REGISTRY_PORT = 4873; const REGISTRY_HOST = "localhost"; const NPM_USERNAME = "integration"; @@ -16,7 +16,7 @@ const NPM_EMAIL = "integration@test.com"; const docker = new Docker(); const __dirname = dirname(fileURLToPath(import.meta.url)); -let container; +let container, npmToken; /** * Download the `npm-registry-docker` Docker image, create a new container and start it. @@ -57,16 +57,23 @@ export async function start() { email: NPM_EMAIL, }, }); + + // Create token for user + ({ token: npmToken } = await got(`http://${REGISTRY_HOST}:${REGISTRY_PORT}/-/npm/v1/tokens`, { + username: NPM_USERNAME, + password: NPM_PASSWORD, + method: "POST", + headers: { "content-type": "application/json" }, + json: { password: NPM_PASSWORD, readonly: false, cidr_whitelist: [] }, + }).json()); } export const url = `http://${REGISTRY_HOST}:${REGISTRY_PORT}/`; -export const authEnv = { +export const authEnv = () => ({ npm_config_registry: url, // eslint-disable-line camelcase - NPM_USERNAME, - NPM_PASSWORD, - NPM_EMAIL, -}; + NPM_TOKEN: npmToken, +}); /** * Stop and remove the `npm-registry-docker` Docker container. diff --git a/test/integration.test.js b/test/integration.test.js index c64de9a9..dbd4f2ae 100644 --- a/test/integration.test.js +++ b/test/integration.test.js @@ -10,18 +10,17 @@ import * as npmRegistry from "./helpers/npm-registry.js"; /* eslint camelcase: ["error", {properties: "never"}] */ // Environment variables used only for the local npm command used to do verification -const testEnv = { - ...process.env, - ...npmRegistry.authEnv, - npm_config_registry: npmRegistry.url, - LEGACY_TOKEN: Buffer.from(`${npmRegistry.authEnv.NPM_USERNAME}:${npmRegistry.authEnv.NPM_PASSWORD}`, "utf8").toString( - "base64" - ), -}; +let testEnv; test.before(async () => { // Start the local NPM registry await npmRegistry.start(); + + testEnv = { + ...process.env, + ...npmRegistry.authEnv(), + npm_config_registry: npmRegistry.url, + }; }); test.after.always(async () => { @@ -131,7 +130,7 @@ test("Verify npm auth and package", async (t) => { {}, { cwd, - env: npmRegistry.authEnv, + env: npmRegistry.authEnv(), options: {}, stdout: t.context.stdout, stderr: t.context.stderr, @@ -150,7 +149,7 @@ test("Verify npm auth and package from a sub-directory", async (t) => { { pkgRoot: "dist" }, { cwd, - env: npmRegistry.authEnv, + env: npmRegistry.authEnv(), options: {}, stdout: t.context.stdout, stderr: t.context.stderr, @@ -169,7 +168,7 @@ test('Verify npm auth and package with "npm_config_registry" env var set by yarn {}, { cwd, - env: { ...npmRegistry.authEnv, npm_config_registry: "https://registry.yarnpkg.com" }, + env: { ...npmRegistry.authEnv(), npm_config_registry: "https://registry.yarnpkg.com" }, options: { publish: [] }, stdout: t.context.stdout, stderr: t.context.stderr, @@ -216,7 +215,7 @@ test("Throw SemanticReleaseError Array if config option are not valid in verifyC test("Publish the package", async (t) => { const cwd = temporaryDirectory(); - const env = npmRegistry.authEnv; + const env = npmRegistry.authEnv(); const pkg = { name: "publish", version: "0.0.0", publishConfig: { registry: npmRegistry.url } }; await fs.outputJson(path.resolve(cwd, "package.json"), pkg); @@ -241,7 +240,7 @@ test("Publish the package", async (t) => { test("Publish the package on a dist-tag", async (t) => { const cwd = temporaryDirectory(); - const env = { ...npmRegistry.authEnv, DEFAULT_NPM_REGISTRY: npmRegistry.url }; + const env = { ...npmRegistry.authEnv(), DEFAULT_NPM_REGISTRY: npmRegistry.url }; const pkg = { name: "publish-tag", version: "0.0.0", publishConfig: { registry: npmRegistry.url, tag: "next" } }; await fs.outputJson(path.resolve(cwd, "package.json"), pkg); @@ -270,7 +269,7 @@ test("Publish the package on a dist-tag", async (t) => { test("Publish the package from a sub-directory", async (t) => { const cwd = temporaryDirectory(); - const env = npmRegistry.authEnv; + const env = npmRegistry.authEnv(); const pkg = { name: "publish-sub-dir", version: "0.0.0", publishConfig: { registry: npmRegistry.url } }; await fs.outputJson(path.resolve(cwd, "dist/package.json"), pkg); @@ -295,7 +294,7 @@ test("Publish the package from a sub-directory", async (t) => { test('Create the package and skip publish ("npmPublish" is false)', async (t) => { const cwd = temporaryDirectory(); - const env = npmRegistry.authEnv; + const env = npmRegistry.authEnv(); const pkg = { name: "skip-publish", version: "0.0.0", publishConfig: { registry: npmRegistry.url } }; await fs.outputJson(path.resolve(cwd, "package.json"), pkg); @@ -320,7 +319,7 @@ test('Create the package and skip publish ("npmPublish" is false)', async (t) => test('Create the package and skip publish ("package.private" is true)', async (t) => { const cwd = temporaryDirectory(); - const env = npmRegistry.authEnv; + const env = npmRegistry.authEnv(); const pkg = { name: "skip-publish-private", version: "0.0.0", @@ -350,7 +349,7 @@ test('Create the package and skip publish ("package.private" is true)', async (t test('Create the package and skip publish from a sub-directory ("npmPublish" is false)', async (t) => { const cwd = temporaryDirectory(); - const env = npmRegistry.authEnv; + const env = npmRegistry.authEnv(); const pkg = { name: "skip-publish-sub-dir", version: "0.0.0", publishConfig: { registry: npmRegistry.url } }; await fs.outputJson(path.resolve(cwd, "dist/package.json"), pkg); @@ -375,7 +374,7 @@ test('Create the package and skip publish from a sub-directory ("npmPublish" is test('Create the package and skip publish from a sub-directory ("package.private" is true)', async (t) => { const cwd = temporaryDirectory(); - const env = npmRegistry.authEnv; + const env = npmRegistry.authEnv(); const pkg = { name: "skip-publish-sub-dir-private", version: "0.0.0", @@ -440,7 +439,7 @@ test("Throw SemanticReleaseError Array if config option are not valid in publish test("Prepare the package", async (t) => { const cwd = temporaryDirectory(); - const env = npmRegistry.authEnv; + const env = npmRegistry.authEnv(); const pkg = { name: "prepare", version: "0.0.0", publishConfig: { registry: npmRegistry.url } }; await fs.outputJson(path.resolve(cwd, "package.json"), pkg); @@ -463,7 +462,7 @@ test("Prepare the package", async (t) => { test("Prepare the package from a sub-directory", async (t) => { const cwd = temporaryDirectory(); - const env = npmRegistry.authEnv; + const env = npmRegistry.authEnv(); const pkg = { name: "prepare-sub-dir", version: "0.0.0", publishConfig: { registry: npmRegistry.url } }; await fs.outputJson(path.resolve(cwd, "dist/package.json"), pkg); @@ -521,7 +520,7 @@ test("Throw SemanticReleaseError Array if config option are not valid in prepare test("Publish the package and add to default dist-tag", async (t) => { const cwd = temporaryDirectory(); - const env = npmRegistry.authEnv; + const env = npmRegistry.authEnv(); const pkg = { name: "add-channel", version: "0.0.0", publishConfig: { registry: npmRegistry.url } }; await fs.outputJson(path.resolve(cwd, "package.json"), pkg); @@ -557,7 +556,7 @@ test("Publish the package and add to default dist-tag", async (t) => { test("Publish the package and add to lts dist-tag", async (t) => { const cwd = temporaryDirectory(); - const env = npmRegistry.authEnv; + const env = npmRegistry.authEnv(); const pkg = { name: "add-channel-legacy", version: "1.0.0", publishConfig: { registry: npmRegistry.url } }; await fs.outputJson(path.resolve(cwd, "package.json"), pkg); @@ -596,7 +595,7 @@ test("Publish the package and add to lts dist-tag", async (t) => { test('Skip adding the package to a channel ("npmPublish" is false)', async (t) => { const cwd = temporaryDirectory(); - const env = npmRegistry.authEnv; + const env = npmRegistry.authEnv(); const pkg = { name: "skip-add-channel", version: "0.0.0", publishConfig: { registry: npmRegistry.url } }; await fs.outputJson(path.resolve(cwd, "package.json"), pkg); @@ -619,7 +618,7 @@ test('Skip adding the package to a channel ("npmPublish" is false)', async (t) = test('Skip adding the package to a channel ("package.private" is true)', async (t) => { const cwd = temporaryDirectory(); - const env = npmRegistry.authEnv; + const env = npmRegistry.authEnv(); const pkg = { name: "skip-add-channel-private", version: "0.0.0", @@ -647,7 +646,7 @@ test('Skip adding the package to a channel ("package.private" is true)', async ( test("Create the package in addChannel step", async (t) => { const cwd = temporaryDirectory(); - const env = npmRegistry.authEnv; + const env = npmRegistry.authEnv(); const pkg = { name: "add-channel-pkg", version: "0.0.0", publishConfig: { registry: npmRegistry.url } }; await fs.outputJson(path.resolve(cwd, "package.json"), pkg); @@ -670,7 +669,7 @@ test("Create the package in addChannel step", async (t) => { test("Throw SemanticReleaseError Array if config option are not valid in addChannel", async (t) => { const cwd = temporaryDirectory(); - const env = npmRegistry.authEnv; + const env = npmRegistry.authEnv(); const pkg = { publishConfig: { registry: npmRegistry.url } }; await fs.outputJson(path.resolve(cwd, "package.json"), pkg); const npmPublish = 42; @@ -706,7 +705,7 @@ test("Throw SemanticReleaseError Array if config option are not valid in addChan test("Verify token and set up auth only on the fist call, then prepare on prepare call only", async (t) => { const cwd = temporaryDirectory(); - const env = npmRegistry.authEnv; + const env = npmRegistry.authEnv(); const pkg = { name: "test-module", version: "0.0.0-dev", publishConfig: { registry: npmRegistry.url } }; await fs.outputJson(path.resolve(cwd, "package.json"), pkg); diff --git a/test/set-npmrc-auth.test.js b/test/set-npmrc-auth.test.js index 6c709850..5a5944b4 100644 --- a/test/set-npmrc-auth.test.js +++ b/test/set-npmrc-auth.test.js @@ -40,17 +40,6 @@ test.serial('Set auth with "NPM_TOKEN"', async (t) => { t.deepEqual(t.context.log.args[1], [`Wrote NPM_TOKEN to ${npmrc}`]); }); -test.serial('Set auth with "NPM_USERNAME", "NPM_PASSWORD" and "NPM_EMAIL"', async (t) => { - const npmrc = temporaryFile({ name: ".npmrc" }); - const env = { NPM_USERNAME: "npm_username", NPM_PASSWORD: "npm_pasword", NPM_EMAIL: "npm_email" }; - - const setNpmrcAuth = (await import("../lib/set-npmrc-auth.js")).default; - await setNpmrcAuth(npmrc, "http://custom.registry.com", { cwd, env, logger: t.context.logger }); - - t.is((await fs.readFile(npmrc)).toString(), `_auth = \${LEGACY_TOKEN}\nemail = \${NPM_EMAIL}`); - t.deepEqual(t.context.log.args[1], [`Wrote NPM_USERNAME, NPM_PASSWORD and NPM_EMAIL to ${npmrc}`]); -}); - test.serial('Preserve home ".npmrc"', async (t) => { const npmrc = temporaryFile({ name: ".npmrc" }); const env = { NPM_TOKEN: "npm_token" }; @@ -153,48 +142,6 @@ test.serial('Emulate npm config resolution if "NPM_CONFIG_USERCONFIG" is set', a t.deepEqual(t.context.log.args[1], ["Reading npm config from %s", [resolve(cwd, ".custom-npmrc")].join(", ")]); }); -test.serial('Throw error if "NPM_USERNAME" is missing', async (t) => { - const npmrc = temporaryFile({ name: ".npmrc" }); - const env = { NPM_PASSWORD: "npm_pasword", NPM_EMAIL: "npm_email" }; - - const setNpmrcAuth = (await import("../lib/set-npmrc-auth.js")).default; - const [error] = await t.throwsAsync( - setNpmrcAuth(npmrc, "http://custom.registry.com", { cwd, env, logger: t.context.logger }) - ); - - t.is(error.name, "SemanticReleaseError"); - t.is(error.message, "No npm token specified."); - t.is(error.code, "ENONPMTOKEN"); -}); - -test.serial('Throw error if "NPM_PASSWORD" is missing', async (t) => { - const npmrc = temporaryFile({ name: ".npmrc" }); - const env = { NPM_USERNAME: "npm_username", NPM_EMAIL: "npm_email" }; - - const setNpmrcAuth = (await import("../lib/set-npmrc-auth.js")).default; - const [error] = await t.throwsAsync( - setNpmrcAuth(npmrc, "http://custom.registry.com", { cwd, env, logger: t.context.logger }) - ); - - t.is(error.name, "SemanticReleaseError"); - t.is(error.message, "No npm token specified."); - t.is(error.code, "ENONPMTOKEN"); -}); - -test.serial('Throw error if "NPM_EMAIL" is missing', async (t) => { - const npmrc = temporaryFile({ name: ".npmrc" }); - const env = { NPM_USERNAME: "npm_username", NPM_PASSWORD: "npm_password" }; - - const setNpmrcAuth = (await import("../lib/set-npmrc-auth.js")).default; - const [error] = await t.throwsAsync( - setNpmrcAuth(npmrc, "http://custom.registry.com", { cwd, env, logger: t.context.logger }) - ); - - t.is(error.name, "SemanticReleaseError"); - t.is(error.message, "No npm token specified."); - t.is(error.code, "ENONPMTOKEN"); -}); - test.serial("Prefer .npmrc over environment variables", async (t) => { const npmrc = temporaryFile({ name: ".npmrc" }); // Specify an NPM token environment variable