Skip to content

Commit

Permalink
feat(commit): handle Github Revert commits
Browse files Browse the repository at this point in the history
  • Loading branch information
Andreas Richter committed Jul 31, 2020
1 parent 11665ca commit e32cffe
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 73 deletions.
2 changes: 2 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ The empty lines between the different parts are required.
| **perf** | Performance optimizations | patch |
| **refactor** | Changes to the code structure without fixing bugs or adding features | patch |
| **chore** | Changes to the project setup and tools, dependency bumps, house-keeping | patch |
| **revert** | reverts a previous commit | patch |

||
| **docs** | Changes to the documentation | none |
| **style** | Cleanup & lint rule fixes. Note that often it's better to just amend the previous commit if it introduced lint errors | none |
Expand Down
8 changes: 8 additions & 0 deletions lib/git/commits.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,17 @@ function parseCommit(commit) {
const parentSha = meta.shift() || null;
const data = commitParser.sync(message, {
issuePrefixes: ['#', 'https?://[\\w\\.-/]*[-/]+'],
revertPattern: /^revert:?\s"([\s\S]*)"/i,
revertCorrespondence: ['message'],
});
const prMatch = message.match(PR_MERGE_PATTERN);

if (data.revert) {
data.type = 'pr';
data.subject = data.revert.message;
data.header = data.header.replace(/^Revert /, 'revert: ');
}

if (prMatch) {
const prId = prMatch[1];
data.type = 'pr';
Expand Down
75 changes: 38 additions & 37 deletions lib/steps/release-info.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,51 +32,51 @@

'use strict';

const util = require('util');

const INVALID_COMMITS_MESSAGE = [
'This repository uses AngularJS Git Commit Message Conventions[1]',
'to automatically determine the semver implications of changes',
'and to generate changelogs for releases.',
'',
'The following commits could not be parsed:',
'',
'<<COMMITS>>',
'',
'Most likely they are missing one of the valid type prefixes',
'(feat, fix, docs, style, refactor, test, chore).',
'',
'You can reword commit messages using rebase[2]:',
'',
'~~~bash',
'git rebase -i <<FIRST_PARENT>>',
'~~~',
'',
'[1] Docs on the conventions: http://gr.pn/1OWll98',
'[2] https://git-scm.com/docs/git-rebase',
].join('\n');
const INVALID_COMMITS_MESSAGE = `This repository uses AngularJS Git Commit Message Conventions[1]
to automatically determine the semver implications of changes
and to generate changelogs for releases.
The following commits could not be parsed:
<<COMMITS>>
Most likely they are missing one of the valid type prefixes
(feat, fix, docs, style, refactor, test, chore).
You can reword commit messages using rebase[2]:
~~~bash
git rebase -i <<FIRST_PARENT>>
~~~
[1] Docs on the conventions: http://gr.pn/1OWll98
[2] https://git-scm.com/docs/git-rebase`;

const RELEASE_TYPES = ['none', 'patch', 'minor', 'major'];

function formatInvalidCommit(commit) {
return `* [${commit.sha.slice(0, 7)}] ${commit.header}`;
}

function InvalidCommitsError(commits) {
Error.call(this);
Error.captureStackTrace(this, InvalidCommitsError);
const commitsBlock = commits.map(formatInvalidCommit).join('\n');
this.message = INVALID_COMMITS_MESSAGE.replace(
'<<COMMITS>>',
commitsBlock
).replace('<<FIRST_PARENT>>', commits[0].parentSha || '--root');
this.code = 'EINVALIDCOMMITS';
this.commits = commits;
class InvalidCommitsError extends Error {
constructor(commits) {
super();
// this.captureStackTrace(this, InvalidCommitsError);
const commitsBlock = commits.map(formatInvalidCommit).join('\n');
this.message = INVALID_COMMITS_MESSAGE.replace(
'<<COMMITS>>',
commitsBlock
).replace('<<FIRST_PARENT>>', commits[0].parentSha || '--root');
this.code = 'EINVALIDCOMMITS';
this.commits = commits;
}
}

util.inherits(InvalidCommitsError, Error);

function isBreaking(note) {
/**
* @param {{title: string}} note
* @return {boolean}
*/
function hasBreakingChange(note) {
return note.title.startsWith('BREAKING CHANGE');
}

Expand All @@ -87,7 +87,7 @@ function determineReleaseInfo(commits, acceptInvalidCommits) {
for (let idx = 0; idx < commits.length; ++idx) {
const commit = commits[idx];

if ((commit.notes || []).some(isBreaking)) {
if ((commit.notes || []).some(hasBreakingChange)) {
releaseType = 3;
continue;
}
Expand All @@ -97,6 +97,7 @@ function determineReleaseInfo(commits, acceptInvalidCommits) {
case 'fix':
case 'refactor':
case 'perf':
case 'revert':
releaseType = Math.max(releaseType, 1);
break;

Expand Down
13 changes: 13 additions & 0 deletions test/fixtures/breaking-change-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -e
git init

echo "console.log('do stuff');" > index.js
git add index.js
git commit -m "fix: Do stuff
something
BREAKING CHANGE:
Ups something broke :D
"
7 changes: 7 additions & 0 deletions test/fixtures/merge-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -e
git init

echo "console.log('do stuff');" > merge.js
git add merge.js
git commit -m 'Merge pull request #119 from theowner/some/branch'
17 changes: 17 additions & 0 deletions test/fixtures/revert-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env bash
set -e
git init

echo "console.log('do stuff');" > revert-multi-line.js
git add revert-multi-line.js
git commit -m 'Revert "throw an error if a callback is passed"
This reverts commit 9bb4d6c.'

echo "console.log('do stuff');" > revert.js
git add revert.js
git commit -m 'Revert "Fix: remove peek baseURL"'

echo "console.log('do stuff');" > index.js
git add index.js
git commit -m 'revert: this'
4 changes: 0 additions & 4 deletions test/fixtures/silent-commits
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ git init

git checkout -b silent-commits

echo "console.log('more');" > doc.js
git add doc.js
git commit -m "doc: Adding doc commit"

echo "console.log('more');" > docs.js
git add docs.js
git commit -m "docs: Adding docs commit"
Expand Down
77 changes: 45 additions & 32 deletions test/steps/release-info.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,39 +96,52 @@ describe('determineReleaseInfo', () => {
});
});

describe('with "chore", "fix", "refactor" & "perf" commit messages', () => {
const dirname = withFixture('patch-commits');
let commits = [];
before('load commits', async () => {
commits = await getCommits(dirname);
});
const testcases = [
{
desc: 'with "chore", "fix", "refactor" & "perf" commit type',
fixture: 'patch-commits',
expected: 'patch',
},
{
desc: 'with "feat" commit type',
fixture: 'minor-commits',
expected: 'minor',
},
{
desc: 'with "doc", "docs", "style", "test" & "pr" commit type',
fixture: 'silent-commits',
expected: 'none',
},
{
desc: 'with "BREAKING CHANGE" commit footer',
fixture: 'breaking-change-commit',
expected: 'major',
},
{
desc: 'with github "Revert" commit message or "revert" commit type',
fixture: 'revert-commit',
expected: 'patch',
},
{
desc: 'with github "Merge" commit message',
fixture: 'merge-commit',
expected: 'none',
},
];

it('returns "patch" version info', () => {
assert.equal('patch', determineReleaseInfo(commits, true));
});
});

describe('with "feat" commit messages', () => {
const dirname = withFixture('minor-commits');
let commits = [];
before('load commits', async () => {
commits = await getCommits(dirname);
});
describe('test cases', () => {
for (const testcase of testcases) {
describe(testcase.desc, () => {
const dirname = withFixture(testcase.fixture);
let commits = [];
before('load commits', async () => {
commits = await getCommits(dirname);
});

it('returns "minor" version info', () => {
assert.equal('minor', determineReleaseInfo(commits, true));
});
});

describe('with "doc", "docs", "style", "test" & "pr" commit messages', () => {
const dirname = withFixture('silent-commits');
let commits = [];
before('load commits', async () => {
commits = await getCommits(dirname);
});

it('returns "none" version info', () => {
assert.equal('none', determineReleaseInfo(commits, true));
});
it(`returns "${testcase.expected}" version info`, () => {
assert.equal(testcase.expected, determineReleaseInfo(commits));
});
});
}
});
});

0 comments on commit e32cffe

Please sign in to comment.