Skip to content

Commit e11be30

Browse files
feat: add an otpProvider option to allow users to use a module to provide an OTP to semantic-release
1 parent 45ddc5b commit e11be30

File tree

4 files changed

+46
-8
lines changed

4 files changed

+46
-8
lines changed

README.md

+7-6
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ The npm authentication configuration is **required** and can be set via [environ
4343

4444
Both the [token](https://docs.npmjs.com/getting-started/working_with_tokens) and the legacy (`username`, `password` and `email`) authentication are supported. It is recommended to use the [token](https://docs.npmjs.com/getting-started/working_with_tokens) authentication. The legacy authentication is supported as the alternative npm registries [Artifactory](https://www.jfrog.com/open-source/#os-arti) and [npm-registry-couchapp](https://github.com/npm/npm-registry-couchapp) only supports that form of authentication.
4545

46-
**Note**: Only the `auth-only` [level of npm two-factor authentication](https://docs.npmjs.com/getting-started/using-two-factor-authentication#levels-of-authentication) is supported, **semantic-release** will not work with the default `auth-and-writes` level.
46+
**Note**: Only the `auth-only` [level of npm two-factor authentication](https://docs.npmjs.com/getting-started/using-two-factor-authentication#levels-of-authentication) is supported, **semantic-release** will not work with the default `auth-and-writes` level by default. If you want to use `auth-and-writes` you will need to use the `otpProvider` option to somehow give semantic-release an OTP token to use.
4747

4848
### Environment variables
4949

@@ -58,11 +58,12 @@ Use either `NPM_TOKEN` for token authentication or `NPM_USERNAME`, `NPM_PASSWORD
5858

5959
### Options
6060

61-
| Options | Description | Default |
62-
|--------------|---------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------|
63-
| `npmPublish` | Whether to publish the `npm` package to the registry. If `false` the `package.json` version will still be updated. | `false` if the `package.json` [private](https://docs.npmjs.com/files/package.json#private) property is `true`, `true` otherwise. |
64-
| `pkgRoot` | Directory path to publish. | `.` |
65-
| `tarballDir` | Directory path in which to write the the package tarball. If `false` the tarball is not be kept on the file system. | `false` |
61+
| Options | Description | Default |
62+
|---------------|---------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------|
63+
| `npmPublish` | Whether to publish the `npm` package to the registry. If `false` the `package.json` version will still be updated. | `false` if the `package.json` [private](https://docs.npmjs.com/files/package.json#private) property is `true`, `true` otherwise. |
64+
| `pkgRoot` | Directory path to publish. | `.` |
65+
| `tarballDir` | Directory path in which to write the the package tarball. If `false` the tarball is not be kept on the file system. | `false` |
66+
| `otpProvider` | Package name for an npm module that exports a single async function that resolves with an OTP code. | `undefined` |
6667

6768
**Note**: The `pkgRoot` directory must contains a `package.json`. The version will be updated only in the `package.json` and `npm-shrinkwrap.json` within the `pkgRoot` directory.
6869

lib/publish.js

+11-2
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 (npmrc, {npmPublish, pkgRoot}, pkg, context) => {
6+
module.exports = async (npmrc, {npmPublish, otpProvider, pkgRoot}, pkg, context) => {
77
const {
88
cwd,
99
env,
@@ -18,7 +18,16 @@ module.exports = async (npmrc, {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, '--userconfig', npmrc, '--registry', registry], {cwd, env});
21+
const otpArgs = [];
22+
if (otpProvider) {
23+
const otp = await require(otpProvider)();
24+
otpArgs.push('--otp', otp);
25+
}
26+
27+
const result = execa('npm', ['publish', basePath, '--userconfig', npmrc, '--registry', registry, ...otpArgs], {
28+
cwd,
29+
env,
30+
});
2231
result.stdout.pipe(stdout, {end: false});
2332
result.stderr.pipe(stderr, {end: false});
2433
await result;

test/helpers/fake-otp-provider.js

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = () => Promise.resolve('123');

test/integration.test.js

+27
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,33 @@ test('Publish the package on a dist-tag', async t => {
267267
t.is((await execa('npm', ['view', pkg.name, 'version'], {cwd, env: testEnv})).stdout, '1.0.0');
268268
});
269269

270+
test('Publish the package with OTP', async t => {
271+
const cwd = tempy.directory();
272+
const env = npmRegistry.authEnv;
273+
const pkg = {name: 'publish-otp', version: '0.0.0', publishConfig: {registry: npmRegistry.url}};
274+
await outputJson(path.resolve(cwd, 'package.json'), pkg);
275+
276+
const result = await t.context.m.publish(
277+
{
278+
otpProvider: path.resolve(__dirname, 'helpers', 'fake-otp-provider.js'),
279+
},
280+
{
281+
cwd,
282+
env,
283+
options: {},
284+
stdout: t.context.stdout,
285+
stderr: t.context.stderr,
286+
logger: t.context.logger,
287+
nextRelease: {version: '1.0.0'},
288+
}
289+
);
290+
291+
t.deepEqual(result, {name: 'npm package (@latest dist-tag)', url: undefined});
292+
t.is((await readJson(path.resolve(cwd, 'package.json'))).version, '1.0.0');
293+
t.false(await pathExists(path.resolve(cwd, `${pkg.name}-1.0.0.tgz`)));
294+
t.is((await execa('npm', ['view', pkg.name, 'version'], {cwd, env: testEnv})).stdout, '1.0.0');
295+
});
296+
270297
test('Publish the package from a sub-directory', async t => {
271298
const cwd = tempy.directory();
272299
const env = npmRegistry.authEnv;

0 commit comments

Comments
 (0)