feat: add tagFormat option to customize Git tag name
This commit is contained in:
@@ -46,6 +46,7 @@ module.exports = async (opts, logger) => {
|
||||
options = {
|
||||
branch: 'master',
|
||||
repositoryUrl: (await pkgRepoUrl()) || (await repoUrl()),
|
||||
tagFormat: `v\${version}`,
|
||||
// Remove `null` and `undefined` options so they can be replaced with default ones
|
||||
...pickBy(options, option => !isUndefined(option) && !isNull(option)),
|
||||
};
|
||||
|
||||
+24
-8
@@ -1,3 +1,4 @@
|
||||
const {escapeRegExp, template} = require('lodash');
|
||||
const semver = require('semver');
|
||||
const pLocate = require('p-locate');
|
||||
const debug = require('debug')('semantic-release:get-last-release');
|
||||
@@ -15,21 +16,36 @@ const {gitTags, isRefInHistory, gitTagHead} = require('./git');
|
||||
* Determine the Git tag and version of the last tagged release.
|
||||
*
|
||||
* - Obtain all the tags referencing commits in the current branch history
|
||||
* - Filter out the ones that are not valid semantic version
|
||||
* - Sort the tags
|
||||
* - Retrive the highest tag
|
||||
* - Filter out the ones that are not valid semantic version or doesn't match the `tagFormat`
|
||||
* - Sort the versions
|
||||
* - Retrive the highest version
|
||||
*
|
||||
* @param {Object} logger Global logger.
|
||||
* @return {Promise<LastRelease>} The last tagged release or `undefined` if none is found.
|
||||
*/
|
||||
module.exports = async logger => {
|
||||
const tags = (await gitTags()).filter(tag => semver.valid(semver.clean(tag))).sort(semver.rcompare);
|
||||
module.exports = async (tagFormat, logger) => {
|
||||
// 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 tags = (await gitTags())
|
||||
.map(tag => {
|
||||
return {gitTag: tag, version: (tag.match(tagRegexp) || new Array(2))[1]};
|
||||
})
|
||||
.filter(tag => tag.version && semver.valid(semver.clean(tag.version)))
|
||||
.sort((a, b) => semver.rcompare(a.version, b.version));
|
||||
|
||||
debug('found tags: %o', tags);
|
||||
|
||||
if (tags.length > 0) {
|
||||
const gitTag = await pLocate(tags, tag => isRefInHistory(tag), {concurrency: 1, preserveOrder: true});
|
||||
logger.log('Found git tag version %s', gitTag);
|
||||
return {gitTag, gitHead: await gitTagHead(gitTag), version: semver.valid(semver.clean(gitTag))};
|
||||
const {gitTag, version} = await pLocate(tags, tag => isRefInHistory(tag.gitTag), {
|
||||
concurrency: 1,
|
||||
preserveOrder: true,
|
||||
});
|
||||
logger.log('Found git tag %s associated with version %s', gitTag, version);
|
||||
|
||||
return {gitHead: await gitTagHead(gitTag), gitTag, version};
|
||||
}
|
||||
|
||||
logger.log('No git tag version found');
|
||||
|
||||
+12
@@ -112,6 +112,17 @@ async function deleteTag(origin, tagName) {
|
||||
debug('delete remote tag', shell);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a tag name is a valid Git reference.
|
||||
*
|
||||
* @method verifyTagName
|
||||
* @param {string} tagName the tag name to verify.
|
||||
* @return {boolean} `true` if valid, `false` otherwise.
|
||||
*/
|
||||
async function verifyTagName(tagName) {
|
||||
return (await execa('git', ['check-ref-format', `refs/tags/${tagName}`], {reject: false})).code === 0;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
gitTagHead,
|
||||
gitTags,
|
||||
@@ -124,4 +135,5 @@ module.exports = {
|
||||
tag,
|
||||
push,
|
||||
deleteTag,
|
||||
verifyTagName,
|
||||
};
|
||||
|
||||
+21
-1
@@ -1,6 +1,7 @@
|
||||
const {template} = require('lodash');
|
||||
const SemanticReleaseError = require('@semantic-release/error');
|
||||
const AggregateError = require('aggregate-error');
|
||||
const {isGitRepo, verifyAuth} = require('./git');
|
||||
const {isGitRepo, verifyAuth, verifyTagName} = require('./git');
|
||||
|
||||
module.exports = async (options, branch, logger) => {
|
||||
const errors = [];
|
||||
@@ -21,6 +22,25 @@ module.exports = async (options, branch, logger) => {
|
||||
);
|
||||
}
|
||||
|
||||
// Verify that compiling the `tagFormat` produce a valid Git tag
|
||||
if (!await verifyTagName(template(options.tagFormat)({version: '0.0.0'}))) {
|
||||
errors.push(
|
||||
new SemanticReleaseError('The tagFormat template must compile to a valid Git tag format', 'EINVALIDTAGFORMAT')
|
||||
);
|
||||
}
|
||||
|
||||
// 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(options.tagFormat)({version: ' '}).match(/ /g) || []).length !== 1) {
|
||||
errors.push(
|
||||
new SemanticReleaseError(
|
||||
`The tagFormat template must contain the variable "\${version}" exactly once`,
|
||||
'ETAGNOVERSION'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
throw new AggregateError(errors);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user