Skip to content

Commit

Permalink
fix: preserve config in both home and local .npmrc
Browse files Browse the repository at this point in the history
  • Loading branch information
pvdlg committed Oct 28, 2019
1 parent 7db019f commit de677ea
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 35 deletions.
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

0 comments on commit de677ea

Please sign in to comment.