Skip to content

Commit 8726eff

Browse files
committed
feat: preserve local .npmrc file
1 parent 0126704 commit 8726eff

11 files changed

+115
-71
lines changed

index.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const {defaultTo, castArray} = require('lodash');
22
const AggregateError = require('aggregate-error');
3+
const tempy = require('tempy');
34
const setLegacyToken = require('./lib/set-legacy-token');
45
const getPkg = require('./lib/get-pkg');
56
const verifyNpmConfig = require('./lib/verify-config');
@@ -9,6 +10,7 @@ const publishNpm = require('./lib/publish');
910

1011
let verified;
1112
let prepared;
13+
const npmrc = tempy.file({name: '.npmrc'});
1214

1315
async function verifyConditions(pluginConfig, context) {
1416
// 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
@@ -30,7 +32,7 @@ async function verifyConditions(pluginConfig, context) {
3032

3133
// Verify the npm authentication only if `npmPublish` is not false and `pkg.private` is not `true`
3234
if (pluginConfig.npmPublish !== false && pkg.private !== true) {
33-
await verifyNpmAuth(pluginConfig, pkg, context);
35+
await verifyNpmAuth(npmrc, pkg, context);
3436
}
3537
} catch (error) {
3638
errors.push(...error);
@@ -52,7 +54,7 @@ async function prepare(pluginConfig, context) {
5254
// Reload package.json in case a previous external step updated it
5355
const pkg = await getPkg(pluginConfig, context);
5456
if (!verified && pluginConfig.npmPublish !== false && pkg.private !== true) {
55-
await verifyNpmAuth(pluginConfig, pkg, context);
57+
await verifyNpmAuth(npmrc, pkg, context);
5658
}
5759
} catch (error) {
5860
errors.push(...error);
@@ -62,7 +64,7 @@ async function prepare(pluginConfig, context) {
6264
throw new AggregateError(errors);
6365
}
6466

65-
await prepareNpm(pluginConfig, context);
67+
await prepareNpm(npmrc, pluginConfig, context);
6668
prepared = true;
6769
}
6870

@@ -76,7 +78,7 @@ async function publish(pluginConfig, context) {
7678
// Reload package.json in case a previous external step updated it
7779
pkg = await getPkg(pluginConfig, context);
7880
if (!verified && pluginConfig.npmPublish !== false && pkg.private !== true) {
79-
await verifyNpmAuth(pluginConfig, pkg, context);
81+
await verifyNpmAuth(npmrc, pkg, context);
8082
}
8183
} catch (error) {
8284
errors.push(...error);
@@ -87,10 +89,10 @@ async function publish(pluginConfig, context) {
8789
}
8890

8991
if (!prepared) {
90-
await prepareNpm(pluginConfig, context);
92+
await prepareNpm(npmrc, pluginConfig, context);
9193
}
9294

93-
return publishNpm(pluginConfig, pkg, context);
95+
return publishNpm(npmrc, pluginConfig, pkg, context);
9496
}
9597

9698
module.exports = {verifyConditions, prepare, publish};

lib/get-release-info.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ const execa = require('execa');
22
const normalizeUrl = require('normalize-url');
33

44
module.exports = async (
5+
npmrc,
56
{name, publishConfig: {tag} = {}},
67
{cwd, env: {DEFAULT_NPM_REGISTRY = 'https://registry.npmjs.org/', ...env}},
78
registry
89
) => {
9-
const distTag = tag || (await execa('npm', ['config', 'get', 'tag'], {cwd, env})).stdout || 'latest';
10+
const distTag =
11+
tag || (await execa('npm', ['config', 'get', 'tag', '--userconfig', npmrc], {cwd, env})).stdout || 'latest';
1012

1113
return {
1214
name: `npm package (@${distTag} dist-tag)`,

lib/prepare.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@ const path = require('path');
22
const {move} = require('fs-extra');
33
const execa = require('execa');
44

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

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

10-
const versionResult = execa('npm', ['version', version, '--no-git-tag-version'], {cwd: basePath, env});
10+
const versionResult = execa('npm', ['version', version, '--userconfig', npmrc, '--no-git-tag-version'], {
11+
cwd: basePath,
12+
env,
13+
});
1114
versionResult.stdout.pipe(
1215
stdout,
1316
{end: false}
@@ -21,7 +24,7 @@ module.exports = async ({tarballDir, pkgRoot}, {cwd, env, stdout, stderr, nextRe
2124

2225
if (tarballDir) {
2326
logger.log('Creating npm package version %s', version);
24-
const packResult = execa('npm', ['pack', basePath], {cwd, env});
27+
const packResult = execa('npm', ['pack', basePath, '--userconfig', npmrc], {cwd, env});
2528
packResult.stdout.pipe(
2629
stdout,
2730
{end: false}

lib/publish.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const execa = require('execa');
33
const getRegistry = require('./get-registry');
44
const getReleaseInfo = require('./get-release-info');
55

6-
module.exports = async ({npmPublish, pkgRoot}, pkg, context) => {
6+
module.exports = async (npmrc, {npmPublish, pkgRoot}, pkg, context) => {
77
const {
88
cwd,
99
env,
@@ -18,7 +18,7 @@ module.exports = async ({npmPublish, pkgRoot}, pkg, context) => {
1818
const registry = getRegistry(pkg, context);
1919

2020
logger.log('Publishing version %s to npm registry', version);
21-
const result = execa('npm', ['publish', basePath, '--registry', registry], {cwd, env});
21+
const result = execa('npm', ['publish', basePath, '--userconfig', npmrc, '--registry', registry], {cwd, env});
2222
result.stdout.pipe(
2323
stdout,
2424
{end: false}
@@ -30,7 +30,7 @@ module.exports = async ({npmPublish, pkgRoot}, pkg, context) => {
3030
await result;
3131

3232
logger.log(`Published ${pkg.name}@${pkg.version} on ${registry}`);
33-
return getReleaseInfo(pkg, context, registry);
33+
return getReleaseInfo(npmrc, pkg, context, registry);
3434
}
3535

3636
logger.log(

lib/set-npmrc-auth.js

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,37 @@
11
const path = require('path');
22
const rc = require('rc');
3-
const {appendFile} = require('fs-extra');
3+
const {outputFile, copy, readFile} = require('fs-extra');
44
const getAuthToken = require('registry-auth-token');
55
const nerfDart = require('nerf-dart');
66
const AggregateError = require('aggregate-error');
77
const getError = require('./get-error');
88

9+
const readFileIfExists = async path => {
10+
try {
11+
return await readFile(path);
12+
} catch (_) {
13+
return '';
14+
}
15+
};
16+
917
module.exports = async (
18+
npmrc,
1019
registry,
1120
{cwd, env: {NPM_TOKEN, NPM_CONFIG_USERCONFIG, NPM_USERNAME, NPM_PASSWORD, NPM_EMAIL}, logger}
1221
) => {
1322
logger.log('Verify authentication for registry %s', registry);
1423
const config = NPM_CONFIG_USERCONFIG || path.resolve(cwd, '.npmrc');
1524
if (getAuthToken(registry, {npmrc: rc('npm', {registry: 'https://registry.npmjs.org/'}, {config})})) {
25+
await copy(config, npmrc);
1626
return;
1727
}
1828

1929
if (NPM_USERNAME && NPM_PASSWORD && NPM_EMAIL) {
20-
await appendFile(config, `\n_auth = \${LEGACY_TOKEN}\nemail = \${NPM_EMAIL}`);
21-
logger.log(`Wrote NPM_USERNAME, NPM_PASSWORD and NPM_EMAIL to ${config}`);
30+
await outputFile(npmrc, `${await readFileIfExists(config)}\n_auth = \${LEGACY_TOKEN}\nemail = \${NPM_EMAIL}`);
31+
logger.log(`Wrote NPM_USERNAME, NPM_PASSWORD and NPM_EMAIL to ${npmrc}`);
2232
} else if (NPM_TOKEN) {
23-
await appendFile(config, `\n${nerfDart(registry)}:_authToken = \${NPM_TOKEN}`);
24-
logger.log(`Wrote NPM_TOKEN to ${config}`);
33+
await outputFile(npmrc, `${await readFileIfExists(config)}\n${nerfDart(registry)}:_authToken = \${NPM_TOKEN}`);
34+
logger.log(`Wrote NPM_TOKEN to ${npmrc}`);
2535
} else {
2636
throw new AggregateError([getError('ENONPMTOKEN', {registry})]);
2737
}

lib/verify-auth.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,18 @@ const getError = require('./get-error');
55
const getRegistry = require('./get-registry');
66
const setNpmrcAuth = require('./set-npmrc-auth');
77

8-
module.exports = async (pluginConfig, pkg, context) => {
8+
module.exports = async (npmrc, pkg, context) => {
99
const {
1010
cwd,
1111
env: {DEFAULT_NPM_REGISTRY = 'https://registry.npmjs.org/', ...env},
1212
} = context;
1313
const registry = getRegistry(pkg, context);
1414

15-
await setNpmrcAuth(registry, context);
15+
await setNpmrcAuth(npmrc, registry, context);
1616

1717
if (normalizeUrl(registry) === normalizeUrl(DEFAULT_NPM_REGISTRY)) {
1818
try {
19-
await execa('npm', ['whoami', '--registry', registry], {cwd, env});
19+
await execa('npm', ['whoami', '--userconfig', npmrc, '--registry', registry], {cwd, env});
2020
} catch (_) {
2121
throw new AggregateError([getError('EINVALIDNPMTOKEN', {registry})]);
2222
}

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
"npm": "^6.10.3",
3030
"rc": "^1.2.8",
3131
"read-pkg": "^5.0.0",
32-
"registry-auth-token": "^4.0.0"
32+
"registry-auth-token": "^4.0.0",
33+
"tempy": "^0.3.0"
3334
},
3435
"devDependencies": {
3536
"ava": "^2.0.0",
@@ -44,7 +45,6 @@
4445
"semantic-release": "^15.0.0",
4546
"sinon": "^7.1.1",
4647
"stream-buffers": "^3.0.2",
47-
"tempy": "^0.3.0",
4848
"xo": "^0.25.0"
4949
},
5050
"engines": {

test/get-release-info.test.js

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import path from 'path';
21
import test from 'ava';
32
import {writeFile} from 'fs-extra';
43
import tempy from 'tempy';
@@ -15,37 +14,41 @@ test.beforeEach(() => {
1514

1615
test('Default registry and tag', async t => {
1716
const cwd = tempy.directory();
17+
const npmrc = tempy.file({name: '.npmrc'});
1818

19-
t.deepEqual(await getReleaseInfo({name: 'module'}, {cwd, env: {}}, 'https://registry.npmjs.org/'), {
19+
t.deepEqual(await getReleaseInfo(npmrc, {name: 'module'}, {cwd, env: {}}, 'https://registry.npmjs.org/'), {
2020
name: 'npm package (@latest dist-tag)',
2121
url: 'https://www.npmjs.com/package/module',
2222
});
2323
});
2424

2525
test('Default registry, tag and scoped module', async t => {
2626
const cwd = tempy.directory();
27+
const npmrc = tempy.file({name: '.npmrc'});
2728

28-
t.deepEqual(await getReleaseInfo({name: '@scope/module'}, {cwd, env: {}}, 'https://registry.npmjs.org/'), {
29+
t.deepEqual(await getReleaseInfo(npmrc, {name: '@scope/module'}, {cwd, env: {}}, 'https://registry.npmjs.org/'), {
2930
name: 'npm package (@latest dist-tag)',
3031
url: 'https://www.npmjs.com/package/@scope/module',
3132
});
3233
});
3334

3435
test('Custom registry, tag and scoped module', async t => {
3536
const cwd = tempy.directory();
37+
const npmrc = tempy.file({name: '.npmrc'});
3638

37-
t.deepEqual(await getReleaseInfo({name: '@scope/module'}, {cwd, env: {}}, 'https://custom.registry.org/'), {
39+
t.deepEqual(await getReleaseInfo(npmrc, {name: '@scope/module'}, {cwd, env: {}}, 'https://custom.registry.org/'), {
3840
name: 'npm package (@latest dist-tag)',
3941
url: undefined,
4042
});
4143
});
4244

4345
test('Default registry and tag from .npmrc', async t => {
4446
const cwd = tempy.directory();
45-
await writeFile(path.resolve(cwd, '.npmrc'), 'tag=npmrc');
47+
const npmrc = tempy.file({name: '.npmrc'});
48+
await writeFile(npmrc, 'tag=npmrc');
4649

4750
t.deepEqual(
48-
await getReleaseInfo({name: 'module', publishConfig: {}}, {cwd, env: {}}, 'https://registry.npmjs.org/'),
51+
await getReleaseInfo(npmrc, {name: 'module', publishConfig: {}}, {cwd, env: {}}, 'https://registry.npmjs.org/'),
4952
{
5053
name: 'npm package (@npmrc dist-tag)',
5154
url: 'https://www.npmjs.com/package/module',
@@ -55,22 +58,29 @@ test('Default registry and tag from .npmrc', async t => {
5558

5659
test('Default registry and tag from package.json', async t => {
5760
const cwd = tempy.directory();
61+
const npmrc = tempy.file({name: '.npmrc'});
5862

59-
await writeFile(path.resolve(cwd, '.npmrc'), 'tag=npmrc');
63+
await writeFile(npmrc, 'tag=npmrc');
6064

6165
t.deepEqual(
62-
await getReleaseInfo({name: 'module', publishConfig: {tag: 'pkg'}}, {cwd, env: {}}, 'https://registry.npmjs.org/'),
66+
await getReleaseInfo(
67+
npmrc,
68+
{name: 'module', publishConfig: {tag: 'pkg'}},
69+
{cwd, env: {}},
70+
'https://registry.npmjs.org/'
71+
),
6372
{name: 'npm package (@pkg dist-tag)', url: 'https://www.npmjs.com/package/module'}
6473
);
6574
});
6675

6776
test('Default tag', async t => {
6877
const cwd = tempy.directory();
78+
const npmrc = tempy.file({name: '.npmrc'});
6979

70-
await writeFile(path.resolve(cwd, '.npmrc'), 'tag=');
80+
await writeFile(npmrc, 'tag=');
7181

7282
t.deepEqual(
73-
await getReleaseInfo({name: 'module', publishConfig: {}}, {cwd, env: {}}, 'https://registry.npmjs.org/'),
83+
await getReleaseInfo(npmrc, {name: 'module', publishConfig: {}}, {cwd, env: {}}, 'https://registry.npmjs.org/'),
7484
{
7585
name: 'npm package (@latest dist-tag)',
7686
url: 'https://www.npmjs.com/package/module',

test/integration.test.js

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import path from 'path';
22
import test from 'ava';
3-
import {outputJson, readJson, readFile, pathExists} from 'fs-extra';
3+
import {outputJson, readJson, pathExists} from 'fs-extra';
44
import execa from 'execa';
55
import {spy} from 'sinon';
66
import tempy from 'tempy';
@@ -110,9 +110,6 @@ test('Throws error if NPM token is invalid', async t => {
110110
t.is(error.name, 'SemanticReleaseError');
111111
t.is(error.code, 'EINVALIDNPMTOKEN');
112112
t.is(error.message, 'Invalid npm token.');
113-
114-
const npmrc = (await readFile(path.resolve(cwd, '.npmrc'))).toString();
115-
t.regex(npmrc, /:_authToken/);
116113
});
117114

118115
test('Skip Token validation if the registry configured is not the default one', async t => {
@@ -126,9 +123,6 @@ test('Skip Token validation if the registry configured is not the default one',
126123
{cwd, env, options: {}, stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger}
127124
)
128125
);
129-
130-
const npmrc = (await readFile(path.resolve(cwd, '.npmrc'))).toString();
131-
t.regex(npmrc, /:_authToken/);
132126
});
133127

134128
test('Verify npm auth and package', async t => {
@@ -148,10 +142,6 @@ test('Verify npm auth and package', async t => {
148142
}
149143
)
150144
);
151-
152-
const npmrc = (await readFile(path.resolve(cwd, '.npmrc'))).toString();
153-
t.regex(npmrc, /_auth =/);
154-
t.regex(npmrc, /email =/);
155145
});
156146

157147
test('Verify npm auth and package from a sub-directory', async t => {
@@ -171,10 +161,6 @@ test('Verify npm auth and package from a sub-directory', async t => {
171161
}
172162
)
173163
);
174-
175-
const npmrc = (await readFile(path.resolve(cwd, '.npmrc'))).toString();
176-
t.regex(npmrc, /_auth =/);
177-
t.regex(npmrc, /email =/);
178164
});
179165

180166
test('Verify npm auth and package with "npm_config_registry" env var set by yarn', async t => {
@@ -194,10 +180,6 @@ test('Verify npm auth and package with "npm_config_registry" env var set by yarn
194180
}
195181
)
196182
);
197-
198-
const npmrc = (await readFile(path.resolve(cwd, '.npmrc'))).toString();
199-
t.regex(npmrc, /_auth =/);
200-
t.regex(npmrc, /email =/);
201183
});
202184

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

0 commit comments

Comments
 (0)