diff --git a/bin/semantic-release.js b/bin/semantic-release.js index 407475dac3..12dd2b56b6 100755 --- a/bin/semantic-release.js +++ b/bin/semantic-release.js @@ -21,9 +21,8 @@ See https://github.com/semantic-release/semantic-release/blob/master/docs/suppor process.exit(1); } -execa - .stdout('git', ['--version']) - .then(stdout => { +execa('git', ['--version']) + .then(({stdout}) => { var gitVersion = findVersions(stdout)[0]; if (semver.lt(gitVersion, MIN_GIT_VERSION)) { console.error(`[semantic-release]: Git version ${MIN_GIT_VERSION} is required. Found ${gitVersion}.`); diff --git a/lib/git.js b/lib/git.js index 9aa6588520..28ee556e04 100644 --- a/lib/git.js +++ b/lib/git.js @@ -11,7 +11,7 @@ const debug = require('debug')('semantic-release:git'); */ async function getTagHead(tagName, execaOpts) { try { - return await execa.stdout('git', ['rev-list', '-1', tagName], execaOpts); + return (await execa('git', ['rev-list', '-1', tagName], execaOpts)).stdout; } catch (error) { debug(error); } @@ -26,7 +26,7 @@ async function getTagHead(tagName, execaOpts) { * @throws {Error} If the `git` command fails. */ async function getTags(execaOpts) { - return (await execa.stdout('git', ['tag'], execaOpts)) + return (await execa('git', ['tag'], execaOpts)).stdout .split('\n') .map(tag => tag.trim()) .filter(Boolean); @@ -45,7 +45,7 @@ async function isRefInHistory(ref, execaOpts) { await execa('git', ['merge-base', '--is-ancestor', ref, 'HEAD'], execaOpts); return true; } catch (error) { - if (error.code === 1) { + if (error.exitCode === 1) { return false; } @@ -75,8 +75,8 @@ async function fetch(repositoryUrl, execaOpts) { * * @return {String} the sha of the HEAD commit. */ -function getGitHead(execaOpts) { - return execa.stdout('git', ['rev-parse', 'HEAD'], execaOpts); +async function getGitHead(execaOpts) { + return (await execa('git', ['rev-parse', 'HEAD'], execaOpts)).stdout; } /** @@ -88,7 +88,7 @@ function getGitHead(execaOpts) { */ async function repoUrl(execaOpts) { try { - return await execa.stdout('git', ['config', '--get', 'remote.origin.url'], execaOpts); + return (await execa('git', ['config', '--get', 'remote.origin.url'], execaOpts)).stdout; } catch (error) { debug(error); } @@ -103,7 +103,7 @@ async function repoUrl(execaOpts) { */ async function isGitRepo(execaOpts) { try { - return (await execa('git', ['rev-parse', '--git-dir'], execaOpts)).code === 0; + return (await execa('git', ['rev-parse', '--git-dir'], execaOpts)).exitCode === 0; } catch (error) { debug(error); } @@ -161,7 +161,7 @@ async function push(repositoryUrl, execaOpts) { */ async function verifyTagName(tagName, execaOpts) { try { - return (await execa('git', ['check-ref-format', `refs/tags/${tagName}`], execaOpts)).code === 0; + return (await execa('git', ['check-ref-format', `refs/tags/${tagName}`], execaOpts)).exitCode === 0; } catch (error) { debug(error); } @@ -176,7 +176,7 @@ async function verifyTagName(tagName, execaOpts) { * @return {Boolean} `true` is the HEAD of the current local branch is the same as the HEAD of the remote branch, falsy otherwise. */ async function isBranchUpToDate(branch, execaOpts) { - const remoteHead = await execa.stdout('git', ['ls-remote', '--heads', 'origin', branch], execaOpts); + const {stdout: remoteHead} = await execa('git', ['ls-remote', '--heads', 'origin', branch], execaOpts); try { return await isRefInHistory(remoteHead.match(/^(\w+)?/)[1], execaOpts); } catch (error) { diff --git a/package.json b/package.json index dbd38d7f67..95ce942054 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "cosmiconfig": "^5.0.1", "debug": "^4.0.0", "env-ci": "^4.0.0", - "execa": "^1.0.0", + "execa": "^2.0.0", "figures": "^3.0.0", "find-versions": "^3.0.0", "get-stream": "^5.0.0", diff --git a/test/helpers/git-utils.js b/test/helpers/git-utils.js index 38b464bc90..8d39c16169 100644 --- a/test/helpers/git-utils.js +++ b/test/helpers/git-utils.js @@ -69,8 +69,10 @@ export async function initBareRepo(repositoryUrl, branch = 'master') { * @returns {Array} The created commits, in reverse order (to match `git log` order). */ export async function gitCommits(messages, execaOpts) { - await pReduce(messages, (_, message) => - execa.stdout('git', ['commit', '-m', message, '--allow-empty', '--no-gpg-sign'], execaOpts) + await pReduce( + messages, + async (_, message) => + (await execa('git', ['commit', '-m', message, '--allow-empty', '--no-gpg-sign'], execaOpts)).stdout ); return (await gitGetCommits(undefined, execaOpts)).slice(0, messages.length); } @@ -112,8 +114,8 @@ export async function gitCheckout(branch, create = true, execaOpts) { * * @return {String} The sha of the head commit in the current git repository. */ -export function gitHead(execaOpts) { - return execa.stdout('git', ['rev-parse', 'HEAD'], execaOpts); +export async function gitHead(execaOpts) { + return (await execa('git', ['rev-parse', 'HEAD'], execaOpts)).stdout; } /** @@ -181,8 +183,8 @@ export async function gitAddConfig(name, value, execaOpts) { * * @return {String} The sha of the commit associated with `tagName` on the local repository. */ -export function gitTagHead(tagName, execaOpts) { - return execa.stdout('git', ['rev-list', '-1', tagName], execaOpts); +export async function gitTagHead(tagName, execaOpts) { + return (await execa('git', ['rev-list', '-1', tagName], execaOpts)).stdout; } /** @@ -195,7 +197,7 @@ export function gitTagHead(tagName, execaOpts) { * @return {String} The sha of the commit associated with `tagName` on the remote repository. */ export async function gitRemoteTagHead(repositoryUrl, tagName, execaOpts) { - return (await execa.stdout('git', ['ls-remote', '--tags', repositoryUrl, tagName], execaOpts)) + return (await execa('git', ['ls-remote', '--tags', repositoryUrl, tagName], execaOpts)).stdout .split('\n') .filter(tag => Boolean(tag)) .map(tag => tag.match(/^(\S+)/)[1])[0]; @@ -209,8 +211,8 @@ export async function gitRemoteTagHead(repositoryUrl, tagName, execaOpts) { * * @return {String} The tag associatedwith the sha in parameter or `null`. */ -export function gitCommitTag(gitHead, execaOpts) { - return execa.stdout('git', ['describe', '--tags', '--exact-match', gitHead], execaOpts); +export async function gitCommitTag(gitHead, execaOpts) { + return (await execa('git', ['describe', '--tags', '--exact-match', gitHead], execaOpts)).stdout; } /** diff --git a/test/integration.test.js b/test/integration.test.js index e0d8f08236..2a0daab884 100644 --- a/test/integration.test.js +++ b/test/integration.test.js @@ -72,9 +72,9 @@ test('Release patch, minor and major versions', async t => { t.log('Commit a chore'); await gitCommits(['chore: Init repository'], {cwd}); t.log('$ semantic-release'); - let {stdout, code} = await execa(cli, [], {env, cwd}); + let {stdout, exitCode} = await execa(cli, [], {env, cwd}); t.regex(stdout, /There are no relevant changes, so no new version is released/); - t.is(code, 0); + t.is(exitCode, 0); /* Initial release */ let version = '1.0.0'; @@ -95,10 +95,10 @@ test('Release patch, minor and major versions', async t => { t.log('Commit a feature'); await gitCommits(['feat: Initial commit'], {cwd}); t.log('$ semantic-release'); - ({stdout, code} = await execa(cli, [], {env, cwd})); + ({stdout, exitCode} = await execa(cli, [], {env, cwd})); t.regex(stdout, new RegExp(`Published GitHub release: release-url/${version}`)); t.regex(stdout, new RegExp(`Publishing version ${version} to npm registry`)); - t.is(code, 0); + t.is(exitCode, 0); // Verify package.json and npm-shrinkwrap.json have been updated t.is((await readJson(path.resolve(cwd, 'package.json'))).version, version); @@ -137,10 +137,10 @@ test('Release patch, minor and major versions', async t => { t.log('Commit a fix'); await gitCommits(['fix: bar'], {cwd}); t.log('$ semantic-release'); - ({stdout, code} = await execa(cli, [], {env, cwd})); + ({stdout, exitCode} = await execa(cli, [], {env, cwd})); t.regex(stdout, new RegExp(`Published GitHub release: release-url/${version}`)); t.regex(stdout, new RegExp(`Publishing version ${version} to npm registry`)); - t.is(code, 0); + t.is(exitCode, 0); // Verify package.json and npm-shrinkwrap.json have been updated t.is((await readJson(path.resolve(cwd, 'package.json'))).version, version); @@ -179,10 +179,10 @@ test('Release patch, minor and major versions', async t => { t.log('Commit a feature'); await gitCommits(['feat: baz'], {cwd}); t.log('$ semantic-release'); - ({stdout, code} = await execa(cli, [], {env, cwd})); + ({stdout, exitCode} = await execa(cli, [], {env, cwd})); t.regex(stdout, new RegExp(`Published GitHub release: release-url/${version}`)); t.regex(stdout, new RegExp(`Publishing version ${version} to npm registry`)); - t.is(code, 0); + t.is(exitCode, 0); // Verify package.json and npm-shrinkwrap.json have been updated t.is((await readJson(path.resolve(cwd, 'package.json'))).version, version); @@ -221,10 +221,10 @@ test('Release patch, minor and major versions', async t => { t.log('Commit a breaking change'); await gitCommits(['feat: foo\n\n BREAKING CHANGE: bar'], {cwd}); t.log('$ semantic-release'); - ({stdout, code} = await execa(cli, [], {env, cwd})); + ({stdout, exitCode} = await execa(cli, [], {env, cwd})); t.regex(stdout, new RegExp(`Published GitHub release: release-url/${version}`)); t.regex(stdout, new RegExp(`Publishing version ${version} to npm registry`)); - t.is(code, 0); + t.is(exitCode, 0); // Verify package.json and npm-shrinkwrap.json have been updated t.is((await readJson(path.resolve(cwd, 'package.json'))).version, version); @@ -258,8 +258,8 @@ test('Exit with 1 if a plugin is not found', async t => { release: {analyzeCommits: 'non-existing-path', success: false, fail: false}, }); - const {code, stderr} = await t.throwsAsync(execa(cli, [], {env, cwd})); - t.is(code, 1); + const {exitCode, stderr} = await t.throwsAsync(execa(cli, [], {env, cwd})); + t.is(exitCode, 1); t.regex(stderr, /Cannot find module/); }); @@ -276,8 +276,8 @@ test('Exit with 1 if a shareable config is not found', async t => { release: {extends: 'non-existing-path', success: false, fail: false}, }); - const {code, stderr} = await t.throwsAsync(execa(cli, [], {env, cwd})); - t.is(code, 1); + const {exitCode, stderr} = await t.throwsAsync(execa(cli, [], {env, cwd})); + t.is(exitCode, 1); t.regex(stderr, /Cannot find module/); }); @@ -297,8 +297,8 @@ test('Exit with 1 if a shareable config reference a not found plugin', async t = }); await writeJson(path.resolve(cwd, 'shareable.json'), shareable); - const {code, stderr} = await t.throwsAsync(execa(cli, [], {env, cwd})); - t.is(code, 1); + const {exitCode, stderr} = await t.throwsAsync(execa(cli, [], {env, cwd})); + t.is(exitCode, 1); t.regex(stderr, /Cannot find module/); }); @@ -327,11 +327,11 @@ test('Dry-run', async t => { t.log('Commit a feature'); await gitCommits(['feat: Initial commit'], {cwd}); t.log('$ semantic-release -d'); - const {stdout, code} = await execa(cli, ['-d'], {env, cwd}); + const {stdout, exitCode} = await execa(cli, ['-d'], {env, cwd}); t.regex(stdout, new RegExp(`There is no previous release, the next release version is ${version}`)); t.regex(stdout, new RegExp(`Release note for version ${version}`)); t.regex(stdout, /Initial commit/); - t.is(code, 0); + t.is(exitCode, 0); // Verify package.json and has not been modified t.is((await readJson(path.resolve(cwd, 'package.json'))).version, '0.0.0-dev'); @@ -375,10 +375,10 @@ test('Allow local releases with "noCi" option', async t => { t.log('Commit a feature'); await gitCommits(['feat: Initial commit'], {cwd}); t.log('$ semantic-release --no-ci'); - const {stdout, code} = await execa(cli, ['--no-ci'], {env: envNoCi, cwd}); + const {stdout, exitCode} = await execa(cli, ['--no-ci'], {env: envNoCi, cwd}); t.regex(stdout, new RegExp(`Published GitHub release: release-url/${version}`)); t.regex(stdout, new RegExp(`Publishing version ${version} to npm registry`)); - t.is(code, 0); + t.is(exitCode, 0); // Verify package.json and has been updated t.is((await readJson(path.resolve(cwd, 'package.json'))).version, version); @@ -417,7 +417,7 @@ test('Pass options via CLI arguments', async t => { t.log('Commit a feature'); await gitCommits(['feat: Initial commit'], {cwd}); t.log('$ semantic-release'); - const {stdout, code} = await execa( + const {stdout, exitCode} = await execa( cli, [ '--verify-conditions', @@ -433,7 +433,7 @@ test('Pass options via CLI arguments', async t => { {env, cwd} ); t.regex(stdout, new RegExp(`Publishing version ${version} to npm registry`)); - t.is(code, 0); + t.is(exitCode, 0); // Verify package.json and has been updated t.is((await readJson(path.resolve(cwd, 'package.json'))).version, version); @@ -528,14 +528,14 @@ test('Log unexpected errors from plugins and exit with 1', async t => { t.log('Commit a feature'); await gitCommits(['feat: Initial commit'], {cwd}); t.log('$ semantic-release'); - const {stderr, code} = await execa(cli, [], {env, cwd, reject: false}); + const {stderr, exitCode} = await execa(cli, [], {env, cwd, reject: false}); // Verify the type and message are logged t.regex(stderr, /Error: a/); // Verify the the stacktrace is logged t.regex(stderr, new RegExp(pluginError)); // Verify the Error properties are logged t.regex(stderr, /errorProperty: 'errorProperty'/); - t.is(code, 1); + t.is(exitCode, 1); }); test('Log errors inheriting SemanticReleaseError and exit with 1', async t => { @@ -555,10 +555,10 @@ test('Log errors inheriting SemanticReleaseError and exit with 1', async t => { t.log('Commit a feature'); await gitCommits(['feat: Initial commit'], {cwd}); t.log('$ semantic-release'); - const {stderr, code} = await execa(cli, [], {env, cwd, reject: false}); + const {stderr, exitCode} = await execa(cli, [], {env, cwd, reject: false}); // Verify the type and message are logged t.regex(stderr, /EINHERITED Inherited error/); - t.is(code, 1); + t.is(exitCode, 1); }); test('Exit with 1 if missing permission to push to the remote repository', async t => { @@ -573,14 +573,14 @@ test('Exit with 1 if missing permission to push to the remote repository', async await gitCommits(['feat: Initial commit'], {cwd}); await gitPush('origin', 'master', {cwd}); t.log('$ semantic-release'); - const {stderr, code} = await execa( + const {stderr, exitCode} = await execa( cli, ['--repository-url', 'http://user:wrong_pass@localhost:2080/git/unauthorized.git'], {env: {...env, GH_TOKEN: 'user:wrong_pass'}, cwd, reject: false} ); // Verify the type and message are logged t.regex(stderr, /EGITNOPERMISSION/); - t.is(code, 1); + t.is(exitCode, 1); }); test('Hide sensitive environment variable values from the logs', async t => {