Skip to content

Commit

Permalink
feat(makeReleaseNotes): add inclusive and footers options; closes #24;
Browse files Browse the repository at this point in the history
…closes #25
  • Loading branch information
pskfyi committed May 1, 2023
1 parent b8df0ba commit 7839429
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 25 deletions.
106 changes: 83 additions & 23 deletions git/script/makeReleaseNotes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@ async function getCommitsByType(
log: (message: string) => void,
commit?: string,
types?: string[],
inclusive = false,
) {
commit ??= await getLatestTag();
const rawCommits = (await getCommitSpan([commit, "HEAD"])).reverse();
const rawCommits = (await getCommitSpan([commit, "HEAD"], { inclusive }))
.reverse();

log(` Commits since ${commit}: ${rawCommits.length}`);

Expand Down Expand Up @@ -89,10 +91,17 @@ function sortTypes(
].sort();
}

type MarkDownOptions = {
groupByType?: boolean;
includeFooters?: boolean;
};

function asListItem(
{ type, scope, description, body, breakingChange, hash }: CommitInfo,
groupByType: boolean,
{ type, scope, description, body, footers, breakingChange, hash }: CommitInfo,
opts: MarkDownOptions,
) {
const { groupByType = false, includeFooters = false } = opts;

const typeAndScope = groupByType
? scope ? `(${scope})` : ""
: scope
Expand All @@ -113,40 +122,57 @@ function asListItem(

if (body) listItem += `\n\n${indent(body, 2).replace(/^ +$/mg, "")}`;

if (includeFooters && footers && footers.length) {
const footersSegment = footers
.map(({ key, value }) => `${key}: ${value}`)
.join("\n\n");

listItem += "\n\n" + indent(footersSegment, 2).replace(/^ +$/mg, "");
}

return listItem;
}

function asUnorderedList(commits: CommitInfo[], groupByType = false) {
return commits.map((c) => asListItem(c, groupByType)).join("\n\n");
function asUnorderedList(commits: CommitInfo[], opts: MarkDownOptions) {
return commits.map((c) => asListItem(c, opts)).join("\n\n");
}

function releaseNotesUngrouped(
sortedTypes: string[],
breaking: Record<string, CommitInfo[]>,
nonBreaking: Record<string, CommitInfo[]>,
) {
function releaseNotesUngrouped({
sortedTypes,
breaking,
nonBreaking,
includeFooters,
}: ReleaseNotesOptions) {
let breakingMarkdown = "";
let nonBreakingMarkdown = "";

for (const type of sortedTypes) {
const breakingCommits = breaking[type] ?? [];
const nonBreakingCommits = nonBreaking[type] ?? [];

const opts = { includeFooters };

breakingCommits.length &&
(breakingMarkdown += asUnorderedList(breakingCommits) + "\n\n");
(breakingMarkdown += asUnorderedList(breakingCommits, opts) + "\n\n");

nonBreakingCommits.length &&
(nonBreakingMarkdown += asUnorderedList(nonBreakingCommits) + "\n\n");
(nonBreakingMarkdown += asUnorderedList(nonBreakingCommits, opts) +
"\n\n");
}

return breakingMarkdown + nonBreakingMarkdown;
}

type ReleaseNotesOptions = {
sortedTypes: string[];
breaking: Record<string, CommitInfo[]>;
nonBreaking: Record<string, CommitInfo[]>;
includeFooters?: boolean;
};

function releaseNotesByType(
sortedTypes: string[],
typeNames: Record<string, string>,
breaking: Record<string, CommitInfo[]>,
nonBreaking: Record<string, CommitInfo[]>,
{ sortedTypes, breaking, nonBreaking, includeFooters }: ReleaseNotesOptions,
) {
typeNames = { ...TYPE_NAMES, ...typeNames };
let markdown = "";
Expand All @@ -157,13 +183,15 @@ function releaseNotesByType(

if (!breakingCommits.length && !nonBreakingCommits.length) continue;

const opts = { groupByType: true, includeFooters };

markdown += `## ${typeName(type, typeNames)}\n\n`;

breakingCommits.length &&
(markdown += asUnorderedList(breakingCommits, true) + "\n\n");
(markdown += asUnorderedList(breakingCommits, opts) + "\n\n");

nonBreakingCommits.length &&
(markdown += asUnorderedList(nonBreakingCommits, true) + "\n\n");
(markdown += asUnorderedList(nonBreakingCommits, opts) + "\n\n");
}

return markdown;
Expand All @@ -172,24 +200,54 @@ function releaseNotesByType(
export type MakeReleaseNotesOptions = {
cwd?: string;
commit?: string;
/** Whether to include the starting commit in the release notes.
*
* @example
* // find commits since latest tag, but not including it
* makeReleaseNotes()
* // include latest tag commit
* makeReleaseNotes({ inclusive: true })
*
* // find commits since commit abc1234, but not including it
* makeReleaseNotes({ commit: "abc1234" })
* // include commit abc1234
* makeReleaseNotes({ commit: "abc1234", inclusive: true })
*/
inclusive?: boolean;
/** Whether to include conventional commit footers in the release notes. */
includeFooters?: boolean;
verbose?: boolean;
groupByType?: boolean;
types?: string[];
typeNames?: Record<string, string>;
};

export async function makeReleaseNotes(
{ cwd, commit, verbose, groupByType, types, typeNames = {} }:
MakeReleaseNotesOptions = {},
{
cwd,
commit,
inclusive,
includeFooters,
verbose,
groupByType,
types,
typeNames = {},
}: MakeReleaseNotesOptions = {},
) {
if (cwd) Deno.chdir(cwd);

const log = (message: string) => verbose && console.log(message);
const [breaking, nonBreaking] = await getCommitsByType(log, commit, types);
const [breaking, nonBreaking] = await getCommitsByType(
log,
commit,
types,
inclusive,
);
const sortedTypes = types ? types : sortTypes(breaking, nonBreaking);
const opts = { sortedTypes, breaking, nonBreaking, includeFooters };
const markdown = groupByType
? releaseNotesByType(sortedTypes, typeNames, breaking, nonBreaking)
: releaseNotesUngrouped(sortedTypes, breaking, nonBreaking);
? releaseNotesByType(typeNames, opts)
: releaseNotesUngrouped(opts);

return markdown.trim();
}
Expand Down Expand Up @@ -219,14 +277,16 @@ Usage:
Options:
-h, --help Show this help message
-c, --to-clipboard Copy release notes to clipboard
-i, --inclusive Include the first commit
-f, --footers Include commit footers
-v, --verbose Print verbose output
-g, --group-by-type Group commits by type using H2 headings
--commit=<commit> Commit to use as base for release notes
--types=<types> Comma-separated list of types to include
--<type>=<name> Name to use for a type's H2 when grouping by type
Examples:
deno run -A https://deno.land/x/make_release_notes/mod.ts -cgv
deno run -A https://deno.land/x/make_release_notes/mod.ts -cfgv
deno run -A https://deno.land/x/make_release_notes/mod.ts --commit v1.0.0
Expand Down
6 changes: 4 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,19 +112,21 @@ Usage:
Options:
-h, --help Show this help message
-c, --to-clipboard Copy release notes to clipboard
-i, --inclusive Include the first commit
-f, --footers Include commit footers
-v, --verbose Print verbose output
-g, --group-by-type Group commits by type using H2 headings
--commit=<commit> Commit to use as base for release notes
--types=<types> Comma-separated list of types to include
--<type>=<name> Name to use for a type's H2 when grouping by type
Examples:
deno run -A https://deno.land/x/make_release_notes/mod.ts -cgv
deno run -A https://deno.land/x/make_release_notes/mod.ts -cfgv
deno run -A https://deno.land/x/make_release_notes/mod.ts --commit v1.0.0
deno run -A https://deno.land/x/make_release_notes/mod.ts \
--types=feat,custom --custom="Custom Section Heading"
--types=feat,custom --custom="Custom's Section Heading"
```

## `graph`
Expand Down

0 comments on commit 7839429

Please sign in to comment.