@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
|
@@ -1,86 +1,58 @@
|
|
|
1
1
|
import { bold, dim, sectionHeader } from "./format.js";
|
|
2
2
|
function reportPrepare(result) {
|
|
3
|
-
const
|
|
4
|
-
if (
|
|
5
|
-
return
|
|
3
|
+
const isMultiWorkspace = result.workspaces.some((w) => w.name !== void 0);
|
|
4
|
+
if (isMultiWorkspace) {
|
|
5
|
+
return formatMultiWorkspace(result);
|
|
6
6
|
}
|
|
7
|
-
return
|
|
7
|
+
return formatSingleWorkspace(result);
|
|
8
8
|
}
|
|
9
|
-
function
|
|
9
|
+
function formatSingleWorkspace(result) {
|
|
10
10
|
const lines = [];
|
|
11
|
-
const
|
|
12
|
-
if (
|
|
11
|
+
const workspace = result.workspaces[0];
|
|
12
|
+
if (workspace === void 0) {
|
|
13
13
|
return "";
|
|
14
14
|
}
|
|
15
|
-
const since =
|
|
16
|
-
lines.push(dim(`Found ${
|
|
17
|
-
if (
|
|
18
|
-
lines.push(dim(` Parsed ${
|
|
15
|
+
const since = workspace.previousTag === void 0 ? "the beginning" : workspace.previousTag;
|
|
16
|
+
lines.push(dim(`Found ${workspace.commitCount} commits since ${since}`));
|
|
17
|
+
if (workspace.parsedCommitCount !== void 0) {
|
|
18
|
+
lines.push(dim(` Parsed ${workspace.parsedCommitCount} typed commits`));
|
|
19
19
|
}
|
|
20
|
-
formatUnparseableWarning(lines,
|
|
21
|
-
if (
|
|
22
|
-
lines.push(`\u23ED\uFE0F ${
|
|
20
|
+
formatUnparseableWarning(lines, workspace);
|
|
21
|
+
if (workspace.status === "skipped") {
|
|
22
|
+
lines.push(`\u23ED\uFE0F ${workspace.skipReason ?? "Skipped"}`);
|
|
23
23
|
return lines.join("\n");
|
|
24
24
|
}
|
|
25
|
-
if (
|
|
26
|
-
lines.push(` Using
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
if (workspace.setVersion !== void 0) {
|
|
26
|
+
lines.push(` Using version override: ${workspace.setVersion}`);
|
|
27
|
+
} else if (workspace.parsedCommitCount === void 0 && workspace.releaseType !== void 0) {
|
|
28
|
+
lines.push(` Using bump override: ${workspace.releaseType}`);
|
|
29
|
+
}
|
|
30
|
+
if (workspace.releaseType !== void 0) {
|
|
31
|
+
lines.push(dim(`Bumping versions (${workspace.releaseType})...`));
|
|
32
|
+
} else if (workspace.setVersion !== void 0) {
|
|
33
|
+
lines.push(dim(`Bumping versions (version override)...`));
|
|
34
|
+
}
|
|
35
|
+
if (workspace.currentVersion !== void 0 && workspace.newVersion !== void 0) {
|
|
36
|
+
if (workspace.setVersion !== void 0) {
|
|
37
|
+
lines.push(`\u{1F4E6} ${workspace.currentVersion} \u2192 ${bold(workspace.newVersion)} (version override)`);
|
|
38
|
+
} else if (workspace.releaseType !== void 0) {
|
|
39
|
+
lines.push(`\u{1F4E6} ${workspace.currentVersion} \u2192 ${bold(workspace.newVersion)} (${workspace.releaseType})`);
|
|
40
|
+
}
|
|
33
41
|
}
|
|
34
|
-
formatBumpFiles(lines,
|
|
42
|
+
formatBumpFiles(lines, workspace, result.dryRun);
|
|
35
43
|
lines.push(dim("Generating changelogs..."));
|
|
36
|
-
formatChangelogFiles(lines,
|
|
44
|
+
formatChangelogFiles(lines, workspace, result.dryRun);
|
|
37
45
|
formatFormatCommand(lines, result);
|
|
38
46
|
lines.push(`\u2705 Release preparation complete.`);
|
|
39
|
-
if (
|
|
40
|
-
lines.push(` \u{1F3F7}\uFE0F ${bold(
|
|
47
|
+
if (workspace.tag !== void 0) {
|
|
48
|
+
lines.push(` \u{1F3F7}\uFE0F ${bold(workspace.tag)}`);
|
|
41
49
|
}
|
|
42
50
|
return lines.join("\n");
|
|
43
51
|
}
|
|
44
|
-
function
|
|
52
|
+
function formatMultiWorkspace(result) {
|
|
45
53
|
const lines = [];
|
|
46
|
-
for (const
|
|
47
|
-
|
|
48
|
-
lines.push(`
|
|
49
|
-
${sectionHeader(component.name)}`);
|
|
50
|
-
}
|
|
51
|
-
const since = component.previousTag === void 0 ? "(no previous release found)" : `since ${component.previousTag}`;
|
|
52
|
-
lines.push(dim(` Found ${component.commitCount} commits ${since}`));
|
|
53
|
-
if (component.status === "skipped") {
|
|
54
|
-
lines.push(` \u23ED\uFE0F ${component.skipReason ?? "Skipped"}`);
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
const { propagatedFrom } = component;
|
|
58
|
-
const isPropagatedOnly = propagatedFrom !== void 0 && component.commitCount === 0;
|
|
59
|
-
if (isPropagatedOnly) {
|
|
60
|
-
const depNames = propagatedFrom.map((p) => p.packageName).join(", ");
|
|
61
|
-
lines.push(dim(` 0 commits (bumped via dependency: ${depNames})`));
|
|
62
|
-
} else if (component.parsedCommitCount !== void 0) {
|
|
63
|
-
lines.push(dim(` Parsed ${component.parsedCommitCount} typed commits`));
|
|
64
|
-
}
|
|
65
|
-
formatUnparseableWarning(lines, component, " ");
|
|
66
|
-
if (component.parsedCommitCount === void 0 && component.releaseType !== void 0 && !isPropagatedOnly) {
|
|
67
|
-
lines.push(` Using bump override: ${component.releaseType}`);
|
|
68
|
-
}
|
|
69
|
-
if (component.releaseType !== void 0) {
|
|
70
|
-
lines.push(dim(` Bumping versions (${component.releaseType})...`));
|
|
71
|
-
}
|
|
72
|
-
if (component.currentVersion !== void 0 && component.newVersion !== void 0 && component.releaseType !== void 0) {
|
|
73
|
-
const suffix = isPropagatedOnly ? formatPropagationSuffix(propagatedFrom) : "";
|
|
74
|
-
lines.push(
|
|
75
|
-
` \u{1F4E6} ${component.currentVersion} \u2192 ${bold(component.newVersion)} (${component.releaseType}${suffix})`
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
formatBumpFiles(lines, component, result.dryRun, " ");
|
|
79
|
-
lines.push(dim(" Generating changelogs..."));
|
|
80
|
-
formatChangelogFiles(lines, component, result.dryRun, " ");
|
|
81
|
-
if (component.tag !== void 0) {
|
|
82
|
-
lines.push(` \u{1F3F7}\uFE0F ${bold(component.tag)}`);
|
|
83
|
-
}
|
|
54
|
+
for (const workspace of result.workspaces) {
|
|
55
|
+
formatWorkspaceSection(lines, workspace, result.dryRun);
|
|
84
56
|
}
|
|
85
57
|
formatFormatCommand(lines, result);
|
|
86
58
|
formatWarnings(lines, result);
|
|
@@ -92,12 +64,67 @@ ${sectionHeader(component.name)}`);
|
|
|
92
64
|
}
|
|
93
65
|
} else {
|
|
94
66
|
lines.push(`
|
|
95
|
-
\u23ED\uFE0F No
|
|
67
|
+
\u23ED\uFE0F No workspaces had release-worthy changes.`);
|
|
96
68
|
}
|
|
97
69
|
return lines.join("\n");
|
|
98
70
|
}
|
|
99
|
-
function
|
|
100
|
-
|
|
71
|
+
function formatWorkspaceSection(lines, workspace, dryRun) {
|
|
72
|
+
if (workspace.name !== void 0) {
|
|
73
|
+
lines.push(`
|
|
74
|
+
${sectionHeader(workspace.name)}`);
|
|
75
|
+
}
|
|
76
|
+
const since = workspace.previousTag === void 0 ? "(no previous release found)" : `since ${workspace.previousTag}`;
|
|
77
|
+
lines.push(dim(` Found ${workspace.commitCount} commits ${since}`));
|
|
78
|
+
if (workspace.status === "skipped") {
|
|
79
|
+
lines.push(` \u23ED\uFE0F ${workspace.skipReason ?? "Skipped"}`);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const { propagatedFrom } = workspace;
|
|
83
|
+
const isPropagatedOnly = propagatedFrom !== void 0 && workspace.commitCount === 0;
|
|
84
|
+
formatCommitSummary(lines, workspace, propagatedFrom, isPropagatedOnly);
|
|
85
|
+
formatUnparseableWarning(lines, workspace, " ");
|
|
86
|
+
formatBumpLabels(lines, workspace, isPropagatedOnly);
|
|
87
|
+
formatVersionLine(lines, workspace, propagatedFrom, isPropagatedOnly);
|
|
88
|
+
formatBumpFiles(lines, workspace, dryRun, " ");
|
|
89
|
+
lines.push(dim(" Generating changelogs..."));
|
|
90
|
+
formatChangelogFiles(lines, workspace, dryRun, " ");
|
|
91
|
+
if (workspace.tag !== void 0) {
|
|
92
|
+
lines.push(` \u{1F3F7}\uFE0F ${bold(workspace.tag)}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function formatCommitSummary(lines, workspace, propagatedFrom, isPropagatedOnly) {
|
|
96
|
+
if (isPropagatedOnly && propagatedFrom !== void 0) {
|
|
97
|
+
const depNames = propagatedFrom.map((p) => p.packageName).join(", ");
|
|
98
|
+
lines.push(dim(` 0 commits (bumped via dependency: ${depNames})`));
|
|
99
|
+
} else if (workspace.parsedCommitCount !== void 0) {
|
|
100
|
+
lines.push(dim(` Parsed ${workspace.parsedCommitCount} typed commits`));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function formatBumpLabels(lines, workspace, isPropagatedOnly) {
|
|
104
|
+
if (workspace.setVersion !== void 0) {
|
|
105
|
+
lines.push(` Using version override: ${workspace.setVersion}`);
|
|
106
|
+
} else if (workspace.parsedCommitCount === void 0 && workspace.releaseType !== void 0 && !isPropagatedOnly) {
|
|
107
|
+
lines.push(` Using bump override: ${workspace.releaseType}`);
|
|
108
|
+
}
|
|
109
|
+
if (workspace.releaseType !== void 0) {
|
|
110
|
+
lines.push(dim(` Bumping versions (${workspace.releaseType})...`));
|
|
111
|
+
} else if (workspace.setVersion !== void 0) {
|
|
112
|
+
lines.push(dim(` Bumping versions (version override)...`));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function formatVersionLine(lines, workspace, propagatedFrom, isPropagatedOnly) {
|
|
116
|
+
if (workspace.currentVersion === void 0 || workspace.newVersion === void 0) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (workspace.setVersion !== void 0) {
|
|
120
|
+
lines.push(` \u{1F4E6} ${workspace.currentVersion} \u2192 ${bold(workspace.newVersion)} (version override)`);
|
|
121
|
+
} else if (workspace.releaseType !== void 0) {
|
|
122
|
+
const suffix = isPropagatedOnly ? formatPropagationSuffix(propagatedFrom) : "";
|
|
123
|
+
lines.push(` \u{1F4E6} ${workspace.currentVersion} \u2192 ${bold(workspace.newVersion)} (${workspace.releaseType}${suffix})`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function formatBumpFiles(lines, workspace, dryRun, indent = "") {
|
|
127
|
+
for (const file of workspace.bumpedFiles) {
|
|
101
128
|
if (dryRun) {
|
|
102
129
|
lines.push(dim(`${indent} [dry-run] Would bump ${file}`));
|
|
103
130
|
} else {
|
|
@@ -105,8 +132,8 @@ function formatBumpFiles(lines, component, dryRun, indent = "") {
|
|
|
105
132
|
}
|
|
106
133
|
}
|
|
107
134
|
}
|
|
108
|
-
function formatChangelogFiles(lines,
|
|
109
|
-
for (const file of
|
|
135
|
+
function formatChangelogFiles(lines, workspace, dryRun, indent = "") {
|
|
136
|
+
for (const file of workspace.changelogFiles) {
|
|
110
137
|
if (dryRun) {
|
|
111
138
|
lines.push(dim(`${indent} [dry-run] Would run: npx --yes git-cliff ... --output ${file}`));
|
|
112
139
|
} else {
|
|
@@ -114,13 +141,13 @@ function formatChangelogFiles(lines, component, dryRun, indent = "") {
|
|
|
114
141
|
}
|
|
115
142
|
}
|
|
116
143
|
}
|
|
117
|
-
function formatUnparseableWarning(lines,
|
|
118
|
-
const unparseable =
|
|
144
|
+
function formatUnparseableWarning(lines, workspace, indent = "") {
|
|
145
|
+
const unparseable = workspace.unparseableCommits;
|
|
119
146
|
if (unparseable === void 0 || unparseable.length === 0) {
|
|
120
147
|
return;
|
|
121
148
|
}
|
|
122
149
|
const count = unparseable.length;
|
|
123
|
-
const isPatchFloor =
|
|
150
|
+
const isPatchFloor = workspace.parsedCommitCount === 0;
|
|
124
151
|
const suffix = isPatchFloor ? " (defaulting to patch bump)" : "";
|
|
125
152
|
lines.push(`${indent} \u26A0\uFE0F ${count} commit${count === 1 ? "" : "s"} could not be parsed${suffix}`);
|
|
126
153
|
for (const commit of unparseable) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
|
-
import { findPackageRoot } from "
|
|
3
|
+
import { findPackageRoot } from "@williamthorsen/nmr-core";
|
|
4
4
|
function resolveCliffConfigPath(cliffConfigPath, metaUrl) {
|
|
5
5
|
if (cliffConfigPath !== void 0) {
|
|
6
6
|
return cliffConfigPath;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { ResolvedTag } from './resolveReleaseTags.ts';
|
|
2
|
-
export declare function resolveCommandTags(
|
|
2
|
+
export declare function resolveCommandTags(tags: string[] | undefined): Promise<ResolvedTag[]>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { deriveWorkspaceConfig } from "./deriveWorkspaceConfig.js";
|
|
2
2
|
import { discoverWorkspaces } from "./discoverWorkspaces.js";
|
|
3
3
|
import { resolveReleaseTags } from "./resolveReleaseTags.js";
|
|
4
|
-
async function resolveCommandTags(
|
|
4
|
+
async function resolveCommandTags(tags) {
|
|
5
5
|
let discoveredPaths;
|
|
6
6
|
try {
|
|
7
7
|
discoveredPaths = await discoverWorkspaces();
|
|
@@ -9,25 +9,29 @@ async function resolveCommandTags(only) {
|
|
|
9
9
|
console.error(`Error discovering workspaces: ${error instanceof Error ? error.message : String(error)}`);
|
|
10
10
|
process.exit(1);
|
|
11
11
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
let workspaces;
|
|
13
|
+
if (discoveredPaths !== void 0) {
|
|
14
|
+
try {
|
|
15
|
+
workspaces = discoveredPaths.map((workspacePath) => deriveWorkspaceConfig(workspacePath));
|
|
16
|
+
} catch (error) {
|
|
17
|
+
console.error(`Error resolving workspaces: ${error instanceof Error ? error.message : String(error)}`);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
15
20
|
}
|
|
16
|
-
|
|
17
|
-
let resolvedTags = resolveReleaseTags(workspaceMap);
|
|
21
|
+
let resolvedTags = resolveReleaseTags(workspaces);
|
|
18
22
|
if (resolvedTags.length === 0) {
|
|
19
23
|
console.error("Error: No release tags found on HEAD. Create tags with `release-kit tag` first.");
|
|
20
24
|
process.exit(1);
|
|
21
25
|
}
|
|
22
|
-
if (
|
|
23
|
-
const
|
|
24
|
-
for (const name of
|
|
25
|
-
if (!
|
|
26
|
-
console.error(`Error: Unknown
|
|
26
|
+
if (tags !== void 0) {
|
|
27
|
+
const availableTagNames = resolvedTags.map((t) => t.tag);
|
|
28
|
+
for (const name of tags) {
|
|
29
|
+
if (!availableTagNames.includes(name)) {
|
|
30
|
+
console.error(`Error: Unknown tag "${name}" in --tags. Available: ${availableTagNames.join(", ")}`);
|
|
27
31
|
process.exit(1);
|
|
28
32
|
}
|
|
29
33
|
}
|
|
30
|
-
resolvedTags = resolvedTags.filter((t) =>
|
|
34
|
+
resolvedTags = resolvedTags.filter((t) => tags.includes(t.tag));
|
|
31
35
|
}
|
|
32
36
|
return resolvedTags;
|
|
33
37
|
}
|
|
@@ -2,5 +2,12 @@ import type { ReleaseNotesConfig } from './types.ts';
|
|
|
2
2
|
export interface ResolvedReleaseNotesConfig {
|
|
3
3
|
releaseNotes: ReleaseNotesConfig;
|
|
4
4
|
changelogJsonOutputPath: string;
|
|
5
|
+
sectionOrder: string[];
|
|
5
6
|
}
|
|
6
|
-
export
|
|
7
|
+
export interface ResolveReleaseNotesConfigOptions {
|
|
8
|
+
strictLoad?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function resolveReleaseNotesConfig(options?: ResolveReleaseNotesConfigOptions): Promise<ResolvedReleaseNotesConfig>;
|
|
11
|
+
export declare function deriveSectionOrder(workTypes: Record<string, {
|
|
12
|
+
header: string;
|
|
13
|
+
}>): string[];
|
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
import { DEFAULT_CHANGELOG_JSON_CONFIG, DEFAULT_RELEASE_NOTES_CONFIG } from "./defaults.js";
|
|
2
|
-
import { loadConfig } from "./loadConfig.js";
|
|
2
|
+
import { loadConfig, resolveWorkTypes } from "./loadConfig.js";
|
|
3
3
|
import { validateConfig } from "./validateConfig.js";
|
|
4
|
-
async function resolveReleaseNotesConfig() {
|
|
4
|
+
async function resolveReleaseNotesConfig(options = {}) {
|
|
5
|
+
const { strictLoad = false } = options;
|
|
5
6
|
let rawConfig;
|
|
6
7
|
try {
|
|
7
8
|
rawConfig = await loadConfig();
|
|
8
9
|
} catch (error) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
11
|
+
if (strictLoad) {
|
|
12
|
+
console.error(`Error: failed to load config: ${message}`);
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
console.warn(`Warning: failed to load config; using defaults: ${message}`);
|
|
12
16
|
}
|
|
13
17
|
if (rawConfig === void 0) {
|
|
14
18
|
return {
|
|
15
19
|
releaseNotes: { ...DEFAULT_RELEASE_NOTES_CONFIG },
|
|
16
|
-
changelogJsonOutputPath: DEFAULT_CHANGELOG_JSON_CONFIG.outputPath
|
|
20
|
+
changelogJsonOutputPath: DEFAULT_CHANGELOG_JSON_CONFIG.outputPath,
|
|
21
|
+
sectionOrder: deriveSectionOrder(resolveWorkTypes())
|
|
17
22
|
};
|
|
18
23
|
}
|
|
19
24
|
const { config, errors, warnings } = validateConfig(rawConfig);
|
|
@@ -29,9 +34,14 @@ async function resolveReleaseNotesConfig() {
|
|
|
29
34
|
}
|
|
30
35
|
return {
|
|
31
36
|
releaseNotes: { ...DEFAULT_RELEASE_NOTES_CONFIG, ...config.releaseNotes },
|
|
32
|
-
changelogJsonOutputPath: config.changelogJson?.outputPath ?? DEFAULT_CHANGELOG_JSON_CONFIG.outputPath
|
|
37
|
+
changelogJsonOutputPath: config.changelogJson?.outputPath ?? DEFAULT_CHANGELOG_JSON_CONFIG.outputPath,
|
|
38
|
+
sectionOrder: deriveSectionOrder(resolveWorkTypes(config.workTypes))
|
|
33
39
|
};
|
|
34
40
|
}
|
|
41
|
+
function deriveSectionOrder(workTypes) {
|
|
42
|
+
return Object.values(workTypes).map((entry) => entry.header);
|
|
43
|
+
}
|
|
35
44
|
export {
|
|
45
|
+
deriveSectionOrder,
|
|
36
46
|
resolveReleaseNotesConfig
|
|
37
47
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import type { WorkspaceConfig } from './types.ts';
|
|
1
2
|
export interface ResolvedTag {
|
|
2
3
|
tag: string;
|
|
3
4
|
dir: string;
|
|
4
5
|
workspacePath: string;
|
|
5
6
|
}
|
|
6
|
-
export declare function resolveReleaseTags(
|
|
7
|
+
export declare function resolveReleaseTags(workspaces?: readonly WorkspaceConfig[]): ResolvedTag[];
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
2
|
const VERSION_PATTERN = /^v\d+\.\d+\.\d+/;
|
|
3
|
-
|
|
3
|
+
const SEMVER_SUFFIX_PATTERN = /^\d+\.\d+\.\d+/;
|
|
4
|
+
function resolveReleaseTags(workspaces) {
|
|
4
5
|
const output = execFileSync("git", ["tag", "--points-at", "HEAD"], { encoding: "utf8" });
|
|
5
6
|
const tags = output.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
|
|
6
|
-
if (
|
|
7
|
+
if (workspaces === void 0) {
|
|
7
8
|
return resolveSinglePackageTags(tags);
|
|
8
9
|
}
|
|
9
|
-
return resolveMonorepoTags(tags,
|
|
10
|
+
return resolveMonorepoTags(tags, workspaces);
|
|
10
11
|
}
|
|
11
12
|
function resolveSinglePackageTags(tags) {
|
|
12
13
|
const matched = tags.filter((tag) => VERSION_PATTERN.test(tag));
|
|
@@ -18,24 +19,28 @@ function resolveSinglePackageTags(tags) {
|
|
|
18
19
|
}
|
|
19
20
|
return matched.map((tag) => ({ tag, dir: ".", workspacePath: "." }));
|
|
20
21
|
}
|
|
21
|
-
function resolveMonorepoTags(tags,
|
|
22
|
+
function resolveMonorepoTags(tags, workspaces) {
|
|
23
|
+
const sortedWorkspaces = [...workspaces].sort((a, b) => b.tagPrefix.length - a.tagPrefix.length);
|
|
22
24
|
const resolved = [];
|
|
23
25
|
for (const tag of tags) {
|
|
24
|
-
const
|
|
25
|
-
if (
|
|
26
|
-
|
|
26
|
+
const match = findMatchingWorkspace(tag, sortedWorkspaces);
|
|
27
|
+
if (match !== void 0) {
|
|
28
|
+
resolved.push({ tag, dir: match.dir, workspacePath: match.workspacePath });
|
|
27
29
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
}
|
|
31
|
+
return resolved;
|
|
32
|
+
}
|
|
33
|
+
function findMatchingWorkspace(tag, sortedWorkspaces) {
|
|
34
|
+
for (const workspace of sortedWorkspaces) {
|
|
35
|
+
if (!tag.startsWith(workspace.tagPrefix)) {
|
|
31
36
|
continue;
|
|
32
37
|
}
|
|
33
|
-
const
|
|
34
|
-
if (
|
|
35
|
-
|
|
38
|
+
const versionSuffix = tag.slice(workspace.tagPrefix.length);
|
|
39
|
+
if (SEMVER_SUFFIX_PATTERN.test(versionSuffix)) {
|
|
40
|
+
return workspace;
|
|
36
41
|
}
|
|
37
42
|
}
|
|
38
|
-
return
|
|
43
|
+
return void 0;
|
|
39
44
|
}
|
|
40
45
|
export {
|
|
41
46
|
resolveReleaseTags
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function showTagPrefixesCommand(): Promise<number>;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { detectRepoType } from "./init/detectRepoType.js";
|
|
2
|
+
import { previewTagPrefixes } from "./previewTagPrefixes.js";
|
|
3
|
+
async function showTagPrefixesCommand() {
|
|
4
|
+
if (detectRepoType() === "single-package") {
|
|
5
|
+
process.stdout.write(renderSinglePackage());
|
|
6
|
+
return 0;
|
|
7
|
+
}
|
|
8
|
+
const preview = await previewTagPrefixes();
|
|
9
|
+
process.stdout.write(renderMonorepo(preview));
|
|
10
|
+
return computeExitCode(preview);
|
|
11
|
+
}
|
|
12
|
+
function renderSinglePackage() {
|
|
13
|
+
const lines = [
|
|
14
|
+
"Workspace Derived prefix Status",
|
|
15
|
+
". v (single-package mode)",
|
|
16
|
+
""
|
|
17
|
+
];
|
|
18
|
+
return lines.join("\n");
|
|
19
|
+
}
|
|
20
|
+
function renderMonorepo(preview) {
|
|
21
|
+
const lines = ["Workspace tag prefixes:", ""];
|
|
22
|
+
for (const row of preview.workspaces) {
|
|
23
|
+
lines.push(...renderWorkspaceRow(row));
|
|
24
|
+
}
|
|
25
|
+
if (preview.collisions.length > 0) {
|
|
26
|
+
lines.push(
|
|
27
|
+
"",
|
|
28
|
+
...preview.collisions.map(
|
|
29
|
+
(collision) => `\u26D4 tag prefix collision: '${collision.tagPrefix}' used by ${collision.workspacePaths.join(", ")}`
|
|
30
|
+
)
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
if (preview.undeclaredCandidates.length > 0) {
|
|
34
|
+
lines.push(
|
|
35
|
+
"",
|
|
36
|
+
"Undeclared tag prefixes:",
|
|
37
|
+
"",
|
|
38
|
+
...preview.undeclaredCandidates.map(
|
|
39
|
+
(candidate) => ` '${candidate.prefix}' \u2014 ${candidate.tagCount} tags (e.g., ${candidate.exampleTags.join(", ")})`
|
|
40
|
+
),
|
|
41
|
+
"",
|
|
42
|
+
"Suggested config snippet (adjust `dir` to match your workspace if the guess is wrong, and replace the `name` placeholder with the legacy npm name):",
|
|
43
|
+
"",
|
|
44
|
+
renderSuggestedSnippet(preview.undeclaredCandidates),
|
|
45
|
+
"",
|
|
46
|
+
"If the suggested `dir` does not match your workspace, adjust before pasting. Each legacy identity requires a `name` \u2014 replace the `TODO-fill-in-legacy-npm-name` placeholder with the package's prior npm name."
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
lines.push("");
|
|
50
|
+
return lines.join("\n");
|
|
51
|
+
}
|
|
52
|
+
function renderWorkspaceRow(row) {
|
|
53
|
+
const lines = [];
|
|
54
|
+
if (row.derivedPrefix === null) {
|
|
55
|
+
lines.push(` ${row.workspacePath} \u2014 \u26D4 derivation failed: ${row.derivationError ?? "unknown error"}`);
|
|
56
|
+
return lines;
|
|
57
|
+
}
|
|
58
|
+
const statusMarker = row.derivedTagCount > 0 ? `\u2705 ${row.derivedTagCount} tags` : "\u26A0\uFE0F no existing tags";
|
|
59
|
+
lines.push(` ${row.workspacePath} \u2014 derived prefix '${row.derivedPrefix}', ${statusMarker}`);
|
|
60
|
+
for (const entry of row.legacyEntries) {
|
|
61
|
+
if (entry.tagCount > 0) {
|
|
62
|
+
lines.push(` \u2705 ${entry.tagCount} legacy tags with '${entry.prefix}' prefix (recognized)`);
|
|
63
|
+
} else {
|
|
64
|
+
lines.push(` \u26A0\uFE0F recorded legacy prefix '${entry.prefix}' has no tags`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return lines;
|
|
68
|
+
}
|
|
69
|
+
function renderSuggestedSnippet(candidates) {
|
|
70
|
+
const entries = candidates.map(
|
|
71
|
+
(candidate) => ` { dir: '${candidate.suggestedDir}', legacyIdentities: [{ name: 'TODO-fill-in-legacy-npm-name', tagPrefix: '${candidate.prefix}' }] },`
|
|
72
|
+
).join("\n");
|
|
73
|
+
return ` workspaces: [
|
|
74
|
+
${entries}
|
|
75
|
+
],`;
|
|
76
|
+
}
|
|
77
|
+
function computeExitCode(preview) {
|
|
78
|
+
const hasDerivationFailure = preview.workspaces.some((row) => row.derivedPrefix === null);
|
|
79
|
+
const hasCollision = preview.collisions.length > 0;
|
|
80
|
+
return hasDerivationFailure || hasCollision ? 1 : 0;
|
|
81
|
+
}
|
|
82
|
+
export {
|
|
83
|
+
showTagPrefixesCommand
|
|
84
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { reportWriteResult, writeFileWithCheck } from "@williamthorsen/
|
|
1
|
+
import { reportWriteResult, writeFileWithCheck } from "@williamthorsen/nmr-core";
|
|
2
2
|
import { discoverWorkspaces } from "../discoverWorkspaces.js";
|
|
3
3
|
import { generateCommand, LABELS_OUTPUT_PATH } from "./generateCommand.js";
|
|
4
4
|
import { SYNC_LABELS_CONFIG_PATH } from "./loadSyncLabelsConfig.js";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { existsSync, readFileSync } from "node:fs";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
|
+
import { findPackageRoot } from "@williamthorsen/nmr-core";
|
|
4
5
|
import { load } from "js-yaml";
|
|
5
|
-
import { findPackageRoot } from "../findPackageRoot.js";
|
|
6
6
|
import { isRecord } from "../typeGuards.js";
|
|
7
7
|
function resolvePresetPath(presetName) {
|
|
8
8
|
const root = findPackageRoot(import.meta.url);
|
|
@@ -12,7 +12,7 @@ permissions:
|
|
|
12
12
|
|
|
13
13
|
jobs:
|
|
14
14
|
sync:
|
|
15
|
-
uses: williamthorsen/node-monorepo-tools/.github/workflows/sync-labels.reusable.yaml@sync-labels-
|
|
15
|
+
uses: williamthorsen/node-monorepo-tools/.github/workflows/sync-labels.reusable.yaml@workflow/sync-labels-v1
|
|
16
16
|
`;
|
|
17
17
|
}
|
|
18
18
|
function buildScopeLabels(workspacePaths) {
|
package/dist/esm/tagCommand.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { parseArgs, translateParseError } from "@williamthorsen/
|
|
1
|
+
import { parseArgs, translateParseError } from "@williamthorsen/nmr-core";
|
|
2
2
|
import { createTags } from "./createTags.js";
|
|
3
3
|
const tagFlagSchema = {
|
|
4
4
|
dryRun: { long: "--dry-run", type: "boolean" },
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export type ReleaseType = 'major' | 'minor' | 'patch';
|
|
|
2
2
|
export type ChangelogAudience = 'all' | 'dev';
|
|
3
3
|
export interface ChangelogItem {
|
|
4
4
|
description: string;
|
|
5
|
+
body?: string;
|
|
5
6
|
}
|
|
6
7
|
export interface ChangelogSection {
|
|
7
8
|
title: string;
|
|
@@ -20,7 +21,6 @@ export interface ChangelogJsonConfig {
|
|
|
20
21
|
}
|
|
21
22
|
export interface ReleaseNotesConfig {
|
|
22
23
|
shouldInjectIntoReadme: boolean;
|
|
23
|
-
shouldCreateGithubRelease: boolean;
|
|
24
24
|
}
|
|
25
25
|
export interface PropagationSource {
|
|
26
26
|
packageName: string;
|
|
@@ -31,7 +31,7 @@ export interface BumpResult {
|
|
|
31
31
|
newVersion: string;
|
|
32
32
|
files: string[];
|
|
33
33
|
}
|
|
34
|
-
export interface
|
|
34
|
+
export interface WorkspacePrepareResult {
|
|
35
35
|
name?: string | undefined;
|
|
36
36
|
status: 'released' | 'skipped';
|
|
37
37
|
previousTag?: string | undefined;
|
|
@@ -47,9 +47,10 @@ export interface ComponentPrepareResult {
|
|
|
47
47
|
unparseableCommits?: Commit[] | undefined;
|
|
48
48
|
propagatedFrom?: PropagationSource[] | undefined;
|
|
49
49
|
skipReason?: string | undefined;
|
|
50
|
+
setVersion?: string | undefined;
|
|
50
51
|
}
|
|
51
52
|
export interface PrepareResult {
|
|
52
|
-
|
|
53
|
+
workspaces: WorkspacePrepareResult[];
|
|
53
54
|
tags: string[];
|
|
54
55
|
formatCommand?: {
|
|
55
56
|
command: string;
|
|
@@ -68,7 +69,7 @@ export interface VersionPatterns {
|
|
|
68
69
|
minor: string[];
|
|
69
70
|
}
|
|
70
71
|
export interface ReleaseKitConfig {
|
|
71
|
-
|
|
72
|
+
workspaces?: WorkspaceOverride[];
|
|
72
73
|
versionPatterns?: VersionPatterns;
|
|
73
74
|
workTypes?: Record<string, WorkTypeConfig>;
|
|
74
75
|
formatCommand?: string;
|
|
@@ -76,10 +77,21 @@ export interface ReleaseKitConfig {
|
|
|
76
77
|
scopeAliases?: Record<string, string>;
|
|
77
78
|
changelogJson?: Partial<ChangelogJsonConfig>;
|
|
78
79
|
releaseNotes?: Partial<ReleaseNotesConfig>;
|
|
80
|
+
retiredPackages?: RetiredPackage[];
|
|
79
81
|
}
|
|
80
|
-
export interface
|
|
82
|
+
export interface LegacyIdentity {
|
|
83
|
+
name: string;
|
|
84
|
+
tagPrefix: string;
|
|
85
|
+
}
|
|
86
|
+
export interface RetiredPackage {
|
|
87
|
+
name: string;
|
|
88
|
+
tagPrefix: string;
|
|
89
|
+
successor?: string;
|
|
90
|
+
}
|
|
91
|
+
export interface WorkspaceOverride {
|
|
81
92
|
dir: string;
|
|
82
93
|
shouldExclude?: boolean;
|
|
94
|
+
legacyIdentities?: LegacyIdentity[];
|
|
83
95
|
}
|
|
84
96
|
export interface Commit {
|
|
85
97
|
message: string;
|
|
@@ -93,15 +105,18 @@ export interface ParsedCommit {
|
|
|
93
105
|
scope?: string;
|
|
94
106
|
breaking: boolean;
|
|
95
107
|
}
|
|
96
|
-
export interface
|
|
108
|
+
export interface WorkspaceConfig {
|
|
97
109
|
dir: string;
|
|
110
|
+
name: string;
|
|
98
111
|
tagPrefix: string;
|
|
112
|
+
workspacePath: string;
|
|
99
113
|
packageFiles: string[];
|
|
100
114
|
changelogPaths: string[];
|
|
101
115
|
paths: string[];
|
|
116
|
+
legacyIdentities?: LegacyIdentity[];
|
|
102
117
|
}
|
|
103
118
|
export interface MonorepoReleaseConfig {
|
|
104
|
-
|
|
119
|
+
workspaces: WorkspaceConfig[];
|
|
105
120
|
workTypes?: Record<string, WorkTypeConfig>;
|
|
106
121
|
versionPatterns?: VersionPatterns;
|
|
107
122
|
formatCommand?: string;
|