@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
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { buildChangelogEntries } from "./buildChangelogEntries.js";
|
|
2
|
+
import { bumpAllVersions } from "./bumpAllVersions.js";
|
|
3
|
+
import { resolveChangelogJsonPath, writeChangelogJson } from "./changelogJsonFile.js";
|
|
4
|
+
import { decideRelease } from "./decideRelease.js";
|
|
5
|
+
import { DEFAULT_VERSION_PATTERNS, DEFAULT_WORK_TYPES } from "./defaults.js";
|
|
6
|
+
import { buildTagPattern, generateChangelog } from "./generateChangelogs.js";
|
|
7
|
+
import { getCommitsSinceTarget } from "./getCommitsSinceTarget.js";
|
|
8
|
+
import { deriveSectionOrder } from "./resolveReleaseNotesConfig.js";
|
|
9
|
+
import { writeReleaseNotesPreviews } from "./writeReleaseNotesPreviews.js";
|
|
10
|
+
const ROOT_PACKAGE_FILE = "./package.json";
|
|
11
|
+
const ROOT_CHANGELOG_PATH = ".";
|
|
12
|
+
function releasePrepareProject(args) {
|
|
13
|
+
const { config, options, modifiedFiles, tags } = args;
|
|
14
|
+
const { dryRun, bumpOverride, withReleaseNotes, force } = options;
|
|
15
|
+
const project = config.project;
|
|
16
|
+
if (project === void 0) {
|
|
17
|
+
throw new Error("releasePrepareProject called without a configured project block");
|
|
18
|
+
}
|
|
19
|
+
const workTypes = config.workTypes ?? { ...DEFAULT_WORK_TYPES };
|
|
20
|
+
const versionPatterns = config.versionPatterns ?? { ...DEFAULT_VERSION_PATTERNS };
|
|
21
|
+
const contributingPaths = config.workspaces.flatMap((workspace) => workspace.paths);
|
|
22
|
+
const { tag, commits } = getCommitsSinceTarget([project.tagPrefix], contributingPaths);
|
|
23
|
+
const since = tag === void 0 ? "(no previous release found)" : `since ${tag}`;
|
|
24
|
+
const decision = decideRelease({
|
|
25
|
+
commits,
|
|
26
|
+
force,
|
|
27
|
+
bumpOverride,
|
|
28
|
+
workTypes,
|
|
29
|
+
versionPatterns,
|
|
30
|
+
scopeAliases: config.scopeAliases,
|
|
31
|
+
skipReasons: {
|
|
32
|
+
noCommits: `No commits ${since}. Pass --force to release at patch. Skipping.`,
|
|
33
|
+
noBumpWorthy: `No bump-worthy commits ${since}. Pass --force to release at patch (or --force --bump=X for a different level). Skipping.`
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
if (decision.outcome === "skip") {
|
|
37
|
+
const skipped = {
|
|
38
|
+
status: "skipped",
|
|
39
|
+
commitCount: commits.length,
|
|
40
|
+
parsedCommitCount: decision.parsedCommitCount,
|
|
41
|
+
skipReason: decision.skipReason
|
|
42
|
+
};
|
|
43
|
+
if (tag !== void 0) {
|
|
44
|
+
skipped.previousTag = tag;
|
|
45
|
+
}
|
|
46
|
+
if (decision.unparseableCommits !== void 0) {
|
|
47
|
+
skipped.unparseableCommits = decision.unparseableCommits;
|
|
48
|
+
}
|
|
49
|
+
return skipped;
|
|
50
|
+
}
|
|
51
|
+
const { releaseType, parsedCommitCount, unparseableCommits } = decision;
|
|
52
|
+
const bump = bumpAllVersions([ROOT_PACKAGE_FILE], releaseType, dryRun);
|
|
53
|
+
const newTag = `${project.tagPrefix}${bump.newVersion}`;
|
|
54
|
+
const tagPattern = buildTagPattern([project.tagPrefix]);
|
|
55
|
+
const changelogFiles = generateChangelog(config, ROOT_CHANGELOG_PATH, newTag, dryRun, {
|
|
56
|
+
tagPattern,
|
|
57
|
+
includePaths: contributingPaths
|
|
58
|
+
});
|
|
59
|
+
const changelogJsonFiles = [];
|
|
60
|
+
if (config.changelogJson.enabled) {
|
|
61
|
+
const changelogJsonPath = resolveChangelogJsonPath(config, ROOT_CHANGELOG_PATH);
|
|
62
|
+
const entries = buildChangelogEntries(config, newTag, {
|
|
63
|
+
tagPattern,
|
|
64
|
+
includePaths: contributingPaths
|
|
65
|
+
});
|
|
66
|
+
if (!dryRun) {
|
|
67
|
+
writeChangelogJson(changelogJsonPath, entries);
|
|
68
|
+
}
|
|
69
|
+
changelogJsonFiles.push(changelogJsonPath);
|
|
70
|
+
}
|
|
71
|
+
const firstChangelogJsonPath = changelogJsonFiles[0];
|
|
72
|
+
if (withReleaseNotes === true && config.changelogJson.enabled && firstChangelogJsonPath !== void 0) {
|
|
73
|
+
const sectionOrder = deriveSectionOrder(workTypes);
|
|
74
|
+
writeReleaseNotesPreviews({
|
|
75
|
+
workspacePath: ROOT_CHANGELOG_PATH,
|
|
76
|
+
tag: newTag,
|
|
77
|
+
changelogJsonPath: firstChangelogJsonPath,
|
|
78
|
+
sectionOrder,
|
|
79
|
+
dryRun
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
tags.push(newTag);
|
|
83
|
+
modifiedFiles.push(ROOT_PACKAGE_FILE, ...changelogFiles, ...changelogJsonFiles);
|
|
84
|
+
const result = {
|
|
85
|
+
status: "released",
|
|
86
|
+
commitCount: commits.length,
|
|
87
|
+
parsedCommitCount,
|
|
88
|
+
releaseType,
|
|
89
|
+
currentVersion: bump.currentVersion,
|
|
90
|
+
newVersion: bump.newVersion,
|
|
91
|
+
tag: newTag,
|
|
92
|
+
bumpedFiles: bump.files,
|
|
93
|
+
changelogFiles,
|
|
94
|
+
commits
|
|
95
|
+
};
|
|
96
|
+
if (tag !== void 0) {
|
|
97
|
+
result.previousTag = tag;
|
|
98
|
+
}
|
|
99
|
+
if (unparseableCommits !== void 0) {
|
|
100
|
+
result.unparseableCommits = unparseableCommits;
|
|
101
|
+
}
|
|
102
|
+
if (bumpOverride !== void 0) {
|
|
103
|
+
result.bumpOverride = bumpOverride;
|
|
104
|
+
}
|
|
105
|
+
return result;
|
|
106
|
+
}
|
|
107
|
+
export {
|
|
108
|
+
releasePrepareProject
|
|
109
|
+
};
|
|
@@ -2,6 +2,7 @@ import type { ChangelogAudience, ChangelogEntry, ChangelogSection } from './type
|
|
|
2
2
|
export interface RenderOptions {
|
|
3
3
|
filter?: (section: ChangelogSection) => boolean;
|
|
4
4
|
includeHeading?: boolean;
|
|
5
|
+
sectionOrder?: string[];
|
|
5
6
|
}
|
|
6
7
|
export declare function matchesAudience(audience: ChangelogAudience): (section: ChangelogSection) => boolean;
|
|
7
8
|
export declare function renderReleaseNotesSingle(entry: ChangelogEntry, options?: RenderOptions): string;
|
|
@@ -10,7 +10,9 @@ function matchesAudience(audience) {
|
|
|
10
10
|
function renderReleaseNotesSingle(entry, options) {
|
|
11
11
|
const filter = options?.filter;
|
|
12
12
|
const includeHeading = options?.includeHeading ?? true;
|
|
13
|
-
const
|
|
13
|
+
const sectionOrder = options?.sectionOrder;
|
|
14
|
+
const filtered = filter !== void 0 ? entry.sections.filter(filter) : entry.sections;
|
|
15
|
+
const sections = sectionOrder !== void 0 ? sortSectionsByOrder(filtered, sectionOrder) : filtered;
|
|
14
16
|
if (sections.length === 0) {
|
|
15
17
|
return "";
|
|
16
18
|
}
|
|
@@ -23,12 +25,37 @@ function renderReleaseNotesSingle(entry, options) {
|
|
|
23
25
|
lines.push("");
|
|
24
26
|
}
|
|
25
27
|
lines.push(`### ${section.title}`, "");
|
|
26
|
-
for (const item of section.items) {
|
|
28
|
+
for (const [index, item] of section.items.entries()) {
|
|
27
29
|
lines.push(`- ${item.description}`);
|
|
30
|
+
if (item.body !== void 0 && item.body.length > 0) {
|
|
31
|
+
lines.push("", ...indentBodyLines(item.body));
|
|
32
|
+
if (index < section.items.length - 1) {
|
|
33
|
+
lines.push("");
|
|
34
|
+
}
|
|
35
|
+
}
|
|
28
36
|
}
|
|
29
37
|
}
|
|
30
38
|
return lines.join("\n") + "\n";
|
|
31
39
|
}
|
|
40
|
+
function sortSectionsByOrder(sections, order) {
|
|
41
|
+
const priority = /* @__PURE__ */ new Map();
|
|
42
|
+
for (const [index, title] of order.entries()) {
|
|
43
|
+
priority.set(title, index);
|
|
44
|
+
}
|
|
45
|
+
const indexed = sections.map((section, index) => ({ section, index }));
|
|
46
|
+
indexed.sort((a, b) => {
|
|
47
|
+
const priorityA = priority.get(a.section.title) ?? Number.POSITIVE_INFINITY;
|
|
48
|
+
const priorityB = priority.get(b.section.title) ?? Number.POSITIVE_INFINITY;
|
|
49
|
+
if (priorityA !== priorityB) {
|
|
50
|
+
return priorityA - priorityB;
|
|
51
|
+
}
|
|
52
|
+
return a.index - b.index;
|
|
53
|
+
});
|
|
54
|
+
return indexed.map(({ section }) => section);
|
|
55
|
+
}
|
|
56
|
+
function indentBodyLines(body) {
|
|
57
|
+
return body.split("\n").map((line) => line.length === 0 ? "" : ` ${line}`);
|
|
58
|
+
}
|
|
32
59
|
function renderReleaseNotesMulti(entries, options) {
|
|
33
60
|
const parts = entries.map((entry) => renderReleaseNotesSingle(entry, options)).filter((part) => part.length > 0);
|
|
34
61
|
return parts.join("\n");
|
|
@@ -1,86 +1,56 @@
|
|
|
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) || result.project !== 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}`);
|
|
23
23
|
return lines.join("\n");
|
|
24
24
|
}
|
|
25
|
-
if (
|
|
26
|
-
lines.push(` Using
|
|
25
|
+
if (workspace.setVersion !== void 0) {
|
|
26
|
+
lines.push(` Using version override: ${workspace.setVersion}`);
|
|
27
|
+
} else if (workspace.bumpOverride !== void 0) {
|
|
28
|
+
lines.push(` Using bump override: ${workspace.bumpOverride}`);
|
|
27
29
|
}
|
|
28
|
-
if (
|
|
29
|
-
lines.push(dim(`Bumping versions (${
|
|
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)...`));
|
|
30
34
|
}
|
|
31
|
-
if (
|
|
32
|
-
lines.push(`\u{1F4E6} ${
|
|
35
|
+
if (workspace.setVersion !== void 0) {
|
|
36
|
+
lines.push(`\u{1F4E6} ${workspace.currentVersion} \u2192 ${bold(workspace.newVersion)} (version override)`);
|
|
37
|
+
} else if (workspace.releaseType !== void 0) {
|
|
38
|
+
lines.push(`\u{1F4E6} ${workspace.currentVersion} \u2192 ${bold(workspace.newVersion)} (${workspace.releaseType})`);
|
|
33
39
|
}
|
|
34
|
-
formatBumpFiles(lines,
|
|
40
|
+
formatBumpFiles(lines, workspace, result.dryRun);
|
|
35
41
|
lines.push(dim("Generating changelogs..."));
|
|
36
|
-
formatChangelogFiles(lines,
|
|
42
|
+
formatChangelogFiles(lines, workspace, result.dryRun);
|
|
37
43
|
formatFormatCommand(lines, result);
|
|
38
|
-
lines.push(`\u2705 Release preparation complete
|
|
39
|
-
if (component.tag !== void 0) {
|
|
40
|
-
lines.push(` \u{1F3F7}\uFE0F ${bold(component.tag)}`);
|
|
41
|
-
}
|
|
44
|
+
lines.push(`\u2705 Release preparation complete.`, ` \u{1F3F7}\uFE0F ${bold(workspace.tag)}`);
|
|
42
45
|
return lines.join("\n");
|
|
43
46
|
}
|
|
44
|
-
function
|
|
47
|
+
function formatMultiWorkspace(result) {
|
|
45
48
|
const lines = [];
|
|
46
|
-
for (const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
}
|
|
49
|
+
for (const workspace of result.workspaces) {
|
|
50
|
+
formatWorkspaceSection(lines, workspace, result.dryRun);
|
|
51
|
+
}
|
|
52
|
+
if (result.project !== void 0) {
|
|
53
|
+
formatProjectSection(lines, result.project, result.dryRun);
|
|
84
54
|
}
|
|
85
55
|
formatFormatCommand(lines, result);
|
|
86
56
|
formatWarnings(lines, result);
|
|
@@ -92,12 +62,115 @@ ${sectionHeader(component.name)}`);
|
|
|
92
62
|
}
|
|
93
63
|
} else {
|
|
94
64
|
lines.push(`
|
|
95
|
-
\u23ED\uFE0F No
|
|
65
|
+
\u23ED\uFE0F No workspaces had release-worthy changes.`);
|
|
96
66
|
}
|
|
97
67
|
return lines.join("\n");
|
|
98
68
|
}
|
|
99
|
-
function
|
|
100
|
-
|
|
69
|
+
function formatProjectSection(lines, project, dryRun) {
|
|
70
|
+
lines.push(`
|
|
71
|
+
${sectionHeader("project")}`);
|
|
72
|
+
const since = project.previousTag === void 0 ? "(no previous release found)" : `since ${project.previousTag}`;
|
|
73
|
+
lines.push(dim(` Found ${project.commitCount} commits ${since}`));
|
|
74
|
+
if (project.status === "skipped") {
|
|
75
|
+
lines.push(` \u23ED\uFE0F ${project.skipReason}`);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const { releaseType, currentVersion, newVersion, tag } = project;
|
|
79
|
+
if (project.parsedCommitCount > 0) {
|
|
80
|
+
lines.push(dim(` Parsed ${project.parsedCommitCount} typed commits`));
|
|
81
|
+
}
|
|
82
|
+
if (project.bumpOverride !== void 0) {
|
|
83
|
+
lines.push(` Using bump override: ${project.bumpOverride}`);
|
|
84
|
+
}
|
|
85
|
+
formatProjectUnparseable(lines, project);
|
|
86
|
+
lines.push(
|
|
87
|
+
dim(` Bumping versions (${releaseType})...`),
|
|
88
|
+
` \u{1F4E6} ${currentVersion} \u2192 ${bold(newVersion)} (${releaseType})`
|
|
89
|
+
);
|
|
90
|
+
for (const file of project.bumpedFiles) {
|
|
91
|
+
if (dryRun) {
|
|
92
|
+
lines.push(dim(` [dry-run] Would bump ${file}`));
|
|
93
|
+
} else {
|
|
94
|
+
lines.push(dim(` Bumped ${file}`));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
lines.push(dim(" Generating changelogs..."));
|
|
98
|
+
for (const file of project.changelogFiles) {
|
|
99
|
+
if (dryRun) {
|
|
100
|
+
lines.push(dim(` [dry-run] Would run: npx --yes git-cliff ... --output ${file}`));
|
|
101
|
+
} else {
|
|
102
|
+
lines.push(dim(` Generating changelog: ${file}`));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
lines.push(` \u{1F3F7}\uFE0F ${bold(tag)}`);
|
|
106
|
+
}
|
|
107
|
+
function formatProjectUnparseable(lines, project) {
|
|
108
|
+
const unparseable = project.unparseableCommits;
|
|
109
|
+
if (unparseable === void 0 || unparseable.length === 0) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const count = unparseable.length;
|
|
113
|
+
const isPatchFloor = project.parsedCommitCount === 0;
|
|
114
|
+
const suffix = isPatchFloor ? " (defaulting to patch bump)" : "";
|
|
115
|
+
lines.push(` \u26A0\uFE0F ${count} commit${count === 1 ? "" : "s"} could not be parsed${suffix}`);
|
|
116
|
+
for (const commit of unparseable) {
|
|
117
|
+
const shortHash = commit.hash.slice(0, 7);
|
|
118
|
+
const truncatedMessage = commit.message.length > 72 ? `${commit.message.slice(0, 69)}...` : commit.message;
|
|
119
|
+
lines.push(` \xB7 ${shortHash} ${truncatedMessage}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function formatWorkspaceSection(lines, workspace, dryRun) {
|
|
123
|
+
if (workspace.name !== void 0) {
|
|
124
|
+
lines.push(`
|
|
125
|
+
${sectionHeader(workspace.name)}`);
|
|
126
|
+
}
|
|
127
|
+
const since = workspace.previousTag === void 0 ? "(no previous release found)" : `since ${workspace.previousTag}`;
|
|
128
|
+
lines.push(dim(` Found ${workspace.commitCount} commits ${since}`));
|
|
129
|
+
if (workspace.status === "skipped") {
|
|
130
|
+
lines.push(` \u23ED\uFE0F ${workspace.skipReason}`);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const { propagatedFrom } = workspace;
|
|
134
|
+
const isPropagatedOnly = propagatedFrom !== void 0 && workspace.commitCount === 0;
|
|
135
|
+
formatCommitSummary(lines, workspace, propagatedFrom, isPropagatedOnly);
|
|
136
|
+
formatUnparseableWarning(lines, workspace, " ");
|
|
137
|
+
formatBumpLabels(lines, workspace, isPropagatedOnly);
|
|
138
|
+
formatVersionLine(lines, workspace, propagatedFrom, isPropagatedOnly);
|
|
139
|
+
formatBumpFiles(lines, workspace, dryRun, " ");
|
|
140
|
+
lines.push(dim(" Generating changelogs..."));
|
|
141
|
+
formatChangelogFiles(lines, workspace, dryRun, " ");
|
|
142
|
+
lines.push(` \u{1F3F7}\uFE0F ${bold(workspace.tag)}`);
|
|
143
|
+
}
|
|
144
|
+
function formatCommitSummary(lines, workspace, propagatedFrom, isPropagatedOnly) {
|
|
145
|
+
if (isPropagatedOnly && propagatedFrom !== void 0) {
|
|
146
|
+
const depNames = propagatedFrom.map((p) => p.packageName).join(", ");
|
|
147
|
+
lines.push(dim(` 0 commits (bumped via dependency: ${depNames})`));
|
|
148
|
+
} else if (workspace.parsedCommitCount !== void 0 && workspace.parsedCommitCount > 0) {
|
|
149
|
+
lines.push(dim(` Parsed ${workspace.parsedCommitCount} typed commits`));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function formatBumpLabels(lines, workspace, isPropagatedOnly) {
|
|
153
|
+
if (workspace.setVersion !== void 0) {
|
|
154
|
+
lines.push(` Using version override: ${workspace.setVersion}`);
|
|
155
|
+
} else if (workspace.bumpOverride !== void 0 && !isPropagatedOnly) {
|
|
156
|
+
lines.push(` Using bump override: ${workspace.bumpOverride}`);
|
|
157
|
+
}
|
|
158
|
+
if (workspace.releaseType !== void 0) {
|
|
159
|
+
lines.push(dim(` Bumping versions (${workspace.releaseType})...`));
|
|
160
|
+
} else if (workspace.setVersion !== void 0) {
|
|
161
|
+
lines.push(dim(` Bumping versions (version override)...`));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function formatVersionLine(lines, workspace, propagatedFrom, isPropagatedOnly) {
|
|
165
|
+
if (workspace.setVersion !== void 0) {
|
|
166
|
+
lines.push(` \u{1F4E6} ${workspace.currentVersion} \u2192 ${bold(workspace.newVersion)} (version override)`);
|
|
167
|
+
} else if (workspace.releaseType !== void 0) {
|
|
168
|
+
const suffix = isPropagatedOnly ? formatPropagationSuffix(propagatedFrom) : "";
|
|
169
|
+
lines.push(` \u{1F4E6} ${workspace.currentVersion} \u2192 ${bold(workspace.newVersion)} (${workspace.releaseType}${suffix})`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function formatBumpFiles(lines, workspace, dryRun, indent = "") {
|
|
173
|
+
for (const file of workspace.bumpedFiles) {
|
|
101
174
|
if (dryRun) {
|
|
102
175
|
lines.push(dim(`${indent} [dry-run] Would bump ${file}`));
|
|
103
176
|
} else {
|
|
@@ -105,8 +178,8 @@ function formatBumpFiles(lines, component, dryRun, indent = "") {
|
|
|
105
178
|
}
|
|
106
179
|
}
|
|
107
180
|
}
|
|
108
|
-
function formatChangelogFiles(lines,
|
|
109
|
-
for (const file of
|
|
181
|
+
function formatChangelogFiles(lines, workspace, dryRun, indent = "") {
|
|
182
|
+
for (const file of workspace.changelogFiles) {
|
|
110
183
|
if (dryRun) {
|
|
111
184
|
lines.push(dim(`${indent} [dry-run] Would run: npx --yes git-cliff ... --output ${file}`));
|
|
112
185
|
} else {
|
|
@@ -114,13 +187,13 @@ function formatChangelogFiles(lines, component, dryRun, indent = "") {
|
|
|
114
187
|
}
|
|
115
188
|
}
|
|
116
189
|
}
|
|
117
|
-
function formatUnparseableWarning(lines,
|
|
118
|
-
const unparseable =
|
|
190
|
+
function formatUnparseableWarning(lines, workspace, indent = "") {
|
|
191
|
+
const unparseable = workspace.unparseableCommits;
|
|
119
192
|
if (unparseable === void 0 || unparseable.length === 0) {
|
|
120
193
|
return;
|
|
121
194
|
}
|
|
122
195
|
const count = unparseable.length;
|
|
123
|
-
const isPatchFloor =
|
|
196
|
+
const isPatchFloor = workspace.parsedCommitCount === 0;
|
|
124
197
|
const suffix = isPatchFloor ? " (defaulting to patch bump)" : "";
|
|
125
198
|
lines.push(`${indent} \u26A0\uFE0F ${count} commit${count === 1 ? "" : "s"} could not be parsed${suffix}`);
|
|
126
199
|
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>;
|