From 02f60c70c5e702287f7477c6a96203df9e4cb4a4 Mon Sep 17 00:00:00 2001 From: Mikael Finstad Date: Sun, 7 Apr 2024 06:44:17 +0200 Subject: [PATCH] Fix publish OTP for Yarn Berry (#741) --- source/index.js | 6 +++--- source/npm/handle-npm-error.js | 6 +++++- source/npm/publish.js | 15 +++++++++++++++ test/index.js | 10 ++++++++-- 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/source/index.js b/source/index.js index 82e78398..b6da323f 100644 --- a/source/index.js +++ b/source/index.js @@ -14,7 +14,7 @@ import {asyncExitHook} from 'exit-hook'; import logSymbols from 'log-symbols'; import prerequisiteTasks from './prerequisite-tasks.js'; import gitTasks from './git-tasks.js'; -import {getPackagePublishArguments} from './npm/publish.js'; +import {getPackagePublishArguments, runPublish} from './npm/publish.js'; import enable2fa, {getEnable2faArguments} from './npm/enable-2fa.js'; import handleNpmError from './npm/handle-npm-error.js'; import releaseTaskHelper from './release-task-helper.js'; @@ -179,12 +179,12 @@ const np = async (input = 'patch', {packageManager, ...options}, {package_, root task(context, task) { let hasError = false; - return from(execa(...getPublishCommand(options))) + return from(runPublish(getPublishCommand(options))) .pipe( catchError(error => handleNpmError(error, task, otp => { context.otp = otp; - return execa(...getPublishCommand({...options, otp})); + return runPublish(getPublishCommand({...options, otp})); })), ) .pipe( diff --git a/source/npm/handle-npm-error.js b/source/npm/handle-npm-error.js index 9709e2cb..1ebaefab 100644 --- a/source/npm/handle-npm-error.js +++ b/source/npm/handle-npm-error.js @@ -9,7 +9,11 @@ const handleNpmError = (error, task, message, executor) => { } // `one-time pass` is for npm and `Two factor authentication` is for Yarn. - if (error.stderr.includes('one-time pass') || error.stdout.includes('Two factor authentication')) { + if ( + error.stderr.includes('one-time pass') // Npm + || error.stdout.includes('Two factor authentication') // Yarn v1 + || error.stdout.includes('One-time password:') // Yarn berry + ) { const {title} = task; task.title = `${title} ${chalk.yellow('(waiting for input…)')}`; diff --git a/source/npm/publish.js b/source/npm/publish.js index aeb2131e..f25ce2ee 100644 --- a/source/npm/publish.js +++ b/source/npm/publish.js @@ -1,3 +1,5 @@ +import {execa} from 'execa'; + export const getPackagePublishArguments = options => { const arguments_ = ['publish']; @@ -19,3 +21,16 @@ export const getPackagePublishArguments = options => { return arguments_; }; + +export function runPublish(arguments_) { + const cp = execa(...arguments_); + + cp.stdout.on('data', chunk => { + // https://github.com/yarnpkg/berry/blob/a3e5695186f2aec3a68810acafc6c9b1e45191da/packages/plugin-npm/sources/npmHttpUtils.ts#L541 + if (chunk.toString('utf8').includes('One-time password:')) { + cp.kill(); + } + }); + + return cp; +} diff --git a/test/index.js b/test/index.js index 047104d8..0224da46 100644 --- a/test/index.js +++ b/test/index.js @@ -62,7 +62,10 @@ test('skip enabling 2FA if the package exists', async t => { verifyWorkingTreeIsClean: sinon.stub(), }, '../source/npm/enable-2fa.js': enable2faStub, - '../source/npm/publish.js': sinon.stub().returns({pipe: sinon.stub()}), + '../source/npm/publish.js': { + getPackagePublishArguments: sinon.stub().returns([]), + runPublish: sinon.stub().returns(fakeExecaReturn()), + }, }); await t.notThrowsAsync(npMock('1.0.0', { @@ -91,7 +94,10 @@ test('skip enabling 2FA if the `2fa` option is false', async t => { verifyWorkingTreeIsClean: sinon.stub(), }, '../source/npm/enable-2fa.js': enable2faStub, - '../source/npm/publish.js': sinon.stub().returns({pipe: sinon.stub()}), + '../source/npm/publish.js': { + getPackagePublishArguments: sinon.stub().returns([]), + runPublish: sinon.stub().returns(fakeExecaReturn()), + }, }); await t.notThrowsAsync(npMock('1.0.0', {