Skip to content

Commit ddaf44c

Browse files
committed
fix: support older git versions
Check if a tag is in the branch history with `git merge-base` as the `--merge` options of the `git tag` command is available only since `git` version `2.7.0`.
1 parent 79cc028 commit ddaf44c

File tree

5 files changed

+42
-32
lines changed

5 files changed

+42
-32
lines changed

lib/get-last-release.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const semver = require('semver');
2+
const pLocate = require('p-locate');
23
const debug = require('debug')('semantic-release:git');
3-
const {unshallow, gitTags} = require('./git');
4+
const {unshallow, gitTags, isRefInHistory} = require('./git');
45

56
/**
67
* Last release.
@@ -25,11 +26,11 @@ const {unshallow, gitTags} = require('./git');
2526
module.exports = async logger => {
2627
// Unshallow the repo in order to get all the tags
2728
await unshallow();
28-
const tags = (await gitTags()).filter(tag => semver.valid(semver.clean(tag))).sort(semver.compare);
29+
const tags = (await gitTags()).filter(tag => semver.valid(semver.clean(tag))).sort((a, b) => -semver.compare(a, b));
2930
debug('found tags: %o', tags);
3031

3132
if (tags.length > 0) {
32-
const tag = tags[tags.length - 1];
33+
const tag = await pLocate(tags, tag => isRefInHistory(tag), {concurrency: 1, preserveOrder: true});
3334
logger.log('Found git tag version %s', tag);
3435
return {gitHead: tag, version: semver.valid(semver.clean(tag))};
3536
}

lib/git.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ const SemanticReleaseError = require('@semantic-release/error');
33
const debug = require('debug')('semantic-release:git');
44

55
/**
6-
* @return {Array<String>} List of git tags in the history of the current branch.
6+
* @return {Array<String>} List of git tags.
77
* @throws {Error} If the `git` command fails.
88
*/
99
async function gitTags() {
1010
try {
11-
return (await execa.stdout('git', ['tag', '--merge', 'HEAD']))
11+
return (await execa.stdout('git', ['tag']))
1212
.split('\n')
1313
.map(tag => tag.trim())
1414
.filter(tag => Boolean(tag));
@@ -18,6 +18,17 @@ async function gitTags() {
1818
}
1919
}
2020

21+
/**
22+
* Verify if the `ref` is in the direct history of the current branch.
23+
*
24+
* @param {string} ref The reference to look for.
25+
*
26+
* @return {boolean} `true` if the reference is in the history of the current branch, `false` otherwise.
27+
*/
28+
async function isRefInHistory(ref) {
29+
return (await execa('git', ['merge-base', '--is-ancestor', ref, 'HEAD'], {reject: false})).code === 0;
30+
}
31+
2132
/**
2233
* Unshallow the git repository (retriving every commits and tags).
2334
*/
@@ -168,4 +179,5 @@ module.exports = {
168179
commit,
169180
tag,
170181
push,
182+
isRefInHistory,
171183
};

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"git-url-parse": "^7.0.1",
2525
"lodash": "^4.17.4",
2626
"micromatch": "^3.1.4",
27+
"p-locate": "^2.0.0",
2728
"p-reduce": "^1.0.0",
2829
"semver": "^5.4.1"
2930
},

test/get-last-release.test.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import test from 'ava';
22
import {stub} from 'sinon';
33
import getLastRelease from '../lib/get-last-release';
4-
import {gitRepo, gitCommit, gitTagVersion, gitShallowClone} from './helpers/git-utils';
4+
import {gitRepo, gitCommit, gitTagVersion, gitShallowClone, gitCheckout} from './helpers/git-utils';
55

66
// Save the current working diretory
77
const cwd = process.cwd();
@@ -34,6 +34,28 @@ test.serial('Get the highest valid tag', async t => {
3434
t.deepEqual(t.context.log.args[0], ['Found git tag version %s', 'v2.0.0']);
3535
});
3636

37+
test.serial('Get the highest tag in the history of the current branch', async t => {
38+
// Create a git repository, set the current working directory at the root of the repo
39+
await gitRepo();
40+
// Add commit to the master branch
41+
await gitCommit('First');
42+
// Create the tag corresponding to version 1.0.0
43+
// Create the new branch 'other-branch' from master
44+
await gitCheckout('other-branch');
45+
// Add commit to the 'other-branch' branch
46+
await gitCommit('Second');
47+
// Create the tag corresponding to version 3.0.0
48+
await gitTagVersion('v3.0.0');
49+
// Checkout master
50+
await gitCheckout('master', false);
51+
// Add another commit to the master branch
52+
await gitCommit('Third');
53+
// Create the tag corresponding to version 2.0.0
54+
await gitTagVersion('v2.0.0');
55+
56+
t.deepEqual(await getLastRelease(t.context.logger), {gitHead: 'v2.0.0', version: '2.0.0'});
57+
});
58+
3759
test.serial('Return "undefined" if no valid tag is found', async t => {
3860
// Create a git repository, set the current working directory at the root of the repo
3961
await gitRepo();

test/git.test.js

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ import {unshallow, gitTags, add, getModifiedFiles, config, commit, tag, gitHead,
55
import {
66
gitRepo,
77
gitCommit,
8-
gitCheckout,
9-
gitTagVersion,
108
gitShallowClone,
119
gitGetCommit,
1210
gitGetConfig,
@@ -49,30 +47,6 @@ test.serial('Do not throw error when unshallow a complete repository', async t =
4947
await t.notThrows(unshallow());
5048
});
5149

52-
test.serial('Get the tags in the history of the current branch', async t => {
53-
// Create a git repository, set the current working directory at the root of the repo
54-
await gitRepo();
55-
// Add commit to the master branch
56-
await gitCommit('First');
57-
// Create the tag corresponding to version 1.0.0
58-
await gitTagVersion('v1.0.0');
59-
// Create the new branch 'other-branch' from master
60-
await gitCheckout('other-branch');
61-
// Add commit to the 'other-branch' branch
62-
await gitCommit('Second');
63-
// Create the tag corresponding to version 2.0.0
64-
await gitTagVersion('v2.0.0');
65-
// Checkout master
66-
await gitCheckout('master', false);
67-
// Add another commit to the master branch
68-
await gitCommit('Third');
69-
// Create the tag corresponding to version 3.0.0
70-
await gitTagVersion('v3.0.0');
71-
72-
// Verify the git tag v2.0.0 is not returned as it is not accessible on the current branch
73-
t.deepEqual(await gitTags(), ['v1.0.0', 'v3.0.0']);
74-
});
75-
7650
test.serial('Throws error if obtaining the tags fails', async t => {
7751
const dir = tempy.directory();
7852
process.chdir(dir);

0 commit comments

Comments
 (0)