From 1f5a94e06de01ceb8143886b5c00fe845173ee9f Mon Sep 17 00:00:00 2001 From: Ghislain B Date: Wed, 20 Jul 2022 20:52:20 -0400 Subject: [PATCH] feat(version): provide custom format to include commit author fullname (#269) --- lerna.json | 2 +- .../src/cli-commands/cli-version-commands.ts | 7 +- .../__tests__/conventional-commits.spec.ts | 71 ++++++++++++++++--- .../conventional-commits/update-changelog.ts | 25 ++++--- packages/core/src/models/command-options.ts | 12 +++- packages/core/src/models/interfaces.ts | 2 +- packages/version/README.md | 30 ++++++-- packages/version/src/version-command.ts | 4 +- 8 files changed, 118 insertions(+), 35 deletions(-) diff --git a/lerna.json b/lerna.json index f79d8c58..33e93b43 100644 --- a/lerna.json +++ b/lerna.json @@ -11,7 +11,7 @@ "createRelease": "github", "gitDryRun": false, "syncWorkspaceLock": true, - "changelogIncludeCommitAuthor": true, + "changelogIncludeCommitAuthorFullname": " by <%a>", "changelogHeaderMessage": "### Automate your Workspace Versions, Changelogs & Publish with [Lerna-Lite](https://github.com/ghiscoding/lerna-lite) 🚀", "message": "chore(release): publish new version %v" }, diff --git a/packages/cli/src/cli-commands/cli-version-commands.ts b/packages/cli/src/cli-commands/cli-version-commands.ts index 7d5324aa..7ec8e5c4 100644 --- a/packages/cli/src/cli-commands/cli-version-commands.ts +++ b/packages/cli/src/cli-commands/cli-version-commands.ts @@ -63,11 +63,12 @@ export default { requiresArg: true, type: 'string', }, - 'changelog-include-commit-author': { + 'changelog-include-commit-author-fullname': { describe: - "Specify if we want to include the commit author's name when using conventional-commits with changelog", + "Specify if we want to include the commit author's name, when using conventional-commits with changelog. We can optionally provide a custom message or else a default format will be used.", group: 'Version Command Options:', - type: 'boolean', + requiresArg: false, + type: 'string', }, 'changelog-version-message': { describe: diff --git a/packages/core/src/conventional-commits/__tests__/conventional-commits.spec.ts b/packages/core/src/conventional-commits/__tests__/conventional-commits.spec.ts index 3c5ead33..6b71592a 100644 --- a/packages/core/src/conventional-commits/__tests__/conventional-commits.spec.ts +++ b/packages/core/src/conventional-commits/__tests__/conventional-commits.spec.ts @@ -418,7 +418,7 @@ describe('conventional-commits', () => { `); }); - it('supports custom tagPrefix in fixed mode and include commit author', async () => { + it('supports custom tagPrefix in fixed mode and include commit author full name', async () => { const cwd = await initFixture('fixed'); await gitTag(cwd, 'dragons-are-awesome1.0.0'); @@ -435,11 +435,11 @@ describe('conventional-commits', () => { const [leafChangelog, rootChangelog] = await Promise.all([ updateChangelog(pkg1, 'fixed', { - changelogIncludeCommitAuthor: true, + changelogIncludeCommitAuthorFullname: true, tagPrefix: 'dragons-are-awesome', }), updateChangelog({ location: cwd } as Package, 'root', { - changelogIncludeCommitAuthor: true, + changelogIncludeCommitAuthorFullname: true, tagPrefix: 'dragons-are-awesome', version: '1.0.1', }), @@ -451,7 +451,7 @@ describe('conventional-commits', () => { ### Bug Fixes - * A second commit for our CHANGELOG ([SHA](https://github.com/lerna/conventional-commits-fixed/commit/GIT_HEAD)) (@Tester-McPerson) + * A second commit for our CHANGELOG ([SHA](https://github.com/lerna/conventional-commits-fixed/commit/GIT_HEAD)) (Tester McPerson) `); expect(rootChangelog.newEntry.trimRight()).toMatchInlineSnapshot(` ## [1.0.1](/compare/dragons-are-awesome1.0.0...dragons-are-awesome1.0.1) (YYYY-MM-DD) @@ -459,7 +459,7 @@ describe('conventional-commits', () => { ### Bug Fixes - * A second commit for our CHANGELOG ([SHA](https://github.com/lerna/conventional-commits-fixed/commit/GIT_HEAD)) (@Tester-McPerson) + * A second commit for our CHANGELOG ([SHA](https://github.com/lerna/conventional-commits-fixed/commit/GIT_HEAD)) (Tester McPerson) `); await gitAdd(cwd, pkg1.manifestLocation); @@ -472,7 +472,7 @@ describe('conventional-commits', () => { await gitCommit(cwd, 'fix: A third commit for our CHANGELOG'); const lastRootChangelog = await updateChangelog({ location: cwd } as Package, 'root', { - changelogIncludeCommitAuthor: true, + changelogIncludeCommitAuthorFullname: true, tagPrefix: 'dragons-are-awesome', version: '1.0.2', }); @@ -484,7 +484,7 @@ describe('conventional-commits', () => { ### Bug Fixes - * A third commit for our CHANGELOG ([SHA](https://github.com/lerna/conventional-commits-fixed/commit/GIT_HEAD)) (@Tester-McPerson) + * A third commit for our CHANGELOG ([SHA](https://github.com/lerna/conventional-commits-fixed/commit/GIT_HEAD)) (Tester McPerson) `); }); @@ -656,7 +656,7 @@ describe('conventional-commits', () => { `); }); - it('updates independent changelogs and include commit author', async () => { + it('updates independent changelogs and include commit author full name', async () => { const cwd = await initFixture('independent'); await gitTag(cwd, 'package-1@1.0.0'); @@ -680,7 +680,7 @@ describe('conventional-commits', () => { const opts = { changelogPreset: 'conventional-changelog-angular', - changelogIncludeCommitAuthor: true, + changelogIncludeCommitAuthorFullname: true, }; const [changelogOne, changelogTwo] = await Promise.all([ updateChangelog(pkg1, 'independent', opts), @@ -693,7 +693,7 @@ describe('conventional-commits', () => { ### Bug Fixes - * **stuff:** changed ([SHA](https://github.com/lerna/conventional-commits-independent/commit/GIT_HEAD)) (@Tester-McPerson) + * **stuff:** changed ([SHA](https://github.com/lerna/conventional-commits-independent/commit/GIT_HEAD)) (Tester McPerson) `); expect(changelogTwo.newEntry.trimRight()).toMatchInlineSnapshot(` # [1.1.0](/compare/package-2@1.0.0...package-2@1.1.0) (YYYY-MM-DD) @@ -701,7 +701,56 @@ describe('conventional-commits', () => { ### Features - * **thing:** added ([SHA](https://github.com/lerna/conventional-commits-independent/commit/GIT_HEAD)) (@Tester-McPerson) + * **thing:** added ([SHA](https://github.com/lerna/conventional-commits-independent/commit/GIT_HEAD)) (Tester McPerson) + `); + }); + + it('updates independent changelogs and include commit author full name with a custom format when defined', async () => { + const cwd = await initFixture('independent'); + + await gitTag(cwd, 'package-1@1.0.0'); + await gitTag(cwd, 'package-2@1.0.0'); + + const [pkg1, pkg2] = await Project.getPackages(cwd); + + // make a change in package-1 and package-2 + await pkg1.set('changed', 1).serialize(); + await pkg2.set('changed', 2).serialize(); + + await gitAdd(cwd, pkg1.manifestLocation); + await gitCommit(cwd, 'fix(stuff): changed'); + + await gitAdd(cwd, pkg2.manifestLocation); + await gitCommit(cwd, 'feat(thing): added'); + + // update versions + await pkg1.set('version', '1.0.1').serialize(); + await pkg2.set('version', '1.1.0').serialize(); + + const opts = { + changelogPreset: 'conventional-changelog-angular', + changelogIncludeCommitAuthorFullname: ' by <**%a**>', + }; + const [changelogOne, changelogTwo] = await Promise.all([ + updateChangelog(pkg1, 'independent', opts), + updateChangelog(pkg2, 'independent', opts), + ]); + + expect(changelogOne.newEntry.trimRight()).toMatchInlineSnapshot(` + ## [1.0.1](/compare/package-1@1.0.0...package-1@1.0.1) (YYYY-MM-DD) + + + ### Bug Fixes + + * **stuff:** changed ([SHA](https://github.com/lerna/conventional-commits-independent/commit/GIT_HEAD)) by <**Tester McPerson**> + `); + expect(changelogTwo.newEntry.trimRight()).toMatchInlineSnapshot(` + # [1.1.0](/compare/package-2@1.0.0...package-2@1.1.0) (YYYY-MM-DD) + + + ### Features + + * **thing:** added ([SHA](https://github.com/lerna/conventional-commits-independent/commit/GIT_HEAD)) by <**Tester McPerson**> `); }); }); diff --git a/packages/core/src/conventional-commits/update-changelog.ts b/packages/core/src/conventional-commits/update-changelog.ts index 2ab72235..91e27648 100644 --- a/packages/core/src/conventional-commits/update-changelog.ts +++ b/packages/core/src/conventional-commits/update-changelog.ts @@ -26,8 +26,8 @@ export async function updateChangelog( changelogPreset, rootPath, tagPrefix = 'v', - version = undefined, - changelogIncludeCommitAuthor = false, + version, + changelogIncludeCommitAuthorFullname, changelogHeaderMessage = '', changelogVersionMessage = '', } = updateOptions; @@ -53,7 +53,7 @@ export async function updateChangelog( // we will later extract a defined token from the string, of ">>author=%an<<", // and reformat the string to get a commit string that would add (@authorName) to the end of the commit string, ie: // **deps:** update all non-major dependencies ([ed1db35](https://github.com/ghiscoding/lerna-lite/commit/ed1db35)) (@Renovate-Bot) - if (changelogIncludeCommitAuthor) { + if (changelogIncludeCommitAuthorFullname) { gitRawCommitsOpts.format = '%B%n-hash-%n%H>>author=%an<<'; } @@ -91,7 +91,9 @@ export async function updateChangelog( readExistingChangelog(pkg), ]).then(([inputEntry, [changelogFileLoc, changelogContents]]) => { // are we including commit author's name in changelog? - const newEntry = changelogIncludeCommitAuthor ? parseChangelogCommitAuthorName(inputEntry) : inputEntry; + const newEntry = changelogIncludeCommitAuthorFullname + ? parseChangelogCommitAuthorFullName(inputEntry, changelogIncludeCommitAuthorFullname) + : inputEntry; log.silly(type, 'writing new entry: %j', newEntry); @@ -132,17 +134,24 @@ export async function updateChangelog( * @param changelogEntry - changelog entry of a version being released which can contain multiple line entries * @returns */ -function parseChangelogCommitAuthorName(changelogEntry: string) { +function parseChangelogCommitAuthorFullName(changelogEntry: string, commitAuthorFullnameMessage?: string | boolean) { // to transform the string into what we want, we need to move the substring outside of the url and remove extra search tokens // from this: // "...ed1db35>>author=Renovate Bot<<))" // into this: - // "...ed1db35)) (@Renovate-Bot)" + // "...ed1db35)) (Renovate-Bot)" + // or as a custom message like this " by **%a**" into this: + // "...ed1db35)) by **Renovate-Bot**" return changelogEntry.replace( /(.*)(>>author=)(.*)(<<)(.*)/g, - (_: string, lineStart: string, _tokenStart?: string, author?: string, _tokenEnd?: string, lineEnd?: string) => { + (_: string, lineStart: string, _tokenStart?: string, authorName?: string, _tokenEnd?: string, lineEnd?: string) => { // rebuild the commit string, we'll also replace any whitespaces to hypen in author's name to make it a valid "@" user ref - return `${lineStart}${lineEnd || ''} (@${author?.replace(/\s/g, '-') ?? ''})`; + const commitMsg = `${lineStart}${lineEnd || ''}`; + const authorMsg = + typeof commitAuthorFullnameMessage === 'string' + ? commitAuthorFullnameMessage.replace(/%a/g, authorName || '') + : ` (${authorName})`; + return commitMsg + authorMsg; } ); } diff --git a/packages/core/src/models/command-options.ts b/packages/core/src/models/command-options.ts index d6e465e1..329ed16c 100644 --- a/packages/core/src/models/command-options.ts +++ b/packages/core/src/models/command-options.ts @@ -189,10 +189,16 @@ export interface VersionCommandOption { /** Add a custom message at the top of your "changelog.md" which is located in the root of your project. This option only works when using --conventional-commits. */ changelogHeaderMessage?: string; - /** Specify if we want to include the commit author's name when using conventional-commits with changelog */ - changelogIncludeCommitAuthor?: boolean; + /** + * Specify if we want to include the commit author's name, when using conventional-commits with changelog. + * We can optionally provide a custom message or else a default format will be used. + */ + changelogIncludeCommitAuthorFullname?: boolean | string; - /** Add a custom message as a prefix to each new version in your "changelog.md" which is located in the root of your project. This option only works when using --conventional-commits. */ + /** + * Add a custom message as a prefix to each new version in your "changelog.md" which is located in the root of your project. + * This option only works when using --conventional-commits. + */ changelogVersionMessage?: string; /** Defaults 'angular', custom conventional-changelog preset. */ diff --git a/packages/core/src/models/interfaces.ts b/packages/core/src/models/interfaces.ts index 841fe0aa..709838e3 100644 --- a/packages/core/src/models/interfaces.ts +++ b/packages/core/src/models/interfaces.ts @@ -74,7 +74,7 @@ export interface UpdateChangelogOption { changelogHeaderMessage?: string; changelogVersionMessage?: string; changelogPreset?: string; - changelogIncludeCommitAuthor?: boolean; + changelogIncludeCommitAuthorFullname?: boolean | string; rootPath?: string; tagPrefix?: string; version?: string; diff --git a/packages/version/README.md b/packages/version/README.md index 4cb755d6..ae97f112 100644 --- a/packages/version/README.md +++ b/packages/version/README.md @@ -76,7 +76,7 @@ Running `lerna version --conventional-commits` without the above flags will rele - [`--conventional-commits`](#--conventional-commits) - [`--conventional-graduate`](#--conventional-graduate) - [`--conventional-prerelease`](#--conventional-prerelease) - - [`--changelog-include-commit-author`](#--changelog-include-commit-author) (new) + - [`--changelog-include-commit-author-fullname [msg]`](#--changelog-include-commit-author-fullname-msg) (new) - [`--changelog-header-message `](#--changelog-header-message-msg) (new) - [`--changelog-version-message `](#--changelog-version-message-msg) (new) - [`--create-release `](#--create-release-type) @@ -230,16 +230,34 @@ lerna version --conventional-commits --conventional-prerelease When run with this flag, `lerna version` will release with prerelease versions the specified packages (comma-separated) or all packages using `*`. Releases all unreleased changes as pre(patch/minor/major/release) by prefixing the version recommendation from `conventional-commits` with `pre`, eg. if present changes include a feature commit, the recommended bump will be `minor`, so this flag will result in a `preminor` release. If changes are present for packages that are not specified (if specifying packages), or for packages that are already in prerelease, those packages will be versioned as they normally would using `--conventional-commits`. -### `--changelog-include-commit-author` -Specify if we want to include the commit author's name, at the end of each commit entry, when using `--conventional-commits` with changelogs. +### `--changelog-include-commit-author-fullname [msg]` +Specify if we want to include the git commit author's name, at the end of each changelog commit entry, this is only available when using `--conventional-commits` with changelogs. The default format will append the author's name at the end of each commit entry and wrapped in `()`, for exampe "feat: commit message (Author Name)". We could also use a custom format by providing the `%a` token. Note that in every case, the author's name will always be appended as a suffix to each changelog commit entry. + +> **Note** that the author name is the name that was given in the user's Git config, refer to [Git Configuration](https://www.git-scm.com/book/en/v2/Customizing-Git-Git-Configuration) for more info. In other words, this is **not** the same as a GitHub login username. + +```sh +lerna version --conventional-commits --changelog-include-commit-author-fullname +``` + +See below for a sample of a changelog entry with the author's full name (note the url was shorten up for simplicity) +#### Default Format +The default format will append the author's name (wrapped in `()`) to the end of the commit message ```sh -lerna version --conventional-commits --changelog-include-commit-author +* **deps:** update dependency git-url-parse to v12 ([978bf36](https://github.com/ghiscoding/lerna-lite/commit/978bf36)) (Renovate Bot) ``` -See below for a sample of a changelog entry with author (note the url was shorten up for simplicity) +#### Custom Format +If we want to provide a default format, we can do so by using the `%a` token + +```sh +lerna version --conventional-commits --changelog-include-commit-author-fullname " by _%a_" +``` + +will show the following (the use of `_` in this case would display the name in italic) + ```sh -* **deps:** update dependency git-url-parse to v12 ([978bf36](https://github.com/ghiscoding/lerna-lite/commit/978bf36)) (@Renovate-Bot) +* **deps:** update dependency git-url-parse to v12 ([978bf36](https://github.com/ghiscoding/lerna-lite/commit/978bf36)) by _Renovate Bot_ ``` ### `--changelog-header-message ` diff --git a/packages/version/src/version-command.ts b/packages/version/src/version-command.ts index dde46a6f..289d8fcc 100644 --- a/packages/version/src/version-command.ts +++ b/packages/version/src/version-command.ts @@ -601,7 +601,7 @@ export class VersionCommand extends Command { changelogPreset, rootPath, tagPrefix: this.tagPrefix, - changelogIncludeCommitAuthor: this.options.changelogIncludeCommitAuthor, + changelogIncludeCommitAuthorFullname: this.options.changelogIncludeCommitAuthorFullname, changelogHeaderMessage: this.options.changelogHeaderMessage, changelogVersionMessage: this.options.changelogVersionMessage, }).then(({ logPath, newEntry }) => { @@ -683,7 +683,7 @@ export class VersionCommand extends Command { rootPath, tagPrefix: this.tagPrefix, version: this.globalVersion, - changelogIncludeCommitAuthor: this.options.changelogIncludeCommitAuthor, + changelogIncludeCommitAuthorFullname: this.options.changelogIncludeCommitAuthorFullname, changelogHeaderMessage: this.options.changelogHeaderMessage, changelogVersionMessage: this.options.changelogVersionMessage, }).then(({ logPath, newEntry }) => {