feat: use Git notes to store the channels on which a version has been released

BREAKING CHANGE: this feature change the way semantic-release keep track of the channels on which a version has been released.
It now use a JSON object stored in a [Git note](https://git-scm.com/docs/git-notes) instead of Git tags formatted as v{version}@{channel}.

The tags formatted as v{version}@{channel} will now be ignored. If you have made releases with v16.0.0 on branches other than the default one you will have to update your repository.

The changes to make consist in:
- Finding all the versions that have been released on a branch other than the default one by searching for all tags formatted v{version}@{channel}
- For each of those version:
  - Create a tag without the {@channel} if none doesn't already exists
  - Add a Git note to the tag without the {@channel} containing the channels on which the version was released formatted as `{"channels":["channel1","channel2"]}` and using `null` for the default channel (for example.`{"channels":[null,"channel1","channel2"]}`)
  - Push the tags and notes
  - Update the GitHub releases that refer to a tag formatted as v{version}@{channel} to use the tag without it
  - Delete the tags formatted as v{version}@{channel}
This commit is contained in:
Pierre Vanduynslager
2019-12-02 23:38:40 -05:00
parent 2caafbaa2b
commit b2c1b2c670
15 changed files with 386 additions and 210 deletions
+13 -18
View File
@@ -1,34 +1,29 @@
const {template, escapeRegExp, flatMap} = require('lodash');
const {template, escapeRegExp} = require('lodash');
const semver = require('semver');
const pReduce = require('p-reduce');
const debug = require('debug')('semantic-release:get-tags');
const {getTags} = require('../../lib/git');
const {getTags, getNote} = require('../../lib/git');
module.exports = async ({cwd, env, options: {tagFormat}}, branches) => {
// Generate a regex to parse tags formatted with `tagFormat`
// by replacing the `version` variable in the template by `(.+)`.
// The `tagFormat` is compiled with space as the `version` as it's an invalid tag character,
// so it's guaranteed to no be present in the `tagFormat`.
const tagRegexp = `^${escapeRegExp(template(tagFormat)({version: ' '})).replace(' ', '(.[^@]+)@?(.+)?')}`;
const tagRegexp = `^${escapeRegExp(template(tagFormat)({version: ' '})).replace(' ', '(.+)')}`;
return pReduce(
branches,
async (branches, branch) => {
const versions = (await getTags(branch.name, {cwd, env})).reduce((versions, tag) => {
const [, version, channel] = tag.match(tagRegexp) || [];
if (version && semver.valid(semver.clean(version))) {
return {
...versions,
[version]: versions[version]
? {...versions[version], channels: [...versions[version].channels, channel]}
: {gitTag: tag, version, channels: [channel]},
};
}
return versions;
}, {});
const branchTags = flatMap(versions);
const branchTags = await pReduce(
await getTags(branch.name, {cwd, env}),
async (branchTags, tag) => {
const [, version] = tag.match(tagRegexp) || [];
return version && semver.valid(semver.clean(version))
? [...branchTags, {gitTag: tag, version, channels: (await getNote(tag, {cwd, env})).channels || [null]}]
: branchTags;
},
[]
);
debug('found tags for branch %s: %o', branch.name, branchTags);
return [...branches, {...branch, tags: branchTags}];
+3 -1
View File
@@ -3,7 +3,7 @@ const AggregateError = require('aggregate-error');
const pEachSeries = require('p-each-series');
const DEFINITIONS = require('../definitions/branches');
const getError = require('../get-error');
const {fetch, verifyBranchName} = require('../git');
const {fetch, fetchNotes, verifyBranchName} = require('../git');
const expand = require('./expand');
const getTags = require('./get-tags');
const normalize = require('./normalize');
@@ -21,6 +21,8 @@ module.exports = async (repositoryUrl, context) => {
await fetch(repositoryUrl, name, {cwd, env});
});
await fetchNotes(repositoryUrl, {cwd, env});
const branches = await getTags(context, remoteBranches);
const errors = [];