fix(release): allow version plans to have multi-line, arbitrarily formatted messages (#27323)

This commit is contained in:
James Henry 2024-08-08 01:27:34 +04:00 committed by GitHub
parent 555182353b
commit 7e2266177d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 276 additions and 187 deletions

View File

@ -114,7 +114,7 @@ describe('nx release version plans', () => {
await ensureDir(versionPlansDir);
runCLI(
'release plan minor -g fixed-group -m "feat: Update the fixed packages with a minor release." --verbose',
'release plan minor -g fixed-group -m "Update the fixed packages with a minor release." --verbose',
{
silenceError: true,
}
@ -128,7 +128,9 @@ ${pkg4}: preminor
${pkg5}: prerelease
---
feat: Update the independent packages with a patch, preminor, and prerelease.
Update the independent packages with a patch, preminor, and prerelease.
Here is another line in the message.
`
);
@ -193,9 +195,11 @@ feat: Update the independent packages with a patch, preminor, and prerelease.
+ ## 0.0.1 (YYYY-MM-DD)
+
+
+ ### 🚀 Features
+ ### 🩹 Fixes
+
+ - Update the independent packages with a patch, preminor, and prerelease.`
+ - **${pkg3}:** Update the independent packages with a patch, preminor, and prerelease.
+
+ Here is another line in the message.`
);
expect(resultWithoutDate).toContain(
@ -207,7 +211,9 @@ feat: Update the independent packages with a patch, preminor, and prerelease.
+
+ ### 🚀 Features
+
+ - Update the independent packages with a patch, preminor, and prerelease.`
+ - **${pkg4}:** Update the independent packages with a patch, preminor, and prerelease.
+
+ Here is another line in the message.`
);
expect(resultWithoutDate).toContain(
@ -217,9 +223,11 @@ feat: Update the independent packages with a patch, preminor, and prerelease.
+ ## 0.0.1-0 (YYYY-MM-DD)
+
+
+ ### 🚀 Features
+ ### 🩹 Fixes
+
+ - Update the independent packages with a patch, preminor, and prerelease.`
+ - **${pkg5}:** Update the independent packages with a patch, preminor, and prerelease.
+
+ Here is another line in the message.`
);
await writeFile(
@ -229,7 +237,7 @@ ${pkg1}: minor
${pkg3}: patch
---
fix: Update packages in both groups with a bug fix
Update packages in both groups with a mix #1
`
);
await writeFile(
@ -240,7 +248,7 @@ ${pkg4}: preminor
${pkg5}: patch
---
feat: Update packages in both groups with a feat
Update packages in both groups with a mix #2
`
);
@ -291,12 +299,12 @@ feat: Update packages in both groups with a feat
+
+ ### 🚀 Features
+
+ - Update packages in both groups with a feat
+ - **${pkg1}:** Update packages in both groups with a mix #1
+
+
+ ### 🩹 Fixes
+
+ - Update packages in both groups with a bug fix`
+ - Update packages in both groups with a mix #2`
);
expect(result2WithoutDate).toContain(
`NX Generating an entry in ${pkg2}/CHANGELOG.md for v0.2.0
@ -306,14 +314,9 @@ feat: Update packages in both groups with a feat
+ ## 0.2.0 (YYYY-MM-DD)
+
+
+ ### 🚀 Features
+
+ - Update packages in both groups with a feat
+
+
+ ### 🩹 Fixes
+
+ - Update packages in both groups with a bug fix
+ - Update packages in both groups with a mix #2
`
);
expect(result2WithoutDate).toContain(
@ -326,7 +329,7 @@ feat: Update packages in both groups with a feat
+
+ ### 🩹 Fixes
+
+ - Update packages in both groups with a bug fix`
+ - **${pkg3}:** Update packages in both groups with a mix #1`
);
expect(result2WithoutDate).toContain(
@ -339,7 +342,7 @@ feat: Update packages in both groups with a feat
+
+ ### 🚀 Features
+
+ - Update packages in both groups with a feat`
+ - **${pkg4}:** Update packages in both groups with a mix #2`
);
expect(result2WithoutDate).toContain(
@ -350,9 +353,9 @@ feat: Update packages in both groups with a feat
+ ## 0.0.1 (YYYY-MM-DD)
+
+
+ ### 🚀 Features
+ ### 🩹 Fixes
+
+ - Update packages in both groups with a feat`
+ - **${pkg5}:** Update packages in both groups with a mix #2`
);
expect(exists(join(versionPlansDir, 'bump-mixed1.md'))).toBeFalsy();
@ -394,7 +397,7 @@ feat: Update packages in both groups with a feat
fixed-group: minor
---
feat: Update the fixed packages with a minor release.
Update the fixed packages with a minor release.
`
);
@ -406,7 +409,7 @@ ${pkg4}: preminor
${pkg5}: prerelease
---
feat: Update the independent packages with a patch, preminor, and prerelease.
Update the independent packages with a patch, preminor, and prerelease.
`
);
@ -530,9 +533,9 @@ const yargs = require('yargs');
+ ## 0.0.1 (YYYY-MM-DD)
+
+
+ ### 🚀 Features
+ ### 🩹 Fixes
+
+ - Update the independent packages with a patch, preminor, and prerelease.`
+ - **${pkg3}:** Update the independent packages with a patch, preminor, and prerelease.`
);
expect(resultWithoutDate).toContain(
@ -544,7 +547,7 @@ const yargs = require('yargs');
+
+ ### 🚀 Features
+
+ - Update the independent packages with a patch, preminor, and prerelease.`
+ - **${pkg4}:** Update the independent packages with a patch, preminor, and prerelease.`
);
expect(resultWithoutDate).toContain(
@ -554,9 +557,9 @@ const yargs = require('yargs');
+ ## 0.0.1-0 (YYYY-MM-DD)
+
+
+ ### 🚀 Features
+ ### 🩹 Fixes
+
+ - Update the independent packages with a patch, preminor, and prerelease.`
+ - **${pkg5}:** Update the independent packages with a patch, preminor, and prerelease.`
);
expect(exists(join(versionPlansDir, 'bump-fixed.md'))).toBeFalsy();
@ -569,8 +572,8 @@ ${pkg1}: minor
${pkg3}: patch
---
fix: Update packages in both groups with a bug fix
`
Update packages in both groups with a mix #1
`
);
await writeFile(
join(versionPlansDir, 'bump-mixed2.md'),
@ -580,8 +583,8 @@ ${pkg4}: preminor
${pkg5}: patch
---
feat: Update packages in both groups with a feat
`
Update packages in both groups with a mix #2
`
);
await runCommandAsync(`git add ${join(versionPlansDir, 'bump-mixed1.md')}`);
@ -631,12 +634,12 @@ feat: Update packages in both groups with a feat
+
+ ### 🚀 Features
+
+ - Update packages in both groups with a feat
+ - **${pkg1}:** Update packages in both groups with a mix #1
+
+
+ ### 🩹 Fixes
+
+ - Update packages in both groups with a bug fix`
+ - Update packages in both groups with a mix #2`
);
expect(result2WithoutDate).toContain(
`NX Generating an entry in ${pkg2}/CHANGELOG.md for v0.2.0
@ -646,14 +649,9 @@ feat: Update packages in both groups with a feat
+ ## 0.2.0 (YYYY-MM-DD)
+
+
+ ### 🚀 Features
+
+ - Update packages in both groups with a feat
+
+
+ ### 🩹 Fixes
+
+ - Update packages in both groups with a bug fix
+ - Update packages in both groups with a mix #2
`
);
expect(result2WithoutDate).toContain(
@ -666,7 +664,7 @@ feat: Update packages in both groups with a feat
+
+ ### 🩹 Fixes
+
+ - Update packages in both groups with a bug fix`
+ - **${pkg3}:** Update packages in both groups with a mix #1`
);
expect(result2WithoutDate).toContain(
@ -679,7 +677,7 @@ feat: Update packages in both groups with a feat
+
+ ### 🚀 Features
+
+ - Update packages in both groups with a feat`
+ - **${pkg4}:** Update packages in both groups with a mix #2`
);
expect(result2WithoutDate).toContain(
@ -690,9 +688,9 @@ feat: Update packages in both groups with a feat
+ ## 0.0.1 (YYYY-MM-DD)
+
+
+ ### 🚀 Features
+ ### 🩹 Fixes
+
+ - Update packages in both groups with a feat`
+ - **${pkg5}:** Update packages in both groups with a mix #2`
);
expect(exists(join(versionPlansDir, 'bump-mixed1.md'))).toBeFalsy();
@ -715,7 +713,7 @@ feat: Update packages in both groups with a feat
await ensureDir(versionPlansDir);
runCLI(
'release plan minor -m "feat: Update the fixed packages with a minor release." --verbose',
'release plan minor -m "Update the fixed packages with a minor release." --verbose',
{
silenceError: true,
}

View File

@ -404,14 +404,30 @@ function formatChange(
changelogRenderOptions: DefaultChangelogRenderOptions,
repoSlug?: RepoSlug
): string {
let description = change.description;
let extraLines = [];
let extraLinesStr = '';
if (description.includes('\n')) {
[description, ...extraLines] = description.split('\n');
// Align the extra lines with the start of the description for better readability
const indentation = ' ';
extraLinesStr = extraLines
.filter((l) => l.trim().length > 0)
.map((l) => `${indentation}${l}`)
.join('\n');
}
let changeLine =
'- ' +
(change.isBreaking ? '⚠️ ' : '') +
(change.scope ? `**${change.scope.trim()}:** ` : '') +
change.description;
description;
if (repoSlug && changelogRenderOptions.commitReferences) {
changeLine += formatReferences(change.githubReferences, repoSlug);
}
if (extraLinesStr) {
changeLine += '\n\n' + extraLinesStr;
}
return changeLine;
}

View File

@ -2,7 +2,7 @@ import * as chalk from 'chalk';
import { prompt } from 'enquirer';
import { removeSync } from 'fs-extra';
import { readFileSync, writeFileSync } from 'node:fs';
import { valid } from 'semver';
import { ReleaseType, valid } from 'semver';
import { dirSync } from 'tmp';
import type { DependencyBump } from '../../../release/changelog-renderer';
import {
@ -56,7 +56,6 @@ import {
gitPush,
gitTag,
parseCommits,
parseConventionalCommitsMessage,
} from './utils/git';
import { createOrUpdateGithubRelease, getGitHubRepoSlug } from './utils/github';
import { launchEditor } from './utils/launch-editor';
@ -281,30 +280,37 @@ export function createAPI(overrideReleaseConfig: NxReleaseConfiguration) {
const releaseGroup = releaseGroups[0];
if (releaseGroup.projectsRelationship === 'fixed') {
const versionPlans = releaseGroup.versionPlans as GroupVersionPlan[];
workspaceChangelogChanges = filterHiddenChanges(
versionPlans
.map((vp) => {
const parsedMessage = parseConventionalCommitsMessage(
vp.message
);
// only properly formatted conventional commits messages will be included in the changelog
if (!parsedMessage) {
return null;
}
return <ChangelogChange>{
type: parsedMessage.type,
scope: parsedMessage.scope,
description: parsedMessage.description,
body: '',
isBreaking: parsedMessage.breaking,
githubReferences: [],
};
})
.filter(Boolean),
nxReleaseConfig.conventionalCommits
);
workspaceChangelogChanges = versionPlans
.flatMap((vp) => {
const releaseType = versionPlanSemverReleaseTypeToChangelogType(
vp.groupVersionBump
);
const changes: ChangelogChange | ChangelogChange[] =
!vp.triggeredByProjects
? {
type: releaseType.type,
scope: '',
description: vp.message,
body: '',
isBreaking: releaseType.isBreaking,
githubReferences: [],
affectedProjects: '*',
}
: vp.triggeredByProjects.map((project) => {
return {
type: releaseType.type,
scope: project,
description: vp.message,
body: '',
// TODO: what about github references?
isBreaking: releaseType.isBreaking,
githubReferences: [],
affectedProjects: [project],
};
});
return changes;
})
.filter(Boolean);
}
}
} else {
@ -485,31 +491,26 @@ export function createAPI(overrideReleaseConfig: NxReleaseConfiguration) {
let commits: GitCommit[];
if (releaseGroup.versionPlans) {
changes = filterHiddenChanges(
(releaseGroup.versionPlans as ProjectsVersionPlan[])
.map((vp) => {
const parsedMessage = parseConventionalCommitsMessage(
vp.message
);
// only properly formatted conventional commits messages will be included in the changelog
if (!parsedMessage) {
return null;
}
return {
type: parsedMessage.type,
scope: parsedMessage.scope,
description: parsedMessage.description,
body: '',
isBreaking: parsedMessage.breaking,
affectedProjects: Object.keys(vp.projectVersionBumps),
githubReferences: [],
};
})
.filter(Boolean),
nxReleaseConfig.conventionalCommits
);
changes = (releaseGroup.versionPlans as ProjectsVersionPlan[])
.map((vp) => {
const bumpForProject = vp.projectVersionBumps[project.name];
if (!bumpForProject) {
return null;
}
const releaseType =
versionPlanSemverReleaseTypeToChangelogType(bumpForProject);
return {
type: releaseType.type,
scope: project.name,
description: vp.message,
body: '',
isBreaking: releaseType.isBreaking,
affectedProjects: Object.keys(vp.projectVersionBumps),
// TODO: can we include github references when using version plans?
githubReferences: [],
};
})
.filter(Boolean);
} else {
let fromRef =
args.from ||
@ -637,31 +638,37 @@ export function createAPI(overrideReleaseConfig: NxReleaseConfiguration) {
// TODO: remove this after the changelog renderer is refactored to remove coupling with git commits
let commits: GitCommit[] = [];
if (releaseGroup.versionPlans) {
changes = filterHiddenChanges(
(releaseGroup.versionPlans as GroupVersionPlan[])
.map((vp) => {
const parsedMessage = parseConventionalCommitsMessage(
vp.message
);
// only properly formatted conventional commits messages will be included in the changelog
if (!parsedMessage) {
return null;
}
return <ChangelogChange>{
type: parsedMessage.type,
scope: parsedMessage.scope,
description: parsedMessage.description,
body: '',
isBreaking: parsedMessage.breaking,
githubReferences: [],
affectedProjects: '*',
};
})
.filter(Boolean),
nxReleaseConfig.conventionalCommits
);
changes = (releaseGroup.versionPlans as GroupVersionPlan[])
.flatMap((vp) => {
const releaseType = versionPlanSemverReleaseTypeToChangelogType(
vp.groupVersionBump
);
const changes: ChangelogChange | ChangelogChange[] =
!vp.triggeredByProjects
? {
type: releaseType.type,
scope: '',
description: vp.message,
body: '',
isBreaking: releaseType.isBreaking,
githubReferences: [],
affectedProjects: '*',
}
: vp.triggeredByProjects.map((project) => {
return {
type: releaseType.type,
scope: project,
description: vp.message,
body: '',
// TODO: what about github references?
isBreaking: releaseType.isBreaking,
githubReferences: [],
affectedProjects: [project],
};
});
return changes;
})
.filter(Boolean);
} else {
let fromRef =
args.from ||
@ -1408,3 +1415,23 @@ function createFileToProjectMap(
}
return fileToProjectMap;
}
function versionPlanSemverReleaseTypeToChangelogType(bump: ReleaseType): {
type: 'fix' | 'feat';
isBreaking: boolean;
} {
switch (bump) {
case 'premajor':
case 'major':
return { type: 'feat', isBreaking: true };
case 'preminor':
case 'minor':
return { type: 'feat', isBreaking: false };
case 'prerelease':
case 'prepatch':
case 'patch':
return { type: 'fix', isBreaking: false };
default:
throw new Error(`Invalid semver bump type: ${bump}`);
}
}

View File

@ -4,3 +4,5 @@ pkg4: minor
---
This is a change to packages 3 and 4
...and it includes multiple lines of text

View File

@ -75,7 +75,8 @@ describe('version-plans', () => {
pkg1: patch,
},
fileName: plan1.md,
message: This is a change to just package 1,
message: This is a change to just package 1
,
relativePath: .nx/version-plans/plan1.md,
},
{
@ -85,7 +86,8 @@ describe('version-plans', () => {
pkg2: patch,
},
fileName: plan2.md,
message: This is a change to package 1 and package 2,
message: This is a change to package 1 and package 2
,
relativePath: .nx/version-plans/plan2.md,
},
{
@ -95,7 +97,10 @@ describe('version-plans', () => {
pkg4: minor,
},
fileName: plan3.md,
message: This is a change to packages 3 and 4,
message: This is a change to packages 3 and 4
...and it includes multiple lines of text
,
relativePath: .nx/version-plans/plan3.md,
},
{
@ -107,7 +112,8 @@ describe('version-plans', () => {
pkg6: preminor,
},
fileName: plan4.md,
message: This is a change to packages 3, 4, 5, and 6,
message: This is a change to packages 3, 4, 5, and 6
,
relativePath: .nx/version-plans/plan4.md,
},
{
@ -116,7 +122,8 @@ describe('version-plans', () => {
fixed-group-1: minor,
},
fileName: plan5.md,
message: This is a change to fixed-group-1,
message: This is a change to fixed-group-1
,
relativePath: .nx/version-plans/plan5.md,
},
{
@ -127,7 +134,8 @@ describe('version-plans', () => {
pkg3: major,
},
fileName: plan6.md,
message: This is a major change to fixed-group-1 and pkg3 and a minor change to fixed-group-2,
message: This is a major change to fixed-group-1 and pkg3 and a minor change to fixed-group-2
,
relativePath: .nx/version-plans/plan6.md,
},
]
@ -786,6 +794,11 @@ describe('version-plans', () => {
groupVersionBump: patch,
message: plan1 message,
relativePath: .nx/version-plans/plan1.md,
triggeredByProjects: [
pkg1,
pkg2,
pkg3,
],
},
{
absolutePath: <workspace-root>/version-plans/plan2.md,
@ -794,6 +807,11 @@ describe('version-plans', () => {
groupVersionBump: minor,
message: plan2 message,
relativePath: .nx/version-plans/plan2.md,
triggeredByProjects: [
pkg1,
pkg2,
pkg3,
],
},
],
},
@ -1005,6 +1023,9 @@ describe('version-plans', () => {
groupVersionBump: minor,
message: plan2 message,
relativePath: .nx/version-plans/plan2.md,
triggeredByProjects: [
pkg1,
],
},
],
},
@ -1026,6 +1047,9 @@ describe('version-plans', () => {
groupVersionBump: minor,
message: plan2 message,
relativePath: .nx/version-plans/plan2.md,
triggeredByProjects: [
pkg2,
],
},
],
},
@ -1047,6 +1071,9 @@ describe('version-plans', () => {
groupVersionBump: minor,
message: plan2 message,
relativePath: .nx/version-plans/plan2.md,
triggeredByProjects: [
pkg3,
],
},
],
},

View File

@ -25,6 +25,11 @@ export interface VersionPlan extends VersionPlanFile {
export interface GroupVersionPlan extends VersionPlan {
groupVersionBump: ReleaseType;
/**
* Will not be set if the group name was the trigger, otherwise will be a list of
* all the individual project names explicitly found in the version plan file.
*/
triggeredByProjects?: string[];
}
export interface ProjectsVersionPlan extends VersionPlan {
@ -54,7 +59,7 @@ export async function readRawVersionPlans(): Promise<RawVersionPlan[]> {
relativePath: join(versionPlansDirectory, versionPlanFile),
fileName: versionPlanFile,
content: parsedContent.attributes,
message: getSingleLineMessage(parsedContent.body),
message: parsedContent.body,
createdOnMs: versionPlanStats.birthtimeMs,
});
}
@ -74,6 +79,12 @@ export function setVersionPlansOnGroups(
const isDefaultGroup = isDefault(releaseGroups);
for (const rawVersionPlan of rawVersionPlans) {
if (!rawVersionPlan.message) {
throw new Error(
`Please add a changelog message to version plan: '${rawVersionPlan.fileName}'`
);
}
for (const [key, value] of Object.entries(rawVersionPlan.content)) {
if (groupsByName.has(key)) {
const group = groupsByName.get(key);
@ -232,6 +243,8 @@ export function setVersionPlansOnGroups(
`Found a version bump for project '${key}' in '${rawVersionPlan.fileName}' that conflicts with another project's version bump in the same release group '${groupForProject.name}'. When the group is in fixed versioning mode, all projects' version bumps within the same group must match.`
);
}
} else {
existingPlan.triggeredByProjects.push(key);
}
} else {
groupForProject.versionPlans.push(<GroupVersionPlan>{
@ -241,7 +254,9 @@ export function setVersionPlansOnGroups(
createdOnMs: rawVersionPlan.createdOnMs,
message: rawVersionPlan.message,
// This is a fixed group, so the version bump is for the group, even if a project within it was specified
// but we track the projects that triggered the version bump so that we can accurately produce changelog entries.
groupVersionBump: value,
triggeredByProjects: [key],
});
}
}
@ -273,8 +288,3 @@ export function getVersionPlansAbsolutePath() {
function isReleaseType(value: string): value is ReleaseType {
return RELEASE_TYPES.includes(value as ReleaseType);
}
// changelog messages may only be a single line long, so ignore anything else
function getSingleLineMessage(message: string) {
return message.trim().split('\n')[0];
}

View File

@ -1,7 +1,8 @@
import { prompt } from 'enquirer';
import { ensureDir, writeFile } from 'fs-extra';
import { join } from 'path';
import { ensureDir, readFileSync, writeFile, writeFileSync } from 'fs-extra';
import { join } from 'node:path';
import { RELEASE_TYPES } from 'semver';
import { dirSync } from 'tmp';
import { NxReleaseConfiguration, readNxJson } from '../../config/nx-json';
import { createProjectFileMapUsingProjectGraph } from '../../project-graph/file-map-utils';
import { createProjectGraphAsync } from '../../project-graph/project-graph';
@ -13,13 +14,13 @@ import {
createNxReleaseConfig,
handleNxReleaseConfigError,
} from './config/config';
import { deepMergeJson } from './config/deep-merge-json';
import { filterReleaseGroups } from './config/filter-release-groups';
import { getVersionPlansAbsolutePath } from './config/version-plans';
import { generateVersionPlanContent } from './utils/generate-version-plan-content';
import { parseConventionalCommitsMessage } from './utils/git';
import { launchEditor } from './utils/launch-editor';
import { printDiff } from './utils/print-changes';
import { printConfigAndExit } from './utils/print-config';
import { deepMergeJson } from './config/deep-merge-json';
export const releasePlanCLIHandler = (args: PlanOptions) =>
handleErrors(args.verbose, () => createAPI({})(args));
@ -79,26 +80,6 @@ export function createAPI(overrideReleaseConfig: NxReleaseConfiguration) {
}
};
if (args.message) {
const message = parseConventionalCommitsMessage(args.message);
if (!message) {
output.error({
title: 'Changelog message is not in conventional commits format.',
bodyLines: [
'Please ensure your message is in the form of:',
' type(optional scope): description',
'',
'For example:',
' feat(pkg-b): add new feature',
' fix(pkg-a): correct a bug',
' chore: update build process',
' fix(core)!: breaking change in core package',
],
});
process.exit(1);
}
}
if (releaseGroups[0].name === IMPLICIT_DEFAULT_RELEASE_GROUP) {
const group = releaseGroups[0];
if (group.projectsRelationship === 'independent') {
@ -153,12 +134,14 @@ export function createAPI(overrideReleaseConfig: NxReleaseConfiguration) {
return 0;
}
const versionPlanMessage = args.message || (await promptForMessage());
const versionPlanName = `version-plan-${new Date().getTime()}`;
const versionPlanMessage =
args.message || (await promptForMessage(versionPlanName));
const versionPlanFileContent = generateVersionPlanContent(
versionPlanBumps,
versionPlanMessage
);
const versionPlanFileName = `version-plan-${new Date().getTime()}.md`;
const versionPlanFileName = `${versionPlanName}.md`;
if (args.dryRun) {
output.logSingleLine(
@ -202,47 +185,49 @@ async function promptForVersion(message: string): Promise<string> {
}
}
async function promptForMessage(): Promise<string> {
async function promptForMessage(versionPlanName: string): Promise<string> {
let message: string;
do {
message = await _promptForMessage();
message = await _promptForMessage(versionPlanName);
} while (!message);
return message;
}
// TODO: support non-conventional commits messages (will require significant changelog renderer changes)
async function _promptForMessage(): Promise<string> {
async function _promptForMessage(versionPlanName: string): Promise<string> {
try {
const reply = await prompt<{ message: string }>([
{
name: 'message',
message:
'What changelog message would you like associated with this change?',
'What changelog message would you like associated with this change? (Leave blank to open an external editor for multi-line messages/easier editing)',
type: 'input',
},
]);
const conventionalCommitsMessage = parseConventionalCommitsMessage(
reply.message
);
if (!conventionalCommitsMessage) {
output.warn({
title: 'Changelog message is not in conventional commits format.',
bodyLines: [
'Please ensure your message is in the form of:',
' type(optional scope): description',
'',
'For example:',
' feat(pkg-b): add new feature',
' fix(pkg-a): correct a bug',
' chore: update build process',
' fix(core)!: breaking change in core package',
],
});
return null;
let message = reply.message.trim();
if (!message.length) {
const tmpDir = dirSync().name;
const messageFilePath = join(
tmpDir,
`DRAFT_MESSAGE__${versionPlanName}.md`
);
writeFileSync(messageFilePath, '');
await launchEditor(messageFilePath);
message = readFileSync(messageFilePath, 'utf-8');
}
return reply.message;
message = message.trim();
if (!message) {
output.warn({
title:
'A changelog message is required in order to create the version plan file',
bodyLines: [],
});
}
return message;
} catch (e) {
output.log({
title: 'Cancelled version plan creation.',

View File

@ -29,4 +29,28 @@ describe('generateVersionPlanContent()', () => {
"
`);
});
it('should work without a message', () => {
expect(generateVersionPlanContent({ proj: '1.0.0' }, ''))
.toMatchInlineSnapshot(`
"---
proj: 1.0.0
---
"
`);
});
it('should work with multi-line messages', () => {
expect(generateVersionPlanContent({ proj: '1.0.0' }, 'foo\nbar\nbaz'))
.toMatchInlineSnapshot(`
"---
proj: 1.0.0
---
foo
bar
baz
"
`);
});
});

View File

@ -2,7 +2,7 @@ export function generateVersionPlanContent(
bumps: Record<string, string>,
message: string
): string {
return `---
const frontMatter = `---
${Object.entries(bumps)
.filter(([_, version]) => version !== 'none')
.map(([projectOrGroup, version]) => {
@ -15,7 +15,7 @@ ${Object.entries(bumps)
})
.join('\n')}
---
${message}
`;
return `${frontMatter}${message ? `\n${message}\n` : ''}`;
}