Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 12 additions & 14 deletions lib/set-npmrc-auth.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,34 @@
const path = require('path');
const rc = require('rc');
const {outputFile, copy, readFile} = require('fs-extra');
const {outputFile, 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);
const {configs, ...rcConfig} = rc(
'npm',
{registry: 'https://registry.npmjs.org/'},
{config: NPM_CONFIG_USERCONFIG || path.resolve(cwd, '.npmrc')}
);
const currentConfig = configs ? (await Promise.all(configs.map(config => readFile(config)))).join('\n') : '';

if (getAuthToken(registry, {npmrc: rcConfig})) {
await outputFile(npmrc, currentConfig);
return;
}

if (NPM_USERNAME && NPM_PASSWORD && NPM_EMAIL) {
await outputFile(npmrc, `${await readFileIfExists(config)}\n_auth = \${LEGACY_TOKEN}\nemail = \${NPM_EMAIL}`);
await outputFile(npmrc, `${currentConfig}\n_auth = \${LEGACY_TOKEN}\nemail = \${NPM_EMAIL}`);
logger.log(`Wrote NPM_USERNAME, NPM_PASSWORD and NPM_EMAIL to ${npmrc}`);
} else if (NPM_TOKEN) {
await outputFile(npmrc, `${await readFileIfExists(config)}\n${nerfDart(registry)}:_authToken = \${NPM_TOKEN}`);
await outputFile(npmrc, `${currentConfig}\n${nerfDart(registry)}:_authToken = \${NPM_TOKEN}`);
logger.log(`Wrote NPM_TOKEN to ${npmrc}`);
} else {
throw new AggregateError([getError('ENONPMTOKEN', {registry})]);
Expand Down
107 changes: 86 additions & 21 deletions test/set-npmrc-auth.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,86 +3,145 @@ import test from 'ava';
import {readFile, appendFile} from 'fs-extra';
import {stub} from 'sinon';
import tempy from 'tempy';
import setNpmrcAuth from '../lib/set-npmrc-auth';
import clearModule from 'clear-module';

const {HOME} = process.env;
const cwd = process.cwd();

test.beforeEach(t => {
// Stub the logger
t.context.log = stub();
t.context.logger = {log: t.context.log};

clearModule('rc');
clearModule('../lib/set-npmrc-auth');
});

test.afterEach.always(() => {
process.env.HOME = HOME;
process.chdir(cwd);
});

test('Set auth with "NPM_TOKEN"', async t => {
test.serial('Set auth with "NPM_TOKEN"', async t => {
process.env.HOME = tempy.directory();
const cwd = tempy.directory();
process.chdir(cwd);
const npmrc = tempy.file({name: '.npmrc'});
const env = {NPM_TOKEN: 'npm_token'};

await setNpmrcAuth(npmrc, 'http://custom.registry.com', {cwd, env, logger: t.context.logger});
await require('../lib/set-npmrc-auth')(npmrc, 'http://custom.registry.com', {cwd, env, logger: t.context.logger});

t.regex((await readFile(npmrc)).toString(), /\/\/custom.registry.com\/:_authToken = \$\{NPM_TOKEN\}/);
t.deepEqual(t.context.log.args[1], [`Wrote NPM_TOKEN to ${npmrc}`]);
});

test('Set auth with "NPM_USERNAME", "NPM_PASSWORD" and "NPM_EMAIL"', async t => {
test.serial('Set auth with "NPM_USERNAME", "NPM_PASSWORD" and "NPM_EMAIL"', async t => {
process.env.HOME = tempy.directory();
const cwd = tempy.directory();
process.chdir(cwd);
const npmrc = tempy.file({name: '.npmrc'});
const env = {NPM_USERNAME: 'npm_username', NPM_PASSWORD: 'npm_pasword', NPM_EMAIL: 'npm_email'};

await setNpmrcAuth(npmrc, 'http://custom.registry.com', {cwd, env, logger: t.context.logger});
await require('../lib/set-npmrc-auth')(npmrc, 'http://custom.registry.com', {cwd, env, logger: t.context.logger});

t.is((await readFile(npmrc)).toString(), `\n_auth = \${LEGACY_TOKEN}\nemail = \${NPM_EMAIL}`);
t.deepEqual(t.context.log.args[1], [`Wrote NPM_USERNAME, NPM_PASSWORD and NPM_EMAIL to ${npmrc}`]);
});

test('Copy ".npmrc" if auth is already configured', async t => {
test.serial('Preserve home ".npmrc"', async t => {
process.env.HOME = tempy.directory();
const cwd = tempy.directory();
process.chdir(cwd);
const npmrc = tempy.file({name: '.npmrc'});
const env = {NPM_TOKEN: 'npm_token'};

await appendFile(path.resolve(process.env.HOME, '.npmrc'), 'home_config = test');

await require('../lib/set-npmrc-auth')(npmrc, 'http://custom.registry.com', {cwd, env, logger: t.context.logger});

t.is((await readFile(npmrc)).toString(), `home_config = test\n//custom.registry.com/:_authToken = \${NPM_TOKEN}`);
t.deepEqual(t.context.log.args[1], [`Wrote NPM_TOKEN to ${npmrc}`]);
});

test.serial('Preserve home and local ".npmrc"', async t => {
process.env.HOME = tempy.directory();
const cwd = tempy.directory();
process.chdir(cwd);
const npmrc = tempy.file({name: '.npmrc'});
const env = {NPM_TOKEN: 'npm_token'};

await appendFile(path.resolve(cwd, '.npmrc'), 'cwd_config = test');
await appendFile(path.resolve(process.env.HOME, '.npmrc'), 'home_config = test');

await require('../lib/set-npmrc-auth')(npmrc, 'http://custom.registry.com', {cwd, env, logger: t.context.logger});

t.is(
(await readFile(npmrc)).toString(),
`home_config = test\ncwd_config = test\n//custom.registry.com/:_authToken = \${NPM_TOKEN}`
);
t.deepEqual(t.context.log.args[1], [`Wrote NPM_TOKEN to ${npmrc}`]);
});

test.serial('Preserve all ".npmrc" if auth is already configured', async t => {
process.env.HOME = tempy.directory();
const cwd = tempy.directory();
process.chdir(cwd);
const npmrc = tempy.file({name: '.npmrc'});

await appendFile(path.resolve(cwd, '.npmrc'), `//custom.registry.com/:_authToken = \${NPM_TOKEN}`);
await appendFile(path.resolve(process.env.HOME, '.npmrc'), 'home_config = test');

await setNpmrcAuth(npmrc, 'http://custom.registry.com', {cwd, env: {}, logger: t.context.logger});
await require('../lib/set-npmrc-auth')(npmrc, 'http://custom.registry.com', {cwd, env: {}, logger: t.context.logger});

t.is((await readFile(npmrc)).toString(), `//custom.registry.com/:_authToken = \${NPM_TOKEN}`);
t.is((await readFile(npmrc)).toString(), `home_config = test\n//custom.registry.com/:_authToken = \${NPM_TOKEN}`);
t.is(t.context.log.callCount, 1);
});

test('Copy ".npmrc" if auth is already configured for a scoped package', async t => {
test.serial('Preserve ".npmrc" if auth is already configured for a scoped package', async t => {
process.env.HOME = tempy.directory();
const cwd = tempy.directory();
process.chdir(cwd);
const npmrc = tempy.file({name: '.npmrc'});

await appendFile(
path.resolve(cwd, '.npmrc'),
`@scope:registry=http://custom.registry.com\n//custom.registry.com/:_authToken = \${NPM_TOKEN}`
);
await appendFile(path.resolve(process.env.HOME, '.npmrc'), 'home_config = test');

await setNpmrcAuth(npmrc, 'http://custom.registry.com', {cwd, env: {}, logger: t.context.logger});
await require('../lib/set-npmrc-auth')(npmrc, 'http://custom.registry.com', {cwd, env: {}, logger: t.context.logger});

t.is(
(await readFile(npmrc)).toString(),
`@scope:registry=http://custom.registry.com\n//custom.registry.com/:_authToken = \${NPM_TOKEN}`
`home_config = test\n@scope:registry=http://custom.registry.com\n//custom.registry.com/:_authToken = \${NPM_TOKEN}`
);
t.is(t.context.log.callCount, 1);
});

test('Throw error if "NPM_TOKEN" is missing', async t => {
test.serial('Throw error if "NPM_TOKEN" is missing', async t => {
process.env.HOME = tempy.directory();
const cwd = tempy.directory();
process.chdir(cwd);
const npmrc = tempy.file({name: '.npmrc'});

const [error] = await t.throwsAsync(
setNpmrcAuth(npmrc, 'http://custom.registry.com', {cwd, env: {}, logger: t.context.logger})
require('../lib/set-npmrc-auth')(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('Emulate npm config resolution if "NPM_CONFIG_USERCONFIG" is set', async t => {
test.serial('Emulate npm config resolution if "NPM_CONFIG_USERCONFIG" is set', async t => {
process.env.HOME = tempy.directory();
const cwd = tempy.directory();
process.chdir(cwd);
const npmrc = tempy.file({name: '.npmrc'});

await appendFile(path.resolve(cwd, '.custom-npmrc'), `//custom.registry.com/:_authToken = \${NPM_TOKEN}`);

await setNpmrcAuth(npmrc, 'http://custom.registry.com', {
await require('../lib/set-npmrc-auth')(npmrc, 'http://custom.registry.com', {
cwd,
env: {NPM_CONFIG_USERCONFIG: path.resolve(cwd, '.custom-npmrc')},
logger: t.context.logger,
Expand All @@ -92,41 +151,47 @@ test('Emulate npm config resolution if "NPM_CONFIG_USERCONFIG" is set', async t
t.is(t.context.log.callCount, 1);
});

test('Throw error if "NPM_USERNAME" is missing', async t => {
test.serial('Throw error if "NPM_USERNAME" is missing', async t => {
process.env.HOME = tempy.directory();
const cwd = tempy.directory();
process.chdir(cwd);
const npmrc = tempy.file({name: '.npmrc'});
const env = {NPM_PASSWORD: 'npm_pasword', NPM_EMAIL: 'npm_email'};

const [error] = await t.throwsAsync(
setNpmrcAuth(npmrc, 'http://custom.registry.com', {cwd, env, logger: t.context.logger})
require('../lib/set-npmrc-auth')(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('Throw error if "NPM_PASSWORD" is missing', async t => {
test.serial('Throw error if "NPM_PASSWORD" is missing', async t => {
process.env.HOME = tempy.directory();
const cwd = tempy.directory();
process.chdir(cwd);
const npmrc = tempy.file({name: '.npmrc'});
const env = {NPM_USERNAME: 'npm_username', NPM_EMAIL: 'npm_email'};

const [error] = await t.throwsAsync(
setNpmrcAuth(npmrc, 'http://custom.registry.com', {cwd, env, logger: t.context.logger})
require('../lib/set-npmrc-auth')(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('Throw error if "NPM_EMAIL" is missing', async t => {
test.serial('Throw error if "NPM_EMAIL" is missing', async t => {
process.env.HOME = tempy.directory();
const cwd = tempy.directory();
process.chdir(cwd);
const npmrc = tempy.file({name: '.npmrc'});
const env = {NPM_USERNAME: 'npm_username', NPM_PASSWORD: 'npm_password'};

const [error] = await t.throwsAsync(
setNpmrcAuth(npmrc, 'http://custom.registry.com', {cwd, env, logger: t.context.logger})
require('../lib/set-npmrc-auth')(npmrc, 'http://custom.registry.com', {cwd, env, logger: t.context.logger})
);

t.is(error.name, 'SemanticReleaseError');
Expand Down