diff --git a/lib/commands/config.js b/lib/commands/config.js index 87c79f3..c8c7c98 100644 --- a/lib/commands/config.js +++ b/lib/commands/config.js @@ -1,10 +1,10 @@ 'use strict' -const pino = require('pino') const { Input } = require('enquirer') const { validate } = require('../validation') const { argsNames } = require('../args') const LocalConf = require('../local-conf') +const createLogger = require('../createLogger') const ARGS_SCHEMA = { type: 'object', @@ -24,7 +24,7 @@ const ARGS_SCHEMA = { module.exports = async function (args) { validate(args, ARGS_SCHEMA) - const logger = pino({ level: args.verbose, prettyPrint: true, base: null }) + const logger = createLogger(args) const config = LocalConf() const { arg } = args const prompt = new Input({ message: `What is the value for ${arg}?` }) diff --git a/lib/commands/draft.js b/lib/commands/draft.js index 45dbc17..0a0c99c 100644 --- a/lib/commands/draft.js +++ b/lib/commands/draft.js @@ -1,11 +1,10 @@ 'use strict' -const pino = require('pino') const { validate } = require('../validation') const GitDirectory = require('../git-directory') const GitHub = require('../github') const ReleaseBuilder = require('../release-builder') - +const createLogger = require('../createLogger') const ARGS_SCHEMA = { type: 'object', required: ['path', 'verbose', 'fromCommit'], @@ -35,7 +34,7 @@ const ARGS_SCHEMA = { module.exports = async function (args) { validate(args, ARGS_SCHEMA) - const logger = pino({ level: args.verbose, prettyPrint: true, base: null }) + const logger = createLogger(args) logger.debug('Reading the project from %s', args.path) const git = GitDirectory(args.path) diff --git a/lib/commands/publish.js b/lib/commands/publish.js index 59e9e13..44b2ab3 100644 --- a/lib/commands/publish.js +++ b/lib/commands/publish.js @@ -1,7 +1,7 @@ 'use strict' const assert = require('assert').strict -const pino = require('pino') + const { validate } = require('../validation') const draft = require('./draft') const Npm = require('../npm') @@ -9,7 +9,9 @@ const GitDirectory = require('../git-directory') const GitHub = require('../github') const { editMessage } = require('../editor') const getRepositoryUrl = require('../getRepositoryUrl') -const inquirer = require('inquirer') +const publishToNpmWithOTPInquiry = require('../publishToNpmWithOTPInquiry') +const createLogger = require('../createLogger') + const debug = require('debug') // TODO this could be optimized with a common schema using $ref.. @@ -68,7 +70,7 @@ const CLEAN_REPO = { module.exports = async function (args) { validate(args, ARGS_SCHEMA) - const logger = pino({ level: args.verbose, prettyPrint: true, base: null }) + const logger = createLogger(args) if (args.verbose === 'trace') { debug.enable('simple-git,simple-git:*') @@ -126,33 +128,15 @@ module.exports = async function (args) { // ! DANGER ZONE: from this point if some error occurs we must explain to user what to do to fix // ? publish at the end?? - try { - await npm.publish({ tag: args.npmDistTag, access: args.npmAccess, otp: args.npmOtp }) - } catch (err) { - if (err.message.includes('npm ERR! code EOTP') && !args.silent) { - if (args.npmOtp) { - logger.error(`OTP code "${args.npmOtp}" is not valid`) - } - const { inputtedOtp } = await inquirer.prompt({ - type: 'input', - name: 'inputtedOtp', - message: `OTP code is required to publish ${moduleLink} to NPM:` - }) - - await npm.publish({ tag: args.npmDistTag, access: args.npmAccess, otp: inputtedOtp }) - } else { - throw err - } - } - logger.info('Published new module version %s', moduleLink) + await publishToNpmWithOTPInquiry(args, moduleLink) - let commited + let committed try { // NOTE: we are not creating and pushing any tags, it will be created by GitHub // NOTE 2: If we don't bump any version we aren't committing any change (ex: first release and version already set) await git.add('package.json') - commited = await git.commit({ message: `Bumped v${releasing.version}`, noVerify: args.noVerify }) - logger.debug('Commit id %s on branch %s', commited.commit, commited.branch) + committed = await git.commit({ message: `Bumped v${releasing.version}`, noVerify: args.noVerify }) + logger.debug('Commit id %s on branch %s', committed.commit, committed.branch) await git.push(args.remote) logger.info('Pushed to git %s', args.remote) @@ -183,7 +167,7 @@ Consider creating a release on GitHub by yourself with this message:\n${releasin logger.debug('Creating release on GitHub..') const githubRelease = await github.createRelease({ tag_name: `v${releasing.version}`, - target_commitish: commited.branch, + target_commitish: committed.branch, name: `v${releasing.version}`, body: releasing.message, draft: args.ghReleaseDraft, diff --git a/lib/createLogger.js b/lib/createLogger.js new file mode 100644 index 0000000..435913d --- /dev/null +++ b/lib/createLogger.js @@ -0,0 +1,5 @@ +const pino = require('pino') + +module.exports = function createLogger (args) { + return pino({ level: args.verbose, prettyPrint: true, base: null }) +} diff --git a/lib/publishToNpmWithOTPInquiry.js b/lib/publishToNpmWithOTPInquiry.js new file mode 100644 index 0000000..9558905 --- /dev/null +++ b/lib/publishToNpmWithOTPInquiry.js @@ -0,0 +1,36 @@ +const inquirer = require('inquirer') +const Npm = require('./npm') +const createLogger = require('./createLogger') + +/** + * @param {object} args + * @param {string} moduleLink + */ +module.exports = async function publishToNpmWithOTPInquiry (args, moduleLink) { + const npm = Npm(args.path) + const logger = createLogger(args) + + try { + await npm.publish({ tag: args.npmDistTag, access: args.npmAccess, otp: args.npmOtp }) + } catch (err) { + if (err.message.includes('npm ERR! code EOTP') && !args.silent) { + if (args.npmOtp) { + logger.error(`OTP code "${args.npmOtp}" is not valid`) + } + const { inputtedOtp } = await inquirer.prompt({ + type: 'number', + name: 'inputtedOtp', + message: `OTP code is required to publish ${moduleLink} to NPM:`, + validate: (v) => { + Number.isInteger(v) + } + }) + console.log('~ inputtedOtp', inputtedOtp) + + await npm.publish({ tag: args.npmDistTag, access: args.npmAccess, otp: inputtedOtp }) + } else { + throw err + } + } + logger.info('Published new module version %s', moduleLink) +} diff --git a/package.json b/package.json index 8a31c02..7fcfa78 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ }, "scripts": { "lint:fix": "standard --fix", - "test": "standard && tap --cov -J test/*.test.js" + "test": "standard && tap --cov -J test/*.test.js", + "tap:only": "tap --watch --only" }, "engines": { "node": ">=12" diff --git a/test/publishToNpmWithOTPInquiry.test.js b/test/publishToNpmWithOTPInquiry.test.js new file mode 100644 index 0000000..636a7a9 --- /dev/null +++ b/test/publishToNpmWithOTPInquiry.test.js @@ -0,0 +1,57 @@ + +'use strict' +const proxyquire = require('proxyquire') +const { test } = require('tap') + +test('publishToNpmWithOTPInquiry', (t) => { + const publish = proxyquire('../lib/publishToNpmWithOTPInquiry', { + './npm': () => { + return { + publish: (publishArgs) => { + if (!publishArgs.otp || publishArgs.otp === 'wrong OTP') { + throw new Error('npm ERR! code EOTP') + } + } + } + }, + inquirer: { + prompt: async (promptConfig) => { + promptConfig.validate(123456) + + return { inputtedOtp: '123456' } + } + } + }) + test('publishes', async (t) => { + await publish({ path: process.cwd(), verbose: 'info', npmDistTag: '0.0.2', npmOtp: '111111' }, 'my-module@0.0.1') + t.end() + }) + + test('asks for OTP and retries publishes', async (t) => { + await publish({ path: process.cwd(), verbose: 'info', npmDistTag: '0.0.3', silent: false, npmOtp: 'wrong OTP' }, 'my-module@0.0.1') + await publish({ path: process.cwd(), verbose: 'info', npmDistTag: '0.0.3', silent: false }, 'my-module@0.0.1') + + t.end() + }) + + test('throws other errors than OTP', async (t) => { + const randomError = 'any random error' + + const publish = proxyquire('../lib/publishToNpmWithOTPInquiry', { + './npm': () => { + return { + publish: () => { + throw new Error(randomError) + } + } + } + }) + try { + await publish({ path: process.cwd(), verbose: 'info', npmDistTag: '0.0.3', silent: false }, 'my-module@0.0.1') + } catch (err) { + t.equal(err.message, randomError) + } + t.end() + }) + t.end() +})