diff --git a/package.json b/package.json index ff98fc53..03e3472c 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,9 @@ "debug": "^3.1.0", "execa": "^0.8.0", "fs-extra": "^4.0.2", + "get-stream": "^3.0.0", "git-head": "^1.2.1", + "git-log-parser": "^1.2.0", "github": "^12.0.0", "lodash": "^4.0.0", "marked": "^0.3.6", @@ -52,7 +54,6 @@ "eslint-plugin-prettier": "^2.3.0", "eslint-plugin-promise": "^3.5.0", "eslint-plugin-standard": "^3.0.1", - "get-stream": "^3.0.0", "mockserver-client": "^1.0.16", "nock": "^9.0.2", "npm-registry-couchapp": "^2.6.12", diff --git a/src/lib/get-commits.js b/src/lib/get-commits.js index 86110090..b2229a99 100644 --- a/src/lib/get-commits.js +++ b/src/lib/get-commits.js @@ -1,4 +1,6 @@ const execa = require('execa'); +const gitLogParser = require('git-log-parser'); +const getStream = require('get-stream'); const debug = require('debug')('semantic-release:get-commits'); const getVersionHead = require('./get-version-head'); const {debugShell} = require('./debug'); @@ -65,28 +67,17 @@ module.exports = async ({version, gitHead}, branch) => { debugShell('Unshallow repo', shell, debug); } - try { - const shell = await execa('git', [ - 'log', - '--format=format:%H==SPLIT==%B==END==', - `${gitHead ? gitHead + '..' : ''}HEAD`, - ]); - debugShell('Get commits', shell, debug); - const commits = shell.stdout - .split('==END==') - .filter(raw => !!raw.trim()) - .map(raw => { - const [hash, message] = raw.trim().split('==SPLIT=='); - return {hash, message}; - }); - logger.log('Found %s commits since last release', commits.length); - debug('Parsed commits: %o', commits); - return {commits, lastRelease: {version, gitHead}}; - } catch (err) { - debug(err); - logger.log('Found no commit since last release'); - return {commits: [], lastRelease: {version, gitHead}}; - } + Object.assign(gitLogParser.fields, {hash: 'H', message: 'B', gitTags: 'd', committerDate: {key: 'ci', type: Date}}); + const commits = (await getStream.array( + gitLogParser.parse({_: `${gitHead ? gitHead + '..' : ''}HEAD`}) + )).map(commit => { + commit.message = commit.message.trim(); + commit.gitTags = commit.gitTags.trim(); + return commit; + }); + logger.log('Found %s commits since last release', commits.length); + debug('Parsed commits: %o', commits); + return {commits, lastRelease: {version, gitHead}}; }; function noGitHeadMessage(branch, version) { diff --git a/test/get-commits.test.js b/test/get-commits.test.js index f0e9bf0a..1d082d3b 100644 --- a/test/get-commits.test.js +++ b/test/get-commits.test.js @@ -42,14 +42,48 @@ test.serial('Get all commits when there is no last release', async t => { t.is(result.commits.length, 2); t.is(result.commits[0].hash.substring(0, 7), commits[0].hash); t.is(result.commits[0].message, commits[0].message); + t.truthy(result.commits[0].committerDate); + t.truthy(result.commits[0].author.name); + t.truthy(result.commits[0].committer.name); t.is(result.commits[1].hash.substring(0, 7), commits[1].hash); t.is(result.commits[1].message, commits[1].message); + t.truthy(result.commits[1].committerDate); + t.truthy(result.commits[1].author.name); + t.truthy(result.commits[1].committer.name); // Verify the last release is returned and updated t.truthy(result.lastRelease); t.falsy(result.lastRelease.gitHead); t.falsy(result.lastRelease.version); }); +test.serial('Get all commits with gitTags', async t => { + // Create a git repository, set the current working directory at the root of the repo + await gitRepo(); + // Add commits to the master branch + let commits = await gitCommits(['First']); + // Create the tag corresponding to version 1.0.0 + await gitTagVersion('v1.0.0'); + // Add new commits to the master branch + commits = (await gitCommits(['Second'])).concat(commits); + + // Retrieve the commits with the commits module + const result = await t.context.getCommits({}, 'master'); + // Verify the commits created and retrieved by the module are identical + t.is(result.commits.length, 2); + t.is(result.commits[0].hash.substring(0, 7), commits[0].hash); + t.is(result.commits[0].message, commits[0].message); + t.truthy(result.commits[0].committerDate); + t.truthy(result.commits[0].author.name); + t.truthy(result.commits[0].committer.name); + t.is(result.commits[0].gitTags, '(HEAD -> master)'); + t.is(result.commits[1].hash.substring(0, 7), commits[1].hash); + t.is(result.commits[1].message, commits[1].message); + t.truthy(result.commits[1].committerDate); + t.truthy(result.commits[1].author.name); + t.truthy(result.commits[1].committer.name); + t.is(result.commits[1].gitTags, '(tag: v1.0.0)'); +}); + test.serial('Get all commits when there is no last release, including the ones not in the shallow clone', async t => { // Create a git repository, set the current working directory at the root of the repo const repo = await gitRepo(); @@ -68,8 +102,14 @@ test.serial('Get all commits when there is no last release, including the ones n t.is(result.commits.length, 2); t.is(result.commits[0].hash.substring(0, 7), commits[0].hash); t.is(result.commits[0].message, commits[0].message); + t.truthy(result.commits[0].committerDate); + t.truthy(result.commits[0].author.name); + t.truthy(result.commits[0].committer.name); t.is(result.commits[1].hash.substring(0, 7), commits[1].hash); t.is(result.commits[1].message, commits[1].message); + t.truthy(result.commits[1].committerDate); + t.truthy(result.commits[1].author.name); + t.truthy(result.commits[1].committer.name); // Verify the last release is returned and updated t.truthy(result.lastRelease); t.falsy(result.lastRelease.gitHead); @@ -89,8 +129,14 @@ test.serial('Get all commits since gitHead (from lastRelease)', async t => { t.is(result.commits.length, 2); t.is(result.commits[0].hash.substring(0, 7), commits[0].hash); t.is(result.commits[0].message, commits[0].message); + t.truthy(result.commits[0].committerDate); + t.truthy(result.commits[0].author.name); + t.truthy(result.commits[0].committer.name); t.is(result.commits[1].hash.substring(0, 7), commits[1].hash); t.is(result.commits[1].message, commits[1].message); + t.truthy(result.commits[1].committerDate); + t.truthy(result.commits[1].author.name); + t.truthy(result.commits[1].committer.name); // Verify the last release is returned and updated t.truthy(result.lastRelease); t.is(result.lastRelease.gitHead, commits[commits.length - 1].hash); @@ -112,6 +158,9 @@ test.serial('Get all commits since gitHead (from lastRelease) on a detached head t.is(result.commits.length, 1); t.is(result.commits[0].hash.substring(0, 7), commits[1].hash); t.is(result.commits[0].message, commits[1].message); + t.truthy(result.commits[0].committerDate); + t.truthy(result.commits[0].author.name); + t.truthy(result.commits[0].committer.name); // Verify the last release is returned and updated t.truthy(result.lastRelease); t.is(result.lastRelease.gitHead, commits[commits.length - 1].hash); @@ -135,8 +184,14 @@ test.serial('Get all commits since gitHead (from tag) ', async t => { t.is(result.commits.length, 2); t.is(result.commits[0].hash.substring(0, 7), commits[0].hash); t.is(result.commits[0].message, commits[0].message); + t.truthy(result.commits[0].committerDate); + t.truthy(result.commits[0].author.name); + t.truthy(result.commits[0].committer.name); t.is(result.commits[1].hash.substring(0, 7), commits[1].hash); t.is(result.commits[1].message, commits[1].message); + t.truthy(result.commits[1].committerDate); + t.truthy(result.commits[1].author.name); + t.truthy(result.commits[1].committer.name); // Verify the last release is returned and updated t.truthy(result.lastRelease); t.is(result.lastRelease.gitHead.substring(0, 7), commits[commits.length - 1].hash); @@ -162,6 +217,9 @@ test.serial('Get all commits since gitHead (from tag) on a detached head repo', t.is(result.commits.length, 1); t.is(result.commits[0].hash.substring(0, 7), commits[1].hash); t.is(result.commits[0].message, commits[1].message); + t.truthy(result.commits[0].committerDate); + t.truthy(result.commits[0].author.name); + t.truthy(result.commits[0].committer.name); // Verify the last release is returned and updated t.truthy(result.lastRelease); t.is(result.lastRelease.gitHead.substring(0, 7), commits[commits.length - 1].hash); @@ -185,8 +243,14 @@ test.serial('Get all commits since gitHead (from tag formatted like v) t.is(result.commits.length, 2); t.is(result.commits[0].hash.substring(0, 7), commits[0].hash); t.is(result.commits[0].message, commits[0].message); + t.truthy(result.commits[0].committerDate); + t.truthy(result.commits[0].author.name); + t.truthy(result.commits[0].committer.name); t.is(result.commits[1].hash.substring(0, 7), commits[1].hash); t.is(result.commits[1].message, commits[1].message); + t.truthy(result.commits[1].committerDate); + t.truthy(result.commits[1].author.name); + t.truthy(result.commits[1].committer.name); // Verify the last release is returned and updated t.truthy(result.lastRelease); t.is(result.lastRelease.gitHead.substring(0, 7), commits[commits.length - 1].hash); @@ -210,8 +274,14 @@ test.serial('Get commits when last release gitHead is missing but a tag match th t.is(result.commits.length, 2); t.is(result.commits[0].hash.substring(0, 7), commits[0].hash); t.is(result.commits[0].message, commits[0].message); + t.truthy(result.commits[0].committerDate); + t.truthy(result.commits[0].author.name); + t.truthy(result.commits[0].committer.name); t.is(result.commits[1].hash.substring(0, 7), commits[1].hash); t.is(result.commits[1].message, commits[1].message); + t.truthy(result.commits[1].committerDate); + t.truthy(result.commits[1].author.name); + t.truthy(result.commits[1].committer.name); // Verify the last release is returned and updated t.truthy(result.lastRelease); t.is(result.lastRelease.gitHead.substring(0, 7), commits[commits.length - 1].hash); @@ -233,8 +303,14 @@ test.serial('Get all commits since gitHead, when gitHead are mising from the sha t.is(result.commits.length, 2); t.is(result.commits[0].hash.substring(0, 7), commits[0].hash); t.is(result.commits[0].message, commits[0].message); + t.truthy(result.commits[0].committerDate); + t.truthy(result.commits[0].author.name); + t.truthy(result.commits[0].committer.name); t.is(result.commits[1].hash.substring(0, 7), commits[1].hash); t.is(result.commits[1].message, commits[1].message); + t.truthy(result.commits[1].committerDate); + t.truthy(result.commits[1].author.name); + t.truthy(result.commits[1].committer.name); // Verify the last release is returned and updated t.truthy(result.lastRelease); t.is(result.lastRelease.gitHead.substring(0, 7), commits[commits.length - 1].hash); @@ -263,8 +339,14 @@ test.serial('Get all commits since gitHead from tag, when tags are mising from t t.is(result.commits.length, 2); t.is(result.commits[0].hash.substring(0, 7), commits[0].hash); t.is(result.commits[0].message, commits[0].message); + t.truthy(result.commits[0].committerDate); + t.truthy(result.commits[0].author.name); + t.truthy(result.commits[0].committer.name); t.is(result.commits[1].hash.substring(0, 7), commits[1].hash); t.is(result.commits[1].message, commits[1].message); + t.truthy(result.commits[1].committerDate); + t.truthy(result.commits[1].author.name); + t.truthy(result.commits[1].committer.name); // Verify the last release is returned and updated t.truthy(result.lastRelease); t.is(result.lastRelease.gitHead.substring(0, 7), commits[commits.length - 1].hash);