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
286 lines
7.6 KiB
JavaScript
286 lines
7.6 KiB
JavaScript
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;
|
|
|
|
test.beforeEach((t) => {
|
|
t.context.logs = '';
|
|
t.context.errors = '';
|
|
t.context.stdout = stub(process.stdout, 'write').callsFake((value) => {
|
|
t.context.logs += value.toString();
|
|
});
|
|
t.context.stderr = stub(process.stderr, 'write').callsFake((value) => {
|
|
t.context.errors += value.toString();
|
|
});
|
|
|
|
previousArgv = process.argv;
|
|
previousEnv = process.env;
|
|
});
|
|
|
|
test.afterEach.always((t) => {
|
|
t.context.stdout.restore();
|
|
t.context.stderr.restore();
|
|
|
|
process.argv = previousArgv;
|
|
process.env = previousEnv;
|
|
|
|
td.reset();
|
|
});
|
|
|
|
test.serial('Pass options to semantic-release API', async (t) => {
|
|
const argv = [
|
|
'',
|
|
'',
|
|
'-b',
|
|
'master',
|
|
'next',
|
|
'-r',
|
|
'https://github/com/owner/repo.git',
|
|
'-t',
|
|
`v\${version}`,
|
|
'-p',
|
|
'plugin1',
|
|
'plugin2',
|
|
'-e',
|
|
'config1',
|
|
'config2',
|
|
'--verify-conditions',
|
|
'condition1',
|
|
'condition2',
|
|
'--analyze-commits',
|
|
'analyze',
|
|
'--verify-release',
|
|
'verify1',
|
|
'verify2',
|
|
'--generate-notes',
|
|
'notes',
|
|
'--prepare',
|
|
'prepare1',
|
|
'prepare2',
|
|
'--publish',
|
|
'publish1',
|
|
'publish2',
|
|
'--success',
|
|
'success1',
|
|
'success2',
|
|
'--fail',
|
|
'fail1',
|
|
'fail2',
|
|
'--debug',
|
|
'-d',
|
|
];
|
|
const index = await td.replaceEsm('../index.js');
|
|
process.argv = argv;
|
|
const cli = (await import('../cli.js')).default;
|
|
|
|
const exitCode = await cli();
|
|
|
|
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 argv = [
|
|
'',
|
|
'',
|
|
'--branches',
|
|
'master',
|
|
'--repository-url',
|
|
'https://github/com/owner/repo.git',
|
|
'--tag-format',
|
|
`v\${version}`,
|
|
'--plugins',
|
|
'plugin1',
|
|
'plugin2',
|
|
'--extends',
|
|
'config1',
|
|
'config2',
|
|
'--dry-run',
|
|
];
|
|
const index = await td.replaceEsm('../index.js');
|
|
process.argv = argv;
|
|
const cli = (await import('../cli.js')).default;
|
|
|
|
const exitCode = await cli();
|
|
|
|
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 argv = ['', '', '--bool', '--first-option', 'value1', '--second-option', 'value2', '--second-option', 'value3'];
|
|
const index = await td.replaceEsm('../index.js');
|
|
process.argv = argv;
|
|
const cli = (await import('../cli.js')).default;
|
|
|
|
const exitCode = await cli();
|
|
|
|
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 argv = ['', '', '--publish', 'false'];
|
|
const index = await td.replaceEsm('../index.js');
|
|
process.argv = argv;
|
|
const cli = (await import('../cli.js')).default;
|
|
|
|
const exitCode = await cli();
|
|
|
|
td.verify(index.default({publish: [], _: [], '$0': ''}));
|
|
|
|
t.is(exitCode, 0);
|
|
});
|
|
|
|
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'];
|
|
await td.replaceEsm('../index.js', null, run);
|
|
process.argv = argv;
|
|
const cli = (await import('../cli.js')).default;
|
|
|
|
await cli();
|
|
|
|
t.false('ci' in run.args[0][0]);
|
|
t.false('d' in run.args[0][0]);
|
|
t.false('dry-run' in run.args[0][0]);
|
|
t.false('debug' in run.args[0][0]);
|
|
t.false('r' in run.args[0][0]);
|
|
t.false('t' in run.args[0][0]);
|
|
t.false('p' in run.args[0][0]);
|
|
t.false('e' in run.args[0][0]);
|
|
});
|
|
|
|
test.serial('Display help', async (t) => {
|
|
const run = stub().resolves(true);
|
|
const argv = ['', '', '--help'];
|
|
await td.replaceEsm('../index.js', null, run);
|
|
process.argv = argv;
|
|
const cli = (await import('../cli.js')).default;
|
|
|
|
const exitCode = await cli();
|
|
|
|
t.regex(t.context.logs, /Run automated package publishing/);
|
|
t.is(exitCode, 0);
|
|
});
|
|
|
|
test.serial('Return error exitCode and prints help if called with a command', async (t) => {
|
|
const run = stub().resolves(true);
|
|
const argv = ['', '', 'pre'];
|
|
await td.replaceEsm('../index.js', null, run);
|
|
process.argv = argv;
|
|
const cli = (await import('../cli.js')).default;
|
|
|
|
const exitCode = await cli();
|
|
|
|
t.regex(t.context.errors, /Run automated package publishing/);
|
|
t.regex(t.context.errors, /Too many non-option arguments/);
|
|
t.is(exitCode, 1);
|
|
});
|
|
|
|
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'];
|
|
await td.replaceEsm('../index.js', null, run);
|
|
process.argv = argv;
|
|
const cli = (await import('../cli.js')).default;
|
|
|
|
const exitCode = await cli();
|
|
|
|
t.regex(t.context.errors, /Run automated package publishing/);
|
|
t.regex(t.context.errors, /Too many non-option arguments/);
|
|
t.is(exitCode, 1);
|
|
});
|
|
|
|
test.serial('Return error exitCode if semantic-release throw error', async (t) => {
|
|
const argv = ['', ''];
|
|
const index = await td.replaceEsm('../index.js');
|
|
td.when(index.default({_: [], '$0': ''})).thenReject(new Error('semantic-release error'));
|
|
process.argv = argv;
|
|
const cli = (await import('../cli.js')).default;
|
|
|
|
const exitCode = await cli();
|
|
|
|
t.regex(t.context.errors, /semantic-release error/);
|
|
t.is(exitCode, 1);
|
|
});
|
|
|
|
test.serial('Hide sensitive environment variable values from the logs', async (t) => {
|
|
const env = {MY_TOKEN: 'secret token'};
|
|
const argv = ['', ''];
|
|
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 = (await import('../cli.js')).default;
|
|
|
|
const exitCode = await cli();
|
|
|
|
t.regex(t.context.errors, new RegExp(`Throw error: Exposing token ${escapeRegExp(SECRET_REPLACEMENT)}`));
|
|
t.is(exitCode, 1);
|
|
});
|