Skip to content

Commit

Permalink
Merge branch 'master' into beta
Browse files Browse the repository at this point in the history
  • Loading branch information
pvdlg committed Oct 4, 2019
2 parents 4f932b4 + 8726eff commit b8e51ff
Show file tree
Hide file tree
Showing 13 changed files with 134 additions and 82 deletions.
4 changes: 3 additions & 1 deletion README.md
Expand Up @@ -74,7 +74,9 @@ Use either `NPM_TOKEN` for token authentication or `NPM_USERNAME`, `NPM_PASSWORD

The plugin uses the [`npm` CLI](https://github.com/npm/cli) which will read the configuration from [`.npmrc`](https://docs.npmjs.com/files/npmrc). See [`npm config`](https://docs.npmjs.com/misc/config) for the option list.

The [`registry`](https://docs.npmjs.com/misc/registry) and [`dist-tag`](https://docs.npmjs.com/cli/dist-tag) can be configured in the `package.json` and will take precedence over the configuration in `.npmrc`:
The [`registry`](https://docs.npmjs.com/misc/registry) can be configured via the npm environment variable `NPM_CONFIG_REGISTRY` and will take precedence over the configuration in `.npmrc`.

The [`registry`](https://docs.npmjs.com/misc/registry) and [`dist-tag`](https://docs.npmjs.com/cli/dist-tag) can be configured in the `package.json` and will take precedence over the configuration in `.npmrc` and `NPM_CONFIG_REGISTRY`:
```json
{
"publishConfig": {
Expand Down
18 changes: 10 additions & 8 deletions index.js
@@ -1,5 +1,6 @@
const {defaultTo, castArray} = require('lodash');
const AggregateError = require('aggregate-error');
const tempy = require('tempy');
const setLegacyToken = require('./lib/set-legacy-token');
const getPkg = require('./lib/get-pkg');
const verifyNpmConfig = require('./lib/verify-config');
Expand All @@ -10,6 +11,7 @@ const publishNpm = require('./lib/publish');

let verified;
let prepared;
const npmrc = tempy.file({name: '.npmrc'});

async function verifyConditions(pluginConfig, context) {
// If the npm publish plugin is used and has `npmPublish`, `tarballDir` or `pkgRoot` configured, validate them now in order to prevent any release if the configuration is wrong
Expand All @@ -31,7 +33,7 @@ async function verifyConditions(pluginConfig, context) {

// Verify the npm authentication only if `npmPublish` is not false and `pkg.private` is not `true`
if (pluginConfig.npmPublish !== false && pkg.private !== true) {
await verifyNpmAuth(pluginConfig, pkg, context);
await verifyNpmAuth(npmrc, pkg, context);
}
} catch (error) {
errors.push(...error);
Expand All @@ -53,7 +55,7 @@ async function prepare(pluginConfig, context) {
// Reload package.json in case a previous external step updated it
const pkg = await getPkg(pluginConfig, context);
if (!verified && pluginConfig.npmPublish !== false && pkg.private !== true) {
await verifyNpmAuth(pluginConfig, pkg, context);
await verifyNpmAuth(npmrc, pkg, context);
}
} catch (error) {
errors.push(...error);
Expand All @@ -63,7 +65,7 @@ async function prepare(pluginConfig, context) {
throw new AggregateError(errors);
}

await prepareNpm(pluginConfig, context);
await prepareNpm(npmrc, pluginConfig, context);
prepared = true;
}

Expand All @@ -77,7 +79,7 @@ async function publish(pluginConfig, context) {
// Reload package.json in case a previous external step updated it
pkg = await getPkg(pluginConfig, context);
if (!verified && pluginConfig.npmPublish !== false && pkg.private !== true) {
await verifyNpmAuth(pluginConfig, pkg, context);
await verifyNpmAuth(npmrc, pkg, context);
}
} catch (error) {
errors.push(...error);
Expand All @@ -88,10 +90,10 @@ async function publish(pluginConfig, context) {
}

if (!prepared) {
await prepareNpm(pluginConfig, context);
await prepareNpm(npmrc, pluginConfig, context);
}

return publishNpm(pluginConfig, pkg, context);
return publishNpm(npmrc, pluginConfig, pkg, context);
}

async function addChannel(pluginConfig, context) {
Expand All @@ -104,7 +106,7 @@ async function addChannel(pluginConfig, context) {
// Reload package.json in case a previous external step updated it
pkg = await getPkg(pluginConfig, context);
if (!verified && pluginConfig.npmPublish !== false && pkg.private !== true) {
await verifyNpmAuth(pluginConfig, pkg, context);
await verifyNpmAuth(npmrc, pkg, context);
}
} catch (error) {
errors.push(...error);
Expand All @@ -114,7 +116,7 @@ async function addChannel(pluginConfig, context) {
throw new AggregateError(errors);
}

return addChannelNpm(pluginConfig, pkg, context);
return addChannelNpm(npmrc, pluginConfig, pkg, context);
}

module.exports = {verifyConditions, prepare, publish, addChannel};
14 changes: 9 additions & 5 deletions lib/add-channel.js
Expand Up @@ -3,7 +3,7 @@ const getRegistry = require('./get-registry');
const getChannel = require('./get-channel');
const getReleaseInfo = require('./get-release-info');

module.exports = async ({npmPublish}, pkg, context) => {
module.exports = async (npmrc, {npmPublish}, pkg, context) => {
const {
cwd,
env,
Expand All @@ -18,10 +18,14 @@ module.exports = async ({npmPublish}, pkg, context) => {
const distTag = getChannel(channel);

logger.log(`Adding version ${version} to npm registry on dist-tag ${distTag}`);
const result = execa('npm', ['dist-tag', 'add', `${pkg.name}@${version}`, distTag, '--registry', registry], {
cwd,
env,
});
const result = execa(
'npm',
['dist-tag', 'add', `${pkg.name}@${version}`, distTag, '--userconfig', npmrc, '--registry', registry],
{
cwd,
env,
}
);
result.stdout.pipe(
stdout,
{end: false}
Expand Down
14 changes: 7 additions & 7 deletions lib/get-registry.js
Expand Up @@ -2,10 +2,10 @@ const path = require('path');
const rc = require('rc');
const getRegistryUrl = require('registry-auth-token/registry-url');

module.exports = ({publishConfig: {registry} = {}, name}, {cwd}) =>
registry
? registry
: getRegistryUrl(
name.split('/')[0],
rc('npm', {registry: 'https://registry.npmjs.org/'}, {config: path.resolve(cwd, '.npmrc')})
);
module.exports = ({publishConfig: {registry} = {}, name}, {cwd, env}) =>
registry ||
env.NPM_CONFIG_REGISTRY ||
getRegistryUrl(
name.split('/')[0],
rc('npm', {registry: 'https://registry.npmjs.org/'}, {config: path.resolve(cwd, '.npmrc')})
);
16 changes: 10 additions & 6 deletions lib/prepare.js
Expand Up @@ -2,15 +2,19 @@ const path = require('path');
const {move} = require('fs-extra');
const execa = require('execa');

module.exports = async ({tarballDir, pkgRoot}, {cwd, env, stdout, stderr, nextRelease: {version}, logger}) => {
module.exports = async (npmrc, {tarballDir, pkgRoot}, {cwd, env, stdout, stderr, nextRelease: {version}, logger}) => {
const basePath = pkgRoot ? path.resolve(cwd, pkgRoot) : cwd;

logger.log('Write version %s to package.json in %s', version, basePath);

const versionResult = execa('npm', ['version', version, '--no-git-tag-version', '--allow-same-version'], {
cwd: basePath,
env,
});
const versionResult = execa(
'npm',
['version', version, '--userconfig', npmrc, '--no-git-tag-version', '--allow-same-version'],
{
cwd: basePath,
env,
}
);
versionResult.stdout.pipe(
stdout,
{end: false}
Expand All @@ -24,7 +28,7 @@ module.exports = async ({tarballDir, pkgRoot}, {cwd, env, stdout, stderr, nextRe

if (tarballDir) {
logger.log('Creating npm package version %s', version);
const packResult = execa('npm', ['pack', basePath], {cwd, env});
const packResult = execa('npm', ['pack', basePath, '--userconfig', npmrc], {cwd, env});
packResult.stdout.pipe(
stdout,
{end: false}
Expand Down
8 changes: 6 additions & 2 deletions lib/publish.js
Expand Up @@ -4,7 +4,7 @@ const getRegistry = require('./get-registry');
const getChannel = require('./get-channel');
const getReleaseInfo = require('./get-release-info');

module.exports = async ({npmPublish, pkgRoot}, pkg, context) => {
module.exports = async (npmrc, {npmPublish, pkgRoot}, pkg, context) => {
const {
cwd,
env,
Expand All @@ -20,7 +20,11 @@ module.exports = async ({npmPublish, pkgRoot}, pkg, context) => {
const distTag = getChannel(channel);

logger.log(`Publishing version ${version} to npm registry on dist-tag ${distTag}`);
const result = execa('npm', ['publish', basePath, '--tag', distTag, '--registry', registry], {cwd, env});
const result = execa(
'npm',
['publish', basePath, '--userconfig', npmrc, '--tag', distTag, '--registry', registry],
{cwd, env}
);
result.stdout.pipe(
stdout,
{end: false}
Expand Down
20 changes: 15 additions & 5 deletions lib/set-npmrc-auth.js
@@ -1,27 +1,37 @@
const path = require('path');
const rc = require('rc');
const {appendFile} = require('fs-extra');
const {outputFile, copy, readFile} = require('fs-extra');
const getAuthToken = require('registry-auth-token');
const nerfDart = require('nerf-dart');
const AggregateError = require('aggregate-error');
const getError = require('./get-error');

const readFileIfExists = async path => {
try {
return await readFile(path);
} catch (_) {
return '';
}
};

module.exports = async (
npmrc,
registry,
{cwd, env: {NPM_TOKEN, NPM_CONFIG_USERCONFIG, NPM_USERNAME, NPM_PASSWORD, NPM_EMAIL}, logger}
) => {
logger.log('Verify authentication for registry %s', registry);
const config = NPM_CONFIG_USERCONFIG || path.resolve(cwd, '.npmrc');
if (getAuthToken(registry, {npmrc: rc('npm', {registry: 'https://registry.npmjs.org/'}, {config})})) {
await copy(config, npmrc);
return;
}

if (NPM_USERNAME && NPM_PASSWORD && NPM_EMAIL) {
await appendFile(config, `\n_auth = \${LEGACY_TOKEN}\nemail = \${NPM_EMAIL}`);
logger.log(`Wrote NPM_USERNAME, NPM_PASSWORD and NPM_EMAIL to ${config}`);
await outputFile(npmrc, `${await readFileIfExists(config)}\n_auth = \${LEGACY_TOKEN}\nemail = \${NPM_EMAIL}`);
logger.log(`Wrote NPM_USERNAME, NPM_PASSWORD and NPM_EMAIL to ${npmrc}`);
} else if (NPM_TOKEN) {
await appendFile(config, `\n${nerfDart(registry)}:_authToken = \${NPM_TOKEN}`);
logger.log(`Wrote NPM_TOKEN to ${config}`);
await outputFile(npmrc, `${await readFileIfExists(config)}\n${nerfDart(registry)}:_authToken = \${NPM_TOKEN}`);
logger.log(`Wrote NPM_TOKEN to ${npmrc}`);
} else {
throw new AggregateError([getError('ENONPMTOKEN', {registry})]);
}
Expand Down
6 changes: 3 additions & 3 deletions lib/verify-auth.js
Expand Up @@ -5,18 +5,18 @@ const getError = require('./get-error');
const getRegistry = require('./get-registry');
const setNpmrcAuth = require('./set-npmrc-auth');

module.exports = async (pluginConfig, pkg, context) => {
module.exports = async (npmrc, pkg, context) => {
const {
cwd,
env: {DEFAULT_NPM_REGISTRY = 'https://registry.npmjs.org/', ...env},
} = context;
const registry = getRegistry(pkg, context);

await setNpmrcAuth(registry, context);
await setNpmrcAuth(npmrc, registry, context);

if (normalizeUrl(registry) === normalizeUrl(DEFAULT_NPM_REGISTRY)) {
try {
await execa('npm', ['whoami', '--registry', registry], {cwd, env});
await execa('npm', ['whoami', '--userconfig', npmrc, '--registry', registry], {cwd, env});
} catch (_) {
throw new AggregateError([getError('EINVALIDNPMTOKEN', {registry})]);
}
Expand Down
4 changes: 2 additions & 2 deletions package.json
Expand Up @@ -30,7 +30,8 @@
"rc": "^1.2.8",
"read-pkg": "^5.0.0",
"registry-auth-token": "^4.0.0",
"semver": "^5.5.0"
"semver": "^5.5.0",
"tempy": "^0.3.0"
},
"devDependencies": {
"ava": "^2.0.0",
Expand All @@ -45,7 +46,6 @@
"semantic-release": "^16.0.0-beta",
"sinon": "^7.1.1",
"stream-buffers": "^3.0.2",
"tempy": "^0.3.0",
"xo": "^0.25.0"
},
"engines": {
Expand Down
19 changes: 14 additions & 5 deletions test/get-registry.test.js
Expand Up @@ -6,30 +6,39 @@ import getRegistry from '../lib/get-registry';

test('Get default registry', t => {
const cwd = tempy.directory();
t.is(getRegistry({name: 'package-name'}, {cwd}), 'https://registry.npmjs.org/');
t.is(getRegistry({name: 'package-name', publishConfig: {}}, {cwd}), 'https://registry.npmjs.org/');
t.is(getRegistry({name: 'package-name'}, {cwd, env: {}}), 'https://registry.npmjs.org/');
t.is(getRegistry({name: 'package-name', publishConfig: {}}, {cwd, env: {}}), 'https://registry.npmjs.org/');
});

test('Get the registry configured in ".npmrc" and normalize trailing slash', async t => {
const cwd = tempy.directory();
await appendFile(path.resolve(cwd, '.npmrc'), 'registry = https://custom1.registry.com');

t.is(getRegistry({name: 'package-name'}, {cwd}), 'https://custom1.registry.com/');
t.is(getRegistry({name: 'package-name'}, {cwd, env: {}}), 'https://custom1.registry.com/');
});

test('Get the registry configured from "publishConfig"', async t => {
const cwd = tempy.directory();
await appendFile(path.resolve(cwd, '.npmrc'), 'registry = https://custom2.registry.com');

t.is(
getRegistry({name: 'package-name', publishConfig: {registry: 'https://custom3.registry.com/'}}, {cwd}),
getRegistry({name: 'package-name', publishConfig: {registry: 'https://custom3.registry.com/'}}, {cwd, env: {}}),
'https://custom3.registry.com/'
);
});

test('Get the registry configured in "NPM_CONFIG_REGISTRY"', t => {
const cwd = tempy.directory();

t.is(
getRegistry({name: 'package-name'}, {cwd, env: {NPM_CONFIG_REGISTRY: 'https://custom1.registry.com/'}}),
'https://custom1.registry.com/'
);
});

test('Get the registry configured in ".npmrc" for scoped package', async t => {
const cwd = tempy.directory();
await appendFile(path.resolve(cwd, '.npmrc'), '@scope:registry = https://custom3.registry.com');

t.is(getRegistry({name: '@scope/package-name'}, {cwd}), 'https://custom3.registry.com/');
t.is(getRegistry({name: '@scope/package-name'}, {cwd, env: {}}), 'https://custom3.registry.com/');
});
20 changes: 1 addition & 19 deletions test/integration.test.js
@@ -1,6 +1,6 @@
import path from 'path';
import test from 'ava';
import {outputJson, readJson, readFile, pathExists} from 'fs-extra';
import {outputJson, readJson, pathExists} from 'fs-extra';
import execa from 'execa';
import {spy} from 'sinon';
import tempy from 'tempy';
Expand Down Expand Up @@ -110,9 +110,6 @@ test('Throws error if NPM token is invalid', async t => {
t.is(error.name, 'SemanticReleaseError');
t.is(error.code, 'EINVALIDNPMTOKEN');
t.is(error.message, 'Invalid npm token.');

const npmrc = (await readFile(path.resolve(cwd, '.npmrc'))).toString();
t.regex(npmrc, /:_authToken/);
});

test('Skip Token validation if the registry configured is not the default one', async t => {
Expand All @@ -126,9 +123,6 @@ test('Skip Token validation if the registry configured is not the default one',
{cwd, env, options: {}, stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger}
)
);

const npmrc = (await readFile(path.resolve(cwd, '.npmrc'))).toString();
t.regex(npmrc, /:_authToken/);
});

test('Verify npm auth and package', async t => {
Expand All @@ -148,10 +142,6 @@ test('Verify npm auth and package', async t => {
}
)
);

const npmrc = (await readFile(path.resolve(cwd, '.npmrc'))).toString();
t.regex(npmrc, /_auth =/);
t.regex(npmrc, /email =/);
});

test('Verify npm auth and package from a sub-directory', async t => {
Expand All @@ -171,10 +161,6 @@ test('Verify npm auth and package from a sub-directory', async t => {
}
)
);

const npmrc = (await readFile(path.resolve(cwd, '.npmrc'))).toString();
t.regex(npmrc, /_auth =/);
t.regex(npmrc, /email =/);
});

test('Verify npm auth and package with "npm_config_registry" env var set by yarn', async t => {
Expand All @@ -194,10 +180,6 @@ test('Verify npm auth and package with "npm_config_registry" env var set by yarn
}
)
);

const npmrc = (await readFile(path.resolve(cwd, '.npmrc'))).toString();
t.regex(npmrc, /_auth =/);
t.regex(npmrc, /email =/);
});

test('Throw SemanticReleaseError Array if config option are not valid in verifyConditions', async t => {
Expand Down

0 comments on commit b8e51ff

Please sign in to comment.