Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: generate changelog based on conventional commits #5487

Merged
merged 8 commits into from
May 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 14 additions & 1 deletion .github/workflows/lint-pr-title.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,20 @@ jobs:
with:
# Configure which types are allowed (newline-delimited).
# Default: https://github.com/commitizen/conventional-commit-types
#types: |
# Customized based on sections defined in scripts/generate_changelog.mjs
types: |
feat
fix
perf
refactor
revert
deps
build
ci
test
style
chore
docs

# Configure which scopes are allowed (newline-delimited).
# These are regex patterns auto-wrapped in `^ $`.
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish-rc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ jobs:
# </common-build>

- name: Generate changelog
run: node scripts/generate_changelog_simple.js ${{ needs.tag.outputs.prev_tag }} ${{ needs.tag.outputs.tag }} CHANGELOG.md
run: node scripts/generate_changelog.mjs ${{ needs.tag.outputs.prev_tag }} ${{ needs.tag.outputs.tag }} CHANGELOG.md

- name: Create Release
id: create_release
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish-stable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ jobs:
# </common-build>

- name: Generate changelog
run: node scripts/generate_changelog_simple.js ${{ needs.tag.outputs.prev_tag }} ${{ needs.tag.outputs.tag }} CHANGELOG.md
run: node scripts/generate_changelog.mjs ${{ needs.tag.outputs.prev_tag }} ${{ needs.tag.outputs.tag }} CHANGELOG.md

- name: Create Release
id: create_release
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
no-console
*/

const {execSync} = require("node:child_process");
const fs = require("node:fs");
import {execSync} from "node:child_process";
import fs from "node:fs";

// Docs
// [script] <fromTag> <toTag>
//
// Exmaple
// Example
nflaig marked this conversation as resolved.
Show resolved Hide resolved
// ```
// node scripts/changelog_simple.js v0.32.0 v0.33.0
// node scripts/generate_changelog.mjs v0.32.0 v0.33.0
// ```

/**
Expand All @@ -23,8 +23,8 @@ const fs = require("node:fs");
*/
const knownAuthors = {
"caymannava@gmail.com": "wemeetagain",
"76567250+g11tech@users.noreply.github.com": "g11tech",
"vutuyen2636@gmail.com": "tuyennhv",
"develop@g11tech.io": "g11tech",
"tuyen@chainsafe.io": "tuyennhv",
"35266934+dapplion@users.noreply.github.com": "dapplion",
"41898282+github-actions[bot]@users.noreply.github.com": "github-actions[bot]",
"49699333+dependabot[bot]@users.noreply.github.com": "dependabot[bot]",
Expand All @@ -39,6 +39,9 @@ const knownAuthors = {
"ammar1lakho@gmail.com": "ammarlakho",
"dadepo@gmail.com": "dadepo",
"hi@enriqueortiz.dev": "Evalir",
"nflaig@protonmail.com": "nflaig",
"nazarhussain@gmail.com": "nazarhussain",
"me@matthewkeil.com": "matthewkeil",
};

const fromTag = process.argv[2];
Expand All @@ -49,36 +52,96 @@ if (!fromTag) throw Error("No process.argv[2]");
if (!toTag) throw Error("No process.argv[3]");
if (!outpath) throw Error("No process.argv[4]");

/**
* @type {Record<string, {heading: string; commitsByScope: Record<string, string[]>}>}
*/
const sections = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Order and wording LGTM

feat: {heading: "Features", commitsByScope: {"": []}},
fix: {heading: "Bug Fixes", commitsByScope: {"": []}},
perf: {heading: "Performance", commitsByScope: {"": []}},
refactor: {heading: "Refactoring", commitsByScope: {"": []}},
revert: {heading: "Reverts", commitsByScope: {"": []}},
deps: {heading: "Dependencies", commitsByScope: {"": []}},
build: {heading: "Build System", commitsByScope: {"": []}},
ci: {heading: "Continuous Integration", commitsByScope: {"": []}},
test: {heading: "Tests", commitsByScope: {"": []}},
style: {heading: "Styles", commitsByScope: {"": []}},
chore: {heading: "Maintenance", commitsByScope: {"": []}},
docs: {heading: "Documentation", commitsByScope: {"": []}},
_: {heading: "Miscellaneous", commitsByScope: {"": []}},
};

const isPrCommitRg = /\(#\d+\)/;
const conventionalCommitRg = /^([a-z]+)(?:\((.*)\))?(?:(!))?: (.*)$/;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thats pretty a pretty fancy regexp. Did this get pulled from somewhere? Perhaps we can unit test it but that feels silly honestly for a workflow script...

Found this thread and it had a a pretty cool one in it to support emojis.

Not something I feel strongly about but thought was worth a mention. I put my faith in your trusting hands sir

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not in favor of adding tests for the script. I have verified that the output is correct.

As I also do not trust any regex, I created some test cases which I just executed locally.

a pretty cool one in it to support emojis

The regex allows emojis in the commit message itself. It is really not that strict, a more strict format will be enforced by the PR title checker.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added console logs to print out ignored commits (dc26df1), this should be sufficient for now to verify that the regex works and there are no false positives.


const commitHashes = shell(`git log --pretty=format:"%H" ${fromTag}...${toTag}`);

let commitListStr = "";

for (const commitHash of commitHashes.trim().split("\n")) {
const subject = shell(`git log --format='%s' ${commitHash}^!`);
if (!isPrCommitRg.test(subject)) {
const rawCommit = shell(`git log --format='%s' ${commitHash}^!`);

if (!isPrCommitRg.test(rawCommit)) {
console.log(`Ignored commit "${rawCommit}" (missing PR reference)`);
continue;
}

const conventionalCommit = rawCommit.match(conventionalCommitRg);
if (!conventionalCommit) {
console.log(`Ignored commit "${rawCommit}" (not conventional commit)`);
continue;
}

const [, type, scope, _breaking, subject] = conventionalCommit;

const authorEmail = shell(`git log --format='%ae' ${commitHash}^!`);
const authorName = shell(`git log --format='%an' ${commitHash}^!`);
const login = getCommitAuthorLogin(commitHash, authorEmail, authorName);

commitListStr += `- ${subject} (@${login})\n`;
const formattedCommit = `- ${scope ? `**${scope}:** ` : ""}${subject} (@${login})\n`;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice touch looking up the author login name!! Always great to provide visibility to contributors

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it is pretty nice, can easily see contributors of a release. That was already there in our initial version and does not seem to be easily supported by any other tools like release-please or semantic-release which is another good reason besides added complexity to not introduce additional tooling (for now).


// Sort commits by type and scope
// - assign each commit to section based on type
// - group commits by scope within each section
if (sections[type] != null) {
if (scope) {
if (sections[type].commitsByScope[scope] == null) {
sections[type].commitsByScope[scope] = [];
}
sections[type].commitsByScope[scope].push(formattedCommit);
} else {
sections[type].commitsByScope[""].push(formattedCommit);
}
} else {
// Commits with a type that is not defined in sections
sections._.commitsByScope[""].push(formattedCommit);
}
}

// Print knownAuthors to update if necessary
console.log("knownAuthors", knownAuthors);

const changelog = `# Changelog
let changelog = `# Changelog

[Full Changelog](https://github.com/ChainSafe/lodestar/compare/${fromTag}...${toTag})
`;

**Merged pull requests:**
// Write sections to changelog
for (const type in sections) {
const section = sections[type];
let hasCommits = false;
let sectionChangelog = `\n### ${section.heading}\n\n`;

${commitListStr}
`;
for (const commits of Object.values(section.commitsByScope)) {
if (commits.length > 0) {
hasCommits = true;
sectionChangelog += commits.join("");
}
}

if (hasCommits) {
// Only add section if it has at least one commit
changelog += sectionChangelog;
}
}

// Print to console
console.log(changelog);
Expand Down