豆豆友情提示:这是一个非官方 GitHub 代理镜像,主要用于网络测试或访问加速。请勿在此进行登录、注册或处理任何敏感信息。进行这些操作请务必访问官方网站 github.com。 Raw 内容也通过此代理提供。
Skip to content

Commit ad534e2

Browse files
fix(ng-dev/release): Use new changelog writer each time an entry is prepending to the changelog file (#224)
* fix(ng-dev/release): Use new changelog writer each time an entry is prepending to the changelog file Creating a new changelog writer instance reach time the ReleaseNotes prepends to the changelog ensures that even in cases where we "cherry-pick" the entry, old entries are not removed from the changelog file. * fixup! fix(ng-dev/release): Use new changelog writer each time an entry is prepending to the changelog file
1 parent 57a4705 commit ad534e2

File tree

6 files changed

+109
-45
lines changed

6 files changed

+109
-45
lines changed

ng-dev/release/notes/changelog.spec.ts

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,37 @@
11
import {existsSync, readFileSync} from 'fs';
2+
import {GitClient} from '../../utils/git/git-client';
23
import {SemVer} from 'semver';
34
import {dedent} from '../../utils/testing/dedent';
45
import {getMockGitClient} from '../publish/test/test-utils/git-client-mock';
56
import {Changelog, splitMarker} from './changelog';
67

78
describe('Changelog', () => {
89
let changelog: Changelog;
10+
let gitClient: GitClient;
911

1012
beforeEach(() => {
11-
const gitClient = getMockGitClient(
13+
gitClient = getMockGitClient(
1214
{owner: 'angular', name: 'dev-infra-test', mainBranchName: 'main'},
1315
/* useSandboxGitClient */ false,
1416
);
15-
changelog = new Changelog(gitClient);
17+
spyOn(GitClient, 'get').and.returnValue(gitClient);
18+
changelog = Changelog.getChangelogFilePaths();
1619
});
1720

1821
it('throws an error if it cannot find the anchor containing the version for an entry', () => {
19-
expect(() => changelog.prependEntryToChangelog('does not have version <a> tag')).toThrow();
22+
expect(() => Changelog.prependEntryToChangelogFile('does not have version <a> tag')).toThrow();
2023
});
2124

2225
it('throws an error if it cannot determine the version for an entry', () => {
23-
expect(() => changelog.prependEntryToChangelog(createChangelogEntry('NotSemVer'))).toThrow();
26+
expect(() =>
27+
Changelog.prependEntryToChangelogFile(createChangelogEntry('NotSemVer')),
28+
).toThrow();
2429
});
2530

2631
it('concatenates the changelog entries into the changelog file with the split marker between', () => {
27-
changelog.prependEntryToChangelog(createChangelogEntry('1.0.0'));
28-
changelog.prependEntryToChangelog(createChangelogEntry('2.0.0'));
29-
changelog.prependEntryToChangelog(createChangelogEntry('3.0.0'));
32+
Changelog.prependEntryToChangelogFile(createChangelogEntry('1.0.0'));
33+
Changelog.prependEntryToChangelogFile(createChangelogEntry('2.0.0'));
34+
Changelog.prependEntryToChangelogFile(createChangelogEntry('3.0.0'));
3035

3136
expect(readFileAsString(changelog.filePath)).toBe(
3237
dedent`
@@ -42,7 +47,7 @@ describe('Changelog', () => {
4247
`.trim(),
4348
);
4449

45-
changelog.moveEntriesPriorToVersionToArchive(new SemVer('3.0.0'));
50+
Changelog.moveEntriesPriorToVersionToArchive(new SemVer('3.0.0'));
4651

4752
expect(readFileAsString(changelog.archiveFilePath)).toBe(
4853
dedent`
@@ -61,19 +66,19 @@ describe('Changelog', () => {
6166
it('creates a new changelog file if one does not exist.', () => {
6267
expect(existsSync(changelog.filePath)).toBe(false);
6368

64-
changelog.prependEntryToChangelog(createChangelogEntry('0.0.0'));
69+
Changelog.prependEntryToChangelogFile(createChangelogEntry('0.0.0'));
6570
expect(existsSync(changelog.filePath)).toBe(true);
6671
});
6772

6873
it('should not include a split marker when only one changelog entry is in the changelog.', () => {
69-
changelog.prependEntryToChangelog(createChangelogEntry('0.0.0'));
74+
Changelog.prependEntryToChangelogFile(createChangelogEntry('0.0.0'));
7075

7176
expect(readFileAsString(changelog.filePath)).not.toContain(splitMarker);
7277
});
7378

7479
it('separates multiple changelog entries using a standard split marker', () => {
7580
for (let i = 0; i < 2; i++) {
76-
changelog.prependEntryToChangelog(createChangelogEntry(`0.0.${i}`));
81+
Changelog.prependEntryToChangelogFile(createChangelogEntry(`0.0.${i}`));
7782
}
7883

7984
expect(readFileAsString(changelog.filePath)).toContain(splitMarker);
@@ -82,22 +87,31 @@ describe('Changelog', () => {
8287

8388
describe('adds entries to the changelog archive', () => {
8489
it('only updates or creates the changelog archive if necessary', () => {
85-
changelog.prependEntryToChangelog(createChangelogEntry('1.0.0'));
90+
Changelog.prependEntryToChangelogFile(createChangelogEntry('1.0.0'));
8691
expect(existsSync(changelog.archiveFilePath)).toBe(false);
8792

88-
changelog.moveEntriesPriorToVersionToArchive(new SemVer('1.0.0'));
93+
Changelog.moveEntriesPriorToVersionToArchive(new SemVer('1.0.0'));
8994
expect(existsSync(changelog.archiveFilePath)).toBe(false);
9095

91-
changelog.moveEntriesPriorToVersionToArchive(new SemVer('2.0.0'));
96+
Changelog.moveEntriesPriorToVersionToArchive(new SemVer('2.0.0'));
9297
expect(existsSync(changelog.archiveFilePath)).toBe(true);
9398
});
9499

95100
it('from the primary changelog older than a provided version', () => {
96-
changelog.prependEntryToChangelog(createChangelogEntry('1.0.0', 'This is version 1'));
97-
changelog.prependEntryToChangelog(createChangelogEntry('2.0.0', 'This is version 2'));
98-
changelog.prependEntryToChangelog(createChangelogEntry('3.0.0', 'This is version 3'));
99-
100-
changelog.moveEntriesPriorToVersionToArchive(new SemVer('3.0.0'));
101+
Changelog.prependEntryToChangelogFile(
102+
createChangelogEntry('1.0.0', 'This is version 1'),
103+
gitClient,
104+
);
105+
Changelog.prependEntryToChangelogFile(
106+
createChangelogEntry('2.0.0', 'This is version 2'),
107+
gitClient,
108+
);
109+
Changelog.prependEntryToChangelogFile(
110+
createChangelogEntry('3.0.0', 'This is version 3'),
111+
gitClient,
112+
);
113+
114+
Changelog.moveEntriesPriorToVersionToArchive(new SemVer('3.0.0'));
101115
expect(readFileAsString(changelog.archiveFilePath)).toContain('version 1');
102116
expect(readFileAsString(changelog.archiveFilePath)).toContain('version 2');
103117
expect(readFileAsString(changelog.archiveFilePath)).not.toContain('version 3');

ng-dev/release/notes/changelog.ts

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import * as semver from 'semver';
44
import {GitClient} from '../../utils/git/git-client';
55

66
/** Project-relative path for the changelog file. */
7-
export const changelogPath = 'CHANGELOG.md';
7+
const changelogPath = 'CHANGELOG.md';
88

99
/** Project-relative path for the changelog archive file. */
10-
export const changelogArchivePath = 'CHANGELOG_ARCHIVE.md';
10+
const changelogArchivePath = 'CHANGELOG_ARCHIVE.md';
1111

1212
/** A marker used to split a CHANGELOG.md file into individual entries. */
1313
export const splitMarker = '<!-- CHANGELOG SPLIT MARKER -->';
@@ -37,12 +37,47 @@ interface ChangelogEntry {
3737
}
3838

3939
export class Changelog {
40+
/** Prepend a changelog entry to the current changelog file. */
41+
static prependEntryToChangelogFile(entry: string, git = GitClient.get()) {
42+
const changelog = new this(git);
43+
changelog.prependEntryToChangelogFile(entry);
44+
}
45+
46+
/**
47+
* Move all changelog entries from the CHANGELOG.md file for versions prior to the provided
48+
* version to the changelog archive.
49+
*
50+
* Versions should be used to determine which entries are moved to archive as versions are the
51+
* most accurate piece of context found within a changelog entry to determine its relationship to
52+
* other changelog entries. This allows for example, moving all changelog entries out of the
53+
* main changelog when a version moves out of support.
54+
*/
55+
static moveEntriesPriorToVersionToArchive(version: semver.SemVer, git = GitClient.get()) {
56+
const changelog = new this(git);
57+
changelog.moveEntriesPriorToVersionToArchive(version);
58+
}
59+
60+
// TODO(josephperrott): Remove this after it is unused.
61+
/** Retrieve the file paths for the changelog files. */
62+
static getChangelogFilePaths(git = GitClient.get()) {
63+
return new this(git);
64+
}
65+
4066
/** The absolute path to the changelog file. */
4167
readonly filePath = join(this.git.baseDir, changelogPath);
4268
/** The absolute path to the changelog archive file. */
4369
readonly archiveFilePath = join(this.git.baseDir, changelogArchivePath);
44-
/** The changelog entries in the CHANGELOG.md file. */
45-
private entries = this.getEntriesFor(this.filePath);
70+
/**
71+
* The changelog entries in the CHANGELOG.md file.
72+
* Delays reading the CHANGELOG.md file until it is actually used.
73+
*/
74+
private get entries() {
75+
if (this._entries === undefined) {
76+
return (this._entries = this.getEntriesFor(this.filePath));
77+
}
78+
return this._entries;
79+
}
80+
private _entries: undefined | ChangelogEntry[] = undefined;
4681
/**
4782
* The changelog entries in the CHANGELOG_ARCHIVE.md file.
4883
* Delays reading the CHANGELOG_ARCHIVE.md file until it is actually used.
@@ -55,10 +90,10 @@ export class Changelog {
5590
}
5691
private _archiveEntries: undefined | ChangelogEntry[] = undefined;
5792

58-
constructor(private git: GitClient) {}
93+
private constructor(private git: GitClient) {}
5994

6095
/** Prepend a changelog entry to the changelog. */
61-
prependEntryToChangelog(entry: string) {
96+
private prependEntryToChangelogFile(entry: string) {
6297
this.entries.unshift(parseChangelogEntry(entry));
6398
this.writeToChangelogFile();
6499
}
@@ -72,7 +107,7 @@ export class Changelog {
72107
* other changelog entries. This allows for example, moving all changelog entries out of the
73108
* main changelog when a version moves out of support.
74109
*/
75-
moveEntriesPriorToVersionToArchive(version: semver.SemVer) {
110+
private moveEntriesPriorToVersionToArchive(version: semver.SemVer) {
76111
[...this.entries].reverse().forEach((entry: ChangelogEntry) => {
77112
if (semver.lt(entry.version, version)) {
78113
this.archiveEntries.unshift(entry);

ng-dev/release/notes/cli.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ async function handler({releaseVersion, from, to, prependToChangelog, type}: Arg
5858
const releaseNotes = await ReleaseNotes.forRange(releaseVersion, from, to);
5959

6060
if (prependToChangelog) {
61-
await releaseNotes.prependEntryToChangelog();
61+
await releaseNotes.prependEntryToChangelogFile();
6262
info(`Added release notes for "${releaseVersion}" to the changelog`);
6363
return;
6464
}

ng-dev/release/notes/release-notes.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,6 @@ export class ReleaseNotes {
3333
return new ReleaseNotes(version, commits, git);
3434
}
3535

36-
/** The changelog writer. */
37-
private changelog = new Changelog(this.git);
3836
/** The RenderContext to be used during rendering. */
3937
private renderContext: RenderContext | undefined;
4038
/** The title to use for the release. */
@@ -68,16 +66,16 @@ export class ReleaseNotes {
6866
* Prepend generated release note to the CHANGELOG.md file in the base directory of the repository
6967
* provided by the GitClient.
7068
*/
71-
async prependEntryToChangelog() {
72-
this.changelog.prependEntryToChangelog(await this.getChangelogEntry());
69+
async prependEntryToChangelogFile() {
70+
Changelog.prependEntryToChangelogFile(await this.getChangelogEntry(), this.git);
7371

7472
// TODO(josephperrott): Remove file formatting calls.
7573
// Upon reaching a standardized formatting for markdown files, rather than calling a formatter
7674
// for all creation of changelogs, we instead will confirm in our testing that the new changes
7775
// created for changelogs meet on standardized markdown formats via unit testing.
7876
try {
7977
assertValidFormatConfig(this.config);
80-
await formatFiles([this.changelog.filePath]);
78+
await formatFiles([Changelog.getChangelogFilePaths(this.git).filePath]);
8179
} catch {
8280
// If the formatting is either unavailable or fails, continue on with the unformatted result.
8381
}

ng-dev/release/publish/actions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ export abstract class ReleaseAction {
367367
* @returns A boolean indicating whether the release notes have been prepended.
368368
*/
369369
protected async prependReleaseNotesToChangelog(releaseNotes: ReleaseNotes): Promise<void> {
370-
await releaseNotes.prependEntryToChangelog();
370+
await releaseNotes.prependEntryToChangelogFile();
371371
info(green(` ✓ Updated the changelog to capture changes for "${releaseNotes.version}".`));
372372
}
373373

tools/local-actions/changelog/main.js

Lines changed: 29 additions & 12 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)