feat: support multiple branches and distribution channels
- Allow to configure multiple branches to release from - Allow to define a distribution channel associated with each branch - Manage the availability on distribution channels based on git merges - Support regular releases, maintenance releases and pre-releases - Add the `addChannel` plugin step to make an existing release available on a different distribution channel BREAKING CHANGE: the `branch` option has been removed in favor of `branches` The new `branches` option expect either an Array or a single branch definition. To migrate your configuration: - If you want to publish package from multiple branches, please the configuration documentation - If you use the default configuration and want to publish only from `master`: nothing to change - If you use the `branch` configuration and want to publish only from one branch: replace `branch` by `branches` (`"branch": "my-release-branch"` => `"branches": "my-release-branch"`)
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
const {isUndefined, uniqBy} = require('lodash');
|
||||
const semver = require('semver');
|
||||
const {isMaintenanceRange} = require('../utils');
|
||||
|
||||
const maintenance = {
|
||||
filter: ({name, range}) => !isUndefined(range) || isMaintenanceRange(name),
|
||||
branchValidator: ({range}) => (isUndefined(range) ? true : isMaintenanceRange(range)),
|
||||
branchesValidator: branches => uniqBy(branches, ({range}) => semver.validRange(range)).length === branches.length,
|
||||
};
|
||||
|
||||
const prerelease = {
|
||||
filter: ({prerelease}) => !isUndefined(prerelease),
|
||||
branchValidator: ({name, prerelease}) =>
|
||||
Boolean(prerelease) && Boolean(semver.valid(`1.0.0-${prerelease === true ? name : prerelease}.1`)),
|
||||
branchesValidator: branches => uniqBy(branches, 'prerelease').length === branches.length,
|
||||
};
|
||||
|
||||
const release = {
|
||||
filter: branch => !maintenance.filter(branch) && !prerelease.filter(branch),
|
||||
branchesValidator: branches => branches.length <= 3 && branches.length > 0,
|
||||
};
|
||||
|
||||
module.exports = {maintenance, prerelease, release};
|
||||
@@ -1,7 +1,9 @@
|
||||
const RELEASE_TYPE = ['prerelease', 'prepatch', 'patch', 'preminor', 'minor', 'premajor', 'major'];
|
||||
const RELEASE_TYPE = ['patch', 'minor', 'major'];
|
||||
|
||||
const FIRST_RELEASE = '1.0.0';
|
||||
|
||||
const FIRSTPRERELEASE = '1';
|
||||
|
||||
const COMMIT_NAME = 'semantic-release-bot';
|
||||
|
||||
const COMMIT_EMAIL = 'semantic-release-bot@martynus.net';
|
||||
@@ -15,6 +17,7 @@ const SECRET_MIN_SIZE = 5;
|
||||
module.exports = {
|
||||
RELEASE_TYPE,
|
||||
FIRST_RELEASE,
|
||||
FIRSTPRERELEASE,
|
||||
COMMIT_NAME,
|
||||
COMMIT_EMAIL,
|
||||
RELEASE_NOTES_SEPARATOR,
|
||||
|
||||
+118
-1
@@ -1,12 +1,14 @@
|
||||
const url = require('url');
|
||||
const {inspect} = require('util');
|
||||
const {toLower, isString} = require('lodash');
|
||||
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 stringify = obj => (isString(obj) ? obj : inspect(obj, {breakLength: Infinity, depth: 2, maxArrayLength: 5}));
|
||||
const linkify = file => `${homepage}/blob/master/${file}`;
|
||||
const wordsList = words =>
|
||||
`${words.slice(0, -1).join(', ')}${words.length > 1 ? ` or ${words[words.length - 1]}` : trim(words[0])}`;
|
||||
|
||||
module.exports = {
|
||||
ENOGITREPO: ({cwd}) => ({
|
||||
@@ -125,4 +127,119 @@ We recommend to report the issue to the \`${pluginName}\` authors, providing the
|
||||
'docs/developer-guide/plugin.md'
|
||||
)})`,
|
||||
}),
|
||||
EADDCHANNELOUTPUT: ({result, pluginName}) => ({
|
||||
message: 'A `addChannel` plugin returned an invalid value. It must return an `Object`.',
|
||||
details: `The \`addChannel\` plugins must return an \`Object\`.
|
||||
|
||||
The \`addChannel\` function of the \`${pluginName}\` returned \`${stringify(result)}\` instead.
|
||||
|
||||
We recommend to report the issue to the \`${pluginName}\` authors, providing the following informations:
|
||||
- The **semantic-release** version: \`${pkg.version}\`
|
||||
- The **semantic-release** logs from your CI job
|
||||
- The value returned by the plugin: \`${stringify(result)}\`
|
||||
- A link to the **semantic-release** plugin developer guide: [${linkify('docs/developer-guide/plugin.md')}](${linkify(
|
||||
'docs/developer-guide/plugin.md'
|
||||
)})`,
|
||||
}),
|
||||
EINVALIDBRANCH: ({branch}) => ({
|
||||
message: 'A branch is invalid in the `branches` configuration.',
|
||||
details: `Each branch in the [branches configuration](${linkify(
|
||||
'docs/usage/configuration.md#branches'
|
||||
)}) must be either a string, a regexp or an object with a \`name\` property.
|
||||
|
||||
Your configuration for the problematic branch is \`${stringify(branch)}\`.`,
|
||||
}),
|
||||
EINVALIDBRANCHNAME: ({branch}) => ({
|
||||
message: 'A branch name is invalid in the `branches` configuration.',
|
||||
details: `Each branch in the [branches configuration](${linkify(
|
||||
'docs/usage/configuration.md#branches'
|
||||
)}) must be a [valid Git reference](https://git-scm.com/docs/git-check-ref-format#_description).
|
||||
|
||||
Your configuration for the problematic branch is \`${stringify(branch)}\`.`,
|
||||
}),
|
||||
EDUPLICATEBRANCHES: ({duplicates}) => ({
|
||||
message: 'The `branches` configuration has duplicate branches.',
|
||||
details: `Each branch in the [branches configuration](${linkify(
|
||||
'docs/usage/configuration.md#branches'
|
||||
)}) must havea unique name.
|
||||
|
||||
Your configuration contains duplicates for the following branch names: \`${stringify(duplicates)}\`.`,
|
||||
}),
|
||||
EMAINTENANCEBRANCH: ({branch}) => ({
|
||||
message: 'A maintenance branch is invalid in the `branches` configuration.',
|
||||
details: `Each maintenance branch in the [branches configuration](${linkify(
|
||||
'docs/usage/configuration.md#branches'
|
||||
)}) must have a \`range\` property formatted like \`N.x\`, \`N.x.x\` or \`N.N.x\` (\`N\` is a number).
|
||||
|
||||
Your configuration for the problematic branch is \`${stringify(branch)}\`.`,
|
||||
}),
|
||||
EMAINTENANCEBRANCHES: ({branches}) => ({
|
||||
message: 'The maintenance branches are invalid in the `branches` configuration.',
|
||||
details: `Each maintenance branch in the [branches configuration](${linkify(
|
||||
'docs/usage/configuration.md#branches'
|
||||
)}) must have a unique \`range\` property.
|
||||
|
||||
Your configuration for the problematic branches is \`${stringify(branches)}\`.`,
|
||||
}),
|
||||
ERELEASEBRANCHES: ({branches}) => ({
|
||||
message: 'The release branches are invalid in the `branches` configuration.',
|
||||
details: `A minimum of 1 and a maximum of 3 release branches are required in the [branches configuration](${linkify(
|
||||
'docs/usage/configuration.md#branches'
|
||||
)}).
|
||||
|
||||
Your configuration for the problematic branches is \`${stringify(branches)}\`.`,
|
||||
}),
|
||||
EPRERELEASEBRANCH: ({branch}) => ({
|
||||
message: 'A pre-release branch configuration is invalid in the `branches` configuration.',
|
||||
details: `Each pre-release branch in the [branches configuration](${linkify(
|
||||
'docs/usage/configuration.md#branches'
|
||||
)}) must have a \`prerelease\` property valid per the [Semantic Versioning Specification](https://semver.org/#spec-item-9). If the \`prerelease\` property is set to \`true\`, then the \`name\` property is used instead.
|
||||
|
||||
Your configuration for the problematic branch is \`${stringify(branch)}\`.`,
|
||||
}),
|
||||
EPRERELEASEBRANCHES: ({branches}) => ({
|
||||
message: 'The pre-release branches are invalid in the `branches` configuration.',
|
||||
details: `Each pre-release branch in the [branches configuration](${linkify(
|
||||
'docs/usage/configuration.md#branches'
|
||||
)}) must have a unique \`prerelease\` property. If the \`prerelease\` property is set to \`true\`, then the \`name\` property is used instead.
|
||||
|
||||
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}\`.
|
||||
|
||||
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).
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
See the [workflow configuration documentation](${linkify('docs/usage/workflow-configuration.md')}) for more details.`,
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -71,8 +71,22 @@ module.exports = {
|
||||
pipelineConfig: () => ({
|
||||
// Add `nextRelease` and plugin properties to published release
|
||||
transform: (release, step, {nextRelease}) => ({
|
||||
...(isPlainObject(release) ? release : {}),
|
||||
...nextRelease,
|
||||
...(release || {}),
|
||||
...step,
|
||||
}),
|
||||
}),
|
||||
},
|
||||
addChannel: {
|
||||
default: ['@semantic-release/npm', '@semantic-release/github'],
|
||||
required: false,
|
||||
dryRun: false,
|
||||
outputValidator: output => !output || isPlainObject(output),
|
||||
pipelineConfig: () => ({
|
||||
// Add `nextRelease` and plugin properties to published release
|
||||
transform: (release, step, {nextRelease}) => ({
|
||||
...nextRelease,
|
||||
...(release || {}),
|
||||
...step,
|
||||
}),
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user