@williamthorsen/release-kit 4.8.0 → 5.1.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 +134 -4
- package/README.md +404 -40
- package/cliff.toml.template +2 -1
- package/dist/esm/.cache +1 -1
- package/dist/esm/assertCleanWorkingTree.js +1 -1
- package/dist/esm/bin/release-kit.js +45 -14
- package/dist/esm/buildChangelogEntries.d.ts +3 -0
- package/dist/esm/{generateChangelogJson.js → buildChangelogEntries.js} +40 -77
- package/dist/esm/buildDependencyGraph.d.ts +4 -3
- package/dist/esm/buildDependencyGraph.js +18 -11
- package/dist/esm/buildReleaseSummary.js +12 -4
- package/dist/esm/buildSyntheticChangelogEntry.d.ts +5 -0
- package/dist/esm/buildSyntheticChangelogEntry.js +13 -0
- 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/changelogJsonFile.d.ts +4 -0
- package/dist/esm/changelogJsonFile.js +68 -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/decideRelease.d.ts +25 -0
- package/dist/esm/decideRelease.js +28 -0
- package/dist/esm/defaults.d.ts +1 -0
- package/dist/esm/defaults.js +7 -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/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 +2 -39
- package/dist/esm/index.js +0 -75
- 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 +35 -5
- package/dist/esm/injectReleaseNotesIntoReadme.d.ts +6 -1
- package/dist/esm/injectReleaseNotesIntoReadme.js +20 -7
- package/dist/esm/loadConfig.d.ts +12 -2
- package/dist/esm/loadConfig.js +161 -14
- 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 +121 -31
- 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/publish.d.ts +0 -1
- package/dist/esm/publish.js +3 -3
- package/dist/esm/publishCommand.js +18 -14
- package/dist/esm/pushCommand.js +5 -4
- 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 +140 -54
- package/dist/esm/releasePrepareMono.js +312 -143
- package/dist/esm/releasePrepareProject.d.ts +9 -0
- package/dist/esm/releasePrepareProject.js +109 -0
- package/dist/esm/renderReleaseNotes.d.ts +1 -0
- package/dist/esm/renderReleaseNotes.js +29 -2
- package/dist/esm/reportPrepare.js +146 -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/tagCommand.js +1 -1
- package/dist/esm/types.d.ts +77 -19
- package/dist/esm/validateConfig.js +205 -36
- package/dist/esm/validateOnlyExcludesStrandedDependents.d.ts +14 -0
- package/dist/esm/validateOnlyExcludesStrandedDependents.js +109 -0
- 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 +5 -2
- package/presets/labels/common.yaml +9 -6
- package/schemas/label-map.json +24 -0
- 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/generateChangelogJson.d.ts +0 -7
- package/dist/esm/githubReleaseCommand.d.ts +0 -1
- package/dist/esm/githubReleaseCommand.js +0 -35
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { parseArgs, translateParseError } from "@williamthorsen/
|
|
2
|
+
import { parseArgs, translateParseError } from "@williamthorsen/nmr-core";
|
|
3
3
|
import { commitCommand } from "../commitCommand.js";
|
|
4
|
-
import {
|
|
4
|
+
import { createGithubReleaseCommand } from "../createGithubReleaseCommand.js";
|
|
5
5
|
import { initCommand } from "../init/initCommand.js";
|
|
6
6
|
import { prepareCommand } from "../prepareCommand.js";
|
|
7
7
|
import { publishCommand } from "../publishCommand.js";
|
|
8
8
|
import { pushCommand } from "../pushCommand.js";
|
|
9
|
+
import { showTagPrefixesCommand } from "../showTagPrefixesCommand.js";
|
|
9
10
|
import { generateCommand } from "../sync-labels/generateCommand.js";
|
|
10
11
|
import { syncLabelsInitCommand } from "../sync-labels/initCommand.js";
|
|
11
12
|
import { syncLabelsCommand } from "../sync-labels/syncCommand.js";
|
|
@@ -21,7 +22,8 @@ Commands:
|
|
|
21
22
|
tag Create annotated git tags from the tags file
|
|
22
23
|
push Push release commit and tags (one push per tag)
|
|
23
24
|
publish Publish packages with release tags on HEAD
|
|
24
|
-
github-release
|
|
25
|
+
create-github-release Create GitHub Releases from changelog.json for tags on HEAD
|
|
26
|
+
show-tag-prefixes Show derived and declared legacy tag prefixes per workspace
|
|
25
27
|
init Initialize release-kit in the current repository
|
|
26
28
|
sync-labels Manage GitHub label synchronization
|
|
27
29
|
|
|
@@ -100,9 +102,13 @@ Run release preparation with automatic workspace discovery.
|
|
|
100
102
|
|
|
101
103
|
Options:
|
|
102
104
|
--dry-run Run without modifying any files
|
|
103
|
-
--bump=major|minor|patch Override the bump type for all
|
|
105
|
+
--bump=major|minor|patch Override the bump type for all workspaces
|
|
106
|
+
--set-version=X.Y.Z Set an explicit version; bypasses commit-derived bumps. Requires --only in monorepo mode.
|
|
104
107
|
--no-git-checks, -n Skip the clean-working-tree check
|
|
105
|
-
--only=name1,name2 Only process the named
|
|
108
|
+
--only=name1,name2 Only process the named workspaces (comma-separated, monorepo only)
|
|
109
|
+
--with-release-notes Also write per-workspace release-notes previews under {workspacePath}/docs/
|
|
110
|
+
(docs/README.v{version}.md and docs/RELEASE_NOTES.v{version}.md).
|
|
111
|
+
Recommended .gitignore entry: packages/*/docs/*.v*.md (or docs/*.v*.md).
|
|
106
112
|
--help, -h Show this help message
|
|
107
113
|
`);
|
|
108
114
|
}
|
|
@@ -139,23 +145,36 @@ fires a separate workflow run per tag.
|
|
|
139
145
|
|
|
140
146
|
Options:
|
|
141
147
|
--dry-run Preview without pushing
|
|
142
|
-
--
|
|
148
|
+
--tags=tag1,tag2 Only push the named tags (comma-separated, full tag names)
|
|
143
149
|
--tags-only Skip the branch push (push tags only)
|
|
144
150
|
--help, -h Show this help message
|
|
145
151
|
`);
|
|
146
152
|
}
|
|
147
|
-
function
|
|
153
|
+
function showCreateGithubReleaseHelp() {
|
|
148
154
|
console.info(`
|
|
149
|
-
Usage: release-kit github-release [options]
|
|
155
|
+
Usage: release-kit create-github-release [options]
|
|
150
156
|
|
|
151
157
|
Create GitHub Releases from changelog.json for tags on HEAD.
|
|
152
158
|
|
|
153
159
|
Options:
|
|
154
160
|
--dry-run Preview without creating releases
|
|
155
|
-
--
|
|
161
|
+
--tags=tag1,tag2 Only create releases for the named tags (comma-separated, full tag names)
|
|
156
162
|
--help, -h Show this help message
|
|
157
163
|
`);
|
|
158
164
|
}
|
|
165
|
+
function showShowTagPrefixesHelp() {
|
|
166
|
+
console.info(`
|
|
167
|
+
Usage: release-kit show-tag-prefixes [options]
|
|
168
|
+
|
|
169
|
+
Print a per-workspace table of derived tag prefixes, tag counts, and declared
|
|
170
|
+
legacy tag prefixes. Surfaces any release-shaped tags whose prefix is neither a
|
|
171
|
+
derived prefix nor declared in \`legacyIdentities\`, with a copy-pasteable
|
|
172
|
+
config snippet.
|
|
173
|
+
|
|
174
|
+
Options:
|
|
175
|
+
--help, -h Show this help message
|
|
176
|
+
`);
|
|
177
|
+
}
|
|
159
178
|
function showPublishHelp() {
|
|
160
179
|
console.info(`
|
|
161
180
|
Usage: release-kit publish [options]
|
|
@@ -164,8 +183,8 @@ Publish packages that have release tags on HEAD.
|
|
|
164
183
|
|
|
165
184
|
Options:
|
|
166
185
|
--dry-run Preview without publishing
|
|
167
|
-
--no-git-checks Skip
|
|
168
|
-
--
|
|
186
|
+
--no-git-checks Skip the clean-working-tree check
|
|
187
|
+
--tags=tag1,tag2 Only publish the named tags (comma-separated, full tag names)
|
|
169
188
|
--provenance Generate provenance statement (requires OIDC, not supported by classic yarn)
|
|
170
189
|
--help, -h Show this help message
|
|
171
190
|
`);
|
|
@@ -218,12 +237,12 @@ if (command === "push") {
|
|
|
218
237
|
await pushCommand(flags);
|
|
219
238
|
process.exit(0);
|
|
220
239
|
}
|
|
221
|
-
if (command === "github-release") {
|
|
240
|
+
if (command === "create-github-release") {
|
|
222
241
|
if (flags.some((f) => f === "--help" || f === "-h")) {
|
|
223
|
-
|
|
242
|
+
showCreateGithubReleaseHelp();
|
|
224
243
|
process.exit(0);
|
|
225
244
|
}
|
|
226
|
-
await
|
|
245
|
+
await createGithubReleaseCommand(flags);
|
|
227
246
|
process.exit(0);
|
|
228
247
|
}
|
|
229
248
|
if (command === "publish") {
|
|
@@ -234,6 +253,18 @@ if (command === "publish") {
|
|
|
234
253
|
await publishCommand(flags);
|
|
235
254
|
process.exit(0);
|
|
236
255
|
}
|
|
256
|
+
if (command === "show-tag-prefixes") {
|
|
257
|
+
if (flags.some((f) => f === "--help" || f === "-h")) {
|
|
258
|
+
showShowTagPrefixesHelp();
|
|
259
|
+
process.exit(0);
|
|
260
|
+
}
|
|
261
|
+
if (flags.length > 0) {
|
|
262
|
+
console.error(`Error: Unknown option: ${flags[0]}`);
|
|
263
|
+
process.exit(1);
|
|
264
|
+
}
|
|
265
|
+
const exitCode = await showTagPrefixesCommand();
|
|
266
|
+
process.exit(exitCode);
|
|
267
|
+
}
|
|
237
268
|
if (command === "init") {
|
|
238
269
|
if (flags.some((f) => f === "--help" || f === "-h")) {
|
|
239
270
|
showInitHelp();
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { GenerateChangelogOptions } from './generateChangelogs.ts';
|
|
2
|
+
import type { ChangelogEntry, ReleaseConfig } from './types.ts';
|
|
3
|
+
export declare function buildChangelogEntries(config: Pick<ReleaseConfig, 'cliffConfigPath' | 'changelogJson'>, tag: string, options?: GenerateChangelogOptions): ChangelogEntry[];
|
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
|
-
import { copyFileSync,
|
|
2
|
+
import { copyFileSync, mkdtempSync, rmSync } from "node:fs";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import { extractVersion, isChangelogEntry } from "./changelogJsonUtils.js";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { extractVersion } from "./changelogJsonUtils.js";
|
|
7
6
|
import { resolveCliffConfigPath } from "./resolveCliffConfigPath.js";
|
|
8
7
|
import { isRecord, isUnknownArray } from "./typeGuards.js";
|
|
9
|
-
function
|
|
10
|
-
const outputFile = join(changelogPath, config.changelogJson.outputPath);
|
|
11
|
-
if (dryRun) {
|
|
12
|
-
return [outputFile];
|
|
13
|
-
}
|
|
8
|
+
function buildChangelogEntries(config, tag, options) {
|
|
14
9
|
const resolvedConfigPath = resolveCliffConfigPath(config.cliffConfigPath, import.meta.url);
|
|
15
10
|
let cliffConfigPath = resolvedConfigPath;
|
|
16
11
|
let tempDir;
|
|
@@ -33,15 +28,10 @@ function generateChangelogJson(config, changelogPath, tag, dryRun, options) {
|
|
|
33
28
|
});
|
|
34
29
|
const releases = parseCliffContext(contextJson);
|
|
35
30
|
const devOnlySections = new Set(config.changelogJson.devOnlySections);
|
|
36
|
-
|
|
37
|
-
const existingEntries = readExistingEntries(outputFile);
|
|
38
|
-
const merged = mergeEntries(entries, existingEntries);
|
|
39
|
-
mkdirSync(dirname(outputFile), { recursive: true });
|
|
40
|
-
writeFileSync(outputFile, stringify(merged, { maxLength: 100 }) + "\n", "utf8");
|
|
41
|
-
return [outputFile];
|
|
31
|
+
return transformReleases(releases, devOnlySections);
|
|
42
32
|
} catch (error) {
|
|
43
33
|
throw new Error(
|
|
44
|
-
`Failed to
|
|
34
|
+
`Failed to build changelog entries for tag ${tag}: ${error instanceof Error ? error.message : String(error)}`
|
|
45
35
|
);
|
|
46
36
|
} finally {
|
|
47
37
|
if (tempDir !== void 0) {
|
|
@@ -49,25 +39,6 @@ function generateChangelogJson(config, changelogPath, tag, dryRun, options) {
|
|
|
49
39
|
}
|
|
50
40
|
}
|
|
51
41
|
}
|
|
52
|
-
function generateSyntheticChangelogJson(config, changelogPath, newVersion, date, propagatedFrom, dryRun) {
|
|
53
|
-
const outputFile = join(changelogPath, config.changelogJson.outputPath);
|
|
54
|
-
if (dryRun) {
|
|
55
|
-
return [outputFile];
|
|
56
|
-
}
|
|
57
|
-
const items = propagatedFrom.map((dep) => ({
|
|
58
|
-
description: `Bumped \`${dep.packageName}\` to ${dep.newVersion}`
|
|
59
|
-
}));
|
|
60
|
-
const entry = {
|
|
61
|
-
version: newVersion,
|
|
62
|
-
date,
|
|
63
|
-
sections: [{ title: "Dependency updates", audience: "dev", items }]
|
|
64
|
-
};
|
|
65
|
-
const existingEntries = readExistingEntries(outputFile);
|
|
66
|
-
const merged = mergeEntries([entry], existingEntries);
|
|
67
|
-
mkdirSync(dirname(outputFile), { recursive: true });
|
|
68
|
-
writeFileSync(outputFile, stringify(merged, { maxLength: 100 }) + "\n", "utf8");
|
|
69
|
-
return [outputFile];
|
|
70
|
-
}
|
|
71
42
|
function parseCliffContext(json) {
|
|
72
43
|
const parsed = JSON.parse(json);
|
|
73
44
|
if (!isUnknownArray(parsed)) {
|
|
@@ -115,12 +86,17 @@ function transformReleases(releases, devOnlySections) {
|
|
|
115
86
|
for (const commit of release.commits ?? []) {
|
|
116
87
|
const group = commit.group ?? "Other";
|
|
117
88
|
const description = extractDescription(commit.message);
|
|
89
|
+
const body = extractBody(commit.message);
|
|
118
90
|
let items = sectionMap.get(group);
|
|
119
91
|
if (items === void 0) {
|
|
120
92
|
items = [];
|
|
121
93
|
sectionMap.set(group, items);
|
|
122
94
|
}
|
|
123
|
-
|
|
95
|
+
const item = { description };
|
|
96
|
+
if (body !== void 0) {
|
|
97
|
+
item.body = body;
|
|
98
|
+
}
|
|
99
|
+
items.push(item);
|
|
124
100
|
}
|
|
125
101
|
const sections = [];
|
|
126
102
|
for (const [title, items] of sectionMap) {
|
|
@@ -147,50 +123,37 @@ function extractDescription(message) {
|
|
|
147
123
|
}
|
|
148
124
|
return firstLine;
|
|
149
125
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
126
|
+
const TRAILER_PATTERNS = [
|
|
127
|
+
/^Signed-off-by:/i,
|
|
128
|
+
/^Co-authored-by:/i,
|
|
129
|
+
/^(Closes|Fixes|Resolves)\s+#\d+\s*$/i,
|
|
130
|
+
/^https?:\/\/\S+\/pull\/\d+\/?\s*$/
|
|
131
|
+
];
|
|
132
|
+
function extractBody(message) {
|
|
133
|
+
const lines = message.split("\n").slice(1);
|
|
134
|
+
let start = 0;
|
|
135
|
+
while (start < lines.length && (lines[start] ?? "").trim() === "") {
|
|
136
|
+
start += 1;
|
|
137
|
+
}
|
|
138
|
+
let end = lines.length;
|
|
139
|
+
while (end > start) {
|
|
140
|
+
const line = lines[end - 1] ?? "";
|
|
141
|
+
const trimmed = line.trim();
|
|
142
|
+
if (trimmed === "") {
|
|
143
|
+
end -= 1;
|
|
144
|
+
continue;
|
|
159
145
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
return [];
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
function mergeEntries(newEntries, existingEntries) {
|
|
169
|
-
const versionMap = /* @__PURE__ */ new Map();
|
|
170
|
-
for (const entry of existingEntries) {
|
|
171
|
-
versionMap.set(entry.version, entry);
|
|
146
|
+
if (TRAILER_PATTERNS.some((pattern) => pattern.test(trimmed))) {
|
|
147
|
+
end -= 1;
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
break;
|
|
172
151
|
}
|
|
173
|
-
|
|
174
|
-
|
|
152
|
+
if (end <= start) {
|
|
153
|
+
return void 0;
|
|
175
154
|
}
|
|
176
|
-
return
|
|
177
|
-
}
|
|
178
|
-
function parseVersionParts(version) {
|
|
179
|
-
return version.split(".").map((s) => {
|
|
180
|
-
const n = Number(s);
|
|
181
|
-
return Number.isNaN(n) ? 0 : n;
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
function compareVersionsDescending(a, b) {
|
|
185
|
-
const partsA = parseVersionParts(a);
|
|
186
|
-
const partsB = parseVersionParts(b);
|
|
187
|
-
for (let i = 0; i < 3; i++) {
|
|
188
|
-
const diff = (partsB[i] ?? 0) - (partsA[i] ?? 0);
|
|
189
|
-
if (diff !== 0) return diff;
|
|
190
|
-
}
|
|
191
|
-
return 0;
|
|
155
|
+
return lines.slice(start, end).join("\n").trim();
|
|
192
156
|
}
|
|
193
157
|
export {
|
|
194
|
-
|
|
195
|
-
generateSyntheticChangelogJson
|
|
158
|
+
buildChangelogEntries
|
|
196
159
|
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { WorkspaceConfig } from './types.ts';
|
|
2
2
|
export interface DependencyGraph {
|
|
3
3
|
packageNameToDir: Map<string, string>;
|
|
4
4
|
dirToPackageName: Map<string, string>;
|
|
5
|
-
dependentsOf: Map<string,
|
|
5
|
+
dependentsOf: Map<string, WorkspaceConfig[]>;
|
|
6
|
+
dependenciesOf: Map<string, Set<string>>;
|
|
6
7
|
}
|
|
7
|
-
export declare function buildDependencyGraph(
|
|
8
|
+
export declare function buildDependencyGraph(workspaces: readonly WorkspaceConfig[]): DependencyGraph;
|
|
@@ -2,25 +2,26 @@ import { readFileSync } from "node:fs";
|
|
|
2
2
|
function isPackageJsonSubset(value) {
|
|
3
3
|
return typeof value === "object" && value !== null;
|
|
4
4
|
}
|
|
5
|
-
function buildDependencyGraph(
|
|
5
|
+
function buildDependencyGraph(workspaces) {
|
|
6
6
|
const packageNameToDir = /* @__PURE__ */ new Map();
|
|
7
7
|
const dirToPackageName = /* @__PURE__ */ new Map();
|
|
8
8
|
const dependentsOf = /* @__PURE__ */ new Map();
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
const dependenciesOf = /* @__PURE__ */ new Map();
|
|
10
|
+
const workspacePackages = /* @__PURE__ */ new Map();
|
|
11
|
+
for (const workspace of workspaces) {
|
|
12
|
+
const primaryPackageFile = workspace.packageFiles[0];
|
|
12
13
|
if (primaryPackageFile === void 0) {
|
|
13
14
|
continue;
|
|
14
15
|
}
|
|
15
16
|
const pkg = readPackageJsonSubset(primaryPackageFile);
|
|
16
|
-
|
|
17
|
+
workspacePackages.set(workspace, pkg);
|
|
17
18
|
if (pkg.name === void 0) {
|
|
18
19
|
continue;
|
|
19
20
|
}
|
|
20
|
-
packageNameToDir.set(pkg.name,
|
|
21
|
-
dirToPackageName.set(
|
|
21
|
+
packageNameToDir.set(pkg.name, workspace.dir);
|
|
22
|
+
dirToPackageName.set(workspace.dir, pkg.name);
|
|
22
23
|
}
|
|
23
|
-
for (const [
|
|
24
|
+
for (const [workspace, pkg] of workspacePackages) {
|
|
24
25
|
const allDeps = { ...pkg.dependencies, ...pkg.peerDependencies };
|
|
25
26
|
for (const [depName, depVersion] of Object.entries(allDeps)) {
|
|
26
27
|
if (typeof depVersion !== "string" || !depVersion.startsWith("workspace:")) {
|
|
@@ -28,13 +29,19 @@ function buildDependencyGraph(components) {
|
|
|
28
29
|
}
|
|
29
30
|
const existing = dependentsOf.get(depName);
|
|
30
31
|
if (existing === void 0) {
|
|
31
|
-
dependentsOf.set(depName, [
|
|
32
|
+
dependentsOf.set(depName, [workspace]);
|
|
32
33
|
} else {
|
|
33
|
-
existing.push(
|
|
34
|
+
existing.push(workspace);
|
|
35
|
+
}
|
|
36
|
+
const forward = dependenciesOf.get(workspace.dir);
|
|
37
|
+
if (forward === void 0) {
|
|
38
|
+
dependenciesOf.set(workspace.dir, /* @__PURE__ */ new Set([depName]));
|
|
39
|
+
} else {
|
|
40
|
+
forward.add(depName);
|
|
34
41
|
}
|
|
35
42
|
}
|
|
36
43
|
}
|
|
37
|
-
return { packageNameToDir, dirToPackageName, dependentsOf };
|
|
44
|
+
return { packageNameToDir, dirToPackageName, dependentsOf, dependenciesOf };
|
|
38
45
|
}
|
|
39
46
|
function readPackageJsonSubset(filePath) {
|
|
40
47
|
let content;
|
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
import { stripScope } from "./stripScope.js";
|
|
2
2
|
function buildReleaseSummary(result) {
|
|
3
3
|
const sections = [];
|
|
4
|
-
for (const
|
|
5
|
-
if (
|
|
4
|
+
for (const workspace of result.workspaces) {
|
|
5
|
+
if (workspace.status !== "released") {
|
|
6
6
|
continue;
|
|
7
7
|
}
|
|
8
|
-
const commits =
|
|
8
|
+
const commits = workspace.commits;
|
|
9
9
|
if (commits === void 0 || commits.length === 0) {
|
|
10
10
|
continue;
|
|
11
11
|
}
|
|
12
|
-
const lines = [
|
|
12
|
+
const lines = [workspace.tag];
|
|
13
13
|
for (const commit of commits) {
|
|
14
14
|
lines.push(`- ${stripScope(commit.message)}`);
|
|
15
15
|
}
|
|
16
16
|
sections.push(lines.join("\n"));
|
|
17
17
|
}
|
|
18
|
+
const project = result.project;
|
|
19
|
+
if (project !== void 0 && project.status === "released" && project.commits.length > 0) {
|
|
20
|
+
const lines = [project.tag];
|
|
21
|
+
for (const commit of project.commits) {
|
|
22
|
+
lines.push(`- ${stripScope(commit.message)}`);
|
|
23
|
+
}
|
|
24
|
+
sections.push(lines.join("\n"));
|
|
25
|
+
}
|
|
18
26
|
return sections.join("\n\n");
|
|
19
27
|
}
|
|
20
28
|
export {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
function buildSyntheticChangelogEntry(propagatedFrom, version, date) {
|
|
2
|
+
const items = propagatedFrom.map((dep) => ({
|
|
3
|
+
description: `Bumped \`${dep.packageName}\` to ${dep.newVersion}`
|
|
4
|
+
}));
|
|
5
|
+
return {
|
|
6
|
+
version,
|
|
7
|
+
date,
|
|
8
|
+
sections: [{ title: "Dependency updates", audience: "dev", items }]
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
buildSyntheticChangelogEntry
|
|
13
|
+
};
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
import type { BumpResult, ReleaseType } from './types.ts';
|
|
2
2
|
export declare function bumpAllVersions(packageFiles: readonly string[], releaseType: ReleaseType, dryRun: boolean): BumpResult;
|
|
3
|
+
export declare function setAllVersions(packageFiles: readonly string[], newVersion: string, dryRun: boolean): BumpResult;
|
|
@@ -11,6 +11,20 @@ function bumpAllVersions(packageFiles, releaseType, dryRun) {
|
|
|
11
11
|
const firstPkg = readPackageJson(firstFile);
|
|
12
12
|
const currentVersion = firstPkg.version;
|
|
13
13
|
const newVersion = bumpVersion(currentVersion, releaseType);
|
|
14
|
+
writeVersionToAllFiles(packageFiles, firstFile, firstPkg, newVersion, dryRun);
|
|
15
|
+
return { currentVersion, newVersion, files: [...packageFiles] };
|
|
16
|
+
}
|
|
17
|
+
function setAllVersions(packageFiles, newVersion, dryRun) {
|
|
18
|
+
const firstFile = packageFiles[0];
|
|
19
|
+
if (firstFile === void 0) {
|
|
20
|
+
throw new Error("No package files specified");
|
|
21
|
+
}
|
|
22
|
+
const firstPkg = readPackageJson(firstFile);
|
|
23
|
+
const currentVersion = firstPkg.version;
|
|
24
|
+
writeVersionToAllFiles(packageFiles, firstFile, firstPkg, newVersion, dryRun);
|
|
25
|
+
return { currentVersion, newVersion, files: [...packageFiles] };
|
|
26
|
+
}
|
|
27
|
+
function writeVersionToAllFiles(packageFiles, firstFile, firstPkg, newVersion, dryRun) {
|
|
14
28
|
for (const filePath of packageFiles) {
|
|
15
29
|
if (dryRun) {
|
|
16
30
|
continue;
|
|
@@ -23,7 +37,6 @@ function bumpAllVersions(packageFiles, releaseType, dryRun) {
|
|
|
23
37
|
throw new Error(`Failed to write ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
24
38
|
}
|
|
25
39
|
}
|
|
26
|
-
return { currentVersion, newVersion, files: [...packageFiles] };
|
|
27
40
|
}
|
|
28
41
|
function readPackageJson(filePath) {
|
|
29
42
|
let content;
|
|
@@ -44,5 +57,6 @@ function readPackageJson(filePath) {
|
|
|
44
57
|
return parsed;
|
|
45
58
|
}
|
|
46
59
|
export {
|
|
47
|
-
bumpAllVersions
|
|
60
|
+
bumpAllVersions,
|
|
61
|
+
setAllVersions
|
|
48
62
|
};
|
package/dist/esm/bumpVersion.js
CHANGED
|
@@ -14,6 +14,9 @@ function bumpVersion(version, releaseType) {
|
|
|
14
14
|
const patchNum = Number.parseInt(patch, 10);
|
|
15
15
|
switch (releaseType) {
|
|
16
16
|
case "major":
|
|
17
|
+
if (majorNum === 0) {
|
|
18
|
+
return `0.${minorNum + 1}.0`;
|
|
19
|
+
}
|
|
17
20
|
return `${majorNum + 1}.0.0`;
|
|
18
21
|
case "minor":
|
|
19
22
|
return `${majorNum}.${minorNum + 1}.0`;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ChangelogEntry, ReleaseConfig } from './types.ts';
|
|
2
|
+
export declare function resolveChangelogJsonPath(config: Pick<ReleaseConfig, 'changelogJson'>, changelogPath: string): string;
|
|
3
|
+
export declare function writeChangelogJson(filePath: string, entries: ChangelogEntry[]): string;
|
|
4
|
+
export declare function upsertChangelogJson(filePath: string, entries: ChangelogEntry[]): string;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import stringify from "json-stringify-pretty-compact";
|
|
4
|
+
import semver from "semver";
|
|
5
|
+
import { isChangelogEntry } from "./changelogJsonUtils.js";
|
|
6
|
+
import { isUnknownArray } from "./typeGuards.js";
|
|
7
|
+
function resolveChangelogJsonPath(config, changelogPath) {
|
|
8
|
+
return join(changelogPath, config.changelogJson.outputPath);
|
|
9
|
+
}
|
|
10
|
+
function writeChangelogJson(filePath, entries) {
|
|
11
|
+
const sorted = sortNewestFirst(entries);
|
|
12
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
13
|
+
writeFileSync(filePath, stringify(sorted, { maxLength: 100 }) + "\n", "utf8");
|
|
14
|
+
return filePath;
|
|
15
|
+
}
|
|
16
|
+
function upsertChangelogJson(filePath, entries) {
|
|
17
|
+
const existing = readExistingEntries(filePath);
|
|
18
|
+
const merged = mergeEntries(entries, existing);
|
|
19
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
20
|
+
writeFileSync(filePath, stringify(merged, { maxLength: 100 }) + "\n", "utf8");
|
|
21
|
+
return filePath;
|
|
22
|
+
}
|
|
23
|
+
function sortNewestFirst(entries) {
|
|
24
|
+
return [...entries].sort((a, b) => compareVersionsDescending(a.version, b.version));
|
|
25
|
+
}
|
|
26
|
+
function readExistingEntries(filePath) {
|
|
27
|
+
if (!existsSync(filePath)) {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
const content = readFileSync(filePath, "utf8");
|
|
32
|
+
const parsed = JSON.parse(content);
|
|
33
|
+
if (!isUnknownArray(parsed)) {
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
return parsed.filter(isChangelogEntry);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.warn(
|
|
39
|
+
`Warning: could not parse existing ${filePath}: ${error instanceof Error ? error.message : String(error)}; treating as empty`
|
|
40
|
+
);
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function mergeEntries(newEntries, existingEntries) {
|
|
45
|
+
const versionMap = /* @__PURE__ */ new Map();
|
|
46
|
+
for (const entry of existingEntries) {
|
|
47
|
+
versionMap.set(entry.version, entry);
|
|
48
|
+
}
|
|
49
|
+
for (const entry of newEntries) {
|
|
50
|
+
versionMap.set(entry.version, entry);
|
|
51
|
+
}
|
|
52
|
+
return sortNewestFirst(versionMap.values());
|
|
53
|
+
}
|
|
54
|
+
function compareVersionsDescending(a, b) {
|
|
55
|
+
const aValid = semver.valid(a);
|
|
56
|
+
const bValid = semver.valid(b);
|
|
57
|
+
if (aValid && bValid) return semver.rcompare(aValid, bValid);
|
|
58
|
+
if (aValid) return -1;
|
|
59
|
+
if (bValid) return 1;
|
|
60
|
+
if (a > b) return -1;
|
|
61
|
+
if (a < b) return 1;
|
|
62
|
+
return 0;
|
|
63
|
+
}
|
|
64
|
+
export {
|
|
65
|
+
resolveChangelogJsonPath,
|
|
66
|
+
upsertChangelogJson,
|
|
67
|
+
writeChangelogJson
|
|
68
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
|
-
import { parseArgs, translateParseError } from "@williamthorsen/
|
|
3
|
+
import { parseArgs, translateParseError } from "@williamthorsen/nmr-core";
|
|
4
4
|
import { RELEASE_SUMMARY_FILE, RELEASE_TAGS_FILE } from "./prepareCommand.js";
|
|
5
5
|
const commitFlagSchema = {
|
|
6
6
|
dryRun: { long: "--dry-run", type: "boolean" }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isForwardVersion(current: string, target: string): boolean;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const CANONICAL_SEMVER_PATTERN = /^(\d+)\.(\d+)\.(\d+)$/;
|
|
2
|
+
function parseCanonicalSemver(version) {
|
|
3
|
+
const match = version.match(CANONICAL_SEMVER_PATTERN);
|
|
4
|
+
if (!match) {
|
|
5
|
+
throw new Error(`Invalid semver version: '${version}'`);
|
|
6
|
+
}
|
|
7
|
+
const [, major = "", minor = "", patch = ""] = match;
|
|
8
|
+
return {
|
|
9
|
+
major: Number.parseInt(major, 10),
|
|
10
|
+
minor: Number.parseInt(minor, 10),
|
|
11
|
+
patch: Number.parseInt(patch, 10)
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function isForwardVersion(current, target) {
|
|
15
|
+
const currentParsed = parseCanonicalSemver(current);
|
|
16
|
+
const targetParsed = parseCanonicalSemver(target);
|
|
17
|
+
if (targetParsed.major !== currentParsed.major) {
|
|
18
|
+
return targetParsed.major > currentParsed.major;
|
|
19
|
+
}
|
|
20
|
+
if (targetParsed.minor !== currentParsed.minor) {
|
|
21
|
+
return targetParsed.minor > currentParsed.minor;
|
|
22
|
+
}
|
|
23
|
+
return targetParsed.patch > currentParsed.patch;
|
|
24
|
+
}
|
|
25
|
+
export {
|
|
26
|
+
isForwardVersion
|
|
27
|
+
};
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
import type { ReleaseNotesConfig } from './types.ts';
|
|
2
1
|
export interface CreateGithubReleaseOptions {
|
|
3
2
|
tag: string;
|
|
4
3
|
changelogJsonPath: string;
|
|
5
4
|
dryRun: boolean;
|
|
5
|
+
sectionOrder?: string[];
|
|
6
6
|
}
|
|
7
7
|
export declare function createGithubRelease(options: CreateGithubReleaseOptions): boolean;
|
|
8
|
+
export interface CreateGithubReleasesOutcome {
|
|
9
|
+
created: string[];
|
|
10
|
+
skipped: string[];
|
|
11
|
+
}
|
|
8
12
|
export declare function createGithubReleases(tags: Array<{
|
|
9
13
|
tag: string;
|
|
10
14
|
workspacePath: string;
|
|
11
|
-
}>,
|
|
15
|
+
}>, changelogJsonOutputPath: string, dryRun: boolean, sectionOrder?: string[]): CreateGithubReleasesOutcome;
|
|
@@ -4,7 +4,7 @@ import { join } from "node:path";
|
|
|
4
4
|
import { extractVersion, readChangelogEntries } from "./changelogJsonUtils.js";
|
|
5
5
|
import { matchesAudience, renderReleaseNotesSingle } from "./renderReleaseNotes.js";
|
|
6
6
|
function createGithubRelease(options) {
|
|
7
|
-
const { tag, changelogJsonPath, dryRun } = options;
|
|
7
|
+
const { tag, changelogJsonPath, dryRun, sectionOrder } = options;
|
|
8
8
|
if (!existsSync(changelogJsonPath)) {
|
|
9
9
|
console.warn(`Warning: ${changelogJsonPath} not found; skipping GitHub Release creation`);
|
|
10
10
|
return false;
|
|
@@ -25,7 +25,8 @@ function createGithubRelease(options) {
|
|
|
25
25
|
}
|
|
26
26
|
const body = renderReleaseNotesSingle(entry, {
|
|
27
27
|
filter: matchesAudience("all"),
|
|
28
|
-
includeHeading: false
|
|
28
|
+
includeHeading: false,
|
|
29
|
+
...sectionOrder === void 0 ? {} : { sectionOrder }
|
|
29
30
|
});
|
|
30
31
|
if (body.trim() === "") {
|
|
31
32
|
return false;
|
|
@@ -35,24 +36,23 @@ function createGithubRelease(options) {
|
|
|
35
36
|
console.info(`[dry-run] Would run: gh ${args.join(" ")}`);
|
|
36
37
|
return true;
|
|
37
38
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return true;
|
|
41
|
-
} catch (error) {
|
|
42
|
-
console.warn(
|
|
43
|
-
`Warning: failed to create GitHub Release for ${tag}: ${error instanceof Error ? error.message : String(error)}`
|
|
44
|
-
);
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
39
|
+
execFileSync("gh", args, { stdio: "inherit" });
|
|
40
|
+
return true;
|
|
47
41
|
}
|
|
48
|
-
function createGithubReleases(tags,
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
42
|
+
function createGithubReleases(tags, changelogJsonOutputPath, dryRun, sectionOrder) {
|
|
43
|
+
const created = [];
|
|
44
|
+
const skipped = [];
|
|
52
45
|
for (const { tag, workspacePath } of tags) {
|
|
53
46
|
const changelogJsonPath = join(workspacePath, changelogJsonOutputPath);
|
|
54
|
-
createGithubRelease({
|
|
55
|
-
|
|
47
|
+
const result = createGithubRelease({
|
|
48
|
+
tag,
|
|
49
|
+
changelogJsonPath,
|
|
50
|
+
dryRun,
|
|
51
|
+
...sectionOrder === void 0 ? {} : { sectionOrder }
|
|
52
|
+
});
|
|
53
|
+
(result ? created : skipped).push(tag);
|
|
54
|
+
}
|
|
55
|
+
return { created, skipped };
|
|
56
56
|
}
|
|
57
57
|
export {
|
|
58
58
|
createGithubRelease,
|