- Tag sha will now be used also if there is a gitHead in last release and it's not in the history - Use `git merge-base` to determine if a commit is in history, allowing to use CI creating detached head repo - Mention recovery solution by creating a version tag in `ENOTINHISTORY` and `ENOGITHEAD` error messages - Do not mention branches containing missing commit in `ENOTINHISTORY` and `ENOGITHEAD` error messages as it's not available by default on most CI
305 lines
12 KiB
JavaScript
305 lines
12 KiB
JavaScript
import test from 'ava';
|
|
import {writeJson, readJson} from 'fs-extra';
|
|
import {start, stop, uri} from './helpers/registry';
|
|
import {gitRepo, gitCommits, gitHead, gitTagVersion, gitPackRefs, gitAmmendCommit} from './helpers/git-utils';
|
|
import execa from 'execa';
|
|
|
|
// Environment variables used with cli
|
|
const env = {
|
|
CI: true,
|
|
npm_config_registry: uri,
|
|
GH_TOKEN: 'github_token',
|
|
NPM_OLD_TOKEN: 'aW50ZWdyYXRpb246c3VjaHNlY3VyZQ==',
|
|
NPM_EMAIL: 'integration@test.com',
|
|
};
|
|
|
|
test.before(async t => {
|
|
// Start the local NPM registry
|
|
await start();
|
|
});
|
|
|
|
test.beforeEach(async t => {
|
|
// Save the current working diretory
|
|
t.context.cwd = process.cwd();
|
|
});
|
|
|
|
test.afterEach.always(async t => {
|
|
// Restore the current working directory
|
|
process.chdir(t.context.cwd);
|
|
});
|
|
|
|
test.after.always(async t => {
|
|
// Stop the local NPM registry
|
|
await stop();
|
|
});
|
|
|
|
test.serial('Release patch, minor and major versions', async t => {
|
|
// Create a git repository, set the current working directory at the root of the repo
|
|
t.log('Create git repository');
|
|
await gitRepo();
|
|
|
|
// Create package.json in repository root
|
|
await writeJson('./package.json', {
|
|
name: 'test-module',
|
|
version: '0.0.0-dev',
|
|
repository: {url: 'git+https://github.com/semantic-release/test-module'},
|
|
release: {verifyConditions: require.resolve('../src/lib/plugin-noop')},
|
|
});
|
|
// Create a npm-shrinkwrap.json file
|
|
await execa('npm', ['shrinkwrap'], {env});
|
|
|
|
/** No release **/
|
|
|
|
t.log('Commit a chore');
|
|
await gitCommits(['chore: Init repository']);
|
|
t.log('$ semantic-release pre');
|
|
let {stdout, stderr, code} = await t.throws(execa(require.resolve('../bin/semantic-release'), ['pre'], {env}));
|
|
t.regex(stderr, /ENOCHANGE There are no relevant changes, so no new version is released/);
|
|
t.is(code, 1);
|
|
|
|
/** Minor release **/
|
|
|
|
t.log('Commit a feature');
|
|
await gitCommits(['feat: Initial commit']);
|
|
t.log('$ semantic-release pre');
|
|
({stdout, stderr, code} = await execa(require.resolve('../bin/semantic-release'), ['pre'], {env}));
|
|
// Verify package.json and npm-shrinkwrap.json have been updated
|
|
t.is((await readJson('./package.json')).version, '1.0.0');
|
|
t.is((await readJson('./npm-shrinkwrap.json')).version, '1.0.0');
|
|
t.log('$ npm publish');
|
|
({stdout, stderr, code} = await execa('npm', ['publish'], {env}));
|
|
// Verify output of npm publish
|
|
t.regex(stdout, /test-module@1.0.0/);
|
|
t.is(code, 0);
|
|
// Retrieve the published package from the registry and check version and gitHead
|
|
let [, version, releaseGitHead] = /^version = '(.+)'\s+gitHead = '(.+)'$/.exec(
|
|
(await execa('npm', ['show', 'test-module', 'version', 'gitHead'], {env})).stdout
|
|
);
|
|
t.is(version, '1.0.0');
|
|
t.is(releaseGitHead, await gitHead());
|
|
t.log(`+ released ${version} with gitHead ${releaseGitHead}`);
|
|
|
|
/** Patch release **/
|
|
|
|
t.log('Commit a fix');
|
|
await gitCommits(['fix: bar']);
|
|
t.log('$ semantic-release pre');
|
|
({stdout, stderr, code} = await execa(require.resolve('../bin/semantic-release'), ['pre'], {env}));
|
|
// Verify package.json and npm-shrinkwrap.json have been updated
|
|
t.is((await readJson('./package.json')).version, '1.0.1');
|
|
t.is((await readJson('./npm-shrinkwrap.json')).version, '1.0.1');
|
|
t.log('$ npm publish');
|
|
({stdout, stderr, code} = await execa('npm', ['publish'], {env}));
|
|
// Verify output of npm publish
|
|
t.regex(stdout, /test-module@1.0.1/);
|
|
t.is(code, 0);
|
|
// Retrieve the published package from the registry and check version and gitHead
|
|
[, version, releaseGitHead] = /^version = '(.+)'\s+gitHead = '(.+)'$/.exec(
|
|
(await execa('npm', ['show', 'test-module', 'version', 'gitHead'], {env})).stdout
|
|
);
|
|
t.is(version, '1.0.1');
|
|
t.is(releaseGitHead, await gitHead());
|
|
t.log(`+ released ${version} with gitHead ${releaseGitHead}`);
|
|
|
|
/** Minor release **/
|
|
|
|
t.log('Commit a feature');
|
|
await gitCommits(['feat: baz']);
|
|
t.log('$ semantic-release pre');
|
|
({stdout, stderr, code} = await execa(require.resolve('../bin/semantic-release'), ['pre'], {env}));
|
|
// Verify package.json and npm-shrinkwrap.json have been updated
|
|
t.is((await readJson('./package.json')).version, '1.1.0');
|
|
t.is((await readJson('./npm-shrinkwrap.json')).version, '1.1.0');
|
|
t.log('$ npm publish');
|
|
({stdout, stderr, code} = await execa('npm', ['publish'], {env}));
|
|
// Verify output of npm publish
|
|
t.regex(stdout, /test-module@1.1.0/);
|
|
t.is(code, 0);
|
|
// Retrieve the published package from the registry and check version and gitHead
|
|
[, version, releaseGitHead] = /^version = '(.+)'\s+gitHead = '(.+)'$/.exec(
|
|
(await execa('npm', ['show', 'test-module', 'version', 'gitHead'], {env})).stdout
|
|
);
|
|
t.is(version, '1.1.0');
|
|
t.is(releaseGitHead, await gitHead());
|
|
t.log(`+ released ${version} with gitHead ${releaseGitHead}`);
|
|
|
|
/** Major release **/
|
|
|
|
t.log('Commit a breaking change');
|
|
await gitCommits(['feat: foo\n\n BREAKING CHANGE: bar']);
|
|
t.log('$ semantic-release pre');
|
|
({stdout, stderr, code} = await execa(require.resolve('../bin/semantic-release'), ['pre'], {env}));
|
|
// Verify package.json and npm-shrinkwrap.json have been updated
|
|
t.is((await readJson('./package.json')).version, '2.0.0');
|
|
t.is((await readJson('./npm-shrinkwrap.json')).version, '2.0.0');
|
|
t.log('$ npm publish');
|
|
({stdout, stderr, code} = await execa('npm', ['publish'], {env}));
|
|
// Verify output of npm publish
|
|
t.regex(stdout, /test-module@2.0.0/);
|
|
t.is(code, 0);
|
|
// Retrieve the published package from the registry and check version and gitHead
|
|
[, version, releaseGitHead] = /^version = '(.+)'\s+gitHead = '(.+)'$/.exec(
|
|
(await execa('npm', ['show', 'test-module', 'version', 'gitHead'], {env})).stdout
|
|
);
|
|
t.is(version, '2.0.0');
|
|
t.is(releaseGitHead, await gitHead());
|
|
t.log(`+ released ${version} with gitHead ${releaseGitHead}`);
|
|
});
|
|
|
|
test.serial('Release versions from a packed git repository, using tags to determine last release gitHead', async t => {
|
|
// Create a git repository, set the current working directory at the root of the repo
|
|
t.log('Create git repository');
|
|
await gitRepo();
|
|
|
|
// Create package.json in repository root
|
|
await writeJson('./package.json', {
|
|
name: 'test-module-2',
|
|
version: '0.0.0-dev',
|
|
repository: {url: 'git+https://github.com/semantic-release/test-module-2'},
|
|
release: {verifyConditions: require.resolve('../src/lib/plugin-noop')},
|
|
});
|
|
|
|
/** Minor release **/
|
|
|
|
t.log('Commit a feature');
|
|
await gitCommits(['feat: Initial commit']);
|
|
t.log('$ git pack-refs --all');
|
|
await gitPackRefs();
|
|
t.log('$ semantic-release pre');
|
|
let {stdout, code} = await execa(require.resolve('../bin/semantic-release'), ['pre'], {env});
|
|
// Verify package.json has been updated
|
|
t.is((await readJson('./package.json')).version, '1.0.0');
|
|
t.log('$ npm publish');
|
|
({stdout, code} = await execa('npm', ['publish'], {env}));
|
|
// Verify output of npm publish
|
|
t.regex(stdout, /test-module-2@1.0.0/);
|
|
t.is(code, 0);
|
|
// Retrieve the published package from the registry and check version and gitHead
|
|
let version = (await execa('npm', ['show', 'test-module-2', 'version'], {env})).stdout;
|
|
t.is(version, '1.0.0');
|
|
t.log(`+ released ${version}`);
|
|
// Create a tag version so the tag can be used later to determine the commit associated with the version
|
|
await gitTagVersion('v1.0.0');
|
|
t.log('Create git tag v1.0.0');
|
|
|
|
/** Patch release **/
|
|
|
|
t.log('Commit a fix');
|
|
await gitCommits(['fix: bar']);
|
|
t.log('$ semantic-release pre');
|
|
({stdout, code} = await execa(require.resolve('../bin/semantic-release'), ['pre'], {env}));
|
|
// Verify package.json has been updated
|
|
t.is((await readJson('./package.json')).version, '1.0.1');
|
|
t.log('$ npm publish');
|
|
({stdout, code} = await execa('npm', ['publish'], {env}));
|
|
// Verify output of npm publish
|
|
t.regex(stdout, /test-module-2@1.0.1/);
|
|
t.is(code, 0);
|
|
// Retrieve the published package from the registry and check version and gitHead
|
|
version = (await execa('npm', ['show', 'test-module-2', 'version'], {env})).stdout;
|
|
t.is(version, '1.0.1');
|
|
t.log(`+ released ${version}`);
|
|
});
|
|
|
|
test.serial('Exit with 1 in a plugin is not found', async t => {
|
|
// Environment variables used with cli
|
|
const env = {
|
|
CI: true,
|
|
npm_config_registry: uri,
|
|
GH_TOKEN: 'github_token',
|
|
NPM_OLD_TOKEN: 'aW50ZWdyYXRpb246c3VjaHNlY3VyZQ==',
|
|
NPM_EMAIL: 'integration@test.com',
|
|
};
|
|
// Create a git repository, set the current working directory at the root of the repo
|
|
t.log('Create git repository');
|
|
await gitRepo();
|
|
await writeJson('./package.json', {
|
|
name: 'test-module-3',
|
|
version: '0.0.0-dev',
|
|
repository: {url: 'git+https://github.com/semantic-release/test-module-4'},
|
|
release: {analyzeCommits: 'non-existing-path'},
|
|
});
|
|
|
|
const {code} = await t.throws(execa(require.resolve('../bin/semantic-release'), ['pre'], {env}));
|
|
t.is(code, 1);
|
|
});
|
|
|
|
test.serial('Create a tag as a recovery solution for "ENOTINHISTORY" error', async t => {
|
|
// Create a git repository, set the current working directory at the root of the repo
|
|
t.log('Create git repository');
|
|
await gitRepo();
|
|
|
|
// Create package.json in repository root
|
|
await writeJson('./package.json', {
|
|
name: 'test-module-4',
|
|
version: '0.0.0-dev',
|
|
repository: {url: 'git+https://github.com/semantic-release/test-module-2'},
|
|
release: {verifyConditions: require.resolve('../src/lib/plugin-noop')},
|
|
});
|
|
|
|
/** Minor release **/
|
|
|
|
t.log('Commit a feature');
|
|
await gitCommits(['feat: Initial commit']);
|
|
t.log('$ semantic-release pre');
|
|
let {stdout, stderr, code} = await execa(require.resolve('../bin/semantic-release'), ['pre'], {env});
|
|
// Verify package.json has been updated
|
|
t.is((await readJson('./package.json')).version, '1.0.0');
|
|
t.log('$ npm publish');
|
|
({stdout, code} = await execa('npm', ['publish'], {env}));
|
|
// Verify output of npm publish
|
|
t.regex(stdout, /test-module-4@1.0.0/);
|
|
t.is(code, 0);
|
|
// Retrieve the published package from the registry and check version and gitHead
|
|
let [, version, releaseGitHead] = /^version = '(.+)'\s+gitHead = '(.+)'$/.exec(
|
|
(await execa('npm', ['show', 'test-module-4', 'version', 'gitHead'], {env})).stdout
|
|
);
|
|
const head = await gitHead();
|
|
t.is(releaseGitHead, head);
|
|
t.log(`+ released ${version}`);
|
|
t.is(version, '1.0.0');
|
|
// Create a tag version so the tag can be used later to determine the commit associated with the version
|
|
await gitTagVersion('v1.0.0');
|
|
t.log('Create git tag v1.0.0');
|
|
|
|
/** Rewrite sha of commit used for release **/
|
|
|
|
t.log('Amend release commit');
|
|
const {hash} = await gitAmmendCommit('feat: Initial commit');
|
|
|
|
/** Patch release **/
|
|
|
|
t.log('Commit a fix');
|
|
await gitCommits(['fix: bar']);
|
|
t.log('$ semantic-release pre');
|
|
({stderr, stdout, code} = await execa(require.resolve('../bin/semantic-release'), ['pre'], {env, reject: false}));
|
|
|
|
t.log('Fail with "ENOTINHISTORY" error');
|
|
t.is(code, 1);
|
|
t.regex(
|
|
stderr,
|
|
new RegExp(
|
|
`You can recover from this error by restoring the commit "${head}" or by creating a tag for the version "1.0.0" on the commit corresponding to this release`
|
|
)
|
|
);
|
|
|
|
/** Create a tag to recover and redo release **/
|
|
|
|
t.log('Create git tag v1.0.0 to recover');
|
|
await gitTagVersion('v1.0.0', hash);
|
|
|
|
t.log('$ semantic-release pre');
|
|
({stderr, stdout, code} = await execa(require.resolve('../bin/semantic-release'), ['pre'], {env}));
|
|
// Verify package.json has been updated
|
|
t.is((await readJson('./package.json')).version, '1.0.1');
|
|
t.log('$ npm publish');
|
|
({stdout, code} = await execa('npm', ['publish'], {env}));
|
|
// Verify output of npm publish
|
|
t.regex(stdout, /test-module-4@1.0.1/);
|
|
t.is(code, 0);
|
|
// Retrieve the published package from the registry and check version and gitHead
|
|
version = (await execa('npm', ['show', 'test-module-4', 'version'], {env})).stdout;
|
|
t.is(version, '1.0.1');
|
|
t.log(`+ released ${version}`);
|
|
});
|