Compare commits

...
Author SHA1 Message Date
greenkeeper[bot]andPierre Vanduynslager d10268a8fa chore(package): update xo to version 0.24.0 2019-01-30 19:54:29 -05:00
Kevin LluchandPierre Vanduynslager 9d310c958b docs: add semantic-release-ado to community plugins 2019-01-30 19:54:29 -05:00
Austin Cawley-EdwardsandPierre Vanduynslager d7299395d5 docs: fix typo in CircleCI recipe 2019-01-30 19:54:29 -05:00
David AghassiandPierre Vanduynslager 2977462c5b docs: cleaned up wording and typos in docs 2019-01-30 19:54:29 -05:00
David AghassiandPierre Vanduynslager 7f90b7b691 docs: corrected typos in jsdocs 2019-01-30 19:54:29 -05:00
greenkeeper[bot]andPierre Vanduynslager d0891bf037 fix(package): update marked to version 0.6.0 2019-01-30 19:54:29 -05:00
Pierre Vanduynslager 990e85f069 fix: ignore lasst release only if pre-release on the same channel as current branch 2019-01-14 13:05:48 -05:00
Pierre Vanduynslager 0457a074e7 fix: correctly determine next pre-release version 2019-01-07 18:22:41 -05:00
Pierre Vanduynslager 1d8c35d889 docs: update FAQ regarding initial release version 2018-12-31 03:35:32 -05:00
Pierre Vanduynslager 0d91027e9f docs: update release triggering section in README 2018-12-31 03:35:32 -05:00
Pierre Vanduynslager 2144f2064f docs: add missing recipes link in README 2018-12-31 03:35:32 -05:00
Pierre Vanduynslager d55fda3306 fix(pacjage): update @semantic-release/github and @semantic-release/npm 2018-12-27 15:42:15 -05:00
greenkeeper[bot]andPierre Vanduynslager ba00e16f43 chore(package): update ava to version 1.0.1 2018-12-27 15:04:57 -05:00
greenkeeper[bot]andPierre Vanduynslager 5d6fd7c1cd fix(package): update aggregate-error to version 2.0.0 2018-12-27 15:04:57 -05:00
Pierre Vanduynslager f96c660c1b fix: harmonize parameters passed to getError 2018-12-18 18:02:02 -05:00
Pierre Vanduynslager 4aad9cd490 fix: do not call addChannelfor 2 merged branches configured with the same channel 2018-12-18 02:18:19 -05:00
40 changed files with 311 additions and 156 deletions
+7 -4
View File
@@ -77,11 +77,12 @@ Here is an example of the release type that will be done based on a commit messa
### Triggering a release
For each new commits added to the release branch (i.e. `master`) with `git push` or by merging a pull request or merging from another branch, a CI build is triggered and runs the `semantic-release` command to make a release if there are codebase changes since the last release that affect the package functionalities.
For each new commits added to one of the release branches (for example `master`, `next`, `beta`), with `git push` or by merging a pull request or merging from another branch, a CI build is triggered and runs the `semantic-release` command to make a release if there are codebase changes since the last release that affect the package functionalities.
If you need more control over the timing of releases you have a couple of options:
- Publish releases on a distribution channel (for example npm’s [dist-tags](https://docs.npmjs.com/cli/dist-tag)). This way you can keep control over what your users end up using by default, and you can decide when to make an automatically released version available to the stable channel, and promote it.
- Develop on a `dev` branch and merge it to the release branch (i.e. `master`) once you are ready to publish. **semantic-release** will run only on pushes to the release branch.
**semantic-release** offers various ways to control the timing, the content and the audience of published releases. See example workflows in the following recipes:
- [Using distribution channels](docs/recipes/distribution-channels.md#publishing-on-distribution-channels)
- [Maintenance releases](docs/recipes/maintenance-releases.md#publishing-maintenance-releases)
- [Pre-releases](docs/recipes/pre-releases.md#publishing-pre-releases)
### Release steps
@@ -113,6 +114,8 @@ After running the tests, the command `semantic-release` will execute the followi
- [Shareable configuration](docs/extending/shareable-configurations-list.md)
- Recipes
- [CI configurations](docs/recipes/README.md)
- [Git hosted services](docs/recipes/README.md)
- [Release workflow](docs/recipes/README.md)
- [Package managers and languages](docs/recipes/README.md)
- Developer guide
- [JavaScript API](docs/developer-guide/js-api.md)
+1 -1
View File
@@ -3,7 +3,7 @@
// Bad news: We have to write plain ES5 in this file
// Good news: It's the only file of the entire project
/* eslint-disable no-var, promise/prefer-await-to-then, prefer-destructuring */
/* eslint-disable no-var */
var semver = require('semver');
var execa = require('execa');
+3 -1
View File
@@ -1,4 +1,4 @@
const {argv, env, stderr} = require('process');
const {argv, env, stderr} = require('process'); // eslint-disable-line node/prefer-global/process
const util = require('util');
const hideSensitive = require('./lib/hide-sensitive');
@@ -51,12 +51,14 @@ Usage:
// Debug must be enabled before other requires in order to work
require('debug').enable('semantic-release:*');
}
await require('.')(opts);
return 0;
} catch (error) {
if (error.name !== 'YError') {
stderr.write(hideSensitive(env)(util.inspect(error, {colors: true})));
}
return 1;
}
};
+2
View File
@@ -71,3 +71,5 @@
- `verifyRelease`: Checks and warns (does not error by default) if the version numbers found on maven central and within the Git project differ by quite a bit
- `prepare`: Changes the version number in the `pom.xml` (or all `pom.xml` files in maven projects with multiple `pom.xml` files) and optionally creates a commit with this version number and pushes it to `master`
- `publish`: Runs `mvn deploy` to deploy to maven central and optionally will update to next snapshot version and merge changes to development branch
- [semantic-release-ado](https://github.com/lluchmk/semantic-release-ado)
- `prepare`: Stores the version number as an Azure DevOps pipeline variable availabe to downstream steps on the job
+1 -1
View File
@@ -158,6 +158,6 @@ Commit the encrypted private key and the `.circleci/config.yml` file to your rep
```bash
$ git add git_deploy_key.enc .circleci/config.yml
$ git commit -m "ci(cicle): Add the encrypted private ssh key"
$ git commit -m "ci(circle): Add the encrypted private ssh key"
$ git push
```
+2 -2
View File
@@ -145,7 +145,7 @@ If you have introduced a breaking bug in a release you have 2 options:
In both cases **semantic-release** will publish a new release, so your package users' will get the fixed/reverted version.
Depending on the package manager you are using, you might be able to un-publish or deprecate a release, in order to prevent users to download it by accident. For example npm allows you to [un-publish](https://docs.npmjs.com/cli/unpublish) in [next 72 hours](https://www.npmjs.com/policies/unpublish) after releasing or to [deprecate](https://docs.npmjs.com/cli/deprecate) a release.
Depending on the package manager you are using, you might be able to un-publish or deprecate a release, in order to prevent users from downloading it by accident. For example npm allows you to [un-publish](https://docs.npmjs.com/cli/unpublish) [within 72 hours](https://www.npmjs.com/policies/unpublish) after releasing. You may also [deprecate](https://docs.npmjs.com/cli/deprecate) a release if you would rather avoid un-publishing.
In any case **do not remove the Git tag associated with the buggy version**, otherwise **semantic-release** will later try to republish that version. Publishing a version after un-publishing is not supported by most package managers.
@@ -222,7 +222,7 @@ If you need more control over the timing of releases, see [Triggering a release]
This is not supported by **semantic-release** as it's not considered a good practice, mostly because [Semantic Versioning](https://semver.org) rules applies differently to major version zero.
In early development phase when your package is not ready for production yet we recommend to publish releases on a distribution channel (for example npm’s [dist-tags](https://docs.npmjs.com/cli/dist-tag)) or to develop on a `dev` branch and merge it to `master` periodically. See [Triggering a release](../../README.md#triggering-a-release) for more details on those solutions.
If your project is under heavy development, with frequent breaking changes, and is not production ready yet we recommend [publishing pre-releases](../recipes/pre-releases.md#publishing-pre-releases).
See [“Introduction to SemVer” - Irina Gebauer](https://blog.greenkeeper.io/introduction-to-semver-d272990c44f2) for more details on [Semantic Versioning](https://semver.org) and the recommendation to start at version `1.0.0`.
+1 -1
View File
@@ -12,7 +12,7 @@ npm ERR! You do not have permission to publish "<package-name>". Are you logged
This is most likely related to a misconfiguration of the [npm registry authentication](https://github.com/semantic-release/npm#npm-registry-authentication) or to your user [missing permission](https://docs.npmjs.com/cli/team) for publishing.
It might also happen if the package name you are trying to publish already exists (in such case npm consider you are trying to publish a new version of a package that is not yours, hence the permission error).
It might also happen if the package name you are trying to publish already exists (in the case of npm, you may be trying to publish a new version of a package that is not yours, hence the permission error).
To verify if your package name is available you can use [npm-name-cli](https://github.com/sindresorhus/npm-name-cli):
```bash
+1 -1
View File
@@ -26,7 +26,7 @@ Most **semantic-release** [plugins](plugins.md) require setting up authenticatio
| `NPM_TOKEN` | npm token created via [npm token create](https://docs.npmjs.com/getting-started/working_with_tokens#how-to-create-new-tokens).<br/>**Note**: Only the `auth-only` [level of npm two-factor authentication](https://docs.npmjs.com/getting-started/using-two-factor-authentication#levels-of-authentication) is supported. |
| `GH_TOKEN` | GitHub authentication token.<br/>**Note**: Only the [personal token](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line) authentication is supported. |
See each plugin documentation for the environment variables to set up.
See each plugin's documentation for the environment variables required.
The authentication token/credentials have to be made available in the CI service via environment variables.
+1 -1
View File
@@ -1,6 +1,6 @@
# Shareable configurations
A sharable configuration is an [npm](https://www.npmjs.com/) package that exports a **semantic-release** configuration object. It allows for use of the same configuration across several projects.
A shareable configuration is an [npm](https://www.npmjs.com/) package that exports a **semantic-release** configuration object. It allows for use of the same configuration across several projects.
The shareable configurations to use can be set with the [extends](configuration.md#extends) option.
+6 -6
View File
@@ -20,12 +20,12 @@ The type of the branch is automatically determined based on naming convention an
## Branches properties
| Property | Branch type | Description | Default |
|--------------|-------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------|
| `name` | All | **Required.** The Git branch holding the commits to analyze and the code to release. See [name](#name). | - The value itself if defined as a `String` or the matching branches name if defined as a glob. |
| `channel` | All | The distribution channel on which to publish releases from this branch. See [channel](#channel). | `undefined` for the first release branch, the value of `name` for subsequent ones. |
| `range` | [maintenance](#maintenance-branches) only | **Required unless `name` is formatted like `N.N.x` or `N.x` (`N` is a number).** The range of [semantic versions](https://semver.org) to support on this branch. See [range](#range). | The value of `name`. |
| `prerelease` | [pre-release](#pre-release-branches) only | **Required.** The pre-release detonation to append to [semantic versions](https://semver.org) released from this branch. See [prerelease](#prerelease). | - |
| Property | Branch type | Description | Default |
|--------------|-------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------|
| `name` | All | **Required.** The Git branch holding the commits to analyze and the code to release. See [name](#name). | - The value itself if defined as a `String` or the matching branches name if defined as a glob. |
| `channel` | All | The distribution channel on which to publish releases from this branch. Set to `false` to force the default distribution channel instead of using the default. See [channel](#channel). | `undefined` for the first release branch, the value of `name` for subsequent ones. |
| `range` | [maintenance](#maintenance-branches) only | **Required unless `name` is formatted like `N.N.x` or `N.x` (`N` is a number).** The range of [semantic versions](https://semver.org) to support on this branch. See [range](#range). | The value of `name`. |
| `prerelease` | [pre-release](#pre-release-branches) only | **Required.** The pre-release detonation to append to [semantic versions](https://semver.org) released from this branch. See [prerelease](#prerelease). | - |
### name
+5 -3
View File
@@ -64,6 +64,7 @@ async function run(context, plugins) {
);
return false;
}
logger[options.dryRun ? 'warn' : 'success'](
`Run automated release from branch ${ciBranch}${options.dryRun ? ' in dry-run mode' : ''}`
);
@@ -82,11 +83,12 @@ async function run(context, plugins) {
);
return false;
}
throw error;
}
} catch (error) {
logger.error(`The command "${error.cmd}" failed with the error message ${error.stderr}.`);
throw getError('EGITNOPERMISSION', {options});
throw getError('EGITNOPERMISSION', context);
}
logger.success(`Allowed to push to the Git repository`);
@@ -98,8 +100,8 @@ async function run(context, plugins) {
context.releases = [];
await pEachSeries(releasesToAdd, async ({lastRelease, currentRelease, nextRelease}) => {
if (context.branch['merge-range'] && !semver.satisfies(nextRelease.version, context.branch['merge-range'])) {
errors.push(getError('EINVALIDMAINTENANCEMERGE', {nextRelease, branch: context.branch}));
if (context.branch.mergeRange && !semver.satisfies(nextRelease.version, context.branch.mergeRange)) {
errors.push(getError('EINVALIDMAINTENANCEMERGE', {...context, nextRelease}));
return;
}
+7 -5
View File
@@ -1,4 +1,4 @@
const {sortBy} = require('lodash');
const {sortBy, isNil} = require('lodash');
const semver = require('semver');
const semverDiff = require('semver-diff');
const {FIRST_RELEASE, RELEASE_TYPE} = require('../definitions/constants');
@@ -20,7 +20,7 @@ function maintenance({maintenance, release}) {
...rest,
name,
range: range || name,
channel: channel || name,
channel: isNil(channel) ? name : channel,
})),
'range'
).map(({name, range, tags, ...rest}, idx, branches) => {
@@ -51,7 +51,7 @@ function maintenance({maintenance, release}) {
tags,
range: getRange(min, max),
accept: diff ? RELEASE_TYPE.slice(0, RELEASE_TYPE.indexOf(diff)) : [],
'merge-range': getRange(maintenanceMin, getUpperBound(range)),
mergeRange: getRange(maintenanceMin, getUpperBound(range)),
};
});
}
@@ -60,6 +60,7 @@ function release({release}) {
if (release.length === 0) {
return release;
}
const breakpoints = release.length > 2 ? ['minor', 'major'] : ['major'];
// The intial bound is the last release from the base branch of `FIRST_RELEASE` (1.0.0)
@@ -80,10 +81,11 @@ function release({release}) {
// The upper bound is the lowest version between `nextFirstVersion` and the default upper bound
bound = lowest(nextFirstVersion, upperBound);
}
const diff = bound ? semverDiff(min, bound) : null;
return {
...rest,
channel: idx === 0 ? channel : channel || name,
channel: idx === 0 ? channel : isNil(channel) ? name : channel,
tags,
type: 'release',
name,
@@ -98,7 +100,7 @@ function prerelease({prerelease}) {
const preid = prerelease === true ? name : prerelease;
return {
...rest,
channel: channel || name,
channel: isNil(channel) ? name : channel,
type: 'prerelease',
name,
prerelease: preid,
+14 -31
View File
@@ -1,10 +1,9 @@
const url = require('url');
const {inspect} = require('util');
const {toLower, isString, trim} = require('lodash');
const pkg = require('../../package.json');
const {RELEASE_TYPE} = require('./constants');
const homepage = url.format({...url.parse(pkg.homepage), hash: null});
const [homepage] = pkg.homepage.split('#');
const stringify = obj => (isString(obj) ? obj : inspect(obj, {breakLength: Infinity, depth: 2, maxArrayLength: 5}));
const linkify = file => `${homepage}/blob/master/${file}`;
const wordsList = words =>
@@ -29,11 +28,9 @@ Please make sure to add the \`repositoryUrl\` to the [semantic-release configura
'docs/usage/configuration.md'
)}).`,
}),
EGITNOPERMISSION: ({options}) => ({
EGITNOPERMISSION: ({options: {repositoryUrl}, branch: {name}}) => ({
message: 'The push permission to the Git repository is required.',
details: `**semantic-release** cannot push the version tag to the branch \`${
options.branch
}\` on remote Git repository with URL \`${options.repositoryUrl}\`.
details: `**semantic-release** cannot push the version tag to the branch \`${name}\` on remote Git repository with URL \`${repositoryUrl}\`.
Please refer to the [authentication configuration documentation](${linkify(
'docs/usage/ci-configuration.md#authentication'
@@ -41,7 +38,7 @@ Please refer to the [authentication configuration documentation](${linkify(
'docs/usage/configuration.md#repositoryurl'
)}) is configured with a [valid Git URL](https://git-scm.com/book/en/v2/Git-on-the-Server-The-Protocols).`,
}),
EINVALIDTAGFORMAT: ({tagFormat}) => ({
EINVALIDTAGFORMAT: ({options: {tagFormat}}) => ({
message: 'Invalid `tagFormat` option.',
details: `The [tagFormat](${linkify(
'docs/usage/configuration.md#tagformat'
@@ -49,7 +46,7 @@ Please refer to the [authentication configuration documentation](${linkify(
Your configuration for the \`tagFormat\` option is \`${stringify(tagFormat)}\`.`,
}),
ETAGNOVERSION: ({tagFormat}) => ({
ETAGNOVERSION: ({options: {tagFormat}}) => ({
message: 'Invalid `tagFormat` option.',
details: `The [tagFormat](${linkify(
'docs/usage/configuration.md#tagformat'
@@ -205,40 +202,26 @@ Your configuration for the problematic branch is \`${stringify(branch)}\`.`,
Your configuration for the problematic branches is \`${stringify(branches)}\`.`,
}),
EINVALIDNEXTVERSION: ({nextRelease, branch, commits, validBranches}) => ({
message: `The release \`${nextRelease.version}\` on branch \`${
branch.name
}\` cannot be published as it is out of range.`,
details: `Based on the releases published on other branches, only versions within the range \`${
branch.range
}\` can be published from branch \`${branch.name}\`.
EINVALIDNEXTVERSION: ({nextRelease: {version}, branch: {name, range}, commits, validBranches}) => ({
message: `The release \`${version}\` on branch \`${name}\` cannot be published as it is out of range.`,
details: `Based on the releases published on other branches, only versions within the range \`${range}\` can be published from branch \`${name}\`.
The following commit${commits.length > 1 ? 's are' : ' is'} responsible for the invalid release:
${commits.map(({commit: {short}, subject}) => `- ${subject} (${short})`).join('\n')}
${
commits.length > 1 ? 'Those commits' : 'This commit'
} should be moved to a valid branch with [git merge](https://git-scm.com/docs/git-merge) or [git cherry-pick](https://git-scm.com/docs/git-cherry-pick) and removed from branch \`${
branch.name
}\` with [git revert](https://git-scm.com/docs/git-revert) or [git reset](https://git-scm.com/docs/git-reset).
commits.length > 1 ? 'Those commits' : 'This commit'
} should be moved to a valid branch with [git merge](https://git-scm.com/docs/git-merge) or [git cherry-pick](https://git-scm.com/docs/git-cherry-pick) and removed from branch \`${name}\` with [git revert](https://git-scm.com/docs/git-revert) or [git reset](https://git-scm.com/docs/git-reset).
A valid branch could be ${wordsList(validBranches.map(({name}) => `\`${name}\``))}.
See the [workflow configuration documentation](${linkify('docs/usage/workflow-configuration.md')}) for more details.`,
}),
EINVALIDMAINTENANCEMERGE: ({nextRelease, branch}) => ({
message: `The release \`${nextRelease.version}\` on branch \`${
branch.name
}\` cannot be published as it is out of range.`,
details: `Only releases within the range \`${branch['merge-range']}\` can be merged into the maintenance branch \`${
branch.name
}\` and published to the \`${nextRelease.channel}\` distribution channel.
EINVALIDMAINTENANCEMERGE: ({nextRelease: {channel, gitTag, version}, branch: {mergeRange, name}}) => ({
message: `The release \`${version}\` on branch \`${name}\` cannot be published as it is out of range.`,
details: `Only releases within the range \`${mergeRange}\` can be merged into the maintenance branch \`${name}\` and published to the \`${channel}\` distribution channel.
The branch \`${
branch.name
}\` head should be [reset](https://git-scm.com/docs/git-reset) to a previous commit so the commit with tag \`${
nextRelease.gitTag
}\` is removed from the branch history.
The branch \`${name}\` head should be [reset](https://git-scm.com/docs/git-reset) to a previous commit so the commit with tag \`${gitTag}\` is removed from the branch history.
See the [workflow configuration documentation](${linkify('docs/usage/workflow-configuration.md')}) for more details.`,
}),
+1
View File
@@ -59,6 +59,7 @@ module.exports = {
// Regenerate the release notes
context.nextRelease.notes = await generateNotes(context);
}
// Call the next prepare plugin with the updated `nextRelease`
return context;
},
+1
View File
@@ -30,6 +30,7 @@ module.exports = async (context, opts) => {
if (options.ci === false) {
options.noCi = true;
}
const pluginsPath = {};
let extendPaths;
({extends: extendPaths, ...options} = options);
+2 -2
View File
@@ -1,4 +1,4 @@
const {parse, format} = require('url');
const {parse, format} = require('url'); // eslint-disable-line node/no-deprecated-api
const {isNil} = require('lodash');
const hostedGitInfo = require('hosted-git-info');
const {verifyAuth} = require('./git');
@@ -44,7 +44,7 @@ module.exports = async ({cwd, env, branch, options: {repositoryUrl}}) => {
const gitCredentials = `${GIT_TOKENS[envVar] || ''}${env[envVar] || ''}`;
if (gitCredentials) {
// If credentials are set via anvironment variables, convert the URL to http/https and add basic auth, otherwise return `repositoryUrl` as is
// If credentials are set via environment variables, convert the URL to http/https and add basic auth, otherwise return `repositoryUrl` as is
const [match, auth, host, path] = /^(?!.+:\/\/)(?:(.*)@)?(.*?):(.*)$/.exec(repositoryUrl) || [];
return format({
...parse(match ? `ssh://${auth ? `${auth}@` : ''}${host}/${path}` : repositoryUrl),
+3 -3
View File
@@ -23,9 +23,9 @@ const {makeTag} = require('./utils');
*
* @return {LastRelease} The last tagged release or empty object if none is found.
*/
module.exports = ({branch: {tags, type}, options: {tagFormat}}, {before} = {}) => {
const [{version, gitTag, gitHead, channel} = {}] = tags
.filter(tag => type === 'prerelease' || !semver.prerelease(tag.version))
module.exports = ({branch, options: {tagFormat}}, {before} = {}) => {
const [{version, gitTag, gitHead, channel} = {}] = branch.tags
.filter(tag => (branch.type === 'prerelease' && branch.channel === tag.channel) || !semver.prerelease(tag.version))
.filter(tag => isUndefined(before) || semver.lt(tag.version, before))
.sort((a, b) => semver.rcompare(a.version, b.version));
+4 -3
View File
@@ -1,14 +1,15 @@
const semver = require('semver');
const {FIRST_RELEASE, FIRSTPRERELEASE} = require('./definitions/constants');
module.exports = ({branch, nextRelease: {type}, lastRelease, logger}) => {
module.exports = ({branch, nextRelease: {type, channel}, lastRelease, logger}) => {
let version;
if (lastRelease.version) {
const {major, minor, patch} = semver.parse(lastRelease.version);
version =
branch.type === 'prerelease'
? semver.prerelease(lastRelease.version)
? semver.prerelease(lastRelease.version) && lastRelease.channel === channel
? semver.inc(lastRelease.version, 'prerelease')
: `${semver.inc(lastRelease.version, type)}-${branch.prerelease}.${FIRSTPRERELEASE}`
: `${semver.inc(`${major}.${minor}.${patch}`, type)}-${branch.prerelease}.${FIRSTPRERELEASE}`
: semver.inc(lastRelease.version, type);
logger.log('The next release version is %s', version);
} else {
+2 -1
View File
@@ -33,7 +33,8 @@ module.exports = context => {
branch.tags.filter(
({channel, version}) =>
channel === higherBranch.channel &&
(branch.type !== 'maintenance' || semver.gte(version, getLowerBound(branch['merge-range'])))
channel !== branch.channel &&
(branch.type !== 'maintenance' || semver.gte(version, getLowerBound(branch.mergeRange)))
)
)
// Find ones that are not released on the building branch channel
+1
View File
@@ -99,6 +99,7 @@ async function isRefInHistory(ref, branch, findRebasedTags, execaOpts) {
);
return (await getBranchCommits(branch, execaOpts)).some(matches(pick(tagCommit, ['message', 'author'])));
}
return false;
}
+2
View File
@@ -57,10 +57,12 @@ module.exports = (context, pluginsPath) => {
plugin ? [plugin[0], Object.assign(plugin[1], options[type])] : plugin
);
}
if (!validateStep({required}, options[type])) {
errors.push(getError('EPLUGINCONF', {type, required, pluginConf: options[type]}));
return pluginsConf;
}
pluginOpts = options[type];
}
+2
View File
@@ -40,9 +40,11 @@ module.exports = (context, type, pluginOpt, pluginsPath) => {
if (outputValidator && !outputValidator(result)) {
throw getError(`E${type.toUpperCase()}OUTPUT`, {result, pluginName});
}
logger.success(`Completed step "${type}" of plugin "${pluginName}"`);
return result;
}
logger.warn(`Skip step "${type}" of plugin "${pluginName}" in dry-run mode`);
} catch (error) {
logger.error(`Failed step "${type}" of plugin "${pluginName}"`);
+3 -1
View File
@@ -4,7 +4,7 @@ const AggregateError = require('aggregate-error');
const {extractErrors} = require('../utils');
/**
* A Function that execute a list of function sequencially. If at least one Function ins the pipeline throw an Error or rejects, the pipeline function rejects as well.
* A Function that execute a list of function sequencially. If at least one Function ins the pipeline throws an Error or rejects, the pipeline function rejects as well.
*
* @typedef {Function} Pipeline
* @param {Any} input Argument to pass to the first step in the pipeline.
@@ -44,6 +44,7 @@ module.exports = (steps, {settleAll = false, getNextInput = identity, transform
throw error;
}
}
// Prepare input for the next step, passing the input of the last iteration (or initial parameter for the first iteration) and the result of the current one
return getNextInput(lastInput, result);
},
@@ -52,5 +53,6 @@ module.exports = (steps, {settleAll = false, getNextInput = identity, transform
if (errors.length > 0) {
throw new AggregateError(errors);
}
return results;
};
+3
View File
@@ -12,6 +12,7 @@ const validateSteps = conf => {
) {
return true;
}
conf = castArray(conf);
if (conf.length !== 1) {
@@ -39,6 +40,7 @@ function validateStep({required}, conf) {
if (required) {
return conf.length >= 1 && validateSteps(conf);
}
return conf.length === 0 || validateSteps(conf);
}
@@ -59,6 +61,7 @@ function parseConfig(plugin) {
} else {
path = plugin;
}
return [path, config || {}];
}
+1
View File
@@ -59,6 +59,7 @@ function getFirstVersion(versions, lowerBranches) {
if (lowerVersion[0]) {
return versions.sort(semver.compare).find(version => semver.gt(version, lowerVersion[0]));
}
return getEarliestVersion(versions);
}
+8 -3
View File
@@ -3,7 +3,12 @@ const AggregateError = require('aggregate-error');
const {isGitRepo, verifyTagName} = require('./git');
const getError = require('./get-error');
module.exports = async ({cwd, env, options: {repositoryUrl, tagFormat, branches}}) => {
module.exports = async context => {
const {
cwd,
env,
options: {repositoryUrl, tagFormat, branches},
} = context;
const errors = [];
if (!(await isGitRepo({cwd, env}))) {
@@ -14,14 +19,14 @@ module.exports = async ({cwd, env, options: {repositoryUrl, tagFormat, branches}
// Verify that compiling the `tagFormat` produce a valid Git tag
if (!(await verifyTagName(template(tagFormat)({version: '0.0.0'})))) {
errors.push(getError('EINVALIDTAGFORMAT', {tagFormat}));
errors.push(getError('EINVALIDTAGFORMAT', context));
}
// Verify the `tagFormat` contains the variable `version` by compiling the `tagFormat` template
// with a space as the `version` value and verify the result contains the space.
// The space is used as it's an invalid tag character, so it's guaranteed to no be present in the `tagFormat`.
if ((template(tagFormat)({version: ' '}).match(/ /g) || []).length !== 1) {
errors.push(getError('ETAGNOVERSION', {tagFormat}));
errors.push(getError('ETAGNOVERSION', context));
}
branches.forEach(branch => {
+7 -7
View File
@@ -21,10 +21,10 @@
"dependencies": {
"@semantic-release/commit-analyzer": "^7.0.0-beta.1",
"@semantic-release/error": "^2.2.0",
"@semantic-release/github": "^5.3.0-beta.5",
"@semantic-release/npm": "^5.2.0-beta.4",
"@semantic-release/github": "^5.3.0-beta.6",
"@semantic-release/npm": "^5.2.0-beta.5",
"@semantic-release/release-notes-generator": "^7.1.2",
"aggregate-error": "^1.0.0",
"aggregate-error": "^2.0.0",
"cosmiconfig": "^5.0.1",
"debug": "^4.0.0",
"env-ci": "^3.0.0",
@@ -36,8 +36,8 @@
"hook-std": "^1.1.0",
"hosted-git-info": "^2.7.1",
"lodash": "^4.17.4",
"marked": "^0.5.0",
"marked-terminal": "^3.0.0",
"marked": "^0.6.0",
"marked-terminal": "^3.2.0",
"micromatch": "3.1.5",
"p-each-series": "^1.0.0",
"p-reduce": "^1.0.0",
@@ -49,7 +49,7 @@
"yargs": "^12.0.0"
},
"devDependencies": {
"ava": "^0.25.0",
"ava": "^1.0.1",
"clear-module": "^3.0.0",
"codecov": "^3.0.0",
"commitizen": "^3.0.0",
@@ -68,7 +68,7 @@
"sinon": "^7.1.1",
"stream-buffers": "^3.0.2",
"tempy": "^0.2.1",
"xo": "^0.23.0"
"xo": "^0.24.0"
},
"engines": {
"node": ">=8.3"
+3 -3
View File
@@ -146,7 +146,7 @@ test('Throw SemanticReleaseError for invalid configurations', async t => {
{name: 'preview', prerelease: 'alpha', tags: []},
];
const getBranches = proxyquire('../../lib/branches', {'./get-tags': () => branches, './expand': () => []});
const errors = [...(await t.throws(getBranches({options: {branches}})))];
const errors = [...(await t.throwsAsync(getBranches({options: {branches}})))];
t.is(errors[0].name, 'SemanticReleaseError');
t.is(errors[0].code, 'EMAINTENANCEBRANCH');
@@ -174,7 +174,7 @@ test('Throw a SemanticReleaseError if there is duplicate branches', async t => {
const branches = [{name: 'master', tags: []}, {name: 'master', tags: []}];
const getBranches = proxyquire('../../lib/branches', {'./get-tags': () => branches, './expand': () => []});
const errors = [...(await t.throws(getBranches({options: {branches}})))];
const errors = [...(await t.throwsAsync(getBranches({options: {branches}})))];
t.is(errors[0].name, 'SemanticReleaseError');
t.is(errors[0].code, 'EDUPLICATEBRANCHES');
@@ -186,7 +186,7 @@ test('Throw a SemanticReleaseError for each invalid branch name', async t => {
const branches = [{name: '~master', tags: []}, {name: '^master', tags: []}];
const getBranches = proxyquire('../../lib/branches', {'./get-tags': () => branches, './expand': () => []});
const errors = [...(await t.throws(getBranches({options: {branches}})))];
const errors = [...(await t.throwsAsync(getBranches({options: {branches}})))];
t.is(errors[0].name, 'SemanticReleaseError');
t.is(errors[0].code, 'EINVALIDBRANCHNAME');
+53 -34
View File
@@ -4,19 +4,17 @@ import normalize from '../../lib/branches/normalize';
const toTags = versions => versions.map(version => ({version}));
test('Maintenance branches - initial state', t => {
const maintenance = [{name: '1.x', tags: []}, {name: '1.1.x', tags: []}, {name: '1.2.x', tags: []}];
const maintenance = [{name: '1.x', channel: '1.x', tags: []}, {name: '1.1.x', tags: []}, {name: '1.2.x', tags: []}];
const release = [{name: 'master', tags: []}];
t.deepEqual(
normalize
.maintenance({maintenance, release})
.map(({type, name, range, accept, channel, 'merge-range': maintenanceRange}) => ({
type,
name,
range,
accept,
channel,
'merge-range': maintenanceRange,
})),
normalize.maintenance({maintenance, release}).map(({type, name, range, accept, channel, mergeRange}) => ({
type,
name,
range,
accept,
channel,
mergeRange,
})),
[
{
type: 'maintenance',
@@ -24,7 +22,7 @@ test('Maintenance branches - initial state', t => {
range: '>=1.1.0 <1.0.0',
accept: [],
channel: '1.1.x',
'merge-range': '>=1.1.0 <1.2.0',
mergeRange: '>=1.1.0 <1.2.0',
},
{
type: 'maintenance',
@@ -32,7 +30,7 @@ test('Maintenance branches - initial state', t => {
range: '>=1.2.0 <1.0.0',
accept: [],
channel: '1.2.x',
'merge-range': '>=1.2.0 <1.3.0',
mergeRange: '>=1.2.0 <1.3.0',
},
{
type: 'maintenance',
@@ -40,7 +38,7 @@ test('Maintenance branches - initial state', t => {
range: '>=1.3.0 <1.0.0',
accept: [],
channel: '1.x',
'merge-range': '>=1.3.0 <2.0.0',
mergeRange: '>=1.3.0 <2.0.0',
},
]
);
@@ -63,13 +61,13 @@ test('Maintenance branches - cap range to first release present on default branc
t.deepEqual(
normalize
.maintenance({maintenance, release})
.map(({type, name, range, accept, channel, 'merge-range': maintenanceRange}) => ({
.map(({type, name, range, accept, channel, mergeRange: maintenanceRange}) => ({
type,
name,
range,
accept,
channel,
'merge-range': maintenanceRange,
mergeRange: maintenanceRange,
})),
[
{
@@ -78,7 +76,7 @@ test('Maintenance branches - cap range to first release present on default branc
range: '>=1.1.1 <1.2.0',
accept: ['patch'],
channel: 'name',
'merge-range': '>=1.1.0 <1.2.0',
mergeRange: '>=1.1.0 <1.2.0',
},
{
type: 'maintenance',
@@ -86,7 +84,7 @@ test('Maintenance branches - cap range to first release present on default branc
range: '>=1.2.1 <1.3.0',
accept: ['patch'],
channel: '1.2.x',
'merge-range': '>=1.2.0 <1.3.0',
mergeRange: '>=1.2.0 <1.3.0',
},
{
type: 'maintenance',
@@ -94,7 +92,7 @@ test('Maintenance branches - cap range to first release present on default branc
range: '>=1.5.0 <1.6.0',
accept: ['patch'],
channel: '1.x',
'merge-range': '>=1.3.0 <2.0.0',
mergeRange: '>=1.3.0 <2.0.0',
},
{
type: 'maintenance',
@@ -102,7 +100,7 @@ test('Maintenance branches - cap range to first release present on default branc
range: '>=2.0.0 <1.6.0',
accept: [],
channel: '2.x.x',
'merge-range': '>=2.0.0 <3.0.0',
mergeRange: '>=2.0.0 <3.0.0',
},
]
);
@@ -116,16 +114,14 @@ test('Maintenance branches - cap range to default branch last release if all rel
const release = [{name: 'master', tags: toTags(['1.0.0', '1.2.0', '1.3.0', '2.0.0'])}];
t.deepEqual(
normalize
.maintenance({maintenance, release})
.map(({type, name, range, accept, channel, 'merge-range': maintenanceRange}) => ({
type,
name,
range,
accept,
channel,
'merge-range': maintenanceRange,
})),
normalize.maintenance({maintenance, release}).map(({type, name, range, accept, channel, mergeRange}) => ({
type,
name,
range,
accept,
channel,
mergeRange,
})),
[
{
type: 'maintenance',
@@ -133,7 +129,7 @@ test('Maintenance branches - cap range to default branch last release if all rel
range: '>=1.3.0 <2.0.0',
accept: ['patch', 'minor'],
channel: '1.x',
'merge-range': '>=1.0.0 <2.0.0',
mergeRange: '>=1.0.0 <2.0.0',
},
{
type: 'maintenance',
@@ -141,14 +137,18 @@ test('Maintenance branches - cap range to default branch last release if all rel
range: '>=2.0.0 <2.0.0',
accept: [],
channel: '2.x.x',
'merge-range': '>=2.0.0 <3.0.0',
mergeRange: '>=2.0.0 <3.0.0',
},
]
);
});
test('Release branches - initial state', t => {
const release = [{name: 'master', tags: []}, {name: 'next', tags: []}, {name: 'next-major', tags: []}];
const release = [
{name: 'master', tags: []},
{name: 'next', channel: 'next', tags: []},
{name: 'next-major', tags: []},
];
t.deepEqual(
normalize.release({release}).map(({type, name, range, accept, channel}) => ({type, name, range, accept, channel})),
@@ -298,10 +298,29 @@ test('Release branches - limit releases on 2nd and 3rd branche based on 1st bran
});
test('Prerelease branches', t => {
const prerelease = [{name: 'beta', prerelease: true, tags: []}, {name: 'alpha', prerelease: 'preview', tags: []}];
const prerelease = [
{name: 'beta', channel: 'beta', prerelease: true, tags: []},
{name: 'alpha', prerelease: 'preview', tags: []},
];
t.deepEqual(normalize.prerelease({prerelease}).map(({type, name, channel}) => ({type, name, channel})), [
{type: 'prerelease', name: 'beta', channel: 'beta'},
{type: 'prerelease', name: 'alpha', channel: 'alpha'},
]);
});
test('Allow to set channel to "false" to prevent default', t => {
const maintenance = [{name: '1.x', channel: false, tags: []}];
const release = [{name: 'master', channel: false, tags: []}, {name: 'next', channel: false, tags: []}];
const prerelease = [{name: 'beta', channel: false, prerelease: true, tags: []}];
t.deepEqual(normalize.maintenance({maintenance, release}).map(({name, channel}) => ({name, channel})), [
{name: '1.x', channel: false},
]);
t.deepEqual(normalize.release({release}).map(({name, channel}) => ({name, channel})), [
{name: 'master', channel: false},
{name: 'next', channel: false},
]);
t.deepEqual(normalize.prerelease({prerelease}).map(({name, channel}) => ({name, channel})), [
{name: 'beta', channel: false},
]);
});
+1 -1
View File
@@ -511,7 +511,7 @@ test('Throw an Error if one of the shareable config cannot be found', async t =>
await outputJson(path.resolve(cwd, 'package.json'), {release: pkhOptions});
await outputJson(path.resolve(cwd, 'shareable1.json'), options1);
const error = await t.throws(t.context.getConfig({cwd}), Error);
const error = await t.throwsAsync(t.context.getConfig({cwd}), Error);
t.is(error.message, "Cannot find module 'non-existing-path'");
t.is(error.code, 'MODULE_NOT_FOUND');
+25
View File
@@ -18,6 +18,31 @@ test('Get the highest non-prerelease valid tag', t => {
t.deepEqual(result, {version: '2.0.0', gitTag: 'v2.0.0', name: 'v2.0.0', gitHead: '222', channel: undefined});
});
test('Get the highest prerelease valid tag, ignoring other tags from other prerelease channels', t => {
const result = getLastRelease({
branch: {
name: 'beta',
prerelease: 'beta',
channel: 'beta',
tags: [
{version: '1.0.0-beta.1', gitTag: 'v1.0.0-beta.1@beta', gitHead: '111', channel: 'beta'},
{version: '1.0.0-beta.2', gitTag: 'v1.0.0-beta.2@beta', gitHead: '222', channel: 'beta'},
{version: '1.0.0-alpha.1', gitTag: 'v1.0.0-alpha.1@alpha', gitHead: '333', channel: 'alpha'},
],
type: 'prerelease',
},
options: {tagFormat: `v\${version}`},
});
t.deepEqual(result, {
version: '1.0.0-beta.2',
gitTag: 'v1.0.0-beta.2@beta',
name: 'v1.0.0-beta.2',
gitHead: '222',
channel: 'beta',
});
});
test('Return empty object if no valid tag is found', t => {
const result = getLastRelease({
branch: {
+30
View File
@@ -76,6 +76,16 @@ test('Increase version for patch release on prerelease branch', t => {
}),
'1.0.0-beta.2'
);
t.is(
getNextVersion({
branch: {name: 'alpha', type: 'prerelease', prerelease: 'alpha'},
nextRelease: {type: 'patch', channel: 'alpha'},
lastRelease: {version: '1.0.0-beta.1', channel: 'beta'},
logger: t.context.logger,
}),
'1.0.1-alpha.1'
);
});
test('Increase version for minor release on prerelease branch', t => {
@@ -98,6 +108,16 @@ test('Increase version for minor release on prerelease branch', t => {
}),
'1.0.0-beta.2'
);
t.is(
getNextVersion({
branch: {name: 'alpha', type: 'prerelease', prerelease: 'alpha'},
nextRelease: {type: 'minor', channel: 'alpha'},
lastRelease: {version: '1.0.0-beta.1', channel: 'beta'},
logger: t.context.logger,
}),
'1.1.0-alpha.1'
);
});
test('Increase version for major release on prerelease branch', t => {
@@ -120,6 +140,16 @@ test('Increase version for major release on prerelease branch', t => {
}),
'1.0.0-beta.2'
);
t.is(
getNextVersion({
branch: {name: 'alpha', type: 'prerelease', prerelease: 'alpha'},
nextRelease: {type: 'major', channel: 'alpha'},
lastRelease: {version: '1.0.0-beta.1', channel: 'beta'},
logger: t.context.logger,
}),
'2.0.0-alpha.1'
);
});
test('Return 1.0.0 if there is no previous release on prerelease branch', t => {
+66 -3
View File
@@ -7,7 +7,7 @@ test('Return versions merged from release to maintenance branch, excluding lower
name: '2.x',
channel: '2.x',
type: 'maintenance',
'merge-range': '>=2.0.0 <3.0.0',
mergeRange: '>=2.0.0 <3.0.0',
tags: [
{gitTag: 'v2.0.0@2.x', version: '2.0.0', channel: '2.x', gitHead: '111'},
{gitTag: 'v2.0.0', version: '2.0.0', gitHead: '111'},
@@ -63,7 +63,7 @@ test('Return versions merged from release to maintenance branch, excluding lower
]);
});
test('Return versions merged from future branch to release branch', t => {
test('Return versions merged between release branches', t => {
const result = getReleasesToAdd({
branch: {
name: 'master',
@@ -177,7 +177,7 @@ test('Return releases sorted by ascending order', t => {
]);
});
test('no lastRelease', t => {
test('No lastRelease', t => {
const result = getReleasesToAdd({
branch: {name: 'master', tags: [{gitTag: 'v1.0.0@next', version: '1.0.0', channel: 'next', gitHead: '111'}]},
branches: [{name: 'master'}, {name: 'next', channel: 'next'}],
@@ -248,3 +248,66 @@ test('Ignore pre-release versions', t => {
},
]);
});
test('Exclude versions merged from release to maintenance branch if they have the same "channel"', t => {
const result = getReleasesToAdd({
branch: {
name: '2.x',
channel: 'latest',
type: 'maintenance',
mergeRange: '>=2.0.0 <3.0.0',
tags: [
{gitTag: 'v2.0.0', version: '2.0.0', gitHead: '111'},
{gitTag: 'v2.0.0', version: '2.0.0', gitHead: '111'},
{gitTag: 'v2.1.0', version: '2.1.0', gitHead: '222'},
{gitTag: 'v2.1.1', version: '2.1.1', gitHead: '333'},
{gitTag: 'v1.0.0', version: '1.0.0', gitHead: '444'},
{gitTag: 'v1.1.0', version: '1.1.0', gitHead: '555'},
],
},
branches: [{name: '2.x', channel: 'latest'}, {name: 'master', channel: 'latest'}],
options: {tagFormat: `v\${version}`},
});
t.deepEqual(result, []);
});
test('Exclude versions merged between release branches if they have the same "channel"', t => {
const result = getReleasesToAdd({
branch: {
name: 'master',
channel: 'latest',
tags: [
{gitTag: 'v1.0.0', channel: 'latest', version: '1.0.0', gitHead: '111'},
{gitTag: 'v1.1.0', channel: 'latest', version: '1.1.0', gitHead: '222'},
{gitTag: 'v2.0.0', channel: 'latest', version: '2.0.0', gitHead: '333'},
],
},
branches: [
{name: 'master', channel: 'latest'},
{name: 'next', channel: 'latest'},
{name: 'next-major', channel: 'latest'},
],
options: {tagFormat: `v\${version}`},
});
t.deepEqual(result, []);
});
test('Exclude versions merged between release branches if they all have "channel" set to "false"', t => {
const result = getReleasesToAdd({
branch: {
name: 'master',
channel: false,
tags: [
{gitTag: 'v1.0.0', version: '1.0.0', gitHead: '111'},
{gitTag: 'v1.1.0', version: '1.1.0', gitHead: '222'},
{gitTag: 'v2.0.0', version: '2.0.0', gitHead: '333'},
],
},
branches: [{name: 'master', channel: false}, {name: 'next', channel: false}, {name: 'next-major', channel: false}],
options: {tagFormat: `v\${version}`},
});
t.deepEqual(result, []);
});
+5 -5
View File
@@ -44,7 +44,7 @@ test('Throw error if the last commit sha cannot be found', async t => {
// Create a git repository, set the current working directory at the root of the repo
const {cwd} = await gitRepo();
await t.throws(getGitHead({cwd}), Error);
await t.throwsAsync(getGitHead({cwd}), Error);
});
test('Unshallow and fetch repository', async t => {
@@ -73,8 +73,8 @@ test('Do not throw error when unshallow a complete repository', async t => {
await gitCommits(['Second'], {cwd});
await gitPush(repositoryUrl, 'second-branch', {cwd});
await t.notThrows(fetch('master', {cwd}));
await t.notThrows(fetch('second-branch', {cwd}));
await t.notThrowsAsync(fetch('master', {cwd}));
await t.notThrowsAsync(fetch('second-branch', {cwd}));
});
test('Fetch all tags on a detached head repository', async t => {
@@ -108,7 +108,7 @@ test('Verify if the commit `sha` is in the direct history of the current branch'
t.true(await isRefInHistory(commits[0].hash, 'master', false, {cwd}));
t.falsy(await isRefInHistory(otherCommits[0].hash, 'master', false, {cwd}));
t.falsy(await isRefInHistory(otherCommits[0].hash, 'missing-branch', false, {cwd}));
await t.throws(isRefInHistory('non-existant-sha', 'master', false, {cwd}));
await t.throwsAsync(isRefInHistory('non-existant-sha', 'master', false, {cwd}));
});
test('Verify if a branch exists', async t => {
@@ -243,7 +243,7 @@ test('Return falsy for invalid tag names', async t => {
test('Throws error if obtaining the tags fails', async t => {
const cwd = tempy.directory();
await t.throws(getTags({cwd}));
await t.throwsAsync(getTags({cwd}));
});
test('Return "true" if repository is up to date', async t => {
+14 -12
View File
@@ -763,7 +763,7 @@ test('Call all "success" plugins even if one errors out', async t => {
'env-ci': () => ({isCi: true, branch: 'master', isPr: false}),
});
await t.throws(
await t.throwsAsync(
semanticRelease(options, {cwd, env: {}, stdout: new WritableStreamBuffer(), stderr: new WritableStreamBuffer()})
);
@@ -799,7 +799,7 @@ test('Log all "verifyConditions" errors', async t => {
'env-ci': () => ({isCi: true, branch: 'master', isPr: false}),
});
const errors = [
...(await t.throws(
...(await t.throwsAsync(
semanticRelease(options, {cwd, env: {}, stdout: new WritableStreamBuffer(), stderr: new WritableStreamBuffer()})
)),
];
@@ -844,7 +844,7 @@ test('Log all "verifyRelease" errors', async t => {
'env-ci': () => ({isCi: true, branch: 'master', isPr: false}),
});
const errors = [
...(await t.throws(
...(await t.throwsAsync(
semanticRelease(options, {cwd, env: {}, stdout: new WritableStreamBuffer(), stderr: new WritableStreamBuffer()})
)),
];
@@ -949,7 +949,7 @@ test('Dry-run skips fail', async t => {
'env-ci': () => ({isCi: true, branch: 'master', isPr: false}),
});
const errors = [
...(await t.throws(
...(await t.throwsAsync(
semanticRelease(options, {cwd, env: {}, stdout: new WritableStreamBuffer(), stderr: new WritableStreamBuffer()})
)),
];
@@ -1268,7 +1268,7 @@ test('Throws "EINVALIDNEXTVERSION" if next release is out of range of the curren
'env-ci': () => ({isCi: true, branch: '1.x', isPr: false}),
});
const error = await t.throws(
const error = await t.throwsAsync(
semanticRelease(options, {cwd, env: {}, stdout: {write: () => {}}, stderr: {write: () => {}}})
);
@@ -1319,7 +1319,7 @@ test('Throws "EINVALIDNEXTVERSION" if next release is out of range of the curren
'env-ci': () => ({isCi: true, branch: 'master', isPr: false}),
});
const error = await t.throws(
const error = await t.throwsAsync(
semanticRelease(options, {cwd, env: {}, stdout: {write: () => {}}, stderr: {write: () => {}}})
);
@@ -1388,7 +1388,9 @@ test('Throws "EINVALIDMAINTENANCEMERGE" if merge an out of range release in a ma
'env-ci': () => ({isCi: true, branch: '1.1.x', isPr: false}),
});
const errors = [
...(await t.throws(semanticRelease(options, {cwd, env: {}, stdout: {write: () => {}}, stderr: {write: () => {}}}))),
...(await t.throwsAsync(
semanticRelease(options, {cwd, env: {}, stdout: {write: () => {}}, stderr: {write: () => {}}})
)),
];
t.is(addChannel.callCount, 1);
@@ -1587,7 +1589,7 @@ test('Log both plugins errors and errors thrown by "fail" plugin', async t => {
'env-ci': () => ({isCi: true, branch: 'master', isPr: false}),
});
await t.throws(
await t.throwsAsync(
semanticRelease(options, {cwd, env: {}, stdout: new WritableStreamBuffer(), stderr: new WritableStreamBuffer()})
);
@@ -1612,7 +1614,7 @@ test('Call "fail" only if a plugin returns a SemanticReleaseError', async t => {
'env-ci': () => ({isCi: true, branch: 'master', isPr: false}),
});
await t.throws(
await t.throwsAsync(
semanticRelease(options, {cwd, env: {}, stdout: new WritableStreamBuffer(), stderr: new WritableStreamBuffer()})
);
@@ -1629,7 +1631,7 @@ test('Throw SemanticReleaseError if repositoryUrl is not set and cannot be found
'env-ci': () => ({isCi: true, branch: 'master', isPr: false}),
});
const errors = [
...(await t.throws(
...(await t.throwsAsync(
semanticRelease({}, {cwd, env: {}, stdout: new WritableStreamBuffer(), stderr: new WritableStreamBuffer()})
)),
];
@@ -1668,7 +1670,7 @@ test('Throw an Error if plugin returns an unexpected value', async t => {
'./lib/get-logger': () => t.context.logger,
'env-ci': () => ({isCi: true, branch: 'master', isPr: false}),
});
const error = await t.throws(
const error = await t.throwsAsync(
semanticRelease(options, {cwd, env: {}, stdout: new WritableStreamBuffer(), stderr: new WritableStreamBuffer()}),
Error
);
@@ -1698,7 +1700,7 @@ test('Hide sensitive information passed to "fail" plugin', async t => {
'./lib/get-logger': () => t.context.logger,
'env-ci': () => ({isCi: true, branch: 'master', isPr: false}),
});
await t.throws(
await t.throwsAsync(
semanticRelease(options, {cwd, env, stdout: new WritableStreamBuffer(), stderr: new WritableStreamBuffer()}),
Error
);
+3 -3
View File
@@ -317,7 +317,7 @@ test('Exit with 1 if a plugin is not found', async t => {
release: {analyzeCommits: 'non-existing-path', success: false, fail: false},
});
const {code, stderr} = await t.throws(execa(cli, [], {env, cwd}));
const {code, stderr} = await t.throwsAsync(execa(cli, [], {env, cwd}));
t.is(code, 1);
t.regex(stderr, /Cannot find module/);
});
@@ -335,7 +335,7 @@ test('Exit with 1 if a shareable config is not found', async t => {
release: {extends: 'non-existing-path', success: false, fail: false},
});
const {code, stderr} = await t.throws(execa(cli, [], {env, cwd}));
const {code, stderr} = await t.throwsAsync(execa(cli, [], {env, cwd}));
t.is(code, 1);
t.regex(stderr, /Cannot find module/);
});
@@ -356,7 +356,7 @@ test('Exit with 1 if a shareable config reference a not found plugin', async t =
});
await writeJson(path.resolve(cwd, 'shareable.json'), shareable);
const {code, stderr} = await t.throws(execa(cli, [], {env, cwd}));
const {code, stderr} = await t.throwsAsync(execa(cli, [], {env, cwd}));
t.is(code, 1);
t.regex(stderr, /Cannot find module/);
});
+6 -6
View File
@@ -62,7 +62,7 @@ test('Wrap plugin in a function that add the "pluginName" to the error"', async
'./plugin-error': './test/fixtures',
});
const error = await t.throws(plugin({options: {}}));
const error = await t.throwsAsync(plugin({options: {}}));
t.is(error.pluginName, './plugin-error');
});
@@ -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.throws(plugin({options: {}})))];
const errors = [...(await t.throwsAsync(plugin({options: {}})))];
for (const error of errors) {
t.is(error.pluginName, './plugin-errors');
}
@@ -107,7 +107,7 @@ test('Wrap "analyzeCommits" plugin in a function that validate the output of the
{}
);
const error = await t.throws(plugin({options: {}}));
const error = await t.throwsAsync(plugin({options: {}}));
t.is(error.code, 'EANALYZECOMMITSOUTPUT');
t.is(error.name, 'SemanticReleaseError');
@@ -125,7 +125,7 @@ test('Wrap "generateNotes" plugin in a function that validate the output of the
{}
);
const error = await t.throws(plugin({options: {}}));
const error = await t.throwsAsync(plugin({options: {}}));
t.is(error.code, 'EGENERATENOTESOUTPUT');
t.is(error.name, 'SemanticReleaseError');
@@ -143,7 +143,7 @@ test('Wrap "publish" plugin in a function that validate the output of the plugin
{}
);
const error = await t.throws(plugin({options: {}}));
const error = await t.throwsAsync(plugin({options: {}}));
t.is(error.code, 'EPUBLISHOUTPUT');
t.is(error.name, 'SemanticReleaseError');
@@ -161,7 +161,7 @@ test('Wrap "addChannel" plugin in a function that validate the output of the plu
{}
);
const error = await t.throws(plugin({options: {}}));
const error = await t.throwsAsync(plugin({options: {}}));
t.is(error.code, 'EADDCHANNELOUTPUT');
t.is(error.name, 'SemanticReleaseError');
+5 -5
View File
@@ -83,7 +83,7 @@ test('Stop execution and throw error if a step rejects', async t => {
const step2 = stub().rejects(new Error('test error'));
const step3 = stub().resolves(3);
const error = await t.throws(pipeline([step1, step2, step3])(0), Error);
const error = await t.throwsAsync(pipeline([step1, step2, step3])(0), Error);
t.is(error.message, 'test error');
t.true(step1.calledWith(0));
t.true(step2.calledWith(0));
@@ -98,7 +98,7 @@ 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.throws(pipeline([step1, step2, step3])(0));
const errors = await t.throwsAsync(pipeline([step1, step2, step3])(0));
t.deepEqual([...errors], [error1, error2]);
t.true(step1.calledWith(0));
@@ -113,7 +113,7 @@ test('Execute all even if a Promise rejects', async t => {
const step2 = stub().rejects(error1);
const step3 = stub().rejects(error2);
const errors = await t.throws(pipeline([step1, step2, step3], {settleAll: true})(0));
const errors = await t.throwsAsync(pipeline([step1, step2, step3], {settleAll: true})(0));
t.deepEqual([...errors], [error1, error2]);
t.true(step1.calledWith(0));
@@ -129,7 +129,7 @@ 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.throws(pipeline([step1, step2], {settleAll: true})(0));
const errors = await t.throwsAsync(pipeline([step1, step2], {settleAll: true})(0));
t.deepEqual([...errors], [error1, error2, error3, error4]);
t.true(step1.calledWith(0));
@@ -145,7 +145,7 @@ test('Execute each function in series passing a transformed input even if a step
const step4 = stub().resolves(4);
const getNextInput = (prevResult, result) => prevResult + result;
const errors = await t.throws(pipeline([step1, step2, step3, step4], {settleAll: true, getNextInput})(0));
const errors = await t.throwsAsync(pipeline([step1, step2, step3, step4], {settleAll: true, getNextInput})(0));
t.deepEqual([...errors], [error2, error3]);
t.true(step1.calledWith(0));
+2
View File
@@ -173,6 +173,8 @@ test('getRange', t => {
test('makeTag', t => {
t.is(makeTag(`v\${version}`, '1.0.0'), 'v1.0.0');
t.is(makeTag(`v\${version}`, '1.0.0', false), 'v1.0.0');
t.is(makeTag(`v\${version}`, '1.0.0', null), 'v1.0.0');
t.is(makeTag(`v\${version}`, '1.0.0', 'next'), 'v1.0.0@next');
t.is(makeTag(`v\${version}@test`, '1.0.0', 'next'), 'v1.0.0@next@test');
});
+7 -7
View File
@@ -7,7 +7,7 @@ test('Throw a AggregateError', async t => {
const {cwd} = await gitRepo();
const options = {branches: [{name: 'master'}, {name: ''}]};
const errors = [...(await t.throws(verify({cwd, options})))];
const errors = [...(await t.throwsAsync(verify({cwd, options})))];
t.is(errors[0].name, 'SemanticReleaseError');
t.is(errors[0].code, 'ENOREPOURL');
@@ -31,7 +31,7 @@ test('Throw a SemanticReleaseError if does not run on a git repository', async t
const cwd = tempy.directory();
const options = {branches: []};
const errors = [...(await t.throws(verify({cwd, options})))];
const errors = [...(await t.throwsAsync(verify({cwd, options})))];
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.throws(verify({cwd, options})))];
const errors = [...(await t.throwsAsync(verify({cwd, options})))];
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.throws(verify({cwd, options})))];
const errors = [...(await t.throwsAsync(verify({cwd, options})))];
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.throws(verify({cwd, options})))];
const errors = [...(await t.throwsAsync(verify({cwd, options})))];
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.throws(verify({cwd, options})))];
const errors = [...(await t.throwsAsync(verify({cwd, options})))];
t.is(errors[0].name, 'SemanticReleaseError');
t.is(errors[0].code, 'EINVALIDBRANCH');
@@ -113,5 +113,5 @@ test('Return "true" if all verification pass', async t => {
const {cwd, repositoryUrl} = await gitRepo(true);
const options = {repositoryUrl, tagFormat: `v\${version}`, branches: [{name: 'master'}]};
await t.notThrows(verify({cwd, options}));
await t.notThrowsAsync(verify({cwd, options}));
});