Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ or

`npm run auto-changelog update`

#### Use Conventional Commits prefixes to auto-categorize changes

`yarn run auto-changelog update --autoCategorize`

#### Update the current release section of the changelog

`yarn run auto-changelog update --rc`
Expand Down
17 changes: 17 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ type UpdateOptions = {
projectRootDirectory?: string;
tagPrefix: string;
formatter: Formatter;
autoCategorize: boolean;
/**
* The package rename properties, used in case of package is renamed
*/
Expand All @@ -109,6 +110,7 @@ type UpdateOptions = {
* @param options.tagPrefix - The prefix used in tags before the version number.
* @param options.formatter - A custom Markdown formatter to use.
* @param options.packageRename - The package rename properties.
* @param options.autoCategorize - Whether to categorize commits automatically based on their messages.
* An optional, which is required only in case of package renamed.
*/
async function update({
Expand All @@ -120,6 +122,7 @@ async function update({
tagPrefix,
formatter,
packageRename,
autoCategorize,
}: UpdateOptions) {
const changelogContent = await readChangelog(changelogPath);

Expand All @@ -132,6 +135,7 @@ async function update({
tagPrefixes: [tagPrefix],
formatter,
packageRename,
autoCategorize,
});

if (newChangelogContent) {
Expand Down Expand Up @@ -278,6 +282,12 @@ function configureCommonCommandOptions(_yargs: Argv) {
description: 'A version of the package before being renamed.',
type: 'string',
})
.option('autoCategorize', {
default: false,
description:
'Automatically categorize commits based on Conventional Commits prefixes.',
type: 'boolean',
})
.option('tagPrefixBeforePackageRename', {
description: 'A tag prefix of the package before being renamed.',
type: 'string',
Expand All @@ -304,6 +314,11 @@ async function main() {
'The current version of the project that the changelog belongs to.',
type: 'string',
})
.option('autoCategorize', {
default: false,
description:
'Automatically categorize commits based on their messages.',
})
.option('prettier', {
default: true,
description: `Expect the changelog to be formatted with Prettier.`,
Expand Down Expand Up @@ -358,6 +373,7 @@ async function main() {
prettier: usePrettier,
versionBeforePackageRename,
tagPrefixBeforePackageRename,
autoCategorize,
} = argv;
let { currentVersion } = argv;

Expand Down Expand Up @@ -486,6 +502,7 @@ async function main() {
tagPrefix,
formatter,
packageRename,
autoCategorize,
});
} else if (command === 'validate') {
let packageRename: PackageRename | undefined;
Expand Down
13 changes: 13 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,19 @@
*/
export type Version = string;

/**
* A [Conventional Commit](https://www.conventionalcommits.org/en/v1.0.0/) type.
*/
export enum ConventionalCommitType {
/**
* fix: a commit of the type fix patches a bug in your codebase
*/
Fix = 'fix',
/**
* a commit of the type feat introduces a new feature to the codebase
*/
Feat = 'feat',
}
/**
* Change categories.
*
Expand Down
53 changes: 53 additions & 0 deletions src/update-changelog.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import * as ChangeLogManager from './update-changelog';

const uncategorizedChangelog = `# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [1.0.0]
### Uncategorized
- fix: a commit of the type fix patches a bug in your codebase
- feat: a commit of the type feat introduces a new feature to the codebase
- unknown: a commit of the type unknown does not match any of the conventional commit types
[Unreleased]: https://github.com/ExampleUsernameOrOrganization/ExampleRepository/
`;

describe('updateChangelog', () => {
it('should contain conventional support mappings categorization', async () => {
// Call updateChangelog, which internally calls the mocked getNewChangeEntries
const result = await ChangeLogManager.updateChangelog({
changelogContent: uncategorizedChangelog,
currentVersion: '1.0.0',
repoUrl:
'https://github.com/ExampleUsernameOrOrganization/ExampleRepository',
isReleaseCandidate: true,
autoCategorize: true,
});

console.log(result);

// Verify that some of the commits are included in the result
expect(result).toContain('### Fixed');
});

it('should not contain conventional support mappings categorization', async () => {
// Call updateChangelog, which internally calls the mocked getNewChangeEntries
const result = await ChangeLogManager.updateChangelog({
changelogContent: uncategorizedChangelog,
currentVersion: '1.0.0',
repoUrl:
'https://github.com/ExampleUsernameOrOrganization/ExampleRepository',
isReleaseCandidate: true,
autoCategorize: false,
});

// Verify that some of the commits are included in the result
expect(result).toContain('### Uncategorized');
expect(result).not.toContain('### Fixed');
});
});
40 changes: 37 additions & 3 deletions src/update-changelog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import type Changelog from './changelog';
import { Formatter, getKnownPropertyNames } from './changelog';
import { ChangeCategory, Version } from './constants';
import { ChangeCategory, Version, ConventionalCommitType } from './constants';
import { parseChangelog } from './parse-changelog';
import { PackageRename } from './shared-types';

Expand Down Expand Up @@ -180,7 +180,7 @@
* current git repository.
* @returns A list of new change entries to add to the changelog, based on commits made since the last release.
*/
async function getNewChangeEntries({
export async function getNewChangeEntries({
mostRecentTag,
repoUrl,
loggedPrNumbers,
Expand Down Expand Up @@ -215,6 +215,7 @@
projectRootDirectory?: string;
tagPrefixes?: [string, ...string[]];
formatter?: Formatter;
autoCategorize?: boolean;
/**
* The package rename properties, used in case of package is renamed
*/
Expand Down Expand Up @@ -243,6 +244,8 @@
* @param options.formatter - A custom Markdown formatter to use.
* @param options.packageRename - The package rename properties.
* An optional, which is required only in case of package renamed.
* @param options.autoCategorize - A flag indicating whether changes should be auto-categorized
* based on commit message prefixes.
* @returns The updated changelog text.
*/
export async function updateChangelog({
Expand All @@ -254,6 +257,7 @@
tagPrefixes = ['v'],
formatter = undefined,
packageRename,
autoCategorize,
}: UpdateChangelogOptions): Promise<string | undefined> {
const changelog = parseChangelog({
changelogContent,
Expand All @@ -263,6 +267,8 @@
packageRename,
});

console.log(`autoCategorize: ${autoCategorize}`);

Check failure on line 270 in src/update-changelog.ts

View workflow job for this annotation

GitHub Actions / Lint (18.x)

Invalid type "boolean | undefined" of template literal expression

Check failure on line 270 in src/update-changelog.ts

View workflow job for this annotation

GitHub Actions / Lint (20.x)

Invalid type "boolean | undefined" of template literal expression

const mostRecentTag = await getMostRecentTag({
tagPrefixes,
});
Expand Down Expand Up @@ -303,9 +309,13 @@
});

for (const description of newChangeEntries.reverse()) {
const category = autoCategorize
? getCategory(description)
: ChangeCategory.Uncategorized;

changelog.addChange({
version: isReleaseCandidate ? currentVersion : undefined,
category: ChangeCategory.Uncategorized,
category,
description,
});
}
Expand All @@ -329,3 +339,27 @@
.split('\n')
.filter((line) => line !== '');
}

/**
* Determine the category of a change based on the commit message prefix.
*
* @param description - The commit message description.
* @returns The category of the change.
*/
function getCategory(description: string): ChangeCategory {
// Check if description contains a colon
if (description.includes(':')) {
const [prefix] = description.split(':').map((part) => part.trim());
switch (prefix) {
case ConventionalCommitType.Feat:
return ChangeCategory.Added;
case ConventionalCommitType.Fix:
return ChangeCategory.Fixed;
default:
return ChangeCategory.Uncategorized;
}
}

// Return 'Uncategorized' if no colon is found or prefix doesn't match
return ChangeCategory.Uncategorized;
}
Loading