@williamthorsen/release-kit 15.0.2 → 16.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.meta/changelog.json +88 -0
- package/.readyup/kits/default.js +14 -14
- package/.readyup/kits/npm-auto-publish.js +5 -5
- package/.readyup/manifest.json +14 -14
- package/CHANGELOG.md +28 -0
- package/README.md +3 -3
- package/dist/esm/applyChangelogOverrides.js +6 -6
- package/dist/esm/assertCleanWorkingTree.js +2 -2
- package/dist/esm/assertReleasesOnNpm.js +11 -11
- package/dist/esm/assertTaggedBaseline.js +5 -5
- package/dist/esm/bin/release-kit.js +16 -16
- package/dist/esm/buildChangelogEntries.d.ts +19 -19
- package/dist/esm/buildChangelogEntries.js +40 -36
- package/dist/esm/buildChangelogItem.js +24 -24
- package/dist/esm/buildDependencyGraph.js +23 -23
- package/dist/esm/buildReleasedResult.js +21 -19
- package/dist/esm/buildSyntheticChangelogEntry.js +2 -2
- package/dist/esm/bumpVersion.js +8 -8
- package/dist/esm/changelogArchive.d.ts +1 -1
- package/dist/esm/changelogArchive.js +2 -2
- package/dist/esm/changelogJsonFile.js +5 -5
- package/dist/esm/changelogJsonUtils.js +3 -3
- package/dist/esm/changelogOverrides.d.ts +5 -5
- package/dist/esm/changelogOverrides.js +23 -23
- package/dist/esm/classifyChangelogCommit.js +4 -4
- package/dist/esm/commitCommand.js +7 -7
- package/dist/esm/compareVersions.js +13 -13
- package/dist/esm/configSchema.d.ts +2 -2
- package/dist/esm/configSchema.js +1 -1
- package/dist/esm/createGithubRelease.d.ts +4 -4
- package/dist/esm/createGithubRelease.js +10 -10
- package/dist/esm/createGithubReleaseCommand.js +9 -9
- package/dist/esm/createTags.d.ts +2 -2
- package/dist/esm/createTags.js +12 -12
- package/dist/esm/decideDirectReleases.d.ts +5 -5
- package/dist/esm/decideDirectReleases.js +25 -25
- package/dist/esm/decideRelease.d.ts +1 -1
- package/dist/esm/decideRelease.js +2 -2
- package/dist/esm/deriveDirectoryConfig.d.ts +1 -1
- package/dist/esm/deriveDirectoryConfig.js +18 -18
- package/dist/esm/deriveWorkspaceConfig.js +4 -4
- package/dist/esm/detectPackageManager.js +5 -5
- package/dist/esm/detectUndeclaredTagPrefixes.js +8 -8
- package/dist/esm/enumerateReleaseWindows.d.ts +1 -1
- package/dist/esm/enumerateReleaseWindows.js +9 -9
- package/dist/esm/findTransitiveDependents.js +5 -5
- package/dist/esm/findUnroutedEntryScopes.js +11 -11
- package/dist/esm/format.d.ts +1 -1
- package/dist/esm/format.js +1 -1
- package/dist/esm/formatPrepareFiles.d.ts +3 -3
- package/dist/esm/formatPrepareFiles.js +10 -10
- package/dist/esm/formatPrepareWarnings.js +6 -6
- package/dist/esm/hasPrettierConfig.js +2 -2
- package/dist/esm/init/checks.js +7 -5
- package/dist/esm/init/detectRepoType.js +3 -3
- package/dist/esm/init/initCommand.d.ts +4 -4
- package/dist/esm/init/initCommand.js +7 -7
- package/dist/esm/init/parseJsonRecord.d.ts +1 -1
- package/dist/esm/init/parseJsonRecord.js +4 -4
- package/dist/esm/init/scaffold.d.ts +4 -4
- package/dist/esm/init/scaffold.js +18 -9
- package/dist/esm/init/templates.d.ts +10 -10
- package/dist/esm/init/templates.js +5 -5
- package/dist/esm/init/updateTemplatesCommand.d.ts +2 -2
- package/dist/esm/init/updateTemplatesCommand.js +16 -14
- package/dist/esm/injectReleaseNotesIntoReadme.d.ts +1 -1
- package/dist/esm/injectReleaseNotesIntoReadme.js +6 -6
- package/dist/esm/injectSection.js +8 -8
- package/dist/esm/loadChangelogOverrides.d.ts +1 -1
- package/dist/esm/loadChangelogOverrides.js +9 -9
- package/dist/esm/loadConfig.d.ts +3 -3
- package/dist/esm/loadConfig.js +10 -10
- package/dist/esm/loadValidatedConfig.js +4 -4
- package/dist/esm/mergeConfig.d.ts +1 -1
- package/dist/esm/mergeConfig.js +27 -27
- package/dist/esm/monorepoConfigAssertions.js +15 -15
- package/dist/esm/npmRegistry.js +4 -4
- package/dist/esm/packsPath.js +3 -3
- package/dist/esm/parseChangeRecordBlock.js +33 -33
- package/dist/esm/parseCommitMessage.js +5 -5
- package/dist/esm/planChangelogFiles.js +8 -8
- package/dist/esm/planReleaseNotesPreviews.js +10 -10
- package/dist/esm/planVersionBump.js +24 -16
- package/dist/esm/prepareArgs.d.ts +9 -9
- package/dist/esm/prepareArgs.js +11 -11
- package/dist/esm/prepareCommand.js +23 -23
- package/dist/esm/prepareComposite.d.ts +4 -4
- package/dist/esm/prepareComposite.js +16 -16
- package/dist/esm/{releasePrepareTargets.d.ts → prepareReleaseTargets.d.ts} +9 -9
- package/dist/esm/{releasePrepareTargets.js → prepareReleaseTargets.js} +40 -40
- package/dist/esm/prepareRunContext.d.ts +1 -1
- package/dist/esm/prepareRunContext.js +5 -5
- package/dist/esm/prepareTarget.d.ts +3 -3
- package/dist/esm/prepareTarget.js +41 -39
- package/dist/esm/previewTagPrefixes.js +21 -21
- package/dist/esm/propagateBumps.js +8 -8
- package/dist/esm/publish.d.ts +2 -2
- package/dist/esm/publish.js +5 -5
- package/dist/esm/publishCommand.js +20 -20
- package/dist/esm/pushCommand.js +8 -8
- package/dist/esm/pushRelease.d.ts +1 -1
- package/dist/esm/pushRelease.js +4 -4
- package/dist/esm/readChangelogSections.d.ts +1 -1
- package/dist/esm/readChangelogSections.js +16 -16
- package/dist/esm/releasePlan.js +26 -26
- package/dist/esm/renderChangelogMarkdown.d.ts +4 -4
- package/dist/esm/renderChangelogMarkdown.js +18 -15
- package/dist/esm/renderReleaseNotes.d.ts +1 -1
- package/dist/esm/renderReleaseNotes.js +16 -16
- package/dist/esm/reportPrepare.js +34 -34
- package/dist/esm/resolveCommandTags.js +9 -9
- package/dist/esm/resolveReleaseDeclaration.d.ts +2 -2
- package/dist/esm/resolveReleaseDeclaration.js +22 -22
- package/dist/esm/resolveReleaseTags.js +19 -17
- package/dist/esm/resolveReleaseTargets.js +2 -2
- package/dist/esm/showTagPrefixesCommand.js +4 -4
- package/dist/esm/sync-labels/generateCommand.d.ts +3 -3
- package/dist/esm/sync-labels/generateCommand.js +10 -10
- package/dist/esm/sync-labels/initCommand.d.ts +3 -3
- package/dist/esm/sync-labels/initCommand.js +23 -15
- package/dist/esm/sync-labels/presets.js +3 -3
- package/dist/esm/sync-labels/resolveLabels.js +13 -13
- package/dist/esm/sync-labels/syncCommand.js +2 -2
- package/dist/esm/sync-labels/templates.d.ts +4 -4
- package/dist/esm/sync-labels/templates.js +13 -40
- package/dist/esm/tagCommand.js +4 -4
- package/dist/esm/topologicalSort.d.ts +2 -2
- package/dist/esm/topologicalSort.js +7 -7
- package/dist/esm/types.d.ts +3 -3
- package/dist/esm/validateAllChangelogOverrides.js +9 -9
- package/dist/esm/validateConfig.d.ts +1 -1
- package/dist/esm/validateConfig.js +60 -50
- package/dist/esm/validateOverridesCommand.js +2 -2
- package/docs/changelogs.md +5 -5
- package/docs/configuration.md +1 -1
- package/docs/work-types.md +1 -1
- package/package.json +9 -8
- package/templates/sync-labels.yaml.template +36 -0
|
@@ -12,7 +12,23 @@ const UNRELEASED_TAG = 'unreleased';
|
|
|
12
12
|
const CANONICAL_SECTION_ORDER = new Map(Object.values(DEFAULT_WORK_TYPES).map((config, index) => [stripGroupDecorations(config.header), index]));
|
|
13
13
|
export function buildChangelogEntries(config, tag, options) {
|
|
14
14
|
const history = readReleaseHistory(config, options);
|
|
15
|
-
return { entries:
|
|
15
|
+
return { entries: convertToChangelogEntries(history, tag), diagnostics: history.unreleasedReading.diagnostics };
|
|
16
|
+
}
|
|
17
|
+
export function convertToChangelogEntries(history, tag) {
|
|
18
|
+
const { unreleasedReading } = history;
|
|
19
|
+
if (unreleasedReading.sections.length === 0) {
|
|
20
|
+
return [...history.releasedEntries];
|
|
21
|
+
}
|
|
22
|
+
return [
|
|
23
|
+
{ version: extractVersion(tag), date: unreleasedReading.date, sections: unreleasedReading.sections },
|
|
24
|
+
...history.releasedEntries,
|
|
25
|
+
];
|
|
26
|
+
}
|
|
27
|
+
export function convertToReleaseEntries(history, tag, today) {
|
|
28
|
+
if (history.unreleasedReading.sections.length === 0) {
|
|
29
|
+
return [buildEmptyReleaseEntry(extractVersion(tag), today), ...history.releasedEntries];
|
|
30
|
+
}
|
|
31
|
+
return convertToChangelogEntries(history, tag);
|
|
16
32
|
}
|
|
17
33
|
export function readReleaseHistory(config, options) {
|
|
18
34
|
try {
|
|
@@ -39,22 +55,6 @@ export function readReleaseHistory(config, options) {
|
|
|
39
55
|
export function stripGroupDecorations(group) {
|
|
40
56
|
return stripEmojiPrefix(group);
|
|
41
57
|
}
|
|
42
|
-
export function toChangelogEntries(history, tag) {
|
|
43
|
-
const { unreleased } = history;
|
|
44
|
-
if (unreleased.sections.length === 0) {
|
|
45
|
-
return [...history.releasedEntries];
|
|
46
|
-
}
|
|
47
|
-
return [
|
|
48
|
-
{ version: extractVersion(tag), date: unreleased.date, sections: unreleased.sections },
|
|
49
|
-
...history.releasedEntries,
|
|
50
|
-
];
|
|
51
|
-
}
|
|
52
|
-
export function toReleaseEntries(history, tag, today) {
|
|
53
|
-
if (history.unreleased.sections.length === 0) {
|
|
54
|
-
return [buildEmptyReleaseEntry(extractVersion(tag), today), ...history.releasedEntries];
|
|
55
|
-
}
|
|
56
|
-
return toChangelogEntries(history, tag);
|
|
57
|
-
}
|
|
58
58
|
function appendItem(sectionMap, header, item) {
|
|
59
59
|
let items = sectionMap.get(header);
|
|
60
60
|
if (items === undefined) {
|
|
@@ -72,15 +72,15 @@ function buildSections(sectionMap, devOnlySections) {
|
|
|
72
72
|
items,
|
|
73
73
|
});
|
|
74
74
|
}
|
|
75
|
-
return sections.toSorted((a, b) =>
|
|
75
|
+
return sections.toSorted((a, b) => getCanonicalSectionPriority(a.title) - getCanonicalSectionPriority(b.title));
|
|
76
76
|
}
|
|
77
|
-
function
|
|
77
|
+
function formatDate(timestampSec) {
|
|
78
|
+
return new Date(timestampSec * 1_000).toISOString().slice(0, 10);
|
|
79
|
+
}
|
|
80
|
+
function getCanonicalSectionPriority(title) {
|
|
78
81
|
const index = CANONICAL_SECTION_ORDER.get(stripGroupDecorations(title));
|
|
79
82
|
return index ?? Infinity;
|
|
80
83
|
}
|
|
81
|
-
function formatDate(timestamp) {
|
|
82
|
-
return new Date(timestamp * 1_000).toISOString().slice(0, 10);
|
|
83
|
-
}
|
|
84
84
|
function readCommit(commit, context, diagnostics) {
|
|
85
85
|
if (isNonChangeSubject(commit.subject)) {
|
|
86
86
|
return { items: [], isUnparseable: false };
|
|
@@ -100,10 +100,10 @@ function readCommit(commit, context, diagnostics) {
|
|
|
100
100
|
const classification = classifyChangelogCommit(commit, context.workTypes, {
|
|
101
101
|
breakingPolicies: context.breakingPolicies,
|
|
102
102
|
...(diagnostics !== undefined && {
|
|
103
|
-
onPolicyViolation: (
|
|
103
|
+
onPolicyViolation: (violatingCommit, type, surface) => {
|
|
104
104
|
diagnostics.policyViolations.push({
|
|
105
|
-
commitHash:
|
|
106
|
-
commitSubject:
|
|
105
|
+
commitHash: violatingCommit.hash,
|
|
106
|
+
commitSubject: violatingCommit.subject,
|
|
107
107
|
type,
|
|
108
108
|
surface,
|
|
109
109
|
});
|
|
@@ -130,47 +130,51 @@ function transformReleases(windows, context) {
|
|
|
130
130
|
unroutedEntryScopes: [],
|
|
131
131
|
};
|
|
132
132
|
const signals = [];
|
|
133
|
-
const
|
|
133
|
+
const unparseableCommits = [];
|
|
134
134
|
let parsedCommitCount = 0;
|
|
135
135
|
const sectionMap = new Map();
|
|
136
136
|
const unreleasedCommits = unreleasedWindow?.commits ?? [];
|
|
137
137
|
for (const commit of unreleasedCommits) {
|
|
138
138
|
const reading = readCommit(commit, context, diagnostics);
|
|
139
|
-
for (const
|
|
140
|
-
appendItem(sectionMap,
|
|
141
|
-
signals.push(
|
|
139
|
+
for (const derivedItem of reading.items) {
|
|
140
|
+
appendItem(sectionMap, derivedItem.header, derivedItem.item);
|
|
141
|
+
signals.push(derivedItem.signal);
|
|
142
142
|
}
|
|
143
143
|
if (reading.items.length > 0) {
|
|
144
144
|
parsedCommitCount += 1;
|
|
145
145
|
}
|
|
146
146
|
else if (reading.isUnparseable) {
|
|
147
|
-
|
|
147
|
+
unparseableCommits.push(commit);
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
150
|
const releasedEntries = [];
|
|
151
151
|
for (const release of windows.slice(1)) {
|
|
152
152
|
const releaseSections = new Map();
|
|
153
153
|
for (const commit of release.commits) {
|
|
154
|
-
for (const
|
|
155
|
-
appendItem(releaseSections,
|
|
154
|
+
for (const derivedItem of readCommit(commit, context, undefined).items) {
|
|
155
|
+
appendItem(releaseSections, derivedItem.header, derivedItem.item);
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
158
|
const sections = buildSections(releaseSections, context.devOnlySections);
|
|
159
159
|
if (sections.length > 0) {
|
|
160
|
-
releasedEntries.push({
|
|
160
|
+
releasedEntries.push({
|
|
161
|
+
version: extractVersion(release.version),
|
|
162
|
+
date: formatDate(release.timestampSec),
|
|
163
|
+
sections,
|
|
164
|
+
});
|
|
161
165
|
}
|
|
162
166
|
}
|
|
163
167
|
return {
|
|
164
168
|
previousTag: baselineWindow?.version,
|
|
165
169
|
releasedEntries,
|
|
166
|
-
|
|
170
|
+
unreleasedReading: {
|
|
167
171
|
bump: determineBumpType(signals, context.workTypes, context.versionPatterns),
|
|
168
172
|
commits: unreleasedCommits.filter((commit) => !isReleaseSubject(commit.subject)).toReversed(),
|
|
169
|
-
date: formatDate(unreleasedWindow?.
|
|
173
|
+
date: formatDate(unreleasedWindow?.timestampSec ?? Math.floor(Date.now() / 1_000)),
|
|
170
174
|
diagnostics,
|
|
171
175
|
parsedCommitCount,
|
|
172
176
|
sections: buildSections(sectionMap, context.devOnlySections),
|
|
173
|
-
unparseableCommits:
|
|
177
|
+
unparseableCommits: unparseableCommits.length > 0 ? unparseableCommits.toReversed() : undefined,
|
|
174
178
|
},
|
|
175
179
|
};
|
|
176
180
|
}
|
|
@@ -36,10 +36,10 @@ export function buildEntryItem(commit, entry, position, prNumber, context, diagn
|
|
|
36
36
|
prefixSurface: 'entry',
|
|
37
37
|
onPolicyViolation: diagnostics === undefined
|
|
38
38
|
? undefined
|
|
39
|
-
: (
|
|
39
|
+
: (violatingCommit, violatedType, surface) => {
|
|
40
40
|
diagnostics.policyViolations.push({
|
|
41
|
-
commitHash:
|
|
42
|
-
commitSubject:
|
|
41
|
+
commitHash: violatingCommit.hash,
|
|
42
|
+
commitSubject: violatingCommit.subject,
|
|
43
43
|
type: violatedType,
|
|
44
44
|
surface,
|
|
45
45
|
entryPosition: position,
|
|
@@ -65,7 +65,7 @@ export function buildTitleItem(commit, isParsedBreaking, workTypes) {
|
|
|
65
65
|
if (body !== undefined) {
|
|
66
66
|
item.body = body;
|
|
67
67
|
}
|
|
68
|
-
if (isParsedBreaking &&
|
|
68
|
+
if (isParsedBreaking && hasBreakingMarkerInSubject(commit.message, workTypes)) {
|
|
69
69
|
item.breaking = true;
|
|
70
70
|
}
|
|
71
71
|
const migration = extractMigration(body);
|
|
@@ -80,43 +80,43 @@ export function hasRoutedEntry(entries, routing) {
|
|
|
80
80
|
}
|
|
81
81
|
function extractBody(message) {
|
|
82
82
|
const lines = message.split('\n').slice(1);
|
|
83
|
-
let
|
|
84
|
-
while (
|
|
85
|
-
|
|
83
|
+
let startIndex = 0;
|
|
84
|
+
while (startIndex < lines.length && (lines[startIndex] ?? '').trim() === '') {
|
|
85
|
+
startIndex += 1;
|
|
86
86
|
}
|
|
87
|
-
let
|
|
88
|
-
while (
|
|
89
|
-
const line = lines[
|
|
90
|
-
const
|
|
91
|
-
if (
|
|
92
|
-
|
|
87
|
+
let endIndex = lines.length;
|
|
88
|
+
while (endIndex > startIndex) {
|
|
89
|
+
const line = lines[endIndex - 1] ?? '';
|
|
90
|
+
const trimmedLine = line.trim();
|
|
91
|
+
if (trimmedLine === '') {
|
|
92
|
+
endIndex -= 1;
|
|
93
93
|
continue;
|
|
94
94
|
}
|
|
95
|
-
if (TRAILER_PATTERNS.some((pattern) => pattern.test(
|
|
96
|
-
|
|
95
|
+
if (TRAILER_PATTERNS.some((pattern) => pattern.test(trimmedLine))) {
|
|
96
|
+
endIndex -= 1;
|
|
97
97
|
continue;
|
|
98
98
|
}
|
|
99
99
|
break;
|
|
100
100
|
}
|
|
101
|
-
if (
|
|
101
|
+
if (endIndex <= startIndex) {
|
|
102
102
|
return undefined;
|
|
103
103
|
}
|
|
104
|
-
return lines.slice(
|
|
104
|
+
return lines.slice(startIndex, endIndex).join('\n').trim();
|
|
105
105
|
}
|
|
106
106
|
function extractDescription(message) {
|
|
107
107
|
const firstLine = message.split('\n', 1)[0] ?? message;
|
|
108
|
-
const
|
|
109
|
-
return
|
|
108
|
+
const descriptionText = firstLine.split(': ').slice(1).join(': ');
|
|
109
|
+
return descriptionText.charAt(0).toUpperCase() + descriptionText.slice(1);
|
|
110
|
+
}
|
|
111
|
+
function hasBreakingMarkerInSubject(message, workTypes) {
|
|
112
|
+
return parseSubject(message.split('\n', 1)[0] ?? '', workTypes)?.breaking === true;
|
|
110
113
|
}
|
|
111
114
|
function isRoutedTo(scopes, routing) {
|
|
112
115
|
if (scopes.length === 0) {
|
|
113
116
|
return true;
|
|
114
117
|
}
|
|
115
118
|
return scopes.some((scope) => {
|
|
116
|
-
const
|
|
117
|
-
return
|
|
119
|
+
const resolvedScope = routing.scopeAliases[scope] ?? scope;
|
|
120
|
+
return resolvedScope === '*' || resolvedScope === routing.scope;
|
|
118
121
|
});
|
|
119
122
|
}
|
|
120
|
-
function subjectHasBreakingMarker(message, workTypes) {
|
|
121
|
-
return parseSubject(message.split('\n', 1)[0] ?? '', workTypes)?.breaking === true;
|
|
122
|
-
}
|
|
@@ -15,36 +15,36 @@ export function buildDependencyGraph(targets) {
|
|
|
15
15
|
if (primaryPackageFile === undefined) {
|
|
16
16
|
continue;
|
|
17
17
|
}
|
|
18
|
-
const
|
|
19
|
-
targetPackages.set(target,
|
|
20
|
-
if (typeof
|
|
21
|
-
dirToVersion.set(target.id,
|
|
18
|
+
const packageJson = readPackageJsonSubset(primaryPackageFile);
|
|
19
|
+
targetPackages.set(target, packageJson);
|
|
20
|
+
if (typeof packageJson.version === 'string') {
|
|
21
|
+
dirToVersion.set(target.id, packageJson.version);
|
|
22
22
|
}
|
|
23
|
-
if (
|
|
23
|
+
if (packageJson.name === undefined) {
|
|
24
24
|
continue;
|
|
25
25
|
}
|
|
26
|
-
packageNameToDir.set(
|
|
27
|
-
dirToPackageName.set(target.id,
|
|
26
|
+
packageNameToDir.set(packageJson.name, target.id);
|
|
27
|
+
dirToPackageName.set(target.id, packageJson.name);
|
|
28
28
|
}
|
|
29
|
-
for (const [target,
|
|
30
|
-
const
|
|
31
|
-
for (const [
|
|
32
|
-
if (typeof
|
|
29
|
+
for (const [target, packageJson] of targetPackages) {
|
|
30
|
+
const allDependencies = { ...packageJson.dependencies, ...packageJson.peerDependencies };
|
|
31
|
+
for (const [dependencyName, dependencyVersion] of Object.entries(allDependencies)) {
|
|
32
|
+
if (typeof dependencyVersion !== 'string' || !dependencyVersion.startsWith('workspace:')) {
|
|
33
33
|
continue;
|
|
34
34
|
}
|
|
35
|
-
const
|
|
36
|
-
if (
|
|
37
|
-
dependentsOf.set(
|
|
35
|
+
const existingDependents = dependentsOf.get(dependencyName);
|
|
36
|
+
if (existingDependents === undefined) {
|
|
37
|
+
dependentsOf.set(dependencyName, [target]);
|
|
38
38
|
}
|
|
39
39
|
else {
|
|
40
|
-
|
|
40
|
+
existingDependents.push(target);
|
|
41
41
|
}
|
|
42
|
-
const
|
|
43
|
-
if (
|
|
44
|
-
dependenciesOf.set(target.id, new Set([
|
|
42
|
+
const forwardDependencies = dependenciesOf.get(target.id);
|
|
43
|
+
if (forwardDependencies === undefined) {
|
|
44
|
+
dependenciesOf.set(target.id, new Set([dependencyName]));
|
|
45
45
|
}
|
|
46
46
|
else {
|
|
47
|
-
|
|
47
|
+
forwardDependencies.add(dependencyName);
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
}
|
|
@@ -58,15 +58,15 @@ function readPackageJsonSubset(filePath) {
|
|
|
58
58
|
catch (error) {
|
|
59
59
|
throw chainError(`Failed to read ${filePath}`, error);
|
|
60
60
|
}
|
|
61
|
-
let
|
|
61
|
+
let parsedJson;
|
|
62
62
|
try {
|
|
63
|
-
|
|
63
|
+
parsedJson = JSON.parse(content);
|
|
64
64
|
}
|
|
65
65
|
catch (error) {
|
|
66
66
|
throw chainError(`Failed to parse JSON in ${filePath}`, error);
|
|
67
67
|
}
|
|
68
|
-
if (!isPackageJsonSubset(
|
|
68
|
+
if (!isPackageJsonSubset(parsedJson)) {
|
|
69
69
|
throw new Error(`Invalid package.json at ${filePath}`);
|
|
70
70
|
}
|
|
71
|
-
return
|
|
71
|
+
return parsedJson;
|
|
72
72
|
}
|
|
@@ -17,62 +17,64 @@ export function buildReleasedResult(target, fields, release, decision, history)
|
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
function buildReleasedComposite(fields, release, decision, history) {
|
|
20
|
-
const {
|
|
20
|
+
const { unreleasedReading } = history;
|
|
21
21
|
const result = {
|
|
22
22
|
...fields,
|
|
23
23
|
status: 'released',
|
|
24
24
|
kind: 'composite',
|
|
25
|
-
parsedCommitCount:
|
|
25
|
+
parsedCommitCount: unreleasedReading.parsedCommitCount,
|
|
26
26
|
releaseType: release.releaseType,
|
|
27
|
-
commits:
|
|
28
|
-
...(
|
|
27
|
+
commits: unreleasedReading.commits,
|
|
28
|
+
...(unreleasedReading.unparseableCommits !== undefined && {
|
|
29
|
+
unparseableCommits: unreleasedReading.unparseableCommits,
|
|
30
|
+
}),
|
|
29
31
|
...(decision?.bumpOverride !== undefined && { bumpOverride: decision.bumpOverride }),
|
|
30
32
|
};
|
|
31
|
-
attachChangelogDiagnostics(result,
|
|
33
|
+
attachChangelogDiagnostics(result, unreleasedReading.diagnostics);
|
|
32
34
|
return result;
|
|
33
35
|
}
|
|
34
36
|
function buildReleasedDirectory(target, fields, release, decision, history) {
|
|
35
|
-
const {
|
|
37
|
+
const { unreleasedReading } = history;
|
|
36
38
|
const setVersion = decision?.setVersion;
|
|
37
39
|
const result = {
|
|
38
40
|
...fields,
|
|
39
41
|
status: 'released',
|
|
40
42
|
kind: 'directory',
|
|
41
43
|
name: target.id,
|
|
42
|
-
commits:
|
|
44
|
+
commits: unreleasedReading.commits,
|
|
43
45
|
...(setVersion === undefined && {
|
|
44
46
|
releaseType: release.releaseType,
|
|
45
|
-
parsedCommitCount:
|
|
47
|
+
parsedCommitCount: unreleasedReading.parsedCommitCount,
|
|
46
48
|
}),
|
|
47
49
|
...(decision?.bumpOverride !== undefined && { bumpOverride: decision.bumpOverride }),
|
|
48
|
-
...(setVersion !== undefined && { setVersion }),
|
|
50
|
+
...(setVersion !== undefined && { versionOverride: setVersion }),
|
|
49
51
|
};
|
|
50
|
-
if (setVersion === undefined &&
|
|
51
|
-
result.unparseableCommits =
|
|
52
|
+
if (setVersion === undefined && unreleasedReading.unparseableCommits !== undefined) {
|
|
53
|
+
result.unparseableCommits = unreleasedReading.unparseableCommits;
|
|
52
54
|
}
|
|
53
|
-
attachChangelogDiagnostics(result,
|
|
55
|
+
attachChangelogDiagnostics(result, unreleasedReading.diagnostics);
|
|
54
56
|
return result;
|
|
55
57
|
}
|
|
56
58
|
function buildReleasedPackage(target, fields, release, decision, history) {
|
|
57
|
-
const {
|
|
59
|
+
const { unreleasedReading } = history;
|
|
58
60
|
const setVersion = decision?.setVersion;
|
|
59
61
|
const hasParseCounts = decision !== undefined && setVersion === undefined;
|
|
60
62
|
const result = {
|
|
61
63
|
...fields,
|
|
62
64
|
status: 'released',
|
|
63
65
|
kind: 'package',
|
|
64
|
-
commits:
|
|
66
|
+
commits: unreleasedReading.commits,
|
|
65
67
|
...(target.isWorkspace && { name: target.id }),
|
|
66
68
|
...(setVersion === undefined && { releaseType: release.releaseType }),
|
|
67
|
-
...(hasParseCounts && { parsedCommitCount:
|
|
69
|
+
...(hasParseCounts && { parsedCommitCount: unreleasedReading.parsedCommitCount }),
|
|
68
70
|
...(decision === undefined && { propagatedOnly: true }),
|
|
69
71
|
...(release.propagatedFrom !== undefined && { propagatedFrom: release.propagatedFrom }),
|
|
70
72
|
...(decision?.bumpOverride !== undefined && { bumpOverride: decision.bumpOverride }),
|
|
71
|
-
...(setVersion !== undefined && { setVersion }),
|
|
73
|
+
...(setVersion !== undefined && { versionOverride: setVersion }),
|
|
72
74
|
};
|
|
73
|
-
if (setVersion === undefined &&
|
|
74
|
-
result.unparseableCommits =
|
|
75
|
+
if (setVersion === undefined && unreleasedReading.unparseableCommits !== undefined) {
|
|
76
|
+
result.unparseableCommits = unreleasedReading.unparseableCommits;
|
|
75
77
|
}
|
|
76
|
-
attachChangelogDiagnostics(result,
|
|
78
|
+
attachChangelogDiagnostics(result, unreleasedReading.diagnostics);
|
|
77
79
|
return result;
|
|
78
80
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export function buildSyntheticChangelogEntry(propagatedFrom, version, date, sectionTitle) {
|
|
2
|
-
const items = propagatedFrom.map((
|
|
3
|
-
description: `Bumped \`${
|
|
2
|
+
const items = propagatedFrom.map((dependency) => ({
|
|
3
|
+
description: `Bumped \`${dependency.packageName}\` to ${dependency.newVersion}`,
|
|
4
4
|
}));
|
|
5
5
|
return {
|
|
6
6
|
version,
|
package/dist/esm/bumpVersion.js
CHANGED
|
@@ -9,18 +9,18 @@ export function bumpVersion(version, releaseType) {
|
|
|
9
9
|
if (major === undefined || minor === undefined || patch === undefined) {
|
|
10
10
|
throw new Error(`Invalid semver version: '${version}'`);
|
|
11
11
|
}
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
12
|
+
const majorNumber = Number.parseInt(major, 10);
|
|
13
|
+
const minorNumber = Number.parseInt(minor, 10);
|
|
14
|
+
const patchNumber = Number.parseInt(patch, 10);
|
|
15
15
|
switch (releaseType) {
|
|
16
16
|
case 'major':
|
|
17
|
-
if (
|
|
18
|
-
return `0.${
|
|
17
|
+
if (majorNumber === 0) {
|
|
18
|
+
return `0.${minorNumber + 1}.0`;
|
|
19
19
|
}
|
|
20
|
-
return `${
|
|
20
|
+
return `${majorNumber + 1}.0.0`;
|
|
21
21
|
case 'minor':
|
|
22
|
-
return `${
|
|
22
|
+
return `${majorNumber}.${minorNumber + 1}.0`;
|
|
23
23
|
case 'patch':
|
|
24
|
-
return `${
|
|
24
|
+
return `${majorNumber}.${minorNumber}.${patchNumber + 1}`;
|
|
25
25
|
}
|
|
26
26
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
export declare const CHANGELOG_ARCHIVE_DIRECTORY = "changelogs";
|
|
3
3
|
/** An archive file that exists on disk. */
|
|
4
4
|
export interface ChangelogArchivePath {
|
|
5
|
-
|
|
5
|
+
majorVersion: number;
|
|
6
6
|
path: string;
|
|
7
7
|
}
|
|
8
8
|
/** Returns the path, relative to the changelog directory, of the archive file for a major version. */
|
|
@@ -13,9 +13,9 @@ export function listArchiveFiles(changelogDir) {
|
|
|
13
13
|
for (const name of readdirSync(archiveDir)) {
|
|
14
14
|
const match = ARCHIVE_FILE_PATTERN.exec(name);
|
|
15
15
|
if (match?.[1] !== undefined) {
|
|
16
|
-
archives.push({
|
|
16
|
+
archives.push({ majorVersion: Number(match[1]), path: join(archiveDir, name) });
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
-
return archives.toSorted((a, b) => b.
|
|
19
|
+
return archives.toSorted((a, b) => b.majorVersion - a.majorVersion);
|
|
20
20
|
}
|
|
21
21
|
const ARCHIVE_FILE_PATTERN = /^CHANGELOG-v(\d+)\.md$/;
|
|
@@ -12,8 +12,8 @@ export function renderChangelogJson(entries) {
|
|
|
12
12
|
return stringify(sortNewestFirst(entries), { maxLength: 100 }) + '\n';
|
|
13
13
|
}
|
|
14
14
|
export function mergeChangelogEntriesWithDisk(filePath, entries) {
|
|
15
|
-
const
|
|
16
|
-
return mergeEntries(entries,
|
|
15
|
+
const existingEntries = readExistingEntries(filePath);
|
|
16
|
+
return mergeEntries(entries, existingEntries);
|
|
17
17
|
}
|
|
18
18
|
function sortNewestFirst(entries) {
|
|
19
19
|
return [...entries].toSorted((a, b) => compareVersionsDescending(a.version, b.version));
|
|
@@ -24,11 +24,11 @@ function readExistingEntries(filePath) {
|
|
|
24
24
|
}
|
|
25
25
|
try {
|
|
26
26
|
const content = readFileSync(filePath, 'utf8');
|
|
27
|
-
const
|
|
28
|
-
if (!isUnknownArray(
|
|
27
|
+
const parsedJson = JSON.parse(content);
|
|
28
|
+
if (!isUnknownArray(parsedJson)) {
|
|
29
29
|
return [];
|
|
30
30
|
}
|
|
31
|
-
return
|
|
31
|
+
return parsedJson.filter(isChangelogEntry);
|
|
32
32
|
}
|
|
33
33
|
catch (error) {
|
|
34
34
|
console.warn(`Warning: could not parse existing ${filePath}: ${describeError(error)}; treating as empty`);
|
|
@@ -16,11 +16,11 @@ export function readChangelogEntries(filePath) {
|
|
|
16
16
|
}
|
|
17
17
|
try {
|
|
18
18
|
const content = readFileSync(filePath, 'utf8');
|
|
19
|
-
const
|
|
20
|
-
if (!isUnknownArray(
|
|
19
|
+
const parsedJson = JSON.parse(content);
|
|
20
|
+
if (!isUnknownArray(parsedJson)) {
|
|
21
21
|
return undefined;
|
|
22
22
|
}
|
|
23
|
-
return
|
|
23
|
+
return parsedJson.filter(isChangelogEntry);
|
|
24
24
|
}
|
|
25
25
|
catch {
|
|
26
26
|
return undefined;
|
|
@@ -31,15 +31,15 @@ export declare function createOverrideContext(workspacePaths: readonly string[])
|
|
|
31
31
|
* yields an empty map.
|
|
32
32
|
*/
|
|
33
33
|
export declare function loadOverridesForScopes(scopes: {
|
|
34
|
-
|
|
34
|
+
projectPath?: string;
|
|
35
35
|
workspaces?: string[];
|
|
36
36
|
}): LoadOverridesForScopesResult;
|
|
37
37
|
/** Result of {@link loadOverridesForScopes}: per-scope maps plus accumulated load/validation errors. */
|
|
38
38
|
export interface LoadOverridesForScopesResult {
|
|
39
39
|
/** Project-tier (root) overrides. Empty map when the project file is absent. */
|
|
40
|
-
|
|
40
|
+
projectOverrides: Map<string, ChangelogOverride>;
|
|
41
41
|
/** Workspace-tier overrides keyed by `workspacePath` (e.g., `'packages/foo'`). Empty map when absent. */
|
|
42
|
-
|
|
42
|
+
overridesByWorkspace: Map<string, Map<string, ChangelogOverride>>;
|
|
43
43
|
/** All load and validation errors across every requested scope, prefixed with the offending file path. */
|
|
44
44
|
errors: string[];
|
|
45
45
|
}
|
|
@@ -50,8 +50,8 @@ export interface LoadOverridesForScopesResult {
|
|
|
50
50
|
* that matches nothing in its own workspace is stale, and it is warned about at once rather than at the end of the run.
|
|
51
51
|
*/
|
|
52
52
|
export interface OverrideContext {
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
projectOverrides: Map<string, ChangelogOverride>;
|
|
54
|
+
overridesByWorkspace: Map<string, Map<string, ChangelogOverride>>;
|
|
55
55
|
overrideWarnings: string[];
|
|
56
56
|
globalMatchedRootKeys: Set<string>;
|
|
57
57
|
}
|
|
@@ -3,15 +3,15 @@ import { applyChangelogOverrides, formatStaleOverrideKeyWarning } from "./applyC
|
|
|
3
3
|
import { loadChangelogOverrides } from "./loadChangelogOverrides.js";
|
|
4
4
|
const OVERRIDES_FILENAME = '.meta/changelog-overrides.json';
|
|
5
5
|
export function applyWorkspaceOverrides(newEntries, workspacePath, overrideContext) {
|
|
6
|
-
const {
|
|
7
|
-
const workspaceOverrides = workspacePath === undefined ? undefined :
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
if (
|
|
11
|
-
throw new Error(`Changelog override application failed:\n - ${
|
|
6
|
+
const { projectOverrides, overridesByWorkspace, overrideWarnings, globalMatchedRootKeys } = overrideContext;
|
|
7
|
+
const workspaceOverrides = workspacePath === undefined ? undefined : overridesByWorkspace.get(workspacePath);
|
|
8
|
+
const composedOverrides = composeOverrides(projectOverrides, workspaceOverrides);
|
|
9
|
+
const appliedResult = applyChangelogOverrides(newEntries, composedOverrides);
|
|
10
|
+
if (appliedResult.errors.length > 0) {
|
|
11
|
+
throw new Error(`Changelog override application failed:\n - ${appliedResult.errors.join('\n - ')}`);
|
|
12
12
|
}
|
|
13
|
-
overrideWarnings.push(...
|
|
14
|
-
const matchedSet = new Set(
|
|
13
|
+
overrideWarnings.push(...appliedResult.warnings);
|
|
14
|
+
const matchedSet = new Set(appliedResult.matchedKeys);
|
|
15
15
|
if (workspaceOverrides !== undefined) {
|
|
16
16
|
for (const key of workspaceOverrides.keys()) {
|
|
17
17
|
if (!matchedSet.has(key)) {
|
|
@@ -19,45 +19,45 @@ export function applyWorkspaceOverrides(newEntries, workspacePath, overrideConte
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
|
-
for (const key of
|
|
22
|
+
for (const key of appliedResult.matchedKeys) {
|
|
23
23
|
if (workspaceOverrides?.has(key))
|
|
24
24
|
continue;
|
|
25
25
|
globalMatchedRootKeys.add(key);
|
|
26
26
|
}
|
|
27
|
-
return
|
|
27
|
+
return appliedResult;
|
|
28
28
|
}
|
|
29
29
|
export function composeOverrides(rootEntries, workspaceEntries) {
|
|
30
|
-
const
|
|
30
|
+
const composedOverrides = new Map(rootEntries);
|
|
31
31
|
if (workspaceEntries !== undefined) {
|
|
32
32
|
for (const [key, value] of workspaceEntries) {
|
|
33
|
-
|
|
33
|
+
composedOverrides.set(key, value);
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
|
-
return
|
|
36
|
+
return composedOverrides;
|
|
37
37
|
}
|
|
38
38
|
export function createOverrideContext(workspacePaths) {
|
|
39
|
-
const result = loadOverridesForScopes({
|
|
39
|
+
const result = loadOverridesForScopes({ projectPath: '.', workspaces: [...workspacePaths] });
|
|
40
40
|
if (result.errors.length > 0) {
|
|
41
41
|
throw new Error(`Failed to load changelog overrides:\n - ${result.errors.join('\n - ')}`);
|
|
42
42
|
}
|
|
43
43
|
return {
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
projectOverrides: result.projectOverrides,
|
|
45
|
+
overridesByWorkspace: result.overridesByWorkspace,
|
|
46
46
|
overrideWarnings: [],
|
|
47
47
|
globalMatchedRootKeys: new Set(),
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
50
|
export function loadOverridesForScopes(scopes) {
|
|
51
51
|
const errors = [];
|
|
52
|
-
let
|
|
53
|
-
const
|
|
54
|
-
if (scopes.
|
|
55
|
-
const result = loadChangelogOverrides(resolveOverridePath(scopes.
|
|
52
|
+
let projectOverrides = new Map();
|
|
53
|
+
const overridesByWorkspace = new Map();
|
|
54
|
+
if (scopes.projectPath !== undefined) {
|
|
55
|
+
const result = loadChangelogOverrides(resolveOverridePath(scopes.projectPath));
|
|
56
56
|
if ('errors' in result) {
|
|
57
57
|
errors.push(...result.errors);
|
|
58
58
|
}
|
|
59
59
|
else {
|
|
60
|
-
|
|
60
|
+
projectOverrides = result.overrides;
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
const workspacePaths = scopes.workspaces ?? [];
|
|
@@ -67,10 +67,10 @@ export function loadOverridesForScopes(scopes) {
|
|
|
67
67
|
errors.push(...result.errors);
|
|
68
68
|
}
|
|
69
69
|
else if (result.overrides.size > 0) {
|
|
70
|
-
|
|
70
|
+
overridesByWorkspace.set(workspacePath, result.overrides);
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
|
-
return {
|
|
73
|
+
return { projectOverrides, overridesByWorkspace, errors };
|
|
74
74
|
}
|
|
75
75
|
export function resolveOverridePath(scopeRoot) {
|
|
76
76
|
return path.posix.join(scopeRoot, OVERRIDES_FILENAME);
|
|
@@ -10,18 +10,18 @@ export function classifyChangelogCommit(commit, workTypes, options) {
|
|
|
10
10
|
if (!hasTicketPrefix(subject)) {
|
|
11
11
|
return { kind: 'unparseable' };
|
|
12
12
|
}
|
|
13
|
-
const
|
|
14
|
-
if (
|
|
13
|
+
const parsedCommit = parseCommitMessage(commit.message, commit.hash, workTypes, undefined, options);
|
|
14
|
+
if (parsedCommit === undefined) {
|
|
15
15
|
return { kind: 'unparseable' };
|
|
16
16
|
}
|
|
17
|
-
const config = workTypes[
|
|
17
|
+
const config = workTypes[parsedCommit.type];
|
|
18
18
|
if (config === undefined) {
|
|
19
19
|
return { kind: 'unparseable' };
|
|
20
20
|
}
|
|
21
21
|
if (config.excludedFromChangelog === true) {
|
|
22
22
|
return { kind: 'excluded' };
|
|
23
23
|
}
|
|
24
|
-
return { kind: 'header', header: config.header, type:
|
|
24
|
+
return { kind: 'header', header: config.header, type: parsedCommit.type, breaking: parsedCommit.breaking };
|
|
25
25
|
}
|
|
26
26
|
export function isNonChangeSubject(subject) {
|
|
27
27
|
return isReleaseSubject(subject) || MERGE_SUBJECT_PATTERN.test(subject);
|