Skip to content

Commit

Permalink
feat: allow nesting multiple commits that are parsed independently (g…
Browse files Browse the repository at this point in the history
…oogleapis#1566)

* test: failing test for parsing commit with special commit separator annotations

* feat: allow nesting multiple commits that are parsed independently
  • Loading branch information
chingor13 authored Sep 15, 2022
1 parent af213c4 commit 21ed59a
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 25 deletions.
68 changes: 44 additions & 24 deletions src/commit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,23 @@ function parseCommits(message: string): parser.ConventionalChangelogCommit[] {
).map(postProcessCommits);
}

// If someone wishes to aggregate multiple, complex commit messages into a
// single commit, they can include one or more `BEGIN_NESTED_COMMIT`/`END_NESTED_COMMIT`
// blocks into the body of the commit
function splitMessages(message: string): string[] {
const parts = message.split('BEGIN_NESTED_COMMIT');
const messages = [parts.shift()!];
for (const part of parts) {
const [newMessage, ...rest] = part.split('END_NESTED_COMMIT');
messages.push(newMessage);
// anthing outside of the BEGIN/END annotations are added to the original
// commit
messages[0] = messages[0] + rest.join();
}

return messages;
}

/**
* Given a list of raw commits, parse and expand into conventional commits.
*
Expand All @@ -354,31 +371,34 @@ export function parseConventionalCommits(
const conventionalCommits: ConventionalCommit[] = [];

for (const commit of commits) {
const commitMessage = preprocessCommitMessage(commit);
try {
for (const parsedCommit of parseCommits(commitMessage)) {
const breaking =
parsedCommit.notes.filter(note => note.title === 'BREAKING CHANGE')
.length > 0;
conventionalCommits.push({
sha: commit.sha,
message: parsedCommit.header,
files: commit.files,
pullRequest: commit.pullRequest,
type: parsedCommit.type,
scope: parsedCommit.scope,
bareMessage: parsedCommit.subject,
notes: parsedCommit.notes,
references: parsedCommit.references,
breaking,
});
for (const commitMessage of splitMessages(
preprocessCommitMessage(commit)
)) {
try {
for (const parsedCommit of parseCommits(commitMessage)) {
const breaking =
parsedCommit.notes.filter(note => note.title === 'BREAKING CHANGE')
.length > 0;
conventionalCommits.push({
sha: commit.sha,
message: parsedCommit.header,
files: commit.files,
pullRequest: commit.pullRequest,
type: parsedCommit.type,
scope: parsedCommit.scope,
bareMessage: parsedCommit.subject,
notes: parsedCommit.notes,
references: parsedCommit.references,
breaking,
});
}
} catch (_err) {
logger.debug(
`commit could not be parsed: ${commit.sha} ${
commit.message.split('\n')[0]
}`
);
}
} catch (_err) {
logger.debug(
`commit could not be parsed: ${commit.sha} ${
commit.message.split('\n')[0]
}`
);
}
}

Expand Down
40 changes: 39 additions & 1 deletion test/commits.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import {describe, it} from 'mocha';

import {expect} from 'chai';
import {parseConventionalCommits} from '../src/commit';
import {parseConventionalCommits, ConventionalCommit} from '../src/commit';
import {buildCommitFromFixture, buildMockCommit} from './helpers';

describe('parseConventionalCommits', () => {
Expand Down Expand Up @@ -194,6 +194,7 @@ describe('parseConventionalCommits', () => {
expect(conventionalCommits[0].type).to.eql('fix');
expect(conventionalCommits[0].bareMessage).to.eql('some fix');
});

it('can override the commit message from BEGIN_COMMIT_OVERRIDE body with a meta commit', async () => {
const commit = buildMockCommit('chore: some commit');
const body =
Expand All @@ -216,6 +217,32 @@ describe('parseConventionalCommits', () => {
expect(conventionalCommits[1].bareMessage).to.eql('some fix');
});

it('handles a special commit separator', async () => {
const commits = [buildCommitFromFixture('multiple-commits-with-separator')];
const conventionalCommits = parseConventionalCommits(commits);
let commit = assertHasCommit(
conventionalCommits,
'annotating some fields as REQUIRED'
);
expect(commit.type).to.eql('fix');
commit = assertHasCommit(
conventionalCommits,
'include metadata file, add exclusions for samples to handwritten libraries'
);
expect(commit.type).to.eql('docs');
expect(commit.scope).to.eql('samples');
commit = assertHasCommit(
conventionalCommits,
'add flag to distinguish autogenerated libs with a handwritten layer'
);
expect(commit.type).to.eql('build');
commit = assertHasCommit(
conventionalCommits,
'update v2.14.1 gapic-generator-typescript'
);
expect(commit.type).to.eql('chore');
});

// it('ignores reverted commits', async () => {
// const commits = [
// {sha: 'sha1', message: 'feat: some feature', files: ['path1/file1.txt']},
Expand All @@ -236,3 +263,14 @@ describe('parseConventionalCommits', () => {
// expect(conventionalCommits[0].scope).is.null;
// });
});

function assertHasCommit(
commits: ConventionalCommit[],
bareMessage: string
): ConventionalCommit {
const found = commits.find(commit =>
commit.bareMessage.includes(bareMessage)
);
expect(found, `commit with message: '${bareMessage}'`).to.not.be.undefined;
return found!;
}
35 changes: 35 additions & 0 deletions test/fixtures/commit-messages/multiple-commits-with-separator.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
fix: annotating some fields as REQUIRED

These fields were actually always required by the backend, so annotation just documents status quo. I believe this change will not require major version bump for any language.

PiperOrigin-RevId: 429093810
Source-Link: googleapis/googleapis@dc04c1c
Source-Link: https://github.com/googleapis/googleapis-gen/commit/0e23469bea2f397f2b783c5a25e64452f86be6bc
Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiMGUyMzQ2OWJlYTJmMzk3ZjJiNzgzYzVhMjVlNjQ0NTJmODZiZTZiYyJ9
See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md
BEGIN_NESTED_COMMIT
docs(samples): include metadata file, add exclusions for samples to handwritten libraries
PiperOrigin-RevId: 429395631
Source-Link: googleapis/googleapis@84594b3
Source-Link: https://github.com/googleapis/googleapis-gen/commit/ed74f970fd82914874e6b27b04763cfa66bafe9b
Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiZWQ3NGY5NzBmZDgyOTE0ODc0ZTZiMjdiMDQ3NjNjZmE2NmJhZmU5YiJ9
See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md
END_NESTED_COMMIT
BEGIN_NESTED_COMMIT
build: add flag to distinguish autogenerated libs with a handwritten layer
PiperOrigin-RevId: 430741048
Source-Link: googleapis/googleapis@6bd2087
Source-Link: https://github.com/googleapis/googleapis-gen/commit/3691f4b4f9e4ba024ab044b946fc8572129e8c06
Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiMzY5MWY0YjRmOWU0YmEwMjRhYjA0NGI5NDZmYzg1NzIxMjllOGMwNiJ9
END_NESTED_COMMIT
BEGIN_NESTED_COMMIT
chore: update v2.14.1 gapic-generator-typescript
Committer: @summer-ji-eng
PiperOrigin-RevId: 433031262
Source-Link: googleapis/googleapis@2a55d13
Source-Link: https://github.com/googleapis/googleapis-gen/commit/2fce3893ae9da47763e0872c4a3a87d9ff78771f
Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiMmZjZTM4OTNhZTlkYTQ3NzYzZTA4NzJjNGEzYTg3ZDlmZjc4NzcxZiJ9
See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md
END_NESTED_COMMIT
Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
Co-authored-by: Benjamin E. Coe <[email protected]>

0 comments on commit 21ed59a

Please sign in to comment.