feat(esm): convert to esm (#2569)

for #2543

BREAKING CHANGE: semantic-release is now ESM-only. since it is used through its own executable, the impact on consuming projects should be minimal

BREAKING CHANGE: references to plugin files in configs need to include the file extension because of executing in an ESM context
This commit is contained in:
Matt Travi
2022-11-11 09:24:06 -06:00
committed by GitHub
parent 4012f75386
commit 9eab1adb9d
67 changed files with 3001 additions and 1761 deletions
+41 -31
View File
@@ -1,7 +1,7 @@
const test = require('ava');
const {union} = require('lodash');
const semver = require('semver');
const td = require('testdouble');
import test from 'ava';
import {union} from 'lodash-es';
import semver from 'semver';
import * as td from 'testdouble';
const getBranch = (branches, branch) => branches.find(({name}) => name === branch);
const release = (branches, name, version) => getBranch(branches, name).tags.push({version});
@@ -11,8 +11,21 @@ const merge = (branches, source, target, tag) => {
getBranch(branches, target).tags
);
};
const remoteBranches = [];
const repositoryUrl = 'repositoryUrl';
let expand, getTags, getBranches;
test('Enforce ranges with branching release workflow', async (t) => {
test.beforeEach(async (t) => {
getTags = (await td.replaceEsm('../../lib/branches/get-tags.js')).default;
expand = (await td.replaceEsm('../../lib/branches/expand.js')).default;
getBranches = (await import('../../lib/branches/index.js')).default;
})
test.afterEach.always((t) => {
td.reset();
});
test.serial('Enforce ranges with branching release workflow', async (t) => {
const branches = [
{name: '1.x', tags: []},
{name: '1.0.x', tags: []},
@@ -22,14 +35,11 @@ test('Enforce ranges with branching release workflow', async (t) => {
{name: 'beta', prerelease: true, tags: []},
{name: 'alpha', prerelease: true, tags: []},
];
td.replace('../../lib/branches/get-tags', () => branches);
td.replace('../../lib/branches/expand', () => []);
const getBranches = require('../../lib/branches');
const context = {options: {branches}};
td.when(expand(repositoryUrl, context, branches)).thenResolve(remoteBranches);
td.when(getTags(context, remoteBranches)).thenResolve(branches);
let result = (await getBranches('repositoryUrl', 'master', {options: {branches}})).map(({name, range}) => ({
name,
range,
}));
let result = (await getBranches(repositoryUrl, 'master', context)).map(({name, range}) => ({name, range,}));
t.is(getBranch(result, '1.0.x').range, '>=1.0.0 <1.0.0', 'Cannot release on 1.0.x before a releasing on master');
t.is(getBranch(result, '1.x').range, '>=1.1.0 <1.0.0', 'Cannot release on 1.x before a releasing on master');
t.is(getBranch(result, 'master').range, '>=1.0.0');
@@ -37,10 +47,7 @@ test('Enforce ranges with branching release workflow', async (t) => {
t.is(getBranch(result, 'next-major').range, '>=1.0.0');
release(branches, 'master', '1.0.0');
result = (await getBranches('repositoryUrl', 'master', {options: {branches}})).map(({name, range}) => ({
name,
range,
}));
result = (await getBranches('repositoryUrl', 'master', context)).map(({name, range}) => ({name, range}));
t.is(getBranch(result, '1.0.x').range, '>=1.0.0 <1.0.0', 'Cannot release on 1.0.x before a releasing on master');
t.is(getBranch(result, '1.x').range, '>=1.1.0 <1.0.0', 'Cannot release on 1.x before a releasing on master');
t.is(getBranch(result, 'master').range, '>=1.0.0');
@@ -191,7 +198,7 @@ test('Enforce ranges with branching release workflow', async (t) => {
t.is(getBranch(result, '1.x').range, '>=1.2.0 <2.0.0', 'Can release on 1.x only within range');
});
test('Throw SemanticReleaseError for invalid configurations', async (t) => {
test.serial('Throw SemanticReleaseError for invalid configurations', async (t) => {
const branches = [
{name: '123', range: '123', tags: []},
{name: '1.x', tags: []},
@@ -201,10 +208,12 @@ test('Throw SemanticReleaseError for invalid configurations', async (t) => {
{name: 'alpha', prerelease: 'alpha', tags: []},
{name: 'preview', prerelease: 'alpha', tags: []},
];
td.replace('../../lib/branches/get-tags', () => branches);
td.replace('../../lib/branches/expand', () => []);
const getBranches = require('../../lib/branches');
const errors = [...(await t.throwsAsync(getBranches('repositoryUrl', 'master', {options: {branches}})))];
const context = {options: {branches}};
td.when(expand(repositoryUrl, context, branches)).thenResolve(remoteBranches);
td.when(getTags(context, remoteBranches)).thenResolve(branches);
const error = await t.throwsAsync(getBranches(repositoryUrl, 'master', context));
const errors = [...error.errors];
t.is(errors[0].name, 'SemanticReleaseError');
t.is(errors[0].code, 'EMAINTENANCEBRANCH');
@@ -228,16 +237,16 @@ test('Throw SemanticReleaseError for invalid configurations', async (t) => {
t.truthy(errors[4].details);
});
test('Throw a SemanticReleaseError if there is duplicate branches', async (t) => {
test.serial('Throw a SemanticReleaseError if there is duplicate branches', async (t) => {
const branches = [
{name: 'master', tags: []},
{name: 'master', tags: []},
];
td.replace('../../lib/branches/get-tags', () => branches);
td.replace('../../lib/branches/expand', () => []);
const getBranches = require('../../lib/branches');
const context = {options: {branches}};
td.when(expand(repositoryUrl, context, branches)).thenResolve(remoteBranches);
td.when(getTags(context, remoteBranches)).thenResolve(branches);
const errors = [...(await t.throwsAsync(getBranches('repositoryUrl', 'master', {options: {branches}})))];
const errors = [...(await t.throwsAsync(getBranches(repositoryUrl, 'master', context))).errors];
t.is(errors[0].name, 'SemanticReleaseError');
t.is(errors[0].code, 'EDUPLICATEBRANCHES');
@@ -245,16 +254,17 @@ test('Throw a SemanticReleaseError if there is duplicate branches', async (t) =>
t.truthy(errors[0].details);
});
test('Throw a SemanticReleaseError for each invalid branch name', async (t) => {
test.serial('Throw a SemanticReleaseError for each invalid branch name', async (t) => {
const branches = [
{name: '~master', tags: []},
{name: '^master', tags: []},
];
td.replace('../../lib/branches/get-tags', () => branches);
td.replace('../../lib/branches/expand', () => []);
const getBranches = require('../../lib/branches');
const context = {options: {branches}};
const remoteBranches = [];
td.when(expand(repositoryUrl, context, branches)).thenResolve(remoteBranches);
td.when(getTags(context, remoteBranches)).thenResolve(branches);
const errors = [...(await t.throwsAsync(getBranches('repositoryUrl', 'master', {options: {branches}})))];
const errors = [...(await t.throwsAsync(getBranches(repositoryUrl, 'master', context))).errors];
t.is(errors[0].name, 'SemanticReleaseError');
t.is(errors[0].code, 'EINVALIDBRANCHNAME');
+3 -3
View File
@@ -1,6 +1,6 @@
const test = require('ava');
const expand = require('../../lib/branches/expand');
const {gitRepo, gitCommits, gitCheckout, gitPush} = require('../helpers/git-utils');
import test from 'ava';
import expand from '../../lib/branches/expand.js';
import {gitCheckout, gitCommits, gitPush, gitRepo} from '../helpers/git-utils.js';
test('Expand branches defined with globs', async (t) => {
const {cwd, repositoryUrl} = await gitRepo(true);
+3 -3
View File
@@ -1,6 +1,6 @@
const test = require('ava');
const getTags = require('../../lib/branches/get-tags');
const {gitRepo, gitCommits, gitTagVersion, gitCheckout, gitAddNote} = require('../helpers/git-utils');
import test from 'ava';
import getTags from '../../lib/branches/get-tags.js';
import {gitAddNote, gitCheckout, gitCommits, gitRepo, gitTagVersion} from '../helpers/git-utils.js';
test('Get the valid tags', async (t) => {
const {cwd} = await gitRepo();
+2 -2
View File
@@ -1,5 +1,5 @@
const test = require('ava');
const normalize = require('../../lib/branches/normalize');
import test from 'ava';
import * as normalize from '../../lib/branches/normalize.js';
const toTags = (versions) => versions.map((version) => ({version}));
+90 -56
View File
@@ -1,8 +1,8 @@
const test = require('ava');
const {escapeRegExp} = require('lodash');
const td = require('testdouble');
const {stub} = require('sinon');
const {SECRET_REPLACEMENT} = require('../lib/definitions/constants');
import test from 'ava';
import {escapeRegExp} from 'lodash-es';
import * as td from 'testdouble';
import {stub} from 'sinon';
import {SECRET_REPLACEMENT} from '../lib/definitions/constants.js';
let previousArgv;
let previousEnv;
@@ -27,10 +27,11 @@ test.afterEach.always((t) => {
process.argv = previousArgv;
process.env = previousEnv;
td.reset();
});
test.serial('Pass options to semantic-release API', async (t) => {
const run = stub().resolves(true);
const argv = [
'',
'',
@@ -72,33 +73,49 @@ test.serial('Pass options to semantic-release API', async (t) => {
'--debug',
'-d',
];
td.replace('..', run);
const index = await td.replaceEsm('../index.js');
process.argv = argv;
const cli = require('../cli');
const cli = (await import('../cli.js')).default;
const exitCode = await cli();
t.deepEqual(run.args[0][0].branches, ['master', 'next']);
t.is(run.args[0][0].repositoryUrl, 'https://github/com/owner/repo.git');
t.is(run.args[0][0].tagFormat, `v\${version}`);
t.deepEqual(run.args[0][0].plugins, ['plugin1', 'plugin2']);
t.deepEqual(run.args[0][0].extends, ['config1', 'config2']);
t.deepEqual(run.args[0][0].verifyConditions, ['condition1', 'condition2']);
t.is(run.args[0][0].analyzeCommits, 'analyze');
t.deepEqual(run.args[0][0].verifyRelease, ['verify1', 'verify2']);
t.deepEqual(run.args[0][0].generateNotes, ['notes']);
t.deepEqual(run.args[0][0].prepare, ['prepare1', 'prepare2']);
t.deepEqual(run.args[0][0].publish, ['publish1', 'publish2']);
t.deepEqual(run.args[0][0].success, ['success1', 'success2']);
t.deepEqual(run.args[0][0].fail, ['fail1', 'fail2']);
t.is(run.args[0][0].debug, true);
t.is(run.args[0][0].dryRun, true);
td.verify(index.default({
branches: ['master', 'next'],
b: ['master', 'next'],
'repository-url': 'https://github/com/owner/repo.git',
repositoryUrl: 'https://github/com/owner/repo.git',
r: 'https://github/com/owner/repo.git',
'tag-format': `v\${version}`,
tagFormat: `v\${version}`,
t: `v\${version}`,
plugins: ['plugin1', 'plugin2'],
p: ['plugin1', 'plugin2'],
extends: ['config1', 'config2'],
e: ['config1', 'config2'],
'dry-run': true,
dryRun: true,
d: true,
verifyConditions: ['condition1', 'condition2'],
'verify-conditions': ['condition1', 'condition2'],
analyzeCommits: 'analyze',
'analyze-commits': 'analyze',
verifyRelease: ['verify1', 'verify2'],
'verify-release': ['verify1', 'verify2'],
generateNotes: ['notes'],
'generate-notes': ['notes'],
prepare: ['prepare1', 'prepare2'],
publish: ['publish1', 'publish2'],
success: ['success1', 'success2'],
fail: ['fail1', 'fail2'],
debug: true,
_: [],
'$0': ''
}));
t.is(exitCode, 0);
});
test.serial('Pass options to semantic-release API with alias arguments', async (t) => {
const run = stub().resolves(true);
const argv = [
'',
'',
@@ -116,48 +133,65 @@ test.serial('Pass options to semantic-release API with alias arguments', async (
'config2',
'--dry-run',
];
td.replace('..', run);
const index = await td.replaceEsm('../index.js');
process.argv = argv;
const cli = require('../cli');
const cli = (await import('../cli.js')).default;
const exitCode = await cli();
t.deepEqual(run.args[0][0].branches, ['master']);
t.is(run.args[0][0].repositoryUrl, 'https://github/com/owner/repo.git');
t.is(run.args[0][0].tagFormat, `v\${version}`);
t.deepEqual(run.args[0][0].plugins, ['plugin1', 'plugin2']);
t.deepEqual(run.args[0][0].extends, ['config1', 'config2']);
t.is(run.args[0][0].dryRun, true);
td.verify(index.default({
branches: ['master'],
b: ['master'],
'repository-url': 'https://github/com/owner/repo.git',
repositoryUrl: 'https://github/com/owner/repo.git',
r: 'https://github/com/owner/repo.git',
'tag-format': `v\${version}`,
tagFormat: `v\${version}`,
t: `v\${version}`,
plugins: ['plugin1', 'plugin2'],
p: ['plugin1', 'plugin2'],
extends: ['config1', 'config2'],
e: ['config1', 'config2'],
'dry-run': true,
dryRun: true,
d: true,
_: [],
'$0': ''
}));
t.is(exitCode, 0);
});
test.serial('Pass unknown options to semantic-release API', async (t) => {
const run = stub().resolves(true);
const argv = ['', '', '--bool', '--first-option', 'value1', '--second-option', 'value2', '--second-option', 'value3'];
td.replace('..', run);
const index = await td.replaceEsm('../index.js');
process.argv = argv;
const cli = require('../cli');
const cli = (await import('../cli.js')).default;
const exitCode = await cli();
t.is(run.args[0][0].bool, true);
t.is(run.args[0][0].firstOption, 'value1');
t.deepEqual(run.args[0][0].secondOption, ['value2', 'value3']);
td.verify(index.default({
bool: true,
firstOption: 'value1',
'first-option': 'value1',
secondOption: ['value2', 'value3'],
'second-option': ['value2', 'value3'],
_: [],
'$0': ''
}));
t.is(exitCode, 0);
});
test.serial('Pass empty Array to semantic-release API for list option set to "false"', async (t) => {
const run = stub().resolves(true);
const argv = ['', '', '--publish', 'false'];
td.replace('..', run);
const index = await td.replaceEsm('../index.js');
process.argv = argv;
const cli = require('../cli');
const cli = (await import('../cli.js')).default;
const exitCode = await cli();
t.deepEqual(run.args[0][0].publish, []);
td.verify(index.default({publish: [], _: [], '$0': ''}));
t.is(exitCode, 0);
});
@@ -165,9 +199,9 @@ test.serial('Pass empty Array to semantic-release API for list option set to "fa
test.serial('Do not set properties in option for which arg is not in command line', async (t) => {
const run = stub().resolves(true);
const argv = ['', '', '-b', 'master'];
td.replace('..', run);
await td.replaceEsm('../index.js', null, run);
process.argv = argv;
const cli = require('../cli');
const cli = (await import('../cli.js')).default;
await cli();
@@ -184,9 +218,9 @@ test.serial('Do not set properties in option for which arg is not in command lin
test.serial('Display help', async (t) => {
const run = stub().resolves(true);
const argv = ['', '', '--help'];
td.replace('..', run);
await td.replaceEsm('../index.js', null, run);
process.argv = argv;
const cli = require('../cli');
const cli = (await import('../cli.js')).default;
const exitCode = await cli();
@@ -197,9 +231,9 @@ test.serial('Display help', async (t) => {
test.serial('Return error exitCode and prints help if called with a command', async (t) => {
const run = stub().resolves(true);
const argv = ['', '', 'pre'];
td.replace('..', run);
await td.replaceEsm('../index.js', null, run);
process.argv = argv;
const cli = require('../cli');
const cli = (await import('../cli.js')).default;
const exitCode = await cli();
@@ -211,9 +245,9 @@ test.serial('Return error exitCode and prints help if called with a command', as
test.serial('Return error exitCode if multiple plugin are set for single plugin', async (t) => {
const run = stub().resolves(true);
const argv = ['', '', '--analyze-commits', 'analyze1', 'analyze2'];
td.replace('..', run);
await td.replaceEsm('../index.js', null, run);
process.argv = argv;
const cli = require('../cli');
const cli = (await import('../cli.js')).default;
const exitCode = await cli();
@@ -223,11 +257,11 @@ test.serial('Return error exitCode if multiple plugin are set for single plugin'
});
test.serial('Return error exitCode if semantic-release throw error', async (t) => {
const run = stub().rejects(new Error('semantic-release error'));
const argv = ['', ''];
td.replace('..', run);
const index = await td.replaceEsm('../index.js');
td.when(index.default({_: [], '$0': ''})).thenReject(new Error('semantic-release error'));
process.argv = argv;
const cli = require('../cli');
const cli = (await import('../cli.js')).default;
const exitCode = await cli();
@@ -237,12 +271,12 @@ test.serial('Return error exitCode if semantic-release throw error', async (t) =
test.serial('Hide sensitive environment variable values from the logs', async (t) => {
const env = {MY_TOKEN: 'secret token'};
const run = stub().rejects(new Error(`Throw error: Exposing token ${env.MY_TOKEN}`));
const argv = ['', ''];
td.replace('..', run);
const index = await td.replaceEsm('../index.js');
td.when(index.default({_: [], '$0': ''})).thenReject(new Error(`Throw error: Exposing token ${env.MY_TOKEN}`));
process.argv = argv;
process.env = {...process.env, ...env};
const cli = require('../cli');
const cli = (await import('../cli.js')).default;
const exitCode = await cli();
+2 -2
View File
@@ -1,5 +1,5 @@
const test = require('ava');
const {maintenance, prerelease, release} = require('../../lib/definitions/branches');
import test from 'ava';
import {maintenance, prerelease, release} from '../../lib/definitions/branches.js';
test('A "maintenance" branch is identified by having a "range" property or a "name" formatted like "N.x", "N.x.x" or "N.N.x"', (t) => {
/* eslint-disable unicorn/no-fn-reference-in-iterator */
+3 -3
View File
@@ -1,6 +1,6 @@
const test = require('ava');
const plugins = require('../../lib/definitions/plugins');
const {RELEASE_NOTES_SEPARATOR, SECRET_REPLACEMENT} = require('../../lib/definitions/constants');
import test from 'ava';
import plugins from '../../lib/definitions/plugins.js';
import {RELEASE_NOTES_SEPARATOR, SECRET_REPLACEMENT} from '../../lib/definitions/constants.js';
test('The "analyzeCommits" plugin output must be either undefined or a valid semver release type', (t) => {
t.false(plugins.analyzeCommits.outputValidator('invalid'));
+1 -1
View File
@@ -1 +1 @@
module.exports = () => {};
export default () => {}
+3 -3
View File
@@ -1,4 +1,4 @@
const SemanticReleaseError = require('@semantic-release/error');
import SemanticReleaseError from '@semantic-release/error';
class InheritedError extends SemanticReleaseError {
constructor(message, code) {
@@ -9,6 +9,6 @@ class InheritedError extends SemanticReleaseError {
}
}
module.exports = () => {
export default () => {
throw new InheritedError('Inherited error', 'EINHERITED');
};
}
+2 -2
View File
@@ -1,5 +1,5 @@
module.exports = () => {
export default () => {
const error = new Error('a');
error.errorProperty = 'errorProperty';
throw error;
};
}
+3 -3
View File
@@ -1,5 +1,5 @@
const AggregateError = require('aggregate-error');
import AggregateError from 'aggregate-error';
module.exports = () => {
export default () => {
throw new AggregateError([new Error('a'), new Error('b')]);
};
}
+1 -1
View File
@@ -1 +1 @@
module.exports = (pluginConfig, context) => context;
export default (pluginConfig, context) => context
+2 -2
View File
@@ -1,6 +1,6 @@
module.exports = (pluginConfig, {env, logger}) => {
export default (pluginConfig, {env, logger}) => {
console.log(`Console: Exposing token ${env.MY_TOKEN}`);
logger.log(`Log: Exposing token ${env.MY_TOKEN}`);
logger.error(`Error: Console token ${env.MY_TOKEN}`);
throw new Error(`Throw error: Exposing ${env.MY_TOKEN}`);
};
}
+1 -1
View File
@@ -1 +1 @@
module.exports = (pluginConfig, context) => ({pluginConfig, context});
export default (pluginConfig, context) => ({pluginConfig, context})
+4 -4
View File
@@ -1,7 +1,7 @@
const test = require('ava');
const {stub} = require('sinon');
const getCommits = require('../lib/get-commits');
const {gitRepo, gitCommits, gitDetachedHead} = require('./helpers/git-utils');
import test from 'ava';
import {stub} from 'sinon';
import getCommits from '../lib/get-commits.js';
import {gitCommits, gitDetachedHead, gitRepo} from './helpers/git-utils.js';
test.beforeEach((t) => {
// Stub the logger functions
+177 -167
View File
@@ -1,12 +1,15 @@
const path = require('path');
const {format} = require('util');
const test = require('ava');
const {writeFile, outputJson} = require('fs-extra');
const {omit} = require('lodash');
const td = require('testdouble');
const {stub} = require('sinon');
const yaml = require('js-yaml');
const {gitRepo, gitTagVersion, gitCommits, gitShallowClone, gitAddConfig} = require('./helpers/git-utils');
import path from 'node:path';
import {format} from 'node:util';
import test from 'ava';
import fsExtra from 'fs-extra';
import {omit} from 'lodash-es';
import * as td from 'testdouble';
import yaml from 'js-yaml';
import {gitAddConfig, gitCommits, gitRepo, gitShallowClone, gitTagVersion} from './helpers/git-utils.js';
const {outputJson, writeFile} = fsExtra;
const pluginsConfig = {foo: 'bar', baz: 'qux'};
let plugins;
const DEFAULT_PLUGINS = [
'@semantic-release/commit-analyzer',
@@ -15,10 +18,13 @@ const DEFAULT_PLUGINS = [
'@semantic-release/github',
];
test.beforeEach((t) => {
t.context.plugins = stub().returns({});
td.replace('../lib/plugins', t.context.plugins);
t.context.getConfig = require('../lib/get-config');
test.beforeEach(async (t) => {
plugins = (await td.replaceEsm('../lib/plugins/index.js')).default;
t.context.getConfig = (await import('../lib/get-config.js')).default;
});
test.afterEach.always((t) => {
td.reset();
});
test('Default values, reading repositoryUrl from package.json', async (t) => {
@@ -103,7 +109,7 @@ test('Convert "ci" option to "noCi"', async (t) => {
t.is(result.noCi, true);
});
test('Read options from package.json', async (t) => {
test.serial('Read options from package.json', async (t) => {
// Create a git repository, set the current working directory at the root of the repo
const {cwd} = await gitRepo();
const options = {
@@ -114,19 +120,18 @@ test('Read options from package.json', async (t) => {
tagFormat: `v\${version}`,
plugins: false,
};
// Verify the plugins module is called with the plugin options from package.json
td.when(plugins({cwd, options}, {})).thenResolve(pluginsConfig);
// Create package.json in repository root
await outputJson(path.resolve(cwd, 'package.json'), {release: options});
const {options: result} = await t.context.getConfig({cwd});
const result = await t.context.getConfig({cwd});
const expected = {...options, branches: ['test_branch']};
// Verify the options contains the plugin config from package.json
t.deepEqual(result, expected);
// Verify the plugins module is called with the plugin options from package.json
t.deepEqual(t.context.plugins.args[0][0], {options: expected, cwd});
t.deepEqual(result, {options, plugins: pluginsConfig});
});
test('Read options from .releaserc.yml', async (t) => {
test.serial('Read options from .releaserc.yml', async (t) => {
// Create a git repository, set the current working directory at the root of the repo
const {cwd} = await gitRepo();
const options = {
@@ -138,17 +143,16 @@ test('Read options from .releaserc.yml', async (t) => {
};
// Create package.json in repository root
await writeFile(path.resolve(cwd, '.releaserc.yml'), yaml.dump(options));
const {options: result} = await t.context.getConfig({cwd});
const expected = {...options, branches: ['test_branch']};
// Verify the options contains the plugin config from package.json
t.deepEqual(result, expected);
// Verify the plugins module is called with the plugin options from package.json
t.deepEqual(t.context.plugins.args[0][0], {options: expected, cwd});
td.when(plugins({cwd, options}, {})).thenResolve(pluginsConfig);
const result = await t.context.getConfig({cwd});
// Verify the options contains the plugin config from package.json
t.deepEqual(result, {options, plugins: pluginsConfig});
});
test('Read options from .releaserc.json', async (t) => {
test.serial('Read options from .releaserc.json', async (t) => {
// Create a git repository, set the current working directory at the root of the repo
const {cwd} = await gitRepo();
const options = {
@@ -160,17 +164,16 @@ test('Read options from .releaserc.json', async (t) => {
};
// Create package.json in repository root
await outputJson(path.resolve(cwd, '.releaserc.json'), options);
const {options: result} = await t.context.getConfig({cwd});
const expected = {...options, branches: ['test_branch']};
// Verify the options contains the plugin config from package.json
t.deepEqual(result, expected);
// Verify the plugins module is called with the plugin options from package.json
t.deepEqual(t.context.plugins.args[0][0], {options: expected, cwd});
td.when(plugins({cwd, options}, {})).thenResolve(pluginsConfig);
const result = await t.context.getConfig({cwd});
// Verify the options contains the plugin config from package.json
t.deepEqual(result, {options, plugins: pluginsConfig});
});
test('Read options from .releaserc.js', async (t) => {
test.serial('Read options from .releaserc.js', async (t) => {
// Create a git repository, set the current working directory at the root of the repo
const {cwd} = await gitRepo();
const options = {
@@ -182,17 +185,16 @@ test('Read options from .releaserc.js', async (t) => {
};
// Create package.json in repository root
await writeFile(path.resolve(cwd, '.releaserc.js'), `module.exports = ${JSON.stringify(options)}`);
const {options: result} = await t.context.getConfig({cwd});
const expected = {...options, branches: ['test_branch']};
// Verify the options contains the plugin config from package.json
t.deepEqual(result, expected);
// Verify the plugins module is called with the plugin options from package.json
t.deepEqual(t.context.plugins.args[0][0], {options: expected, cwd});
td.when(plugins({cwd, options}, {})).thenResolve(pluginsConfig);
const result = await t.context.getConfig({cwd});
// Verify the options contains the plugin config from package.json
t.deepEqual(result, {options, plugins: pluginsConfig});
});
test('Read options from .releaserc.cjs', async (t) => {
test.serial('Read options from .releaserc.cjs', async (t) => {
// Create a git repository, set the current working directory at the root of the repo
const {cwd} = await gitRepo();
const options = {
@@ -204,17 +206,16 @@ test('Read options from .releaserc.cjs', async (t) => {
};
// Create .releaserc.cjs in repository root
await writeFile(path.resolve(cwd, '.releaserc.cjs'), `module.exports = ${JSON.stringify(options)}`);
const {options: result} = await t.context.getConfig({cwd});
const expected = {...options, branches: ['test_branch']};
// Verify the options contains the plugin config from .releaserc.cjs
t.deepEqual(result, expected);
// Verify the plugins module is called with the plugin options from .releaserc.cjs
t.deepEqual(t.context.plugins.args[0][0], {options: expected, cwd});
td.when(plugins({cwd, options}, {})).thenResolve(pluginsConfig);
const result = await t.context.getConfig({cwd});
// Verify the options contains the plugin config from .releaserc.cjs
t.deepEqual(result, {options, plugins: pluginsConfig});
});
test('Read options from release.config.js', async (t) => {
test.serial('Read options from release.config.js', async (t) => {
// Create a git repository, set the current working directory at the root of the repo
const {cwd} = await gitRepo();
const options = {
@@ -226,17 +227,16 @@ test('Read options from release.config.js', async (t) => {
};
// Create package.json in repository root
await writeFile(path.resolve(cwd, 'release.config.js'), `module.exports = ${JSON.stringify(options)}`);
const {options: result} = await t.context.getConfig({cwd});
const expected = {...options, branches: ['test_branch']};
// Verify the options contains the plugin config from package.json
t.deepEqual(result, expected);
// Verify the plugins module is called with the plugin options from package.json
t.deepEqual(t.context.plugins.args[0][0], {options: expected, cwd});
td.when(plugins({cwd, options}, {})).thenResolve(pluginsConfig);
const result = await t.context.getConfig({cwd});
// Verify the options contains the plugin config from package.json
t.deepEqual(result, {options, plugins: pluginsConfig});
});
test('Read options from release.config.cjs', async (t) => {
test.serial('Read options from release.config.cjs', async (t) => {
// Create a git repository, set the current working directory at the root of the repo
const {cwd} = await gitRepo();
const options = {
@@ -246,19 +246,18 @@ test('Read options from release.config.cjs', async (t) => {
tagFormat: `v\${version}`,
plugins: false,
};
// Verify the plugins module is called with the plugin options from release.config.cjs
td.when(plugins({cwd, options}, {})).thenResolve(pluginsConfig);
// Create release.config.cjs in repository root
await writeFile(path.resolve(cwd, 'release.config.cjs'), `module.exports = ${JSON.stringify(options)}`);
const {options: result} = await t.context.getConfig({cwd});
const result = await t.context.getConfig({cwd});
const expected = {...options, branches: ['test_branch']};
// Verify the options contains the plugin config from release.config.cjs
t.deepEqual(result, expected);
// Verify the plugins module is called with the plugin options from release.config.cjs
t.deepEqual(t.context.plugins.args[0][0], {options: expected, cwd});
t.deepEqual(result, {options, plugins: pluginsConfig});
});
test('Prioritise CLI/API parameters over file configuration and git repo', async (t) => {
test.serial('Prioritise CLI/API parameters over file configuration and git repo', async (t) => {
// Create a git repository, set the current working directory at the root of the repo
let {cwd, repositoryUrl} = await gitRepo();
await gitCommits(['First'], {cwd});
@@ -275,20 +274,19 @@ test('Prioritise CLI/API parameters over file configuration and git repo', async
tagFormat: `cli\${version}`,
plugins: false,
};
// Verify the plugins module is called with the plugin options from CLI/API
td.when(plugins({cwd, options}, {})).thenResolve(pluginsConfig);
const pkg = {release: pkgOptions, repository: 'git@host.null:owner/module.git'};
// Create package.json in repository root
await outputJson(path.resolve(cwd, 'package.json'), pkg);
const result = await t.context.getConfig({cwd}, options);
const expected = {...options, branches: ['branch_cli']};
// Verify the options contains the plugin config from CLI/API
t.deepEqual(result.options, expected);
// Verify the plugins module is called with the plugin options from CLI/API
t.deepEqual(t.context.plugins.args[0][0], {options: expected, cwd});
t.deepEqual(result, {options, plugins: pluginsConfig});
});
test('Read configuration from file path in "extends"', async (t) => {
test.serial('Read configuration from file path in "extends"', async (t) => {
// Create a git repository, set the current working directory at the root of the repo
const {cwd} = await gitRepo();
const pkgOptions = {extends: './shareable.json'};
@@ -303,23 +301,24 @@ test('Read configuration from file path in "extends"', async (t) => {
// Create package.json and shareable.json in repository root
await outputJson(path.resolve(cwd, 'package.json'), {release: pkgOptions});
await outputJson(path.resolve(cwd, 'shareable.json'), options);
const {options: result} = await t.context.getConfig({cwd});
const expected = {...options, branches: ['test_branch']};
// Verify the options contains the plugin config from shareable.json
t.deepEqual(result, expected);
// Verify the plugins module is called with the plugin options from shareable.json
t.deepEqual(t.context.plugins.args[0][0], {options: expected, cwd});
t.deepEqual(t.context.plugins.args[0][1], {
analyzeCommits: './shareable.json',
generateNotes: './shareable.json',
'plugin-1': './shareable.json',
'plugin-2': './shareable.json',
});
td.when(plugins(
{cwd, options},
{
analyzeCommits: './shareable.json',
generateNotes: './shareable.json',
'plugin-1': './shareable.json',
'plugin-2': './shareable.json',
}
)).thenResolve(pluginsConfig);
const result = await t.context.getConfig({cwd});
// Verify the options contains the plugin config from shareable.json
t.deepEqual(result, {options, plugins: pluginsConfig});
});
test('Read configuration from module path in "extends"', async (t) => {
test.serial('Read configuration from module path in "extends"', async (t) => {
// Create a git repository, set the current working directory at the root of the repo
const {cwd} = await gitRepo();
const pkgOptions = {extends: 'shareable'};
@@ -334,21 +333,19 @@ test('Read configuration from module path in "extends"', async (t) => {
// Create package.json and shareable.json in repository root
await outputJson(path.resolve(cwd, 'package.json'), {release: pkgOptions});
await outputJson(path.resolve(cwd, 'node_modules/shareable/index.json'), options);
const {options: result} = await t.context.getConfig({cwd});
const expected = {...options, branches: ['test_branch']};
// Verify the options contains the plugin config from shareable.json
t.deepEqual(result, expected);
// Verify the plugins module is called with the plugin options from shareable.json
t.deepEqual(t.context.plugins.args[0][0], {options: expected, cwd});
t.deepEqual(t.context.plugins.args[0][1], {
analyzeCommits: 'shareable',
generateNotes: 'shareable',
});
td.when(plugins(
{cwd, options},
{analyzeCommits: 'shareable', generateNotes: 'shareable'}
)).thenResolve(pluginsConfig);
const result = await t.context.getConfig({cwd});
// Verify the options contains the plugin config from shareable.json
t.deepEqual(result, {options, plugins: pluginsConfig});
});
test('Read configuration from an array of paths in "extends"', async (t) => {
test.serial('Read configuration from an array of paths in "extends"', async (t) => {
// Create a git repository, set the current working directory at the root of the repo
const {cwd} = await gitRepo();
const pkgOptions = {extends: ['./shareable1.json', './shareable2.json']};
@@ -370,24 +367,26 @@ test('Read configuration from an array of paths in "extends"', async (t) => {
await outputJson(path.resolve(cwd, 'package.json'), {release: pkgOptions});
await outputJson(path.resolve(cwd, 'shareable1.json'), options1);
await outputJson(path.resolve(cwd, 'shareable2.json'), options2);
const {options: result} = await t.context.getConfig({cwd});
const expected = {...options1, ...options2, branches: ['test_branch']};
// Verify the options contains the plugin config from shareable1.json and shareable2.json
t.deepEqual(result, expected);
const expectedOptions = {...options1, ...options2, branches: ['test_branch']};
// Verify the plugins module is called with the plugin options from shareable1.json and shareable2.json
t.deepEqual(t.context.plugins.args[0][0], {options: expected, cwd});
t.deepEqual(t.context.plugins.args[0][1], {
verifyRelease1: './shareable1.json',
verifyRelease2: './shareable2.json',
generateNotes2: './shareable2.json',
analyzeCommits1: './shareable1.json',
analyzeCommits2: './shareable2.json',
});
td.when(plugins(
{options: expectedOptions, cwd},
{
verifyRelease1: './shareable1.json',
verifyRelease2: './shareable2.json',
generateNotes2: './shareable2.json',
analyzeCommits1: './shareable1.json',
analyzeCommits2: './shareable2.json',
}
)).thenResolve(pluginsConfig);
const result = await t.context.getConfig({cwd});
// Verify the options contains the plugin config from shareable1.json and shareable2.json
t.deepEqual(result, {options: expectedOptions, plugins: pluginsConfig});
});
test('Prioritize configuration from config file over "extends"', async (t) => {
test.serial('Prioritize configuration from config file over "extends"', async (t) => {
// Create a git repository, set the current working directory at the root of the repo
const {cwd} = await gitRepo();
const pkgOptions = {
@@ -408,22 +407,24 @@ test('Prioritize configuration from config file over "extends"', async (t) => {
// Create package.json and shareable.json in repository root
await outputJson(path.resolve(cwd, 'package.json'), {release: pkgOptions});
await outputJson(path.resolve(cwd, 'shareable.json'), options1);
const {options: result} = await t.context.getConfig({cwd});
const expected = omit({...options1, ...pkgOptions, branches: ['test_pkg']}, 'extends');
// Verify the options contains the plugin config from package.json and shareable.json
t.deepEqual(result, expected);
const expectedOptions = omit({...options1, ...pkgOptions, branches: ['test_pkg']}, 'extends');
// Verify the plugins module is called with the plugin options from package.json and shareable.json
t.deepEqual(t.context.plugins.args[0][0], {options: expected, cwd});
t.deepEqual(t.context.plugins.args[0][1], {
analyzeCommits: './shareable.json',
generateNotesShareable: './shareable.json',
publishShareable: './shareable.json',
});
td.when(plugins(
{cwd, options: expectedOptions},
{
analyzeCommits: './shareable.json',
generateNotesShareable: './shareable.json',
publishShareable: './shareable.json',
}
)).thenResolve(pluginsConfig);
const result = await t.context.getConfig({cwd});
// Verify the options contains the plugin config from package.json and shareable.json
t.deepEqual(result, {options: expectedOptions, plugins: pluginsConfig});
});
test('Prioritize configuration from cli/API options over "extends"', async (t) => {
test.serial('Prioritize configuration from cli/API options over "extends"', async (t) => {
// Create a git repository, set the current working directory at the root of the repo
const {cwd} = await gitRepo();
const cliOptions = {
@@ -456,17 +457,20 @@ test('Prioritize configuration from cli/API options over "extends"', async (t) =
await outputJson(path.resolve(cwd, 'package.json'), {release: pkgOptions});
await outputJson(path.resolve(cwd, 'shareable1.json'), options1);
await outputJson(path.resolve(cwd, 'shareable2.json'), options2);
const {options: result} = await t.context.getConfig({cwd}, cliOptions);
const expected = omit({...options2, ...pkgOptions, ...cliOptions, branches: ['branch_opts']}, 'extends');
// Verify the options contains the plugin config from package.json and shareable2.json
t.deepEqual(result, expected);
const expectedOptions = omit({...options2, ...pkgOptions, ...cliOptions, branches: ['branch_opts']}, 'extends');
// Verify the plugins module is called with the plugin options from package.json and shareable2.json
t.deepEqual(t.context.plugins.args[0][0], {options: expected, cwd});
td.when(plugins(
{cwd, options: expectedOptions},
{analyzeCommits2: './shareable2.json', publishShareable: './shareable2.json'}
)).thenResolve(pluginsConfig);
const result = await t.context.getConfig({cwd}, cliOptions);
// Verify the options contains the plugin config from package.json and shareable2.json
t.deepEqual(result, {options: expectedOptions, plugins: pluginsConfig});
});
test('Allow to unset properties defined in shareable config with "null"', async (t) => {
test.serial('Allow to unset properties defined in shareable config with "null"', async (t) => {
// Create a git repository, set the current working directory at the root of the repo
const {cwd} = await gitRepo();
const pkgOptions = {
@@ -485,33 +489,40 @@ test('Allow to unset properties defined in shareable config with "null"', async
// Create package.json and shareable.json in repository root
await outputJson(path.resolve(cwd, 'package.json'), {release: pkgOptions});
await outputJson(path.resolve(cwd, 'shareable.json'), options1);
// Verify the plugins module is called with the plugin options from shareable.json and the default `plugins`
td.when(plugins(
{
options: {
...omit(options1, 'analyzeCommits'),
...omit(pkgOptions, ['extends', 'analyzeCommits']),
plugins: DEFAULT_PLUGINS,
},
cwd,
},
{
generateNotes: './shareable.json',
analyzeCommits: './shareable.json',
'test-plugin': './shareable.json',
}
)).thenResolve(pluginsConfig);
const {options} = await t.context.getConfig({cwd});
const result = await t.context.getConfig({cwd});
// Verify the options contains the plugin config from shareable.json and the default `plugins`
t.deepEqual(options, {
...omit(options1, ['analyzeCommits']),
...omit(pkgOptions, ['extends', 'analyzeCommits']),
plugins: DEFAULT_PLUGINS,
});
// Verify the plugins module is called with the plugin options from shareable.json and the default `plugins`
t.deepEqual(t.context.plugins.args[0][0], {
options: {
...omit(options1, 'analyzeCommits'),
...omit(pkgOptions, ['extends', 'analyzeCommits']),
plugins: DEFAULT_PLUGINS,
},
cwd,
});
t.deepEqual(t.context.plugins.args[0][1], {
generateNotes: './shareable.json',
analyzeCommits: './shareable.json',
'test-plugin': './shareable.json',
});
t.deepEqual(
result,
{
options: {
...omit(options1, ['analyzeCommits']),
...omit(pkgOptions, ['extends', 'analyzeCommits']),
plugins: DEFAULT_PLUGINS,
},
plugins: pluginsConfig
}
);
});
test('Allow to unset properties defined in shareable config with "undefined"', async (t) => {
test.serial('Allow to unset properties defined in shareable config with "undefined"', async (t) => {
// Create a git repository, set the current working directory at the root of the repo
const {cwd} = await gitRepo();
const pkgOptions = {
@@ -526,25 +537,24 @@ test('Allow to unset properties defined in shareable config with "undefined"', a
tagFormat: `v\${version}`,
plugins: false,
};
// Create package.json and release.config.js in repository root
// Create release.config.js and shareable.json in repository root
await writeFile(path.resolve(cwd, 'release.config.js'), `module.exports = ${format(pkgOptions)}`);
await outputJson(path.resolve(cwd, 'shareable.json'), options1);
const {options: result} = await t.context.getConfig({cwd});
const expected = {
const expectedOptions = {
...omit(options1, 'analyzeCommits'),
...omit(pkgOptions, ['extends', 'analyzeCommits']),
branches: ['test_branch'],
};
// Verify the options contains the plugin config from shareable.json
t.deepEqual(result, expected);
// Verify the plugins module is called with the plugin options from shareable.json
t.deepEqual(t.context.plugins.args[0][0], {options: expected, cwd});
t.deepEqual(t.context.plugins.args[0][1], {
generateNotes: './shareable.json',
analyzeCommits: './shareable.json',
});
td.when(plugins(
{options: expectedOptions, cwd},
{generateNotes: './shareable.json', analyzeCommits: './shareable.json'}
)).thenResolve(pluginsConfig);
const result = await t.context.getConfig({cwd});
// Verify the options contains the plugin config from shareable.json
t.deepEqual(result, {options: expectedOptions, plugins: pluginsConfig});
});
test('Throw an Error if one of the shareable config cannot be found', async (t) => {
+3 -3
View File
@@ -1,6 +1,6 @@
const test = require('ava');
const getAuthUrl = require('../lib/get-git-auth-url');
const {gitRepo} = require('./helpers/git-utils');
import test from 'ava';
import getAuthUrl from '../lib/get-git-auth-url.js';
import {gitRepo} from './helpers/git-utils.js';
const env = {GIT_ASKPASS: 'echo', GIT_TERMINAL_PROMPT: 0};
+2 -2
View File
@@ -1,5 +1,5 @@
const test = require('ava');
const getLastRelease = require('../lib/get-last-release');
import test from 'ava';
import getLastRelease from '../lib/get-last-release.js';
test('Get the highest non-prerelease valid tag', (t) => {
const result = getLastRelease({
+3 -3
View File
@@ -1,6 +1,6 @@
const test = require('ava');
const {spy} = require('sinon');
const getLogger = require('../lib/get-logger');
import test from 'ava';
import {spy} from 'sinon';
import getLogger from '../lib/get-logger.js';
test('Expose "error", "success" and "log" functions', (t) => {
const stdout = spy();
+3 -3
View File
@@ -1,6 +1,6 @@
const test = require('ava');
const {stub} = require('sinon');
const getNextVersion = require('../lib/get-next-version');
import test from 'ava';
import {stub} from 'sinon';
import getNextVersion from '../lib/get-next-version.js';
test.beforeEach((t) => {
// Stub the logger functions
+2 -2
View File
@@ -1,5 +1,5 @@
const test = require('ava');
const getReleaseToAdd = require('../lib/get-release-to-add');
import test from 'ava';
import getReleaseToAdd from '../lib/get-release-to-add.js';
test('Return versions merged from release to maintenance branch, excluding lower than branch start range', (t) => {
const result = getReleaseToAdd({
+30 -30
View File
@@ -1,40 +1,40 @@
const test = require('ava');
const tempy = require('tempy');
const {
getTagHead,
isRefExists,
import test from 'ava';
import {temporaryDirectory} from 'tempy';
import {
addNote,
fetch,
fetchNotes,
getBranches,
getGitHead,
getNote,
getTagHead,
getTags,
isBranchUpToDate,
isGitRepo,
isRefExists,
push,
repoUrl,
tag,
push,
getTags,
getBranches,
isGitRepo,
verifyTagName,
isBranchUpToDate,
getNote,
addNote,
fetchNotes,
} = require('../lib/git');
const {
gitRepo,
gitCommits,
gitCheckout,
gitTagVersion,
gitShallowClone,
gitGetCommits,
verifyTagName
} from '../lib/git.js';
import {
gitAddConfig,
gitAddNote,
gitCheckout,
gitCommits,
gitCommitTag,
gitRemoteTagHead,
gitPush,
gitDetachedHead,
gitDetachedHeadFromBranch,
gitAddNote,
gitGetNote,
gitFetch,
initGit,
} = require('./helpers/git-utils');
gitGetCommits,
gitGetNote,
gitPush,
gitRemoteTagHead,
gitRepo,
gitShallowClone,
gitTagVersion,
initGit
} from './helpers/git-utils.js';
test('Get the last commit sha', async (t) => {
// Create a git repository, set the current working directory at the root of the repo
@@ -268,7 +268,7 @@ test('Return "true" if in a Git repository', async (t) => {
});
test('Return falsy if not in a Git repository', async (t) => {
const cwd = tempy.directory();
const cwd = temporaryDirectory();
t.falsy(await isGitRepo({cwd}));
});
@@ -288,7 +288,7 @@ test('Return falsy for invalid tag names', async (t) => {
});
test('Throws error if obtaining the tags fails', async (t) => {
const cwd = tempy.directory();
const cwd = temporaryDirectory();
await t.throwsAsync(getTags('master', {cwd}));
});
+34 -59
View File
@@ -1,10 +1,10 @@
const tempy = require('tempy');
const execa = require('execa');
const fileUrl = require('file-url');
const pEachSeries = require('p-each-series');
const gitLogParser = require('git-log-parser');
const getStream = require('get-stream');
const {GIT_NOTE_REF} = require('../../lib/definitions/constants');
import {temporaryDirectory} from 'tempy';
import {execa} from 'execa';
import fileUrl from 'file-url';
import pEachSeries from 'p-each-series';
import gitLogParser from 'git-log-parser';
import getStream from 'get-stream';
import {GIT_NOTE_REF} from '../../lib/definitions/constants.js';
/**
* Commit message information.
@@ -23,8 +23,8 @@ const {GIT_NOTE_REF} = require('../../lib/definitions/constants');
* @param {Boolean} withRemote `true` to create a shallow clone of a bare repository.
* @return {String} The path of the repository
*/
async function initGit(withRemote) {
const cwd = tempy.directory();
export async function initGit(withRemote) {
const cwd = temporaryDirectory();
const args = withRemote ? ['--bare', '--initial-branch=master'] : ['--initial-branch=master'];
await execa('git', ['init', ...args], {cwd}).catch(() => {
@@ -45,7 +45,7 @@ async function initGit(withRemote) {
* @param {String} [branch='master'] The branch to initialize.
* @return {String} The path of the clone if `withRemote` is `true`, the path of the repository otherwise.
*/
async function gitRepo(withRemote, branch = 'master') {
export async function gitRepo(withRemote, branch = 'master') {
let {cwd, repositoryUrl} = await initGit(withRemote);
if (withRemote) {
await initBareRepo(repositoryUrl, branch);
@@ -70,8 +70,8 @@ async function gitRepo(withRemote, branch = 'master') {
* @param {String} repositoryUrl The URL of the bare repository.
* @param {String} [branch='master'] the branch to initialize.
*/
async function initBareRepo(repositoryUrl, branch = 'master') {
const cwd = tempy.directory();
export async function initBareRepo(repositoryUrl, branch = 'master') {
const cwd = temporaryDirectory();
await execa('git', ['clone', '--no-hardlinks', repositoryUrl, cwd], {cwd});
await gitCheckout(branch, true, {cwd});
await gitCommits(['Initial commit'], {cwd});
@@ -86,7 +86,7 @@ async function initBareRepo(repositoryUrl, branch = 'master') {
*
* @returns {Array<Commit>} The created commits, in reverse order (to match `git log` order).
*/
async function gitCommits(messages, execaOptions) {
export async function gitCommits(messages, execaOptions) {
await pEachSeries(
messages,
async (message) =>
@@ -103,7 +103,7 @@ async function gitCommits(messages, execaOptions) {
*
* @return {Array<Object>} The list of parsed commits.
*/
async function gitGetCommits(from, execaOptions) {
export async function gitGetCommits(from, execaOptions) {
Object.assign(gitLogParser.fields, {hash: 'H', message: 'B', gitTags: 'd', committerDate: {key: 'ci', type: Date}});
return (
await getStream.array(
@@ -126,7 +126,7 @@ async function gitGetCommits(from, execaOptions) {
* @param {Boolean} create to create the branch, `false` to checkout an existing branch.
* @param {Object} [execaOpts] Options to pass to `execa`.
*/
async function gitCheckout(branch, create, execaOptions) {
export async function gitCheckout(branch, create, execaOptions) {
await execa('git', create ? ['checkout', '-b', branch] : ['checkout', branch], execaOptions);
}
@@ -136,7 +136,7 @@ async function gitCheckout(branch, create, execaOptions) {
* @param {String} repositoryUrl The repository remote URL.
* @param {Object} [execaOpts] Options to pass to `execa`.
*/
async function gitFetch(repositoryUrl, execaOptions) {
export async function gitFetch(repositoryUrl, execaOptions) {
await execa('git', ['fetch', repositoryUrl], execaOptions);
}
@@ -147,7 +147,7 @@ async function gitFetch(repositoryUrl, execaOptions) {
*
* @return {String} The sha of the head commit in the current git repository.
*/
async function gitHead(execaOptions) {
export async function gitHead(execaOptions) {
return (await execa('git', ['rev-parse', 'HEAD'], execaOptions)).stdout;
}
@@ -158,7 +158,7 @@ async function gitHead(execaOptions) {
* @param {String} [sha] The commit on which to create the tag. If undefined the tag is created on the last commit.
* @param {Object} [execaOpts] Options to pass to `execa`.
*/
async function gitTagVersion(tagName, sha, execaOptions) {
export async function gitTagVersion(tagName, sha, execaOptions) {
await execa('git', sha ? ['tag', '-f', tagName, sha] : ['tag', tagName], execaOptions);
}
@@ -171,8 +171,8 @@ async function gitTagVersion(tagName, sha, execaOptions) {
* @param {Number} [depth=1] The number of commit to clone.
* @return {String} The path of the cloned repository.
*/
async function gitShallowClone(repositoryUrl, branch = 'master', depth = 1) {
const cwd = tempy.directory();
export async function gitShallowClone(repositoryUrl, branch = 'master', depth = 1) {
const cwd = temporaryDirectory();
await execa('git', ['clone', '--no-hardlinks', '--no-tags', '-b', branch, '--depth', depth, repositoryUrl, cwd], {
cwd,
@@ -187,8 +187,8 @@ async function gitShallowClone(repositoryUrl, branch = 'master', depth = 1) {
* @param {Number} head A commit sha of the remote repo that will become the detached head of the new one.
* @return {String} The path of the new repository.
*/
async function gitDetachedHead(repositoryUrl, head) {
const cwd = tempy.directory();
export async function gitDetachedHead(repositoryUrl, head) {
const cwd = temporaryDirectory();
await execa('git', ['init'], {cwd});
await execa('git', ['remote', 'add', 'origin', repositoryUrl], {cwd});
@@ -197,8 +197,8 @@ async function gitDetachedHead(repositoryUrl, head) {
return cwd;
}
async function gitDetachedHeadFromBranch(repositoryUrl, branch, head) {
const cwd = tempy.directory();
export async function gitDetachedHeadFromBranch(repositoryUrl, branch, head) {
const cwd = temporaryDirectory();
await execa('git', ['init'], {cwd});
await execa('git', ['remote', 'add', 'origin', repositoryUrl], {cwd});
@@ -215,7 +215,7 @@ async function gitDetachedHeadFromBranch(repositoryUrl, branch, head) {
* @param {String} value Config value.
* @param {Object} [execaOpts] Options to pass to `execa`.
*/
async function gitAddConfig(name, value, execaOptions) {
export async function gitAddConfig(name, value, execaOptions) {
await execa('git', ['config', '--add', name, value], execaOptions);
}
@@ -227,7 +227,7 @@ async function gitAddConfig(name, value, execaOptions) {
*
* @return {String} The sha of the commit associated with `tagName` on the local repository.
*/
async function gitTagHead(tagName, execaOptions) {
export async function gitTagHead(tagName, execaOptions) {
return (await execa('git', ['rev-list', '-1', tagName], execaOptions)).stdout;
}
@@ -240,7 +240,7 @@ async function gitTagHead(tagName, execaOptions) {
*
* @return {String} The sha of the commit associated with `tagName` on the remote repository.
*/
async function gitRemoteTagHead(repositoryUrl, tagName, execaOptions) {
export async function gitRemoteTagHead(repositoryUrl, tagName, execaOptions) {
return (await execa('git', ['ls-remote', '--tags', repositoryUrl, tagName], execaOptions)).stdout
.split('\n')
.filter((tag) => Boolean(tag))
@@ -255,7 +255,7 @@ async function gitRemoteTagHead(repositoryUrl, tagName, execaOptions) {
*
* @return {String} The tag associatedwith the sha in parameter or `null`.
*/
async function gitCommitTag(gitHead, execaOptions) {
export async function gitCommitTag(gitHead, execaOptions) {
return (await execa('git', ['describe', '--tags', '--exact-match', gitHead], execaOptions)).stdout;
}
@@ -268,7 +268,7 @@ async function gitCommitTag(gitHead, execaOptions) {
*
* @throws {Error} if the push failed.
*/
async function gitPush(repositoryUrl, branch, execaOptions) {
export async function gitPush(repositoryUrl, branch, execaOptions) {
await execa('git', ['push', '--tags', repositoryUrl, `HEAD:${branch}`], execaOptions);
}
@@ -278,7 +278,7 @@ async function gitPush(repositoryUrl, branch, execaOptions) {
* @param {String} ref The ref to merge.
* @param {Object} [execaOpts] Options to pass to `execa`.
*/
async function merge(ref, execaOptions) {
export async function merge(ref, execaOptions) {
await execa('git', ['merge', '--no-ff', ref], execaOptions);
}
@@ -288,7 +288,7 @@ async function merge(ref, execaOptions) {
* @param {String} ref The ref to merge.
* @param {Object} [execaOpts] Options to pass to `execa`.
*/
async function mergeFf(ref, execaOptions) {
export async function mergeFf(ref, execaOptions) {
await execa('git', ['merge', '--ff', ref], execaOptions);
}
@@ -298,7 +298,7 @@ async function mergeFf(ref, execaOptions) {
* @param {String} ref The ref to merge.
* @param {Object} [execaOpts] Options to pass to `execa`.
*/
async function rebase(ref, execaOptions) {
export async function rebase(ref, execaOptions) {
await execa('git', ['rebase', ref], execaOptions);
}
@@ -309,7 +309,7 @@ async function rebase(ref, execaOptions) {
* @param {String} ref The ref to add the note to.
* @param {Object} [execaOpts] Options to pass to `execa`.
*/
async function gitAddNote(note, ref, execaOptions) {
export async function gitAddNote(note, ref, execaOptions) {
await execa('git', ['notes', '--ref', GIT_NOTE_REF, 'add', '-m', note, ref], execaOptions);
}
@@ -319,31 +319,6 @@ async function gitAddNote(note, ref, execaOptions) {
* @param {String} ref The ref to get the note from.
* @param {Object} [execaOpts] Options to pass to `execa`.
*/
async function gitGetNote(ref, execaOptions) {
export async function gitGetNote(ref, execaOptions) {
return (await execa('git', ['notes', '--ref', GIT_NOTE_REF, 'show', ref], execaOptions)).stdout;
}
module.exports = {
initGit,
gitRepo,
initBareRepo,
gitCommits,
gitGetCommits,
gitCheckout,
gitFetch,
gitHead,
gitTagVersion,
gitShallowClone,
gitDetachedHead,
gitDetachedHeadFromBranch,
gitAddConfig,
gitTagHead,
gitRemoteTagHead,
gitCommitTag,
gitPush,
merge,
mergeFf,
rebase,
gitAddNote,
gitGetNote,
};
+8 -10
View File
@@ -1,7 +1,7 @@
const Docker = require('dockerode');
const getStream = require('get-stream');
const pRetry = require('p-retry');
const {initBareRepo, gitShallowClone} = require('./git-utils');
import Docker from 'dockerode';
import getStream from 'get-stream';
import pRetry from 'p-retry';
import {gitShallowClone, initBareRepo} from './git-utils.js';
const IMAGE = 'semanticrelease/docker-gitbox:latest';
const SERVER_PORT = 80;
@@ -12,12 +12,12 @@ const GIT_PASSWORD = 'suchsecure';
const docker = new Docker();
let container;
const gitCredential = `${GIT_USERNAME}:${GIT_PASSWORD}`;
export const gitCredential = `${GIT_USERNAME}:${GIT_PASSWORD}`;
/**
* Download the `gitbox` Docker image, create a new container and start it.
*/
async function start() {
export async function start() {
await getStream(await docker.pull(IMAGE));
container = await docker.createContainer({
@@ -38,7 +38,7 @@ async function start() {
/**
* Stop and remote the `mockserver` Docker container.
*/
async function stop() {
export async function stop() {
await container.stop();
await container.remove();
}
@@ -51,7 +51,7 @@ async function stop() {
* @param {String} [description=`Repository ${name}`] The repository description.
* @return {Object} The `repositoryUrl` (URL without auth) and `authUrl` (URL with auth).
*/
async function createRepo(name, branch = 'master', description = `Repository ${name}`) {
export async function createRepo(name, branch = 'master', description = `Repository ${name}`) {
const exec = await container.exec({
Cmd: ['repo-admin', '-n', name, '-d', description],
AttachStdout: true,
@@ -68,5 +68,3 @@ async function createRepo(name, branch = 'master', description = `Repository ${n
return {cwd, repositoryUrl, authUrl};
}
module.exports = {start, stop, gitCredential, createRepo};
+10 -12
View File
@@ -1,8 +1,8 @@
const Docker = require('dockerode');
const getStream = require('get-stream');
const got = require('got');
const pRetry = require('p-retry');
const {mockServerClient} = require('mockserver-client');
import Docker from 'dockerode';
import getStream from 'get-stream';
import got from 'got';
import pRetry from 'p-retry';
import {mockServerClient} from 'mockserver-client';
const IMAGE = 'mockserver/mockserver:latest';
const MOCK_SERVER_PORT = 1080;
@@ -13,7 +13,7 @@ let container;
/**
* Download the `mockserver` Docker image, create a new container and start it.
*/
async function start() {
export async function start() {
await getStream(await docker.pull(IMAGE));
container = await docker.createContainer({
@@ -38,7 +38,7 @@ async function start() {
/**
* Stop and remove the `mockserver` Docker container.
*/
async function stop() {
export async function stop() {
await container.stop();
await container.remove();
}
@@ -50,7 +50,7 @@ const client = mockServerClient(MOCK_SERVER_HOST, MOCK_SERVER_PORT);
/**
* @type {string} the url of the `mockserver` instance
*/
const url = `http://${MOCK_SERVER_HOST}:${MOCK_SERVER_PORT}`;
export const url = `http://${MOCK_SERVER_HOST}:${MOCK_SERVER_PORT}`;
/**
* Set up the `mockserver` instance response for a specific request.
@@ -65,7 +65,7 @@ const url = `http://${MOCK_SERVER_HOST}:${MOCK_SERVER_PORT}`;
* @param {Object} response.body The JSON object to respond in the response body.
* @return {Object} An object representation the expectation. Pass to the `verify` function to validate the `mockserver` has been called with a `request` matching the expectations.
*/
async function mock(
export async function mock(
path,
{body: requestBody, headers: requestHeaders},
{method = 'POST', statusCode = 200, body: responseBody}
@@ -96,8 +96,6 @@ async function mock(
* @param {Object} expectation The expectation created with `mock` function.
* @return {Promise} A Promise that resolves if the expectation is met or reject otherwise.
*/
function verify(expectation) {
export function verify(expectation) {
return client.verify(expectation);
}
module.exports = {start, stop, mock, verify, url};
+12 -12
View File
@@ -1,9 +1,10 @@
const Docker = require('dockerode');
const getStream = require('get-stream');
const got = require('got');
const path = require('path');
const delay = require('delay');
const pRetry = require('p-retry');
import path, {dirname} from 'node:path';
import {fileURLToPath} from 'node:url';
import Docker from 'dockerode';
import getStream from 'get-stream';
import got from 'got';
import delay from 'delay';
import pRetry from 'p-retry';
const IMAGE = 'verdaccio/verdaccio:4';
const REGISTRY_PORT = 4873;
@@ -12,12 +13,13 @@ const NPM_USERNAME = 'integration';
const NPM_PASSWORD = 'suchsecure';
const NPM_EMAIL = 'integration@test.com';
const docker = new Docker();
const __dirname = dirname(fileURLToPath(import.meta.url));
let container;
/**
* Download the `npm-registry-docker` Docker image, create a new container and start it.
*/
async function start() {
export async function start() {
await getStream(await docker.pull(IMAGE));
container = await docker.createContainer({
@@ -55,9 +57,9 @@ async function start() {
});
}
const url = `http://${REGISTRY_HOST}:${REGISTRY_PORT}/`;
export const url = `http://${REGISTRY_HOST}:${REGISTRY_PORT}/`;
const authEnv = {
export const authEnv = {
npm_config_registry: url, // eslint-disable-line camelcase
NPM_USERNAME,
NPM_PASSWORD,
@@ -67,9 +69,7 @@ const authEnv = {
/**
* Stop and remote the `npm-registry-docker` Docker container.
*/
async function stop() {
export async function stop() {
await container.stop();
await container.remove();
}
module.exports = {start, stop, authEnv, url};
+2 -4
View File
@@ -1,7 +1,5 @@
const execa = require('execa');
import {execa} from 'execa';
async function npmView(packageName, env) {
export async function npmView(packageName, env) {
return JSON.parse((await execa('npm', ['view', packageName, '--json'], {env})).stdout);
}
module.exports = {npmView};
+4 -4
View File
@@ -1,7 +1,7 @@
const test = require('ava');
const {repeat} = require('lodash');
const hideSensitive = require('../lib/hide-sensitive');
const {SECRET_REPLACEMENT, SECRET_MIN_SIZE} = require('../lib/definitions/constants');
import test from 'ava';
import {repeat} from 'lodash-es';
import hideSensitive from '../lib/hide-sensitive.js';
import {SECRET_MIN_SIZE, SECRET_REPLACEMENT} from '../lib/definitions/constants.js';
test('Replace multiple sensitive environment variable values', (t) => {
const env = {SOME_PASSWORD: 'password', SOME_TOKEN: 'secret'};
+121 -122
View File
@@ -1,29 +1,28 @@
const test = require('ava');
const {escapeRegExp, isString, sortBy, omit} = require('lodash');
const td = require('testdouble');
const {spy, stub} = require('sinon');
const {WritableStreamBuffer} = require('stream-buffers');
const AggregateError = require('aggregate-error');
const SemanticReleaseError = require('@semantic-release/error');
const {COMMIT_NAME, COMMIT_EMAIL, SECRET_REPLACEMENT} = require('../lib/definitions/constants');
const {
gitHead: getGitHead,
import test from 'ava';
import {escapeRegExp, isString, omit, sortBy} from 'lodash-es';
import * as td from 'testdouble';
import {spy, stub} from 'sinon';
import {WritableStreamBuffer} from 'stream-buffers';
import AggregateError from 'aggregate-error';
import SemanticReleaseError from '@semantic-release/error';
import {COMMIT_EMAIL, COMMIT_NAME, SECRET_REPLACEMENT} from '../lib/definitions/constants.js';
import {
gitAddNote,
gitCheckout,
gitTagHead,
gitRepo,
gitCommits,
gitTagVersion,
gitRemoteTagHead,
gitGetNote,
gitHead as getGitHead,
gitPush,
gitRemoteTagHead,
gitRepo,
gitShallowClone,
gitTagHead,
gitTagVersion,
merge,
mergeFf,
rebase,
gitAddNote,
gitGetNote,
} = require('./helpers/git-utils');
const pluginNoop = require.resolve('./fixtures/plugin-noop');
rebase
} from './helpers/git-utils.js';
import pluginNoop from './fixtures/plugin-noop.cjs';
test.beforeEach((t) => {
// Stub the logger functions
@@ -143,9 +142,9 @@ test('Plugins are called with expected values', async (t) => {
{...nextRelease, notes: `${notes1}\n\n${notes2}\n\n${notes3}`, pluginName: pluginNoop},
];
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => envCi);
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => envCi);
const semanticRelease = (await import('../index.js')).default;
const result = await semanticRelease(options, {
cwd,
env,
@@ -418,9 +417,9 @@ test('Use custom tag format', async (t) => {
fail: stub().resolves(),
};
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = (await import('../index.js')).default;
t.truthy(
await semanticRelease(options, {
cwd,
@@ -476,9 +475,9 @@ test('Use new gitHead, and recreate release notes if a prepare plugin create a c
fail: stub().resolves(),
};
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = (await import('../index.js')).default;
t.truthy(
await semanticRelease(options, {
@@ -542,9 +541,9 @@ test('Make a new release when a commit is forward-ported to an upper branch', as
success,
};
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = (await import('../index.js')).default;
t.truthy(await semanticRelease(options, {cwd, env: {}, stdout: {write: () => {}}, stderr: {write: () => {}}}));
t.is(addChannel.callCount, 0);
@@ -576,9 +575,9 @@ test('Publish a pre-release version', async (t) => {
fail: stub().resolves(),
};
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: true, branch: 'beta', isPr: false}));
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: true, branch: 'beta', isPr: false}));
const semanticRelease = (await import('../index.js')).default;
let {releases} = await semanticRelease(options, {cwd, env: {}, stdout: {write: () => {}}, stderr: {write: () => {}}});
t.is(releases.length, 1);
@@ -628,9 +627,9 @@ test('Publish releases from different branch on the same channel', async (t) =>
fail: stub().resolves(),
};
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: true, branch: 'next', isPr: false}));
let semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: true, branch: 'next', isPr: false}));
let semanticRelease = (await import('../index.js')).default;
let {releases} = await semanticRelease(options, {cwd, env: {}, stdout: {write: () => {}}, stderr: {write: () => {}}});
t.is(releases.length, 1);
@@ -653,9 +652,9 @@ test('Publish releases from different branch on the same channel', async (t) =>
await merge('next', {cwd});
await gitPush('origin', 'master', {cwd});
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: true, branch: 'master', isPr: false}));
semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: true, branch: 'master', isPr: false}));
semanticRelease = (await import('../index.js')).default;
t.falsy(await semanticRelease(options, {cwd, env: {}, stdout: {write: () => {}}, stderr: {write: () => {}}}));
t.is(addChannel.callCount, 0);
@@ -686,9 +685,9 @@ test('Publish pre-releases the same channel as regular releases', async (t) => {
fail: stub().resolves(),
};
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: true, branch: 'beta', isPr: false}));
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: true, branch: 'beta', isPr: false}));
const semanticRelease = (await import('../index.js')).default;
let {releases} = await semanticRelease(options, {cwd, env: {}, stdout: {write: () => {}}, stderr: {write: () => {}}});
t.is(releases.length, 1);
@@ -751,9 +750,9 @@ test('Do not add pre-releases to a different channel', async (t) => {
success,
};
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = (await import('../index.js')).default;
t.truthy(await semanticRelease(options, {cwd, env: {}, stdout: {write: () => {}}, stderr: {write: () => {}}}));
t.is(addChannel.callCount, 0);
@@ -819,9 +818,9 @@ async function addChannelMacro(t, mergeFunction) {
gitHead: commits[2].hash,
};
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = (await import('../index.js')).default;
const result = await semanticRelease(options, {cwd, env: {}, stdout: {write: () => {}}, stderr: {write: () => {}}});
t.deepEqual(result.releases, [
@@ -885,9 +884,9 @@ test('Call all "success" plugins even if one errors out', async (t) => {
success: [success1, success2],
};
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = (await import('../index.js')).default;
await t.throwsAsync(
semanticRelease(options, {cwd, env: {}, stdout: new WritableStreamBuffer(), stderr: new WritableStreamBuffer()})
@@ -929,9 +928,9 @@ test('Log all "verifyConditions" errors', async (t) => {
fail,
};
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = (await import('../index.js')).default;
const errors = [
...(await t.throwsAsync(
semanticRelease(options, {cwd, env: {}, stdout: new WritableStreamBuffer(), stderr: new WritableStreamBuffer()})
@@ -973,9 +972,9 @@ test('Log all "verifyRelease" errors', async (t) => {
fail,
};
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = (await import('../index.js')).default;
const errors = [
...(await t.throwsAsync(
semanticRelease(options, {cwd, env: {}, stdout: new WritableStreamBuffer(), stderr: new WritableStreamBuffer()})
@@ -1026,9 +1025,9 @@ test('Dry-run skips addChannel, prepare, publish and success', async (t) => {
success,
};
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = (await import('../index.js')).default;
t.truthy(
await semanticRelease(options, {
cwd,
@@ -1078,9 +1077,9 @@ test('Dry-run skips fail', async (t) => {
fail,
};
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = (await import('../index.js')).default;
const errors = [
...(await t.throwsAsync(
semanticRelease(options, {cwd, env: {}, stdout: new WritableStreamBuffer(), stderr: new WritableStreamBuffer()})
@@ -1137,9 +1136,9 @@ test('Force a dry-run if not on a CI and "noCi" is not explicitly set', async (t
fail: stub().resolves(),
};
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: false, branch: 'master'}));
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: false, branch: 'master'}));
const semanticRelease = (await import('../index.js')).default;
t.truthy(
await semanticRelease(options, {
cwd,
@@ -1186,9 +1185,9 @@ test('Dry-run does not print changelog if "generateNotes" return "undefined"', a
success: false,
};
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = (await import('../index.js')).default;
t.truthy(
await semanticRelease(options, {
cwd,
@@ -1244,9 +1243,9 @@ test('Allow local releases with "noCi" option', async (t) => {
fail: stub().resolves(),
};
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: false, branch: 'master', isPr: false}));
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: false, branch: 'master', isPr: false}));
const semanticRelease = (await import('../index.js')).default;
t.truthy(
await semanticRelease(options, {
cwd,
@@ -1313,9 +1312,9 @@ test('Accept "undefined" value returned by "generateNotes" and "false" by "publi
fail: stub().resolves(),
};
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = (await import('../index.js')).default;
t.truthy(
await semanticRelease(options, {
cwd,
@@ -1341,9 +1340,9 @@ test('Returns false if triggered by a PR', async (t) => {
// Create a git repository, set the current working directory at the root of the repo
const {cwd, repositoryUrl} = await gitRepo(true);
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: true, branch: 'master', prBranch: 'patch-1', isPr: true}));
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: true, branch: 'master', prBranch: 'patch-1', isPr: true}));
const semanticRelease = (await import('../index.js')).default;
t.false(
await semanticRelease(
@@ -1393,9 +1392,9 @@ test('Throws "EINVALIDNEXTVERSION" if next release is out of range of the curren
success,
};
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: true, branch: '1.x', isPr: false}));
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: true, branch: '1.x', isPr: false}));
const semanticRelease = (await import('../index.js')).default;
const error = await t.throwsAsync(
semanticRelease(options, {cwd, env: {}, stdout: {write: () => {}}, stderr: {write: () => {}}})
@@ -1444,9 +1443,9 @@ test('Throws "EINVALIDNEXTVERSION" if next release is out of range of the curren
success,
};
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = (await import('../index.js')).default;
const error = await t.throwsAsync(
semanticRelease(options, {cwd, env: {}, stdout: {write: () => {}}, stderr: {write: () => {}}})
@@ -1503,9 +1502,9 @@ test('Throws "EINVALIDMAINTENANCEMERGE" if merge an out of range release in a ma
fail,
};
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: true, branch: '1.1.x', isPr: false}));
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: true, branch: '1.1.x', isPr: false}));
const semanticRelease = await import('../index.js');
const errors = [
...(await t.throwsAsync(
semanticRelease(options, {cwd, env: {}, stdout: {write: () => {}}, stderr: {write: () => {}}})
@@ -1539,9 +1538,9 @@ test('Returns false value if triggered on an outdated clone', async (t) => {
await gitCommits(['Third'], {cwd});
await gitPush(repositoryUrl, 'master', {cwd});
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = (await import('../index.js')).default;
t.false(
await semanticRelease(
@@ -1571,9 +1570,9 @@ test('Returns false if not running from the configured branch', async (t) => {
fail: stub().resolves(),
};
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: true, branch: 'other-branch', isPr: false}));
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: true, branch: 'other-branch', isPr: false}));
const semanticRelease = (await import('../index.js')).default;
t.false(
await semanticRelease(options, {
@@ -1615,9 +1614,9 @@ test('Returns false if there is no relevant changes', async (t) => {
fail: stub().resolves(),
};
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = (await import('../index.js')).default;
t.false(
await semanticRelease(options, {
@@ -1670,9 +1669,9 @@ test('Exclude commits with [skip release] or [release skip] from analysis', asyn
fail: stub().resolves(),
};
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = (await import('../index.js')).default;
await semanticRelease(options, {
cwd,
env: {},
@@ -1697,9 +1696,9 @@ test('Log both plugins errors and errors thrown by "fail" plugin', async (t) =>
verifyConditions: stub().rejects(pluginError),
fail: [stub().rejects(failError1), stub().rejects(failError2)],
};
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = (await import('../index.js')).default;
await t.throwsAsync(
semanticRelease(options, {cwd, env: {}, stdout: new WritableStreamBuffer(), stderr: new WritableStreamBuffer()})
@@ -1721,9 +1720,9 @@ test('Call "fail" only if a plugin returns a SemanticReleaseError', async (t) =>
verifyConditions: stub().rejects(pluginError),
fail,
};
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = (await import('../index.js')).default;
await t.throwsAsync(
semanticRelease(options, {cwd, env: {}, stdout: new WritableStreamBuffer(), stderr: new WritableStreamBuffer()})
@@ -1737,9 +1736,9 @@ test('Throw SemanticReleaseError if repositoryUrl is not set and cannot be found
// Create a git repository, set the current working directory at the root of the repo
const {cwd} = await gitRepo();
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = (await import('../index.js')).default;
const errors = [
...(await t.throwsAsync(
semanticRelease({}, {cwd, env: {}, stdout: new WritableStreamBuffer(), stderr: new WritableStreamBuffer()})
@@ -1776,9 +1775,9 @@ test('Throw an Error if plugin returns an unexpected value', async (t) => {
fail: stub().resolves(),
};
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = (await import('../index.js')).default;
const error = await t.throwsAsync(
semanticRelease(options, {cwd, env: {}, stdout: new WritableStreamBuffer(), stderr: new WritableStreamBuffer()}),
{instanceOf: SemanticReleaseError}
@@ -1805,9 +1804,9 @@ test('Hide sensitive information passed to "fail" plugin', async (t) => {
fail,
};
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = (await import('../index.js')).default;
await t.throwsAsync(
semanticRelease(options, {cwd, env, stdout: new WritableStreamBuffer(), stderr: new WritableStreamBuffer()})
);
@@ -1849,9 +1848,9 @@ test('Hide sensitive information passed to "success" plugin', async (t) => {
fail: stub().resolves(),
};
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = (await import('../index.js')).default;
await semanticRelease(options, {cwd, env, stdout: new WritableStreamBuffer(), stderr: new WritableStreamBuffer()});
const release = success.args[0][1].releases[0];
@@ -1898,9 +1897,9 @@ test('Get all commits including the ones not in the shallow clone', async (t) =>
fail: stub().resolves(),
};
td.replace('../lib/get-logger', () => t.context.logger);
td.replace('env-ci', () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = require('..');
await td.replaceEsm('../lib/get-logger.js', null, () => t.context.logger);
await td.replaceEsm('env-ci', null, () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = (await import('../index.js')).default;
t.truthy(
await semanticRelease(options, {
cwd,
+33 -28
View File
@@ -1,28 +1,31 @@
const path = require('path');
const test = require('ava');
const td = require('testdouble');
const {escapeRegExp} = require('lodash');
const {writeJson, readJson} = require('fs-extra');
const execa = require('execa');
const {WritableStreamBuffer} = require('stream-buffers');
const delay = require('delay');
const getAuthUrl = require('../lib/get-git-auth-url');
const {SECRET_REPLACEMENT} = require('../lib/definitions/constants');
const {
gitHead,
gitTagHead,
gitRepo,
gitCommits,
gitRemoteTagHead,
gitPush,
import path from 'path';
import test from 'ava';
import * as td from 'testdouble';
import {escapeRegExp} from 'lodash-es';
import fsExtra from 'fs-extra';
import {execa} from 'execa';
import {WritableStreamBuffer} from 'stream-buffers';
import delay from 'delay';
import getAuthUrl from '../lib/get-git-auth-url.js';
import {SECRET_REPLACEMENT} from '../lib/definitions/constants.js';
import {
gitCheckout,
merge,
gitCommits,
gitGetNote,
} = require('./helpers/git-utils');
const {npmView} = require('./helpers/npm-utils');
const gitbox = require('./helpers/gitbox');
const mockServer = require('./helpers/mockserver');
const npmRegistry = require('./helpers/npm-registry');
gitHead,
gitPush,
gitRemoteTagHead,
gitRepo,
gitTagHead,
merge
} from './helpers/git-utils.js';
import {npmView} from './helpers/npm-utils.js';
import * as gitbox from './helpers/gitbox.js';
import * as mockServer from './helpers/mockserver.js';
import * as npmRegistry from './helpers/npm-registry.js';
const {readJson, writeJson} = fsExtra;
/* eslint camelcase: ["error", {properties: "never"}] */
@@ -47,10 +50,10 @@ const npmTestEnv = {
LEGACY_TOKEN: Buffer.from(`${env.NPM_USERNAME}:${env.NPM_PASSWORD}`, 'utf8').toString('base64'),
};
const cli = require.resolve('../bin/semantic-release');
const pluginError = require.resolve('./fixtures/plugin-error');
const pluginInheritedError = require.resolve('./fixtures/plugin-error-inherited');
const pluginLogEnv = require.resolve('./fixtures/plugin-log-env');
const cli = path.resolve('./bin/semantic-release.js');
const pluginError = path.resolve('./test/fixtures/plugin-error');
const pluginInheritedError = path.resolve('./test/fixtures/plugin-error-inherited');
const pluginLogEnv = path.resolve('./test/fixtures/plugin-log-env');
test.before(async () => {
await Promise.all([gitbox.start(), npmRegistry.start(), mockServer.start()]);
@@ -509,7 +512,7 @@ test('Pass options via CLI arguments', async (t) => {
test('Run via JS API', async (t) => {
td.replace('../lib/logger', {log: () => {}, error: () => {}, stdout: () => {}});
td.replace('env-ci', () => ({isCi: true, branch: 'master', isPr: false}));
const semanticRelease = require('..');
const semanticRelease = (await import('../index.js')).default;
const packageName = 'test-js-api';
const owner = 'git';
// Create a git repository, set the current working directory at the root of the repo
@@ -656,6 +659,8 @@ test('Hide sensitive environment variable values from the logs', async (t) => {
extendEnv: false,
});
console.log({stderr})
t.regex(stdout, new RegExp(`Console: Exposing token ${escapeRegExp(SECRET_REPLACEMENT)}`));
t.regex(stdout, new RegExp(`Log: Exposing token ${escapeRegExp(SECRET_REPLACEMENT)}`));
t.regex(stderr, new RegExp(`Error: Console token ${escapeRegExp(SECRET_REPLACEMENT)}`));
+19 -19
View File
@@ -1,7 +1,7 @@
const test = require('ava');
const {noop} = require('lodash');
const {stub} = require('sinon');
const normalize = require('../../lib/plugins/normalize');
import test from 'ava';
import {noop} from 'lodash-es';
import {stub} from 'sinon';
import normalize from '../../lib/plugins/normalize.js';
const cwd = process.cwd();
@@ -23,37 +23,37 @@ test('Normalize and load plugin from string', async (t) => {
const plugin = await normalize(
{cwd, options: {}, logger: t.context.logger},
'verifyConditions',
'./test/fixtures/plugin-noop',
'./test/fixtures/plugin-noop.cjs',
{}
);
t.is(plugin.pluginName, './test/fixtures/plugin-noop');
t.is(plugin.pluginName, './test/fixtures/plugin-noop.cjs');
t.is(typeof plugin, 'function');
t.deepEqual(t.context.success.args[0], ['Loaded plugin "verifyConditions" from "./test/fixtures/plugin-noop"']);
t.deepEqual(t.context.success.args[0], ['Loaded plugin "verifyConditions" from "./test/fixtures/plugin-noop.cjs"']);
});
test('Normalize and load plugin from object', async (t) => {
const plugin = await normalize(
{cwd, options: {}, logger: t.context.logger},
'publish',
{path: './test/fixtures/plugin-noop'},
{path: './test/fixtures/plugin-noop.cjs'},
{}
);
t.is(plugin.pluginName, './test/fixtures/plugin-noop');
t.is(plugin.pluginName, './test/fixtures/plugin-noop.cjs');
t.is(typeof plugin, 'function');
t.deepEqual(t.context.success.args[0], ['Loaded plugin "publish" from "./test/fixtures/plugin-noop"']);
t.deepEqual(t.context.success.args[0], ['Loaded plugin "publish" from "./test/fixtures/plugin-noop.cjs"']);
});
test('Normalize and load plugin from a base file path', async (t) => {
const plugin = await normalize({cwd, options: {}, logger: t.context.logger}, 'verifyConditions', './plugin-noop', {
'./plugin-noop': './test/fixtures',
const plugin = await normalize({cwd, options: {}, logger: t.context.logger}, 'verifyConditions', './plugin-noop.cjs', {
'./plugin-noop.cjs': './test/fixtures',
});
t.is(plugin.pluginName, './plugin-noop');
t.is(plugin.pluginName, './plugin-noop.cjs');
t.is(typeof plugin, 'function');
t.deepEqual(t.context.success.args[0], [
'Loaded plugin "verifyConditions" from "./plugin-noop" in shareable config "./test/fixtures"',
'Loaded plugin "verifyConditions" from "./plugin-noop.cjs" in shareable config "./test/fixtures"',
]);
});
@@ -72,7 +72,7 @@ test('Wrap plugin in a function that add the "pluginName" to multiple errors"',
'./plugin-errors': './test/fixtures',
});
const errors = [...(await t.throwsAsync(plugin({options: {}})))];
const errors = [...(await t.throwsAsync(plugin({options: {}}))).errors];
for (const error of errors) {
t.is(error.pluginName, './plugin-errors');
}
@@ -90,12 +90,12 @@ test('Normalize and load plugin that retuns multiple functions', async (t) => {
const plugin = await normalize(
{cwd, options: {}, logger: t.context.logger},
'verifyConditions',
'./test/fixtures/multi-plugin',
'./test/fixtures/multi-plugin.cjs',
{}
);
t.is(typeof plugin, 'function');
t.deepEqual(t.context.success.args[0], ['Loaded plugin "verifyConditions" from "./test/fixtures/multi-plugin"']);
t.deepEqual(t.context.success.args[0], ['Loaded plugin "verifyConditions" from "./test/fixtures/multi-plugin.cjs"']);
});
test('Wrap "analyzeCommits" plugin in a function that validate the output of the plugin', async (t) => {
@@ -258,7 +258,7 @@ test('Always pass a defined "pluginConfig" for plugin defined with path', async
test('Throws an error if the plugin return an object without the expected plugin function', async (t) => {
const error = await t.throwsAsync(() =>
normalize({cwd, options: {}, logger: t.context.logger}, 'inexistantPlugin', './test/fixtures/multi-plugin', {})
normalize({cwd, options: {}, logger: t.context.logger}, 'nonExistentPlugin', './test/fixtures/multi-plugin.cjs', {})
);
t.is(error.code, 'EPLUGIN');
@@ -269,7 +269,7 @@ test('Throws an error if the plugin return an object without the expected plugin
test('Throws an error if the plugin is not found', async (t) => {
await t.throwsAsync(
() => normalize({cwd, options: {}, logger: t.context.logger}, 'inexistantPlugin', 'non-existing-path', {}),
() => normalize({cwd, options: {}, logger: t.context.logger}, 'nonExistentPlugin', 'non-existing-path', {}),
{
message: /Cannot find module 'non-existing-path'/,
code: 'MODULE_NOT_FOUND',
+12 -12
View File
@@ -1,7 +1,7 @@
const test = require('ava');
const {stub} = require('sinon');
const AggregateError = require('aggregate-error');
const pipeline = require('../../lib/plugins/pipeline');
import test from 'ava';
import {stub} from 'sinon';
import AggregateError from 'aggregate-error';
import pipeline from '../../lib/plugins/pipeline.js';
test('Execute each function in series passing the same input', async (t) => {
const step1 = stub().resolves(1);
@@ -116,9 +116,9 @@ test('Throw all errors from the first step throwing an AggregateError', async (t
const step2 = stub().rejects(new AggregateError([error1, error2]));
const step3 = stub().resolves(3);
const errors = await t.throwsAsync(pipeline([step1, step2, step3])(0));
const error = await t.throwsAsync(pipeline([step1, step2, step3])(0));
t.deepEqual([...errors], [error1, error2]);
t.deepEqual([...error.errors], [error1, error2]);
t.true(step1.calledWith(0));
t.true(step2.calledWith(0));
t.true(step3.notCalled);
@@ -131,9 +131,9 @@ test('Execute all even if a Promise rejects', async (t) => {
const step2 = stub().rejects(error1);
const step3 = stub().rejects(error2);
const errors = await t.throwsAsync(pipeline([step1, step2, step3], {settleAll: true})(0));
const error = await t.throwsAsync(pipeline([step1, step2, step3], {settleAll: true})(0));
t.deepEqual([...errors], [error1, error2]);
t.deepEqual([...error.errors], [error1, error2]);
t.true(step1.calledWith(0));
t.true(step2.calledWith(0));
t.true(step3.calledWith(0));
@@ -147,9 +147,9 @@ test('Throw all errors from all steps throwing an AggregateError', async (t) =>
const step1 = stub().rejects(new AggregateError([error1, error2]));
const step2 = stub().rejects(new AggregateError([error3, error4]));
const errors = await t.throwsAsync(pipeline([step1, step2], {settleAll: true})(0));
const error = await t.throwsAsync(pipeline([step1, step2], {settleAll: true})(0));
t.deepEqual([...errors], [error1, error2, error3, error4]);
t.deepEqual([...error.errors], [error1, error2, error3, error4]);
t.true(step1.calledWith(0));
t.true(step2.calledWith(0));
});
@@ -163,9 +163,9 @@ test('Execute each function in series passing a transformed input even if a step
const step4 = stub().resolves(4);
const getNextInput = (previousResult, result) => previousResult + result;
const errors = await t.throwsAsync(pipeline([step1, step2, step3, step4], {settleAll: true, getNextInput})(0));
const error = await t.throwsAsync(pipeline([step1, step2, step3, step4], {settleAll: true, getNextInput})(0));
t.deepEqual([...errors], [error2, error3]);
t.deepEqual([...error.errors], [error2, error3]);
t.true(step1.calledWith(0));
t.true(step2.calledWith(0 + 1));
t.true(step3.calledWith(0 + 1 + error2));
+21 -21
View File
@@ -1,11 +1,11 @@
const path = require('path');
const test = require('ava');
const {copy, outputFile} = require('fs-extra');
const {stub} = require('sinon');
const tempy = require('tempy');
const getPlugins = require('../../lib/plugins');
import path from 'path';
import test from 'ava';
import {copy, outputFile} from 'fs-extra';
import {stub} from 'sinon';
import {temporaryDirectory} from 'tempy';
import getPlugins from '../../lib/plugins/index.js';
// Save the current working diretory
// Save the current working directory
const cwd = process.cwd();
test.beforeEach((t) => {
@@ -35,9 +35,9 @@ test('Export plugins based on steps config', async (t) => {
cwd,
logger: t.context.logger,
options: {
verifyConditions: ['./test/fixtures/plugin-noop', {path: './test/fixtures/plugin-noop'}],
generateNotes: './test/fixtures/plugin-noop',
analyzeCommits: {path: './test/fixtures/plugin-noop'},
verifyConditions: ['./test/fixtures/plugin-noop.cjs', {path: './test/fixtures/plugin-noop.cjs'}],
generateNotes: './test/fixtures/plugin-noop.cjs',
analyzeCommits: {path: './test/fixtures/plugin-noop.cjs'},
verifyRelease: () => {},
},
},
@@ -137,9 +137,9 @@ test('Unknown steps of plugins configured in "plugins" are ignored', async (t) =
});
test('Export plugins loaded from the dependency of a shareable config module', async (t) => {
const cwd = tempy.directory();
const cwd = temporaryDirectory();
await copy(
'./test/fixtures/plugin-noop.js',
'./test/fixtures/plugin-noop.cjs',
path.resolve(cwd, 'node_modules/shareable-config/node_modules/custom-plugin/index.js')
);
await outputFile(path.resolve(cwd, 'node_modules/shareable-config/index.js'), '');
@@ -170,8 +170,8 @@ test('Export plugins loaded from the dependency of a shareable config module', a
});
test('Export plugins loaded from the dependency of a shareable config file', async (t) => {
const cwd = tempy.directory();
await copy('./test/fixtures/plugin-noop.js', path.resolve(cwd, 'plugin/plugin-noop.js'));
const cwd = temporaryDirectory();
await copy('./test/fixtures/plugin-noop.cjs', path.resolve(cwd, 'plugin/plugin-noop.cjs'));
await outputFile(path.resolve(cwd, 'shareable-config.js'), '');
const plugins = await getPlugins(
@@ -179,9 +179,9 @@ test('Export plugins loaded from the dependency of a shareable config file', asy
cwd,
logger: t.context.logger,
options: {
verifyConditions: ['./plugin/plugin-noop', {path: './plugin/plugin-noop'}],
generateNotes: './plugin/plugin-noop',
analyzeCommits: {path: './plugin/plugin-noop'},
verifyConditions: ['./plugin/plugin-noop.cjs', {path: './plugin/plugin-noop.cjs'}],
generateNotes: './plugin/plugin-noop.cjs',
analyzeCommits: {path: './plugin/plugin-noop.cjs'},
verifyRelease: () => {},
},
},
@@ -269,7 +269,7 @@ test('Throw an error for each invalid plugin configuration', async (t) => {
},
{}
)
)),
)).errors,
];
t.is(errors[0].name, 'SemanticReleaseError');
@@ -289,11 +289,11 @@ test('Throw EPLUGINSCONF error if the "plugins" option contains an old plugin de
{
cwd,
logger: t.context.logger,
options: {plugins: ['./test/fixtures/multi-plugin', './test/fixtures/plugin-noop', () => {}]},
options: {plugins: ['./test/fixtures/multi-plugin.cjs', './test/fixtures/plugin-noop.cjs', () => {}]},
},
{}
)
)),
)).errors,
];
t.is(errors[0].name, 'SemanticReleaseError');
@@ -306,7 +306,7 @@ test('Throw EPLUGINSCONF error for each invalid definition if the "plugins" opti
const errors = [
...(await t.throwsAsync(() =>
getPlugins({cwd, logger: t.context.logger, options: {plugins: [1, {path: 1}, [() => {}, {}, {}]]}}, {})
)),
)).errors,
];
t.is(errors[0].name, 'SemanticReleaseError');
+5 -5
View File
@@ -1,5 +1,5 @@
const test = require('ava');
const {validatePlugin, validateStep, loadPlugin, parseConfig} = require('../../lib/plugins/utils');
import test from 'ava';
import {loadPlugin, parseConfig, validatePlugin, validateStep} from '../../lib/plugins/utils.js';
test('validatePlugin', (t) => {
const path = 'plugin-module';
@@ -193,10 +193,10 @@ test('loadPlugin', async (t) => {
const cwd = process.cwd();
const func = () => {};
t.is(require('../fixtures/plugin-noop'), await loadPlugin({cwd: './test/fixtures'}, './plugin-noop', {}), 'From cwd');
t.is((await import('../fixtures/plugin-noop.cjs')).default, await loadPlugin({cwd: './test/fixtures'}, './plugin-noop.cjs', {}), 'From cwd');
t.is(
require('../fixtures/plugin-noop'),
await loadPlugin({cwd}, './plugin-noop', {'./plugin-noop': './test/fixtures'}),
(await import('../fixtures/plugin-noop.cjs')).default,
await loadPlugin({cwd}, './plugin-noop.cjs', {'./plugin-noop.cjs': './test/fixtures'}),
'From a shareable config context'
);
t.is(func, await loadPlugin({cwd}, func, {}), 'Defined as a function');
+13 -13
View File
@@ -1,21 +1,21 @@
const test = require('ava');
const AggregateError = require('aggregate-error');
const {
import test from 'ava';
import AggregateError from 'aggregate-error';
import {
extractErrors,
tagsToVersions,
isMajorRange,
isMaintenanceRange,
getUpperBound,
getLowerBound,
highest,
lowest,
getLatestVersion,
getEarliestVersion,
getFirstVersion,
getLatestVersion,
getLowerBound,
getRange,
makeTag,
getUpperBound,
highest,
isMaintenanceRange,
isMajorRange,
isSameChannel,
} = require('../lib/utils');
lowest,
makeTag,
tagsToVersions
} from '../lib/utils.js';
test('extractErrors', (t) => {
const errors = [new Error('Error 1'), new Error('Error 2')];
+11 -11
View File
@@ -1,13 +1,13 @@
const test = require('ava');
const tempy = require('tempy');
const verify = require('../lib/verify');
const {gitRepo} = require('./helpers/git-utils');
import test from 'ava';
import {temporaryDirectory} from 'tempy';
import verify from '../lib/verify.js';
import {gitRepo} from './helpers/git-utils.js';
test('Throw a AggregateError', async (t) => {
const {cwd} = await gitRepo();
const options = {branches: [{name: 'master'}, {name: ''}]};
const errors = [...(await t.throwsAsync(verify({cwd, options})))];
const errors = [...(await t.throwsAsync(verify({cwd, options}))).errors];
t.is(errors[0].name, 'SemanticReleaseError');
t.is(errors[0].code, 'ENOREPOURL');
@@ -28,10 +28,10 @@ test('Throw a AggregateError', async (t) => {
});
test('Throw a SemanticReleaseError if does not run on a git repository', async (t) => {
const cwd = tempy.directory();
const cwd = temporaryDirectory();
const options = {branches: []};
const errors = [...(await t.throwsAsync(verify({cwd, options})))];
const errors = [...(await t.throwsAsync(verify({cwd, options}))).errors];
t.is(errors[0].name, 'SemanticReleaseError');
t.is(errors[0].code, 'ENOGITREPO');
@@ -43,7 +43,7 @@ test('Throw a SemanticReleaseError if the "tagFormat" is not valid', async (t) =
const {cwd, repositoryUrl} = await gitRepo(true);
const options = {repositoryUrl, tagFormat: `?\${version}`, branches: []};
const errors = [...(await t.throwsAsync(verify({cwd, options})))];
const errors = [...(await t.throwsAsync(verify({cwd, options}))).errors];
t.is(errors[0].name, 'SemanticReleaseError');
t.is(errors[0].code, 'EINVALIDTAGFORMAT');
@@ -55,7 +55,7 @@ test('Throw a SemanticReleaseError if the "tagFormat" does not contains the "ver
const {cwd, repositoryUrl} = await gitRepo(true);
const options = {repositoryUrl, tagFormat: 'test', branches: []};
const errors = [...(await t.throwsAsync(verify({cwd, options})))];
const errors = [...(await t.throwsAsync(verify({cwd, options}))).errors];
t.is(errors[0].name, 'SemanticReleaseError');
t.is(errors[0].code, 'ETAGNOVERSION');
@@ -67,7 +67,7 @@ test('Throw a SemanticReleaseError if the "tagFormat" contains multiple "version
const {cwd, repositoryUrl} = await gitRepo(true);
const options = {repositoryUrl, tagFormat: `\${version}v\${version}`, branches: []};
const errors = [...(await t.throwsAsync(verify({cwd, options})))];
const errors = [...(await t.throwsAsync(verify({cwd, options}))).errors];
t.is(errors[0].name, 'SemanticReleaseError');
t.is(errors[0].code, 'ETAGNOVERSION');
@@ -83,7 +83,7 @@ test('Throw a SemanticReleaseError for each invalid branch', async (t) => {
branches: [{name: ''}, {name: ' '}, {name: 1}, {}, {name: ''}, 1, 'master'],
};
const errors = [...(await t.throwsAsync(verify({cwd, options})))];
const errors = [...(await t.throwsAsync(verify({cwd, options}))).errors];
t.is(errors[0].name, 'SemanticReleaseError');
t.is(errors[0].code, 'EINVALIDBRANCH');