@williamthorsen/release-kit 4.7.0 → 5.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/CHANGELOG.md +80 -0
- package/README.md +310 -40
- package/cliff.toml.template +2 -1
- package/dist/esm/.cache +1 -1
- package/dist/esm/bin/release-kit.js +67 -12
- package/dist/esm/buildDependencyGraph.d.ts +3 -3
- package/dist/esm/buildDependencyGraph.js +10 -10
- package/dist/esm/buildReleaseSummary.js +4 -4
- package/dist/esm/bumpAllVersions.d.ts +1 -0
- package/dist/esm/bumpAllVersions.js +16 -2
- package/dist/esm/bumpVersion.js +3 -0
- package/dist/esm/commitCommand.js +1 -1
- package/dist/esm/compareVersions.d.ts +1 -0
- package/dist/esm/compareVersions.js +27 -0
- package/dist/esm/createGithubRelease.d.ts +6 -2
- package/dist/esm/createGithubRelease.js +17 -17
- package/dist/esm/createGithubReleaseCommand.d.ts +1 -0
- package/dist/esm/createGithubReleaseCommand.js +41 -0
- package/dist/esm/defaults.js +5 -3
- package/dist/esm/deriveWorkspaceConfig.d.ts +2 -0
- package/dist/esm/deriveWorkspaceConfig.js +37 -0
- package/dist/esm/detectUndeclaredTagPrefixes.d.ts +7 -0
- package/dist/esm/detectUndeclaredTagPrefixes.js +46 -0
- package/dist/esm/generateChangelogJson.js +37 -1
- package/dist/esm/generateChangelogs.d.ts +1 -1
- package/dist/esm/generateChangelogs.js +14 -3
- package/dist/esm/getCommitsSinceTarget.d.ts +1 -1
- package/dist/esm/getCommitsSinceTarget.js +8 -4
- package/dist/esm/index.d.ts +9 -3
- package/dist/esm/index.js +12 -3
- package/dist/esm/init/detectRepoType.js +1 -2
- package/dist/esm/init/initCommand.js +1 -1
- package/dist/esm/init/scaffold.d.ts +1 -1
- package/dist/esm/init/scaffold.js +8 -5
- package/dist/esm/init/templates.d.ts +1 -0
- package/dist/esm/init/templates.js +40 -10
- package/dist/esm/injectReleaseNotesIntoReadme.d.ts +6 -1
- package/dist/esm/injectReleaseNotesIntoReadme.js +20 -7
- package/dist/esm/loadConfig.d.ts +2 -1
- package/dist/esm/loadConfig.js +65 -12
- package/dist/esm/parseRequestedTags.d.ts +1 -0
- package/dist/esm/parseRequestedTags.js +10 -0
- package/dist/esm/prepareCommand.d.ts +3 -1
- package/dist/esm/prepareCommand.js +75 -27
- package/dist/esm/previewTagPrefixes.d.ts +30 -0
- package/dist/esm/previewTagPrefixes.js +120 -0
- package/dist/esm/propagateBumps.d.ts +1 -0
- package/dist/esm/propagateBumps.js +1 -1
- package/dist/esm/publishCommand.js +8 -13
- package/dist/esm/pushCommand.d.ts +1 -0
- package/dist/esm/pushCommand.js +47 -0
- package/dist/esm/pushRelease.d.ts +11 -0
- package/dist/esm/pushRelease.js +26 -0
- package/dist/esm/readCurrentVersion.d.ts +1 -0
- package/dist/esm/readCurrentVersion.js +21 -0
- package/dist/esm/releasePrepare.d.ts +2 -0
- package/dist/esm/releasePrepare.js +72 -30
- package/dist/esm/releasePrepareMono.js +235 -112
- package/dist/esm/renderReleaseNotes.d.ts +1 -0
- package/dist/esm/renderReleaseNotes.js +29 -2
- package/dist/esm/reportPrepare.js +100 -73
- package/dist/esm/resolveCliffConfigPath.js +1 -1
- package/dist/esm/resolveCommandTags.d.ts +1 -1
- package/dist/esm/resolveCommandTags.js +17 -13
- package/dist/esm/resolveReleaseNotesConfig.d.ts +8 -1
- package/dist/esm/resolveReleaseNotesConfig.js +17 -7
- package/dist/esm/resolveReleaseTags.d.ts +2 -1
- package/dist/esm/resolveReleaseTags.js +19 -14
- package/dist/esm/showTagPrefixesCommand.d.ts +1 -0
- package/dist/esm/showTagPrefixesCommand.js +84 -0
- package/dist/esm/sync-labels/initCommand.js +1 -1
- package/dist/esm/sync-labels/presets.js +1 -1
- package/dist/esm/sync-labels/templates.js +1 -1
- package/dist/esm/tagCommand.js +1 -1
- package/dist/esm/types.d.ts +22 -7
- package/dist/esm/validateConfig.js +179 -36
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/esm/writeReleaseNotesPreviews.d.ts +18 -0
- package/dist/esm/writeReleaseNotesPreviews.js +65 -0
- package/package.json +2 -2
- package/dist/esm/component.d.ts +0 -2
- package/dist/esm/component.js +0 -14
- package/dist/esm/findPackageRoot.d.ts +0 -1
- package/dist/esm/findPackageRoot.js +0 -17
- package/dist/esm/githubReleaseCommand.d.ts +0 -1
- package/dist/esm/githubReleaseCommand.js +0 -35
|
@@ -8,11 +8,12 @@ function validateConfig(raw) {
|
|
|
8
8
|
const knownFields = /* @__PURE__ */ new Set([
|
|
9
9
|
"changelogJson",
|
|
10
10
|
"cliffConfigPath",
|
|
11
|
-
"components",
|
|
12
11
|
"formatCommand",
|
|
13
12
|
"releaseNotes",
|
|
13
|
+
"retiredPackages",
|
|
14
14
|
"scopeAliases",
|
|
15
15
|
"versionPatterns",
|
|
16
|
+
"workspaces",
|
|
16
17
|
"workTypes"
|
|
17
18
|
]);
|
|
18
19
|
for (const key of Object.keys(raw)) {
|
|
@@ -21,26 +22,20 @@ function validateConfig(raw) {
|
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
24
|
validateChangelogJson(raw.changelogJson, config, errors);
|
|
24
|
-
|
|
25
|
+
validateWorkspaces(raw.workspaces, config, errors);
|
|
25
26
|
validateReleaseNotes(raw.releaseNotes, config, errors);
|
|
26
27
|
validateVersionPatterns(raw.versionPatterns, config, errors);
|
|
27
28
|
validateWorkTypes(raw.workTypes, config, errors);
|
|
28
29
|
validateStringField("formatCommand", raw.formatCommand, config, errors);
|
|
29
30
|
validateStringField("cliffConfigPath", raw.cliffConfigPath, config, errors);
|
|
30
31
|
validateScopeAliases(raw.scopeAliases, config, errors);
|
|
32
|
+
validateRetiredPackages(raw.retiredPackages, config, errors);
|
|
31
33
|
const warnings = [];
|
|
32
34
|
const changelogJsonEnabled = config.changelogJson?.enabled ?? true;
|
|
33
|
-
if (!changelogJsonEnabled) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
);
|
|
38
|
-
}
|
|
39
|
-
if (config.releaseNotes?.shouldInjectIntoReadme) {
|
|
40
|
-
warnings.push(
|
|
41
|
-
"releaseNotes.shouldInjectIntoReadme is enabled but changelogJson.enabled is false; README injection will be skipped at runtime"
|
|
42
|
-
);
|
|
43
|
-
}
|
|
35
|
+
if (!changelogJsonEnabled && config.releaseNotes?.shouldInjectIntoReadme) {
|
|
36
|
+
warnings.push(
|
|
37
|
+
"releaseNotes.shouldInjectIntoReadme is enabled but changelogJson.enabled is false; README injection will be skipped at runtime"
|
|
38
|
+
);
|
|
44
39
|
}
|
|
45
40
|
return { config, errors, warnings };
|
|
46
41
|
}
|
|
@@ -86,10 +81,16 @@ function validateReleaseNotes(value, config, errors) {
|
|
|
86
81
|
errors.push("'releaseNotes' must be an object");
|
|
87
82
|
return;
|
|
88
83
|
}
|
|
89
|
-
const knownReleaseNotesFields = /* @__PURE__ */ new Set(["shouldInjectIntoReadme"
|
|
84
|
+
const knownReleaseNotesFields = /* @__PURE__ */ new Set(["shouldInjectIntoReadme"]);
|
|
90
85
|
for (const key of Object.keys(value)) {
|
|
91
86
|
if (!knownReleaseNotesFields.has(key)) {
|
|
92
|
-
|
|
87
|
+
if (key === "shouldCreateGithubRelease") {
|
|
88
|
+
errors.push(
|
|
89
|
+
"releaseNotes.shouldCreateGithubRelease is no longer supported. Adoption is now signaled by installing the create-github-release workflow. Remove this field from your config; see README for the updated workflow."
|
|
90
|
+
);
|
|
91
|
+
} else {
|
|
92
|
+
errors.push(`releaseNotes: unknown field '${key}'`);
|
|
93
|
+
}
|
|
93
94
|
}
|
|
94
95
|
}
|
|
95
96
|
const result = {};
|
|
@@ -100,57 +101,199 @@ function validateReleaseNotes(value, config, errors) {
|
|
|
100
101
|
errors.push("releaseNotes.shouldInjectIntoReadme: must be a boolean");
|
|
101
102
|
}
|
|
102
103
|
}
|
|
103
|
-
if (value.shouldCreateGithubRelease !== void 0) {
|
|
104
|
-
if (typeof value.shouldCreateGithubRelease === "boolean") {
|
|
105
|
-
result.shouldCreateGithubRelease = value.shouldCreateGithubRelease;
|
|
106
|
-
} else {
|
|
107
|
-
errors.push("releaseNotes.shouldCreateGithubRelease: must be a boolean");
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
104
|
config.releaseNotes = result;
|
|
111
105
|
}
|
|
112
106
|
function isStringArray(value) {
|
|
113
107
|
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
114
108
|
}
|
|
115
|
-
function
|
|
109
|
+
function validateWorkspaces(value, config, errors) {
|
|
116
110
|
if (value === void 0) return;
|
|
117
111
|
if (!Array.isArray(value)) {
|
|
118
|
-
errors.push("'
|
|
112
|
+
errors.push("'workspaces' must be an array");
|
|
119
113
|
return;
|
|
120
114
|
}
|
|
121
|
-
const
|
|
122
|
-
const
|
|
115
|
+
const workspaces = [];
|
|
116
|
+
const knownWorkspaceFields = /* @__PURE__ */ new Set(["dir", "shouldExclude", "legacyIdentities"]);
|
|
123
117
|
for (const [i, entry] of value.entries()) {
|
|
124
118
|
if (!isRecord(entry)) {
|
|
125
|
-
errors.push(`
|
|
119
|
+
errors.push(`workspaces[${i}]: must be an object`);
|
|
126
120
|
continue;
|
|
127
121
|
}
|
|
128
122
|
if (typeof entry.dir !== "string" || entry.dir === "") {
|
|
129
|
-
errors.push(`
|
|
123
|
+
errors.push(`workspaces[${i}]: 'dir' is required`);
|
|
130
124
|
continue;
|
|
131
125
|
}
|
|
132
126
|
for (const key of Object.keys(entry)) {
|
|
133
|
-
if (!
|
|
127
|
+
if (!knownWorkspaceFields.has(key)) {
|
|
134
128
|
if (key === "tagPrefix") {
|
|
135
129
|
errors.push(
|
|
136
|
-
`
|
|
130
|
+
`workspaces[${i}]: 'tagPrefix' is no longer supported; remove it to use the default '${entry.dir}-v'`
|
|
131
|
+
);
|
|
132
|
+
} else if (key === "legacyTagPrefixes") {
|
|
133
|
+
errors.push(
|
|
134
|
+
`workspaces[${i}]: 'legacyTagPrefixes' is no longer supported; use 'legacyIdentities: [{ name, tagPrefix }, ...]' instead`
|
|
137
135
|
);
|
|
138
136
|
} else {
|
|
139
|
-
errors.push(`
|
|
137
|
+
errors.push(`workspaces[${i}]: unknown field '${key}'`);
|
|
140
138
|
}
|
|
141
139
|
}
|
|
142
140
|
}
|
|
143
|
-
const
|
|
141
|
+
const workspace = { dir: entry.dir };
|
|
144
142
|
if (entry.shouldExclude !== void 0) {
|
|
145
143
|
if (typeof entry.shouldExclude === "boolean") {
|
|
146
|
-
|
|
144
|
+
workspace.shouldExclude = entry.shouldExclude;
|
|
147
145
|
} else {
|
|
148
|
-
errors.push(`
|
|
146
|
+
errors.push(`workspaces[${i}]: 'shouldExclude' must be a boolean`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (entry.legacyIdentities !== void 0) {
|
|
150
|
+
const identities = validateLegacyIdentities(entry.legacyIdentities, i, errors);
|
|
151
|
+
if (identities !== void 0) {
|
|
152
|
+
workspace.legacyIdentities = identities;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
workspaces.push(workspace);
|
|
156
|
+
}
|
|
157
|
+
config.workspaces = workspaces;
|
|
158
|
+
}
|
|
159
|
+
function validateLegacyIdentities(value, workspaceIndex, errors) {
|
|
160
|
+
if (!Array.isArray(value)) {
|
|
161
|
+
errors.push(`workspaces[${workspaceIndex}]: 'legacyIdentities' must be an array`);
|
|
162
|
+
return void 0;
|
|
163
|
+
}
|
|
164
|
+
const knownIdentityFields = /* @__PURE__ */ new Set(["name", "tagPrefix"]);
|
|
165
|
+
const identities = [];
|
|
166
|
+
const seenTuples = /* @__PURE__ */ new Set();
|
|
167
|
+
for (const [entryIndex, entry] of value.entries()) {
|
|
168
|
+
if (!isRecord(entry)) {
|
|
169
|
+
errors.push(`workspaces[${workspaceIndex}].legacyIdentities[${entryIndex}]: must be an object`);
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
let entryValid = true;
|
|
173
|
+
for (const key2 of Object.keys(entry)) {
|
|
174
|
+
if (!knownIdentityFields.has(key2)) {
|
|
175
|
+
errors.push(`workspaces[${workspaceIndex}].legacyIdentities[${entryIndex}]: unknown field '${key2}'`);
|
|
176
|
+
entryValid = false;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
const { name, tagPrefix } = entry;
|
|
180
|
+
if (typeof name !== "string") {
|
|
181
|
+
errors.push(`workspaces[${workspaceIndex}].legacyIdentities[${entryIndex}].name: must be a string`);
|
|
182
|
+
entryValid = false;
|
|
183
|
+
} else if (name === "") {
|
|
184
|
+
errors.push(`workspaces[${workspaceIndex}].legacyIdentities[${entryIndex}].name: must be a non-empty string`);
|
|
185
|
+
entryValid = false;
|
|
186
|
+
}
|
|
187
|
+
if (typeof tagPrefix !== "string") {
|
|
188
|
+
errors.push(`workspaces[${workspaceIndex}].legacyIdentities[${entryIndex}].tagPrefix: must be a string`);
|
|
189
|
+
entryValid = false;
|
|
190
|
+
} else if (tagPrefix === "") {
|
|
191
|
+
errors.push(
|
|
192
|
+
`workspaces[${workspaceIndex}].legacyIdentities[${entryIndex}].tagPrefix: must be a non-empty string`
|
|
193
|
+
);
|
|
194
|
+
entryValid = false;
|
|
195
|
+
}
|
|
196
|
+
if (!entryValid || typeof name !== "string" || typeof tagPrefix !== "string") {
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
const key = `${name}\0${tagPrefix}`;
|
|
200
|
+
if (seenTuples.has(key)) {
|
|
201
|
+
errors.push(
|
|
202
|
+
`workspaces[${workspaceIndex}].legacyIdentities[${entryIndex}]: duplicate identity (name='${name}', tagPrefix='${tagPrefix}')`
|
|
203
|
+
);
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
seenTuples.add(key);
|
|
207
|
+
identities.push({ name, tagPrefix });
|
|
208
|
+
}
|
|
209
|
+
return identities;
|
|
210
|
+
}
|
|
211
|
+
function validateRetiredPackages(value, config, errors) {
|
|
212
|
+
if (value === void 0) return;
|
|
213
|
+
if (!Array.isArray(value)) {
|
|
214
|
+
errors.push("'retiredPackages' must be an array");
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
const validEntries = [];
|
|
218
|
+
const seenTuples = /* @__PURE__ */ new Set();
|
|
219
|
+
for (const [i, entry] of value.entries()) {
|
|
220
|
+
const retired = validateRetiredPackageEntry(entry, i, errors);
|
|
221
|
+
if (retired === void 0) continue;
|
|
222
|
+
const key = `${retired.name}\0${retired.tagPrefix}`;
|
|
223
|
+
if (seenTuples.has(key)) {
|
|
224
|
+
errors.push(
|
|
225
|
+
`retiredPackages[${i}]: duplicate package (name='${retired.name}', tagPrefix='${retired.tagPrefix}')`
|
|
226
|
+
);
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
seenTuples.add(key);
|
|
230
|
+
validEntries.push({ entry: retired, rawIndex: i });
|
|
231
|
+
}
|
|
232
|
+
detectRetiredVsLegacyCollisions(validEntries, config, errors);
|
|
233
|
+
config.retiredPackages = validEntries.map(({ entry }) => entry);
|
|
234
|
+
}
|
|
235
|
+
function validateRetiredPackageEntry(entry, i, errors) {
|
|
236
|
+
if (!isRecord(entry)) {
|
|
237
|
+
errors.push(`retiredPackages[${i}]: must be an object`);
|
|
238
|
+
return void 0;
|
|
239
|
+
}
|
|
240
|
+
const knownRetiredFields = /* @__PURE__ */ new Set(["name", "tagPrefix", "successor"]);
|
|
241
|
+
let entryValid = true;
|
|
242
|
+
for (const key of Object.keys(entry)) {
|
|
243
|
+
if (!knownRetiredFields.has(key)) {
|
|
244
|
+
errors.push(`retiredPackages[${i}]: unknown field '${key}'`);
|
|
245
|
+
entryValid = false;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
const { name, tagPrefix, successor } = entry;
|
|
249
|
+
if (!validateNonEmptyString(name, `retiredPackages[${i}].name`, errors)) {
|
|
250
|
+
entryValid = false;
|
|
251
|
+
}
|
|
252
|
+
if (!validateNonEmptyString(tagPrefix, `retiredPackages[${i}].tagPrefix`, errors)) {
|
|
253
|
+
entryValid = false;
|
|
254
|
+
}
|
|
255
|
+
if (successor !== void 0 && !validateNonEmptyString(successor, `retiredPackages[${i}].successor`, errors)) {
|
|
256
|
+
entryValid = false;
|
|
257
|
+
}
|
|
258
|
+
if (!entryValid || typeof name !== "string" || typeof tagPrefix !== "string") {
|
|
259
|
+
return void 0;
|
|
260
|
+
}
|
|
261
|
+
const retired = { name, tagPrefix };
|
|
262
|
+
if (typeof successor === "string" && successor !== "") {
|
|
263
|
+
retired.successor = successor;
|
|
264
|
+
}
|
|
265
|
+
return retired;
|
|
266
|
+
}
|
|
267
|
+
function detectRetiredVsLegacyCollisions(retiredPackages, config, errors) {
|
|
268
|
+
if (config.workspaces === void 0) return;
|
|
269
|
+
const legacyPrefixToWorkspace = /* @__PURE__ */ new Map();
|
|
270
|
+
for (const workspace of config.workspaces) {
|
|
271
|
+
if (workspace.legacyIdentities === void 0) continue;
|
|
272
|
+
for (const identity of workspace.legacyIdentities) {
|
|
273
|
+
if (!legacyPrefixToWorkspace.has(identity.tagPrefix)) {
|
|
274
|
+
legacyPrefixToWorkspace.set(identity.tagPrefix, workspace.dir);
|
|
149
275
|
}
|
|
150
276
|
}
|
|
151
|
-
components.push(component);
|
|
152
277
|
}
|
|
153
|
-
|
|
278
|
+
for (const { entry: retired, rawIndex } of retiredPackages) {
|
|
279
|
+
const collidingDir = legacyPrefixToWorkspace.get(retired.tagPrefix);
|
|
280
|
+
if (collidingDir !== void 0) {
|
|
281
|
+
errors.push(
|
|
282
|
+
`retiredPackages[${rawIndex}]: tagPrefix '${retired.tagPrefix}' collides with a declared legacyIdentities[].tagPrefix on workspace '${collidingDir}'`
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
function validateNonEmptyString(value, fieldPath, errors) {
|
|
288
|
+
if (typeof value !== "string") {
|
|
289
|
+
errors.push(`${fieldPath}: must be a string`);
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
if (value === "") {
|
|
293
|
+
errors.push(`${fieldPath}: must be a non-empty string`);
|
|
294
|
+
return false;
|
|
295
|
+
}
|
|
296
|
+
return true;
|
|
154
297
|
}
|
|
155
298
|
function validateVersionPatterns(value, config, errors) {
|
|
156
299
|
if (value === void 0) return;
|
package/dist/esm/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "
|
|
1
|
+
export declare const VERSION = "5.0.0";
|
package/dist/esm/version.js
CHANGED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface WriteReleaseNotesPreviewsOptions {
|
|
2
|
+
workspacePath: string;
|
|
3
|
+
tag: string;
|
|
4
|
+
changelogJsonPath: string;
|
|
5
|
+
sectionOrder: string[];
|
|
6
|
+
dryRun: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface PreviewFileResult {
|
|
9
|
+
filePath: string;
|
|
10
|
+
outcome: 'created' | 'overwritten' | 'skipped-no-readme' | 'failed' | 'dry-run';
|
|
11
|
+
error?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface WriteReleaseNotesPreviewsResult {
|
|
14
|
+
injectedReadme?: PreviewFileResult;
|
|
15
|
+
releaseNotes?: PreviewFileResult;
|
|
16
|
+
renderSkipped: boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare function writeReleaseNotesPreviews(options: WriteReleaseNotesPreviewsOptions): WriteReleaseNotesPreviewsResult;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { writeFileWithCheck } from "@williamthorsen/nmr-core";
|
|
4
|
+
import { extractVersion } from "./changelogJsonUtils.js";
|
|
5
|
+
import { dim } from "./format.js";
|
|
6
|
+
import { renderInjectedReadme } from "./injectReleaseNotesIntoReadme.js";
|
|
7
|
+
function writeReleaseNotesPreviews(options) {
|
|
8
|
+
const { workspacePath, tag, changelogJsonPath, sectionOrder, dryRun } = options;
|
|
9
|
+
const version = extractVersion(tag);
|
|
10
|
+
const readmePath = path.join(workspacePath, "README.md");
|
|
11
|
+
let readmeExists = existsSync(readmePath);
|
|
12
|
+
let readmeUnreadable = false;
|
|
13
|
+
let readmeContent = "";
|
|
14
|
+
if (readmeExists) {
|
|
15
|
+
try {
|
|
16
|
+
readmeContent = readFileSync(readmePath, "utf8");
|
|
17
|
+
} catch (error) {
|
|
18
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
19
|
+
console.warn(`Warning: failed to read ${readmePath}: ${message}; skipping injected-README preview`);
|
|
20
|
+
readmeExists = false;
|
|
21
|
+
readmeUnreadable = true;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const rendered = renderInjectedReadme(readmeContent, changelogJsonPath, tag, sectionOrder);
|
|
25
|
+
if (rendered === void 0) {
|
|
26
|
+
return { renderSkipped: true };
|
|
27
|
+
}
|
|
28
|
+
const docsDir = path.join(workspacePath, "docs");
|
|
29
|
+
const readmePreviewPath = path.join(docsDir, `README.v${version}.md`);
|
|
30
|
+
const releaseNotesPreviewPath = path.join(docsDir, `RELEASE_NOTES.v${version}.md`);
|
|
31
|
+
let injectedReadme;
|
|
32
|
+
if (readmeExists) {
|
|
33
|
+
injectedReadme = writePreviewFile(readmePreviewPath, rendered.injectedReadme, dryRun);
|
|
34
|
+
} else {
|
|
35
|
+
if (!readmeUnreadable) {
|
|
36
|
+
console.warn(
|
|
37
|
+
`Warning: ${readmePath} not found; skipping injected-README preview but still writing standalone release notes`
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
injectedReadme = { filePath: readmePreviewPath, outcome: "skipped-no-readme" };
|
|
41
|
+
}
|
|
42
|
+
const releaseNotesContent = rendered.releaseNotesMarkdown.endsWith("\n") ? rendered.releaseNotesMarkdown : `${rendered.releaseNotesMarkdown}
|
|
43
|
+
`;
|
|
44
|
+
const releaseNotes = writePreviewFile(releaseNotesPreviewPath, releaseNotesContent, dryRun);
|
|
45
|
+
return { injectedReadme, releaseNotes, renderSkipped: false };
|
|
46
|
+
}
|
|
47
|
+
function writePreviewFile(filePath, content, dryRun) {
|
|
48
|
+
if (dryRun) {
|
|
49
|
+
console.info(dim(` [dry-run] Would write ${filePath}`));
|
|
50
|
+
return { filePath, outcome: "dry-run" };
|
|
51
|
+
}
|
|
52
|
+
const result = writeFileWithCheck(filePath, content, { dryRun: false, overwrite: true });
|
|
53
|
+
if (result.outcome === "failed") {
|
|
54
|
+
console.error(`Error writing ${filePath}: ${result.error ?? "unknown error"}`);
|
|
55
|
+
return { filePath, outcome: "failed", ...result.error === void 0 ? {} : { error: result.error } };
|
|
56
|
+
}
|
|
57
|
+
if (result.outcome === "created" || result.outcome === "overwritten") {
|
|
58
|
+
console.info(dim(` Wrote ${filePath}`));
|
|
59
|
+
return { filePath, outcome: result.outcome };
|
|
60
|
+
}
|
|
61
|
+
return { filePath, outcome: "failed", error: `unexpected outcome: ${result.outcome}` };
|
|
62
|
+
}
|
|
63
|
+
export {
|
|
64
|
+
writeReleaseNotesPreviews
|
|
65
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@williamthorsen/release-kit",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"description": "Version-bumping and changelog-generation toolkit for release workflows",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"homepage": "https://github.com/williamthorsen/node-monorepo-tools/tree/main/packages/release-kit#readme",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"jiti": "2.6.1",
|
|
36
36
|
"js-yaml": "4.1.1",
|
|
37
37
|
"json-stringify-pretty-compact": "4.0.0",
|
|
38
|
-
"@williamthorsen/
|
|
38
|
+
"@williamthorsen/nmr-core": "0.3.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/js-yaml": "4.0.9",
|
package/dist/esm/component.d.ts
DELETED
package/dist/esm/component.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { basename } from "node:path";
|
|
2
|
-
function component(workspacePath) {
|
|
3
|
-
const dir = basename(workspacePath);
|
|
4
|
-
return {
|
|
5
|
-
dir,
|
|
6
|
-
tagPrefix: `${dir}-v`,
|
|
7
|
-
packageFiles: [`${workspacePath}/package.json`],
|
|
8
|
-
changelogPaths: [workspacePath],
|
|
9
|
-
paths: [`${workspacePath}/**`]
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
|
-
export {
|
|
13
|
-
component
|
|
14
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function findPackageRoot(fromUrl: string): string;
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import { dirname, resolve } from "node:path";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
|
-
function findPackageRoot(fromUrl) {
|
|
5
|
-
let dir = dirname(fileURLToPath(fromUrl));
|
|
6
|
-
while (!existsSync(resolve(dir, "package.json"))) {
|
|
7
|
-
const parent = dirname(dir);
|
|
8
|
-
if (parent === dir) {
|
|
9
|
-
throw new Error("Could not find package root from " + fromUrl);
|
|
10
|
-
}
|
|
11
|
-
dir = parent;
|
|
12
|
-
}
|
|
13
|
-
return dir;
|
|
14
|
-
}
|
|
15
|
-
export {
|
|
16
|
-
findPackageRoot
|
|
17
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function githubReleaseCommand(argv: string[]): Promise<void>;
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { parseArgs, translateParseError } from "@williamthorsen/node-monorepo-core";
|
|
2
|
-
import { createGithubReleases } from "./createGithubRelease.js";
|
|
3
|
-
import { resolveCommandTags } from "./resolveCommandTags.js";
|
|
4
|
-
import { resolveReleaseNotesConfig } from "./resolveReleaseNotesConfig.js";
|
|
5
|
-
const githubReleaseFlagSchema = {
|
|
6
|
-
dryRun: { long: "--dry-run", type: "boolean" },
|
|
7
|
-
only: { long: "--only", type: "string" }
|
|
8
|
-
};
|
|
9
|
-
async function githubReleaseCommand(argv) {
|
|
10
|
-
let parsed;
|
|
11
|
-
try {
|
|
12
|
-
parsed = parseArgs(argv, githubReleaseFlagSchema);
|
|
13
|
-
} catch (error) {
|
|
14
|
-
console.error(`Error: ${translateParseError(error)}`);
|
|
15
|
-
process.exit(1);
|
|
16
|
-
}
|
|
17
|
-
const { dryRun } = parsed.flags;
|
|
18
|
-
const only = parsed.flags.only?.split(",");
|
|
19
|
-
const resolvedTags = await resolveCommandTags(only);
|
|
20
|
-
const { releaseNotes, changelogJsonOutputPath } = await resolveReleaseNotesConfig();
|
|
21
|
-
try {
|
|
22
|
-
createGithubReleases(
|
|
23
|
-
resolvedTags,
|
|
24
|
-
{ ...releaseNotes, shouldCreateGithubRelease: true },
|
|
25
|
-
changelogJsonOutputPath,
|
|
26
|
-
dryRun
|
|
27
|
-
);
|
|
28
|
-
} catch (error) {
|
|
29
|
-
console.error(`Error creating GitHub Releases: ${error instanceof Error ? error.message : String(error)}`);
|
|
30
|
-
process.exit(1);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
export {
|
|
34
|
-
githubReleaseCommand
|
|
35
|
-
};
|