Skip to content

Commit

Permalink
refactor: migrate to @conventional-commits/parser and update conventi…
Browse files Browse the repository at this point in the history
…onal-changelog (#674)
  • Loading branch information
bcoe committed Jan 5, 2021
1 parent 1cf18b9 commit 7301843
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 143 deletions.
22 changes: 11 additions & 11 deletions __snapshots__/yoshi-go.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,36 +27,36 @@ filename: CHANGES.md
exports['YoshiGo run creates a release PR for google-api-go-client 1'] = `
filename: CHANGES.md
# Changelog
## [0.124.0](https://www.github.com/googleapis/google-api-go-client/compare/v0.123.4...v0.124.0) (1983-10-10)
### Features
* **all:** auto-regenerate discovery clients , refs [#1000](https://www.github.com/googleapis/google-api-go-client/issues/1000) [#1001](https://www.github.com/googleapis/google-api-go-client/issues/1001)
* **asset:** added a really cool feature ([d7d1c72](https://www.github.com/googleapis/google-api-go-client/commit/d7d1c720dc1526f4d62ceedad581f498195c8939))
# Changelog
## [0.124.0](https://www.github.com/googleapis/google-api-go-client/compare/v0.123.4...v0.124.0) (1983-10-10)
### Bug Fixes
### Features
* **all:** auto-regenerate discovery clients , refs [#1000](https://www.github.com/googleapis/google-api-go-client/issues/1000) [#1001](https://www.github.com/googleapis/google-api-go-client/issues/1001)
* **asset:** added a really cool feature ([d7d1c72](https://www.github.com/googleapis/google-api-go-client/commit/d7d1c720dc1526f4d62ceedad581f498195c8939))
* **automl:** fixed a really bad bug ([d7d1c89](https://www.github.com/googleapis/google-api-go-client/commit/d7d1c890dc1526f4d62ceedad581f498195c8939))
* **pubsub/pstest:** this commit should also be included ([dad2c89](https://www.github.com/googleapis/google-api-go-client/commit/dad2c890dc1526f4d62ceedad581f498195c8939))
* **pubsub:** this commit should be included ([dcd1c89](https://www.github.com/googleapis/google-api-go-client/commit/dcd1c890dc1526f4d62ceedad581f498195c8939))
* this commit should be included ([ecd1c89](https://www.github.com/googleapis/google-api-go-client/commit/ecd1c890dc1526f4d62ceedad581f489195c8939))
`

exports['YoshiGo supports releasing submodule from google-cloud-go 1'] = `
filename: pubsublite/CHANGES.md
# Changelog
### [0.123.5](https://www.github.com/googleapis/google-cloud-go/compare/v0.123.4...v0.123.5) (1983-10-10)
# Changelog
### Bug Fixes
### [0.123.5](https://www.github.com/googleapis/google-cloud-go/compare/v0.123.4...v0.123.5) (1983-10-10)
* **pubsublite:** start generating v1 ([1d9662c](https://www.github.com/googleapis/google-cloud-go/commit/1d9662cf08ab1cf3b68d95dee4dc99b7c4aac371))
`
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,15 @@
"typescript": "^3.8.3"
},
"dependencies": {
"@conventional-commits/parser": "^0.3.0",
"@octokit/graphql": "^4.3.1",
"@octokit/request": "^5.3.4",
"@octokit/rest": "^18.0.4",
"chalk": "^4.0.0",
"code-suggester": "^1.4.0",
"concat-stream": "^2.0.0",
"conventional-changelog-conventionalcommits": "^4.4.0",
"conventional-changelog-writer": "^4.0.6",
"conventional-changelog-writer": "^5.0.0",
"conventional-commits-filter": "^2.0.2",
"conventional-commits-parser": "^3.0.3",
"figures": "^3.0.0",
"parse-github-repo-url": "^1.4.1",
"semver": "^7.0.0",
Expand Down
195 changes: 79 additions & 116 deletions src/conventional-commits.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,22 @@
import chalk = require('chalk');
import * as semver from 'semver';
import {ReleaseType} from 'semver';
import {Readable} from 'stream';

import {checkpoint, CheckpointType} from './util/checkpoint';
import {Commit} from './graphql-to-commits';
import {
ConventionalChangelogCommit,
parser,
toConventionalChangelogFormat,
} from '@conventional-commits/parser';

interface CommitWithHash extends ConventionalChangelogCommit {
hash: string | null;
}

// eslint-disable-next-line @typescript-eslint/no-var-requires
const concat = require('concat-stream');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const conventionalCommitsFilter = require('conventional-commits-filter');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const conventionalCommitsParser = require('conventional-commits-parser');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const conventionalChangelogWriter = require('conventional-changelog-writer');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const parseGithubRepoUrl = require('parse-github-repo-url');
Expand Down Expand Up @@ -68,52 +72,27 @@ interface Note {
text: string;
}

interface ParsedConventionalCommit {
type: string;
scope: string | null;
subject: string;
merge: boolean | null;
header: string;
body: string | null;
footer: string | null;
notes: Note[];
references: object[];
mentions: string[];
revert: boolean | null;
}

// Perform some post processing on the commits parsed by conventional commits:
// 1. don't allow BREAKING CHANGES to have two newlines:
import {Transform} from 'stream';

class PostProcessCommits extends Transform {
_transform(
chunk: ParsedConventionalCommit,
_encoding: string,
done: Function
) {
chunk.notes.forEach(note => {
let text = '';
let i = 0;
let extendedContext = false;
for (const chunk of note.text.split(/\r?\n/)) {
if (i > 0 && hasExtendedContext(chunk) && !extendedContext) {
text = `${text.trim()}\n`;
extendedContext = true;
}
if (chunk === '') break;
else if (extendedContext) {
text += ` ${chunk}\n`;
} else {
text += `${chunk} `;
}
i++;
function postProcessCommits(commit: ConventionalChangelogCommit) {
commit.notes.forEach(note => {
let text = '';
let i = 0;
let extendedContext = false;
for (const chunk of note.text.split(/\r?\n/)) {
if (i > 0 && hasExtendedContext(chunk) && !extendedContext) {
text = `${text.trim()}\n`;
extendedContext = true;
}
note.text = text.trim();
});
this.push(JSON.stringify(chunk, null, 4) + '\n');
done();
}
if (chunk === '') break;
else if (extendedContext) {
text += ` ${chunk}\n`;
} else {
text += `${chunk} `;
}
i++;
}
note.text = text.trim();
});
return commit;
}

// If someone wishes to include additional contextual information for a
Expand Down Expand Up @@ -201,78 +180,62 @@ export class ConventionalCommits {
this.headerPartial || preset.writerOpts.headerPartial;
preset.writerOpts.mainTemplate =
this.mainTemplate || preset.writerOpts.mainTemplate;

return new Promise((resolve, reject) => {
let content = '';
const stream = this.commitsReadable()
.pipe(conventionalCommitsParser(preset.parserOpts))
.pipe(new PostProcessCommits({objectMode: true}))
.pipe(conventionalChangelogWriter(context, preset.writerOpts));

stream.on('error', (err: Error) => {
return reject(err);
});

stream.on('data', (buffer: Buffer) => {
content += buffer.toString('utf8');
});

stream.on('end', () => {
return resolve(content.trim());
});
});
const parsedCommits = [];
for (const commit of this.commits) {
try {
const parsedCommit = postProcessCommits(
toConventionalChangelogFormat(parser(commit.message))
) as CommitWithHash;
parsedCommit.hash = commit.sha;
parsedCommits.push(parsedCommit);
} catch (_err) {
// Commit is not in conventional commit format, it does not
// contribute to the CHANGELOG generation.
}
}
const parsed: string = conventionalChangelogWriter
.parseArray(parsedCommits, context, preset.writerOpts)
.trim();
return parsed;
}
private async guessReleaseType(preMajor: boolean): Promise<BumpSuggestion> {
const VERSIONS = ['major', 'minor', 'patch'];
const preset = await presetFactory({preMajor});
return new Promise((resolve: Function, reject: Function) => {
const stream = this.commitsReadable()
.pipe(conventionalCommitsParser(preset.parserOpts))
.pipe(
concat((data: ParsedConventionalCommit[]) => {
const commits = conventionalCommitsFilter(data);

let result = preset.recommendedBumpOpts.whatBump(
commits,
preset.recommendedBumpOpts
);

if (result && result.level !== null) {
result.releaseType = VERSIONS[result.level];
} else if (result === null) {
result = {};
}
const parsedCommits = [];
for (const commit of this.commits) {
try {
const parsedCommit = toConventionalChangelogFormat(
parser(commit.message)
);
parsedCommits.push(parsedCommit);
} catch (_err) {
// Commit is not in conventional commit format, it does not
// contribute to the CHANGELOG generation.
}
}
const commits = conventionalCommitsFilter(
parsedCommits
) as ConventionalChangelogCommit;

// we have slightly different logic than the default of conventional commits,
// the minor should be bumped when features are introduced for pre 1.x.x libs:
if (
result.reason.indexOf(' 0 features') === -1 &&
result.releaseType === 'patch'
) {
result.releaseType = 'minor';
}
let result = preset.recommendedBumpOpts.whatBump(
commits,
preset.recommendedBumpOpts
);

return resolve(result);
})
);
if (result && result.level !== null) {
result.releaseType = VERSIONS[result.level];
} else if (result === null) {
result = {};
}

stream.on('error', (err: Error) => {
return reject(err);
});
});
}
private commitsReadable(): Readable {
// The conventional commits parser expects an array of string commit
// messages terminated by `-hash-` followed by the commit sha. We
// piggyback off of this, and use this sha when choosing a
// point to branch from for PRs.
const commitsReadable = new Readable();
this.commits.forEach((commit: Commit) => {
commitsReadable.push(
`${commit.message}\n-hash-\n${commit.sha ? commit.sha : ''}`
);
});
commitsReadable.push(null);
return commitsReadable;
// we have slightly different logic than the default of conventional commits,
// the minor should be bumped when features are introduced for pre 1.x.x libs:
if (
result.reason.indexOf(' 0 features') === -1 &&
result.releaseType === 'patch'
) {
result.releaseType = 'minor';
}
return result;
}
}
13 changes: 2 additions & 11 deletions test/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,12 @@ import {resolve} from 'path';
* Given an object of chnages expected to be made by code-suggester API,
* stringify content in such a way that it works well for snapshots:
*/
export function stringifyExpectedChanges(
expected: [string, object][],
sort = false
): string {
export function stringifyExpectedChanges(expected: [string, object][]): string {
let stringified = '';
for (const update of expected) {
stringified = `${stringified}\nfilename: ${update[0]}`;
const obj = update[1] as {[key: string]: string};
// TODO(bcoe): dig into issue with Node 10 streams that is forcing us
// to sort content before performing comparison.
// See: https://github.com/googleapis/release-please/issues/601
const content = sort
? obj.content.split('\n').sort().join('\n')
: obj.content;
stringified = `${stringified}\n${content}`;
stringified = `${stringified}\n${obj.content}`;
}
return stringified.replace(
/[0-9]{4}-[0-9]{2}-[0-9]{2}/g,
Expand Down
4 changes: 2 additions & 2 deletions test/releasers/yoshi-go.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ describe('YoshiGo', () => {
req.done();

// eslint-disable-next-line @typescript-eslint/no-explicit-any
snapshot(stringifyExpectedChanges(expectedChanges, true));
snapshot(stringifyExpectedChanges(expectedChanges));
});
});
it('supports releasing submodule from google-cloud-go', async () => {
Expand Down Expand Up @@ -261,6 +261,6 @@ describe('YoshiGo', () => {
req.done();

// eslint-disable-next-line @typescript-eslint/no-explicit-any
snapshot(stringifyExpectedChanges(expectedChanges, true));
snapshot(stringifyExpectedChanges(expectedChanges));
});
});

0 comments on commit 7301843

Please sign in to comment.