fix(release): changelog renderer should prefer breaking change explanation text (#20798)
This commit is contained in:
parent
71ce32a121
commit
32baa4dab1
@ -120,6 +120,7 @@ describe('nx release', () => {
|
|||||||
dependencyRelationshipLogMatch.length !== 1
|
dependencyRelationshipLogMatch.length !== 1
|
||||||
) {
|
) {
|
||||||
// From JamesHenry: explicit error to assist troubleshooting NXC-143
|
// From JamesHenry: explicit error to assist troubleshooting NXC-143
|
||||||
|
// Update: after seeing this error in the wild, it somehow seems to be not finding the dependency relationship sometimes
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`
|
`
|
||||||
Error: Expected to find exactly one dependency relationship log line.
|
Error: Expected to find exactly one dependency relationship log line.
|
||||||
@ -128,7 +129,10 @@ If you are seeing this message then you have been impacted by some currently und
|
|||||||
|
|
||||||
Please report the full nx release version command output below to the Nx team:
|
Please report the full nx release version command output below to the Nx team:
|
||||||
|
|
||||||
${versionOutput}`
|
${{
|
||||||
|
versionOutput,
|
||||||
|
pkg2Contents: readFile(`${pkg2}/package.json`),
|
||||||
|
}}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
expect(dependencyRelationshipLogMatch.length).toEqual(1);
|
expect(dependencyRelationshipLogMatch.length).toEqual(1);
|
||||||
|
|||||||
@ -539,4 +539,125 @@ describe('defaultChangelogRenderer()', () => {
|
|||||||
expect(markdown).toMatchInlineSnapshot(`""`);
|
expect(markdown).toMatchInlineSnapshot(`""`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('breaking changes', () => {
|
||||||
|
it('should work for breaking changes with just the ! and no explanation', async () => {
|
||||||
|
const breakingChangeCommitWithExplanation: GitCommit = {
|
||||||
|
// ! after the type, no BREAKING CHANGE: in the body
|
||||||
|
message: 'feat(WebSocketSubject)!: no longer extends `Subject`.',
|
||||||
|
shortHash: '54f2f6ed1',
|
||||||
|
author: {
|
||||||
|
name: 'James Henry',
|
||||||
|
email: 'jh@example.com',
|
||||||
|
},
|
||||||
|
body:
|
||||||
|
'M\tpackages/rxjs/src/internal/observable/dom/WebSocketSubject.ts\n' +
|
||||||
|
'"',
|
||||||
|
authors: [
|
||||||
|
{
|
||||||
|
name: 'James Henry',
|
||||||
|
email: 'jh@example.com',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
description: 'no longer extends `Subject`.',
|
||||||
|
type: 'feat',
|
||||||
|
scope: 'WebSocketSubject',
|
||||||
|
references: [{ value: '54f2f6ed1', type: 'hash' }],
|
||||||
|
isBreaking: true,
|
||||||
|
revertedHashes: [],
|
||||||
|
affectedFiles: [
|
||||||
|
'packages/rxjs/src/internal/observable/dom/WebSocketSubject.ts',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const markdown = await defaultChangelogRenderer({
|
||||||
|
projectGraph,
|
||||||
|
commits: [breakingChangeCommitWithExplanation],
|
||||||
|
releaseVersion: 'v1.1.0',
|
||||||
|
project: null,
|
||||||
|
entryWhenNoChanges: false,
|
||||||
|
changelogRenderOptions: {
|
||||||
|
includeAuthors: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(markdown).toMatchInlineSnapshot(`
|
||||||
|
"## v1.1.0
|
||||||
|
|
||||||
|
|
||||||
|
### 🚀 Features
|
||||||
|
|
||||||
|
- ⚠️ **WebSocketSubject:** no longer extends \`Subject\`.
|
||||||
|
|
||||||
|
#### ⚠️ Breaking Changes
|
||||||
|
|
||||||
|
- ⚠️ **WebSocketSubject:** no longer extends \`Subject\`.
|
||||||
|
|
||||||
|
### ❤️ Thank You
|
||||||
|
|
||||||
|
- James Henry"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should extract the explanation of a breaking change and render it preferentially', async () => {
|
||||||
|
const breakingChangeCommitWithExplanation: GitCommit = {
|
||||||
|
// No ! after the type, but BREAKING CHANGE: in the body
|
||||||
|
message: 'feat(WebSocketSubject): no longer extends `Subject`.',
|
||||||
|
shortHash: '54f2f6ed1',
|
||||||
|
author: {
|
||||||
|
name: 'James Henry',
|
||||||
|
email: 'jh@example.com',
|
||||||
|
},
|
||||||
|
body:
|
||||||
|
'BREAKING CHANGE: `WebSocketSubject` is no longer `instanceof Subject`. Check for `instanceof WebSocketSubject` instead.\n' +
|
||||||
|
'"\n' +
|
||||||
|
'\n' +
|
||||||
|
'M\tpackages/rxjs/src/internal/observable/dom/WebSocketSubject.ts\n' +
|
||||||
|
'"',
|
||||||
|
authors: [
|
||||||
|
{
|
||||||
|
name: 'James Henry',
|
||||||
|
email: 'jh@example.com',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
description: 'no longer extends `Subject`.',
|
||||||
|
type: 'feat',
|
||||||
|
scope: 'WebSocketSubject',
|
||||||
|
references: [{ value: '54f2f6ed1', type: 'hash' }],
|
||||||
|
isBreaking: true,
|
||||||
|
revertedHashes: [],
|
||||||
|
affectedFiles: [
|
||||||
|
'packages/rxjs/src/internal/observable/dom/WebSocketSubject.ts',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const markdown = await defaultChangelogRenderer({
|
||||||
|
projectGraph,
|
||||||
|
commits: [breakingChangeCommitWithExplanation],
|
||||||
|
releaseVersion: 'v1.1.0',
|
||||||
|
project: null,
|
||||||
|
entryWhenNoChanges: false,
|
||||||
|
changelogRenderOptions: {
|
||||||
|
includeAuthors: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(markdown).toMatchInlineSnapshot(`
|
||||||
|
"## v1.1.0
|
||||||
|
|
||||||
|
|
||||||
|
### 🚀 Features
|
||||||
|
|
||||||
|
- ⚠️ **WebSocketSubject:** no longer extends \`Subject\`.
|
||||||
|
|
||||||
|
#### ⚠️ Breaking Changes
|
||||||
|
|
||||||
|
- **WebSocketSubject:** \`WebSocketSubject\` is no longer \`instanceof Subject\`. Check for \`instanceof WebSocketSubject\` instead.
|
||||||
|
|
||||||
|
### ❤️ Thank You
|
||||||
|
|
||||||
|
- James Henry"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -143,7 +143,16 @@ const defaultChangelogRenderer: ChangelogRenderer = async ({
|
|||||||
const line = formatCommit(commit, repoSlug);
|
const line = formatCommit(commit, repoSlug);
|
||||||
markdownLines.push(line);
|
markdownLines.push(line);
|
||||||
if (commit.isBreaking) {
|
if (commit.isBreaking) {
|
||||||
breakingChanges.push(line);
|
const breakingChangeExplanation = extractBreakingChangeExplanation(
|
||||||
|
commit.body
|
||||||
|
);
|
||||||
|
breakingChanges.push(
|
||||||
|
breakingChangeExplanation
|
||||||
|
? `- ${
|
||||||
|
commit.scope ? `**${commit.scope.trim()}:** ` : ''
|
||||||
|
}${breakingChangeExplanation}`
|
||||||
|
: line
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -188,7 +197,16 @@ const defaultChangelogRenderer: ChangelogRenderer = async ({
|
|||||||
const line = formatCommit(commit, repoSlug);
|
const line = formatCommit(commit, repoSlug);
|
||||||
markdownLines.push(line + '\n');
|
markdownLines.push(line + '\n');
|
||||||
if (commit.isBreaking) {
|
if (commit.isBreaking) {
|
||||||
breakingChanges.push(line);
|
const breakingChangeExplanation = extractBreakingChangeExplanation(
|
||||||
|
commit.body
|
||||||
|
);
|
||||||
|
breakingChanges.push(
|
||||||
|
breakingChangeExplanation
|
||||||
|
? `- ${
|
||||||
|
commit.scope ? `**${commit.scope.trim()}:** ` : ''
|
||||||
|
}${breakingChangeExplanation}`
|
||||||
|
: line
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -294,11 +312,37 @@ function groupBy(items: any[], key: string) {
|
|||||||
function formatCommit(commit: GitCommit, repoSlug?: RepoSlug): string {
|
function formatCommit(commit: GitCommit, repoSlug?: RepoSlug): string {
|
||||||
let commitLine =
|
let commitLine =
|
||||||
'- ' +
|
'- ' +
|
||||||
(commit.scope ? `**${commit.scope.trim()}:** ` : '') +
|
|
||||||
(commit.isBreaking ? '⚠️ ' : '') +
|
(commit.isBreaking ? '⚠️ ' : '') +
|
||||||
|
(commit.scope ? `**${commit.scope.trim()}:** ` : '') +
|
||||||
commit.description;
|
commit.description;
|
||||||
if (repoSlug) {
|
if (repoSlug) {
|
||||||
commitLine += formatReferences(commit.references, repoSlug);
|
commitLine += formatReferences(commit.references, repoSlug);
|
||||||
}
|
}
|
||||||
return commitLine;
|
return commitLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* It is common to add further information about a breaking change in the commit body,
|
||||||
|
* and it is naturally that information that should be included in the BREAKING CHANGES
|
||||||
|
* section of changelog, rather than repeating the commit title/description.
|
||||||
|
*/
|
||||||
|
function extractBreakingChangeExplanation(message: string): string | null {
|
||||||
|
const breakingChangeIdentifier = 'BREAKING CHANGE:';
|
||||||
|
const startIndex = message.indexOf(breakingChangeIdentifier);
|
||||||
|
|
||||||
|
if (startIndex === -1) {
|
||||||
|
// "BREAKING CHANGE:" not found in the message
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const startOfBreakingChange = startIndex + breakingChangeIdentifier.length;
|
||||||
|
const endOfBreakingChange = message.indexOf('\n', startOfBreakingChange);
|
||||||
|
|
||||||
|
if (endOfBreakingChange === -1) {
|
||||||
|
// No newline character found, extract till the end of the message
|
||||||
|
return message.substring(startOfBreakingChange).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract and return the breaking change message
|
||||||
|
return message.substring(startOfBreakingChange, endOfBreakingChange).trim();
|
||||||
|
}
|
||||||
|
|||||||
@ -285,7 +285,8 @@ export function parseGitCommit(commit: RawGitCommit): GitCommit | null {
|
|||||||
|
|
||||||
const scope = match.groups.scope || '';
|
const scope = match.groups.scope || '';
|
||||||
|
|
||||||
const isBreaking = Boolean(match.groups.breaking);
|
const isBreaking =
|
||||||
|
Boolean(match.groups.breaking) || commit.body.includes('BREAKING CHANGE:');
|
||||||
let description = match.groups.description;
|
let description = match.groups.description;
|
||||||
|
|
||||||
// Extract references from message
|
// Extract references from message
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user