@williamthorsen/release-kit 5.0.0 → 5.2.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 +149 -49
- package/README.md +275 -78
- package/cliff.toml.template +26 -17
- package/dist/esm/.cache +1 -1
- package/dist/esm/assertCleanWorkingTree.js +1 -1
- package/dist/esm/bin/release-kit.js +97 -4
- package/dist/esm/buildChangelogEntries.d.ts +4 -0
- package/dist/esm/buildChangelogEntries.js +173 -0
- package/dist/esm/buildDependencyGraph.d.ts +1 -0
- package/dist/esm/buildDependencyGraph.js +8 -1
- package/dist/esm/buildReleaseSummary.js +9 -1
- package/dist/esm/buildSyntheticChangelogEntry.d.ts +5 -0
- package/dist/esm/buildSyntheticChangelogEntry.js +13 -0
- package/dist/esm/changelogJsonFile.d.ts +4 -0
- package/dist/esm/changelogJsonFile.js +68 -0
- package/dist/esm/checkWorkTypesDrift.d.ts +11 -0
- package/dist/esm/checkWorkTypesDrift.js +110 -0
- package/dist/esm/collectPolicyViolations.d.ts +6 -0
- package/dist/esm/collectPolicyViolations.js +15 -0
- package/dist/esm/createGithubRelease.d.ts +12 -2
- package/dist/esm/createGithubRelease.js +12 -8
- package/dist/esm/createGithubReleaseCommand.js +10 -6
- package/dist/esm/decideRelease.d.ts +28 -0
- package/dist/esm/decideRelease.js +44 -0
- package/dist/esm/defaults.d.ts +8 -0
- package/dist/esm/defaults.js +43 -20
- package/dist/esm/deriveWorkspaceConfig.js +3 -0
- package/dist/esm/determineBumpFromCommits.d.ts +6 -1
- package/dist/esm/determineBumpFromCommits.js +9 -3
- package/dist/esm/generateChangelogs.js +14 -29
- package/dist/esm/index.d.ts +2 -43
- package/dist/esm/index.js +0 -82
- package/dist/esm/init/templates.js +2 -2
- package/dist/esm/loadConfig.d.ts +10 -1
- package/dist/esm/loadConfig.js +110 -24
- package/dist/esm/parseCommitMessage.d.ts +8 -2
- package/dist/esm/parseCommitMessage.js +32 -3
- package/dist/esm/prepareCommand.js +51 -9
- package/dist/esm/publish.d.ts +0 -1
- package/dist/esm/publish.js +3 -3
- package/dist/esm/publishCommand.js +31 -3
- package/dist/esm/releasePrepare.js +109 -41
- package/dist/esm/releasePrepareMono.js +156 -87
- package/dist/esm/releasePrepareProject.d.ts +9 -0
- package/dist/esm/releasePrepareProject.js +121 -0
- package/dist/esm/renderReleaseNotes.js +2 -1
- package/dist/esm/reportPrepare.js +88 -24
- package/dist/esm/resolveCommandTags.js +16 -6
- package/dist/esm/resolveReleaseTags.d.ts +8 -1
- package/dist/esm/resolveReleaseTags.js +11 -7
- package/dist/esm/runGitCliff.d.ts +2 -0
- package/dist/esm/runGitCliff.js +27 -0
- package/dist/esm/stripEmojiPrefix.d.ts +1 -0
- package/dist/esm/stripEmojiPrefix.js +7 -0
- package/dist/esm/syncWorkTypes.d.ts +10 -0
- package/dist/esm/syncWorkTypes.js +90 -0
- package/dist/esm/types.d.ts +72 -14
- package/dist/esm/validateConfig.js +26 -0
- package/dist/esm/validateOnlyExcludesStrandedDependents.d.ts +14 -0
- package/dist/esm/validateOnlyExcludesStrandedDependents.js +109 -0
- package/dist/esm/work-types.json +127 -0
- package/dist/esm/work-types.schema.json +73 -0
- package/dist/esm/workTypesData.d.ts +14 -0
- package/dist/esm/workTypesData.js +59 -0
- package/dist/esm/workTypesUtils.d.ts +5 -0
- package/dist/esm/workTypesUtils.js +16 -0
- package/package.json +9 -3
- package/presets/labels/common.yaml +9 -6
- package/schemas/label-map.json +24 -0
- package/dist/esm/generateChangelogJson.d.ts +0 -7
- package/dist/esm/generateChangelogJson.js +0 -232
- package/dist/esm/version.d.ts +0 -1
- package/dist/esm/version.js +0 -4
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { buildChangelogEntries } from "./buildChangelogEntries.js";
|
|
2
|
+
import { bumpAllVersions } from "./bumpAllVersions.js";
|
|
3
|
+
import { resolveChangelogJsonPath, writeChangelogJson } from "./changelogJsonFile.js";
|
|
4
|
+
import { createPolicyViolationCollector } from "./collectPolicyViolations.js";
|
|
5
|
+
import { decideRelease } from "./decideRelease.js";
|
|
6
|
+
import { DEFAULT_BREAKING_POLICIES, DEFAULT_VERSION_PATTERNS, DEFAULT_WORK_TYPES } from "./defaults.js";
|
|
7
|
+
import { buildTagPattern, generateChangelog } from "./generateChangelogs.js";
|
|
8
|
+
import { getCommitsSinceTarget } from "./getCommitsSinceTarget.js";
|
|
9
|
+
import { deriveSectionOrder } from "./resolveReleaseNotesConfig.js";
|
|
10
|
+
import { writeReleaseNotesPreviews } from "./writeReleaseNotesPreviews.js";
|
|
11
|
+
const ROOT_PACKAGE_FILE = "./package.json";
|
|
12
|
+
const ROOT_CHANGELOG_PATH = ".";
|
|
13
|
+
function releasePrepareProject(args) {
|
|
14
|
+
const { config, options, modifiedFiles, tags } = args;
|
|
15
|
+
const { dryRun, bumpOverride, withReleaseNotes, force } = options;
|
|
16
|
+
const project = config.project;
|
|
17
|
+
if (project === void 0) {
|
|
18
|
+
throw new Error("releasePrepareProject called without a configured project block");
|
|
19
|
+
}
|
|
20
|
+
const workTypes = config.workTypes ?? { ...DEFAULT_WORK_TYPES };
|
|
21
|
+
const versionPatterns = config.versionPatterns ?? { ...DEFAULT_VERSION_PATTERNS };
|
|
22
|
+
const breakingPolicies = config.breakingPolicies ?? DEFAULT_BREAKING_POLICIES;
|
|
23
|
+
const contributingPaths = config.workspaces.flatMap((workspace) => workspace.paths);
|
|
24
|
+
const { tag, commits } = getCommitsSinceTarget([project.tagPrefix], contributingPaths);
|
|
25
|
+
const since = tag === void 0 ? "(no previous release found)" : `since ${tag}`;
|
|
26
|
+
const collector = createPolicyViolationCollector();
|
|
27
|
+
const decision = decideRelease({
|
|
28
|
+
commits,
|
|
29
|
+
force,
|
|
30
|
+
bumpOverride,
|
|
31
|
+
workTypes,
|
|
32
|
+
versionPatterns,
|
|
33
|
+
scopeAliases: config.scopeAliases,
|
|
34
|
+
breakingPolicies,
|
|
35
|
+
onPolicyViolation: collector.onPolicyViolation,
|
|
36
|
+
skipReasons: {
|
|
37
|
+
noCommits: `No commits ${since}. Pass --force to release at patch. Skipping.`,
|
|
38
|
+
noBumpWorthy: `No bump-worthy commits ${since}. Pass --force to release at patch (or --force --bump=X for a different level). Skipping.`
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
const policyViolations = collector.violations.length > 0 ? collector.violations : void 0;
|
|
42
|
+
if (decision.outcome === "skip") {
|
|
43
|
+
const skipped = {
|
|
44
|
+
status: "skipped",
|
|
45
|
+
commitCount: commits.length,
|
|
46
|
+
parsedCommitCount: decision.parsedCommitCount,
|
|
47
|
+
skipReason: decision.skipReason
|
|
48
|
+
};
|
|
49
|
+
if (tag !== void 0) {
|
|
50
|
+
skipped.previousTag = tag;
|
|
51
|
+
}
|
|
52
|
+
if (decision.unparseableCommits !== void 0) {
|
|
53
|
+
skipped.unparseableCommits = decision.unparseableCommits;
|
|
54
|
+
}
|
|
55
|
+
if (policyViolations !== void 0) {
|
|
56
|
+
skipped.policyViolations = policyViolations;
|
|
57
|
+
}
|
|
58
|
+
return skipped;
|
|
59
|
+
}
|
|
60
|
+
const { releaseType, parsedCommitCount, unparseableCommits } = decision;
|
|
61
|
+
const bump = bumpAllVersions([ROOT_PACKAGE_FILE], releaseType, dryRun);
|
|
62
|
+
const newTag = `${project.tagPrefix}${bump.newVersion}`;
|
|
63
|
+
const tagPattern = buildTagPattern([project.tagPrefix]);
|
|
64
|
+
const changelogFiles = generateChangelog(config, ROOT_CHANGELOG_PATH, newTag, dryRun, {
|
|
65
|
+
tagPattern,
|
|
66
|
+
includePaths: contributingPaths
|
|
67
|
+
});
|
|
68
|
+
const changelogJsonFiles = [];
|
|
69
|
+
if (config.changelogJson.enabled) {
|
|
70
|
+
const changelogJsonPath = resolveChangelogJsonPath(config, ROOT_CHANGELOG_PATH);
|
|
71
|
+
const entries = buildChangelogEntries(config, newTag, {
|
|
72
|
+
tagPattern,
|
|
73
|
+
includePaths: contributingPaths
|
|
74
|
+
});
|
|
75
|
+
if (!dryRun) {
|
|
76
|
+
writeChangelogJson(changelogJsonPath, entries);
|
|
77
|
+
}
|
|
78
|
+
changelogJsonFiles.push(changelogJsonPath);
|
|
79
|
+
}
|
|
80
|
+
const firstChangelogJsonPath = changelogJsonFiles[0];
|
|
81
|
+
if (withReleaseNotes === true && config.changelogJson.enabled && firstChangelogJsonPath !== void 0) {
|
|
82
|
+
const sectionOrder = deriveSectionOrder(workTypes);
|
|
83
|
+
writeReleaseNotesPreviews({
|
|
84
|
+
workspacePath: ROOT_CHANGELOG_PATH,
|
|
85
|
+
tag: newTag,
|
|
86
|
+
changelogJsonPath: firstChangelogJsonPath,
|
|
87
|
+
sectionOrder,
|
|
88
|
+
dryRun
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
tags.push(newTag);
|
|
92
|
+
modifiedFiles.push(ROOT_PACKAGE_FILE, ...changelogFiles, ...changelogJsonFiles);
|
|
93
|
+
const result = {
|
|
94
|
+
status: "released",
|
|
95
|
+
commitCount: commits.length,
|
|
96
|
+
parsedCommitCount,
|
|
97
|
+
releaseType,
|
|
98
|
+
currentVersion: bump.currentVersion,
|
|
99
|
+
newVersion: bump.newVersion,
|
|
100
|
+
tag: newTag,
|
|
101
|
+
bumpedFiles: bump.files,
|
|
102
|
+
changelogFiles,
|
|
103
|
+
commits
|
|
104
|
+
};
|
|
105
|
+
if (tag !== void 0) {
|
|
106
|
+
result.previousTag = tag;
|
|
107
|
+
}
|
|
108
|
+
if (unparseableCommits !== void 0) {
|
|
109
|
+
result.unparseableCommits = unparseableCommits;
|
|
110
|
+
}
|
|
111
|
+
if (policyViolations !== void 0) {
|
|
112
|
+
result.policyViolations = policyViolations;
|
|
113
|
+
}
|
|
114
|
+
if (bumpOverride !== void 0) {
|
|
115
|
+
result.bumpOverride = bumpOverride;
|
|
116
|
+
}
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
export {
|
|
120
|
+
releasePrepareProject
|
|
121
|
+
};
|
|
@@ -26,7 +26,8 @@ function renderReleaseNotesSingle(entry, options) {
|
|
|
26
26
|
}
|
|
27
27
|
lines.push(`### ${section.title}`, "");
|
|
28
28
|
for (const [index, item] of section.items.entries()) {
|
|
29
|
-
|
|
29
|
+
const prefix = item.breaking === true ? "\u{1F6A8} **Breaking:** " : "";
|
|
30
|
+
lines.push(`- ${prefix}${item.description}`);
|
|
30
31
|
if (item.body !== void 0 && item.body.length > 0) {
|
|
31
32
|
lines.push("", ...indentBodyLines(item.body));
|
|
32
33
|
if (index < section.items.length - 1) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { bold, dim, sectionHeader } from "./format.js";
|
|
2
2
|
function reportPrepare(result) {
|
|
3
|
-
const isMultiWorkspace = result.workspaces.some((w) => w.name !== void 0);
|
|
3
|
+
const isMultiWorkspace = result.workspaces.some((w) => w.name !== void 0) || result.project !== void 0;
|
|
4
4
|
if (isMultiWorkspace) {
|
|
5
5
|
return formatMultiWorkspace(result);
|
|
6
6
|
}
|
|
@@ -18,35 +18,31 @@ function formatSingleWorkspace(result) {
|
|
|
18
18
|
lines.push(dim(` Parsed ${workspace.parsedCommitCount} typed commits`));
|
|
19
19
|
}
|
|
20
20
|
formatUnparseableWarning(lines, workspace);
|
|
21
|
+
formatPolicyViolations(lines, workspace.policyViolations);
|
|
21
22
|
if (workspace.status === "skipped") {
|
|
22
|
-
lines.push(`\u23ED\uFE0F ${workspace.skipReason
|
|
23
|
+
lines.push(`\u23ED\uFE0F ${workspace.skipReason}`);
|
|
23
24
|
return lines.join("\n");
|
|
24
25
|
}
|
|
25
26
|
if (workspace.setVersion !== void 0) {
|
|
26
27
|
lines.push(` Using version override: ${workspace.setVersion}`);
|
|
27
|
-
} else if (workspace.
|
|
28
|
-
lines.push(` Using bump override: ${workspace.
|
|
28
|
+
} else if (workspace.bumpOverride !== void 0) {
|
|
29
|
+
lines.push(` Using bump override: ${workspace.bumpOverride}`);
|
|
29
30
|
}
|
|
30
31
|
if (workspace.releaseType !== void 0) {
|
|
31
32
|
lines.push(dim(`Bumping versions (${workspace.releaseType})...`));
|
|
32
33
|
} else if (workspace.setVersion !== void 0) {
|
|
33
34
|
lines.push(dim(`Bumping versions (version override)...`));
|
|
34
35
|
}
|
|
35
|
-
if (workspace.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
lines.push(`\u{1F4E6} ${workspace.currentVersion} \u2192 ${bold(workspace.newVersion)} (${workspace.releaseType})`);
|
|
40
|
-
}
|
|
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})`);
|
|
41
40
|
}
|
|
42
41
|
formatBumpFiles(lines, workspace, result.dryRun);
|
|
43
42
|
lines.push(dim("Generating changelogs..."));
|
|
44
43
|
formatChangelogFiles(lines, workspace, result.dryRun);
|
|
45
44
|
formatFormatCommand(lines, result);
|
|
46
|
-
lines.push(`\u2705 Release preparation complete
|
|
47
|
-
if (workspace.tag !== void 0) {
|
|
48
|
-
lines.push(` \u{1F3F7}\uFE0F ${bold(workspace.tag)}`);
|
|
49
|
-
}
|
|
45
|
+
lines.push(`\u2705 Release preparation complete.`, ` \u{1F3F7}\uFE0F ${bold(workspace.tag)}`);
|
|
50
46
|
return lines.join("\n");
|
|
51
47
|
}
|
|
52
48
|
function formatMultiWorkspace(result) {
|
|
@@ -54,6 +50,9 @@ function formatMultiWorkspace(result) {
|
|
|
54
50
|
for (const workspace of result.workspaces) {
|
|
55
51
|
formatWorkspaceSection(lines, workspace, result.dryRun);
|
|
56
52
|
}
|
|
53
|
+
if (result.project !== void 0) {
|
|
54
|
+
formatProjectSection(lines, result.project, result.dryRun);
|
|
55
|
+
}
|
|
57
56
|
formatFormatCommand(lines, result);
|
|
58
57
|
formatWarnings(lines, result);
|
|
59
58
|
if (result.tags.length > 0) {
|
|
@@ -68,6 +67,60 @@ function formatMultiWorkspace(result) {
|
|
|
68
67
|
}
|
|
69
68
|
return lines.join("\n");
|
|
70
69
|
}
|
|
70
|
+
function formatProjectSection(lines, project, dryRun) {
|
|
71
|
+
lines.push(`
|
|
72
|
+
${sectionHeader("project")}`);
|
|
73
|
+
const since = project.previousTag === void 0 ? "(no previous release found)" : `since ${project.previousTag}`;
|
|
74
|
+
lines.push(dim(` Found ${project.commitCount} commits ${since}`));
|
|
75
|
+
formatPolicyViolations(lines, project.policyViolations, " ");
|
|
76
|
+
if (project.status === "skipped") {
|
|
77
|
+
lines.push(` \u23ED\uFE0F ${project.skipReason}`);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const { releaseType, currentVersion, newVersion, tag } = project;
|
|
81
|
+
if (project.parsedCommitCount > 0) {
|
|
82
|
+
lines.push(dim(` Parsed ${project.parsedCommitCount} typed commits`));
|
|
83
|
+
}
|
|
84
|
+
if (project.bumpOverride !== void 0) {
|
|
85
|
+
lines.push(` Using bump override: ${project.bumpOverride}`);
|
|
86
|
+
}
|
|
87
|
+
formatProjectUnparseable(lines, project);
|
|
88
|
+
lines.push(
|
|
89
|
+
dim(` Bumping versions (${releaseType})...`),
|
|
90
|
+
` \u{1F4E6} ${currentVersion} \u2192 ${bold(newVersion)} (${releaseType})`
|
|
91
|
+
);
|
|
92
|
+
for (const file of project.bumpedFiles) {
|
|
93
|
+
if (dryRun) {
|
|
94
|
+
lines.push(dim(` [dry-run] Would bump ${file}`));
|
|
95
|
+
} else {
|
|
96
|
+
lines.push(dim(` Bumped ${file}`));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
lines.push(dim(" Generating changelogs..."));
|
|
100
|
+
for (const file of project.changelogFiles) {
|
|
101
|
+
if (dryRun) {
|
|
102
|
+
lines.push(dim(` [dry-run] Would run: npx --yes git-cliff ... --output ${file}`));
|
|
103
|
+
} else {
|
|
104
|
+
lines.push(dim(` Generating changelog: ${file}`));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
lines.push(` \u{1F3F7}\uFE0F ${bold(tag)}`);
|
|
108
|
+
}
|
|
109
|
+
function formatProjectUnparseable(lines, project) {
|
|
110
|
+
const unparseable = project.unparseableCommits;
|
|
111
|
+
if (unparseable === void 0 || unparseable.length === 0) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const count = unparseable.length;
|
|
115
|
+
const isPatchFloor = project.parsedCommitCount === 0;
|
|
116
|
+
const suffix = isPatchFloor ? " (defaulting to patch bump)" : "";
|
|
117
|
+
lines.push(` \u26A0\uFE0F ${count} commit${count === 1 ? "" : "s"} could not be parsed${suffix}`);
|
|
118
|
+
for (const commit of unparseable) {
|
|
119
|
+
const shortHash = commit.hash.slice(0, 7);
|
|
120
|
+
const truncatedMessage = commit.message.length > 72 ? `${commit.message.slice(0, 69)}...` : commit.message;
|
|
121
|
+
lines.push(` \xB7 ${shortHash} ${truncatedMessage}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
71
124
|
function formatWorkspaceSection(lines, workspace, dryRun) {
|
|
72
125
|
if (workspace.name !== void 0) {
|
|
73
126
|
lines.push(`
|
|
@@ -76,35 +129,34 @@ ${sectionHeader(workspace.name)}`);
|
|
|
76
129
|
const since = workspace.previousTag === void 0 ? "(no previous release found)" : `since ${workspace.previousTag}`;
|
|
77
130
|
lines.push(dim(` Found ${workspace.commitCount} commits ${since}`));
|
|
78
131
|
if (workspace.status === "skipped") {
|
|
79
|
-
lines.push(` \u23ED\uFE0F ${workspace.skipReason
|
|
132
|
+
lines.push(` \u23ED\uFE0F ${workspace.skipReason}`);
|
|
80
133
|
return;
|
|
81
134
|
}
|
|
82
135
|
const { propagatedFrom } = workspace;
|
|
83
136
|
const isPropagatedOnly = propagatedFrom !== void 0 && workspace.commitCount === 0;
|
|
84
137
|
formatCommitSummary(lines, workspace, propagatedFrom, isPropagatedOnly);
|
|
85
138
|
formatUnparseableWarning(lines, workspace, " ");
|
|
139
|
+
formatPolicyViolations(lines, workspace.policyViolations, " ");
|
|
86
140
|
formatBumpLabels(lines, workspace, isPropagatedOnly);
|
|
87
141
|
formatVersionLine(lines, workspace, propagatedFrom, isPropagatedOnly);
|
|
88
142
|
formatBumpFiles(lines, workspace, dryRun, " ");
|
|
89
143
|
lines.push(dim(" Generating changelogs..."));
|
|
90
144
|
formatChangelogFiles(lines, workspace, dryRun, " ");
|
|
91
|
-
|
|
92
|
-
lines.push(` \u{1F3F7}\uFE0F ${bold(workspace.tag)}`);
|
|
93
|
-
}
|
|
145
|
+
lines.push(` \u{1F3F7}\uFE0F ${bold(workspace.tag)}`);
|
|
94
146
|
}
|
|
95
147
|
function formatCommitSummary(lines, workspace, propagatedFrom, isPropagatedOnly) {
|
|
96
148
|
if (isPropagatedOnly && propagatedFrom !== void 0) {
|
|
97
149
|
const depNames = propagatedFrom.map((p) => p.packageName).join(", ");
|
|
98
150
|
lines.push(dim(` 0 commits (bumped via dependency: ${depNames})`));
|
|
99
|
-
} else if (workspace.parsedCommitCount !== void 0) {
|
|
151
|
+
} else if (workspace.parsedCommitCount !== void 0 && workspace.parsedCommitCount > 0) {
|
|
100
152
|
lines.push(dim(` Parsed ${workspace.parsedCommitCount} typed commits`));
|
|
101
153
|
}
|
|
102
154
|
}
|
|
103
155
|
function formatBumpLabels(lines, workspace, isPropagatedOnly) {
|
|
104
156
|
if (workspace.setVersion !== void 0) {
|
|
105
157
|
lines.push(` Using version override: ${workspace.setVersion}`);
|
|
106
|
-
} else if (workspace.
|
|
107
|
-
lines.push(` Using bump override: ${workspace.
|
|
158
|
+
} else if (workspace.bumpOverride !== void 0 && !isPropagatedOnly) {
|
|
159
|
+
lines.push(` Using bump override: ${workspace.bumpOverride}`);
|
|
108
160
|
}
|
|
109
161
|
if (workspace.releaseType !== void 0) {
|
|
110
162
|
lines.push(dim(` Bumping versions (${workspace.releaseType})...`));
|
|
@@ -113,9 +165,6 @@ function formatBumpLabels(lines, workspace, isPropagatedOnly) {
|
|
|
113
165
|
}
|
|
114
166
|
}
|
|
115
167
|
function formatVersionLine(lines, workspace, propagatedFrom, isPropagatedOnly) {
|
|
116
|
-
if (workspace.currentVersion === void 0 || workspace.newVersion === void 0) {
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
168
|
if (workspace.setVersion !== void 0) {
|
|
120
169
|
lines.push(` \u{1F4E6} ${workspace.currentVersion} \u2192 ${bold(workspace.newVersion)} (version override)`);
|
|
121
170
|
} else if (workspace.releaseType !== void 0) {
|
|
@@ -156,6 +205,21 @@ function formatUnparseableWarning(lines, workspace, indent = "") {
|
|
|
156
205
|
lines.push(`${indent} \xB7 ${shortHash} ${truncatedMessage}`);
|
|
157
206
|
}
|
|
158
207
|
}
|
|
208
|
+
function formatPolicyViolations(lines, violations, indent = "") {
|
|
209
|
+
if (violations === void 0 || violations.length === 0) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const count = violations.length;
|
|
213
|
+
lines.push(`${indent} \u26A0\uFE0F ${count} policy violation${count === 1 ? "" : "s"}:`);
|
|
214
|
+
for (const violation of violations) {
|
|
215
|
+
const shortHash = violation.commitHash.slice(0, 7);
|
|
216
|
+
const subject = violation.commitSubject;
|
|
217
|
+
const truncatedSubject = subject.length > 72 ? `${subject.slice(0, 69)}...` : subject;
|
|
218
|
+
lines.push(
|
|
219
|
+
`${indent} \xB7 ${shortHash} '${truncatedSubject}' \u2014 type '${violation.type}' at ${violation.surface} surface`
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
159
223
|
function formatPropagationSuffix(propagatedFrom) {
|
|
160
224
|
if (propagatedFrom === void 0 || propagatedFrom.length === 0) {
|
|
161
225
|
return "";
|
|
@@ -10,15 +10,25 @@ async function resolveCommandTags(tags) {
|
|
|
10
10
|
process.exit(1);
|
|
11
11
|
}
|
|
12
12
|
let workspaces;
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
let singleWorkspace;
|
|
14
|
+
try {
|
|
15
|
+
if (discoveredPaths === void 0) {
|
|
16
|
+
singleWorkspace = deriveWorkspaceConfig(".");
|
|
17
|
+
} else {
|
|
15
18
|
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
19
|
}
|
|
20
|
+
} catch (error) {
|
|
21
|
+
console.error(`Error resolving workspaces: ${error instanceof Error ? error.message : String(error)}`);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
let resolvedTags;
|
|
25
|
+
if (workspaces !== void 0) {
|
|
26
|
+
resolvedTags = resolveReleaseTags({ workspaces });
|
|
27
|
+
} else if (singleWorkspace !== void 0) {
|
|
28
|
+
resolvedTags = resolveReleaseTags({ singleWorkspace });
|
|
29
|
+
} else {
|
|
30
|
+
throw new Error("resolveCommandTags: invariant violated \u2014 neither workspaces nor singleWorkspace was derived");
|
|
20
31
|
}
|
|
21
|
-
let resolvedTags = resolveReleaseTags(workspaces);
|
|
22
32
|
if (resolvedTags.length === 0) {
|
|
23
33
|
console.error("Error: No release tags found on HEAD. Create tags with `release-kit tag` first.");
|
|
24
34
|
process.exit(1);
|
|
@@ -3,5 +3,12 @@ export interface ResolvedTag {
|
|
|
3
3
|
tag: string;
|
|
4
4
|
dir: string;
|
|
5
5
|
workspacePath: string;
|
|
6
|
+
isPublishable: boolean;
|
|
6
7
|
}
|
|
7
|
-
|
|
8
|
+
type ResolveReleaseTagsArgs = {
|
|
9
|
+
workspaces: readonly WorkspaceConfig[];
|
|
10
|
+
} | {
|
|
11
|
+
singleWorkspace: WorkspaceConfig;
|
|
12
|
+
};
|
|
13
|
+
export declare function resolveReleaseTags(args?: ResolveReleaseTagsArgs): ResolvedTag[];
|
|
14
|
+
export {};
|
|
@@ -1,23 +1,27 @@
|
|
|
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(
|
|
4
|
+
function resolveReleaseTags(args) {
|
|
5
5
|
const output = execFileSync("git", ["tag", "--points-at", "HEAD"], { encoding: "utf8" });
|
|
6
6
|
const tags = output.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
|
|
7
|
-
if (
|
|
7
|
+
if (args === void 0) {
|
|
8
8
|
return resolveSinglePackageTags(tags);
|
|
9
9
|
}
|
|
10
|
-
|
|
10
|
+
if ("workspaces" in args) {
|
|
11
|
+
return resolveMonorepoTags(tags, args.workspaces);
|
|
12
|
+
}
|
|
13
|
+
return resolveSinglePackageTags(tags, args.singleWorkspace);
|
|
11
14
|
}
|
|
12
|
-
function resolveSinglePackageTags(tags) {
|
|
15
|
+
function resolveSinglePackageTags(tags, singleWorkspace) {
|
|
13
16
|
const matched = tags.filter((tag) => VERSION_PATTERN.test(tag));
|
|
17
|
+
const isPublishable = singleWorkspace?.isPublishable ?? true;
|
|
14
18
|
if (matched.length > 1) {
|
|
15
19
|
console.warn(
|
|
16
20
|
`Warning: Multiple version tags found on HEAD: ${matched.join(", ")}. Publishing the same package multiple times is almost certainly unintended. Using only the first tag.`
|
|
17
21
|
);
|
|
18
|
-
return matched.slice(0, 1).map((tag) => ({ tag, dir: ".", workspacePath: "." }));
|
|
22
|
+
return matched.slice(0, 1).map((tag) => ({ tag, dir: ".", workspacePath: ".", isPublishable }));
|
|
19
23
|
}
|
|
20
|
-
return matched.map((tag) => ({ tag, dir: ".", workspacePath: "." }));
|
|
24
|
+
return matched.map((tag) => ({ tag, dir: ".", workspacePath: ".", isPublishable }));
|
|
21
25
|
}
|
|
22
26
|
function resolveMonorepoTags(tags, workspaces) {
|
|
23
27
|
const sortedWorkspaces = [...workspaces].sort((a, b) => b.tagPrefix.length - a.tagPrefix.length);
|
|
@@ -25,7 +29,7 @@ function resolveMonorepoTags(tags, workspaces) {
|
|
|
25
29
|
for (const tag of tags) {
|
|
26
30
|
const match = findMatchingWorkspace(tag, sortedWorkspaces);
|
|
27
31
|
if (match !== void 0) {
|
|
28
|
-
resolved.push({ tag, dir: match.dir, workspacePath: match.workspacePath });
|
|
32
|
+
resolved.push({ tag, dir: match.dir, workspacePath: match.workspacePath, isPublishable: match.isPublishable });
|
|
29
33
|
}
|
|
30
34
|
}
|
|
31
35
|
return resolved;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { copyFileSync, mkdtempSync, rmSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
function runGitCliff(cliffConfigPath, cliffArgs, stdio) {
|
|
6
|
+
let configPath = cliffConfigPath;
|
|
7
|
+
let tempDir;
|
|
8
|
+
try {
|
|
9
|
+
if (cliffConfigPath.endsWith(".template")) {
|
|
10
|
+
tempDir = mkdtempSync(join(tmpdir(), "cliff-"));
|
|
11
|
+
configPath = join(tempDir, "cliff.toml");
|
|
12
|
+
copyFileSync(cliffConfigPath, configPath);
|
|
13
|
+
}
|
|
14
|
+
return execFileSync("npx", ["--prefer-offline", "--yes", "git-cliff", "--config", configPath, ...cliffArgs], {
|
|
15
|
+
encoding: "utf8",
|
|
16
|
+
stdio,
|
|
17
|
+
env: { ...process.env, npm_config_progress: "false" }
|
|
18
|
+
});
|
|
19
|
+
} finally {
|
|
20
|
+
if (tempDir !== void 0) {
|
|
21
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export {
|
|
26
|
+
runGitCliff
|
|
27
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function stripEmojiPrefix(value: string): string;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface SyncResult {
|
|
2
|
+
exitCode: 0 | 2 | 3;
|
|
3
|
+
message: string;
|
|
4
|
+
}
|
|
5
|
+
export interface SyncWorkTypesDependencies {
|
|
6
|
+
localPath?: string;
|
|
7
|
+
fetch?: typeof globalThis.fetch;
|
|
8
|
+
upstreamUrl?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function syncWorkTypes(dependencies?: SyncWorkTypesDependencies): Promise<SyncResult>;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { UPSTREAM_WORK_TYPES_URL } from "./checkWorkTypesDrift.js";
|
|
5
|
+
import { isRecord } from "./typeGuards.js";
|
|
6
|
+
import { errorMessage, hasExpectedTopLevelShape } from "./workTypesUtils.js";
|
|
7
|
+
function resolveDefaultLocalPath() {
|
|
8
|
+
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
return resolve(moduleDir, "work-types.json");
|
|
10
|
+
}
|
|
11
|
+
function extractLocalSchemaUrl(content) {
|
|
12
|
+
let parsed;
|
|
13
|
+
try {
|
|
14
|
+
parsed = JSON.parse(content);
|
|
15
|
+
} catch {
|
|
16
|
+
return void 0;
|
|
17
|
+
}
|
|
18
|
+
if (!isRecord(parsed)) {
|
|
19
|
+
return void 0;
|
|
20
|
+
}
|
|
21
|
+
const schema = parsed.$schema;
|
|
22
|
+
return typeof schema === "string" ? schema : void 0;
|
|
23
|
+
}
|
|
24
|
+
async function syncWorkTypes(dependencies = {}) {
|
|
25
|
+
const localPath = dependencies.localPath ?? resolveDefaultLocalPath();
|
|
26
|
+
const fetcher = dependencies.fetch ?? globalThis.fetch;
|
|
27
|
+
const url = dependencies.upstreamUrl ?? UPSTREAM_WORK_TYPES_URL;
|
|
28
|
+
let response;
|
|
29
|
+
try {
|
|
30
|
+
response = await fetcher(url);
|
|
31
|
+
} catch (error) {
|
|
32
|
+
return {
|
|
33
|
+
exitCode: 2,
|
|
34
|
+
message: `Network error fetching upstream work-types.json: ${errorMessage(error)}`
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (!response.ok) {
|
|
38
|
+
return {
|
|
39
|
+
exitCode: 2,
|
|
40
|
+
message: `Failed to fetch upstream work-types.json: HTTP ${response.status} ${response.statusText}`
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const upstreamText = await response.text();
|
|
44
|
+
let upstreamJson;
|
|
45
|
+
try {
|
|
46
|
+
upstreamJson = JSON.parse(upstreamText);
|
|
47
|
+
} catch (error) {
|
|
48
|
+
return {
|
|
49
|
+
exitCode: 3,
|
|
50
|
+
message: `Upstream work-types.json is not valid JSON: ${errorMessage(error)}`
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
if (!hasExpectedTopLevelShape(upstreamJson)) {
|
|
54
|
+
return {
|
|
55
|
+
exitCode: 3,
|
|
56
|
+
message: "Upstream work-types.json does not match the expected schema shape (missing `tiers` or `types`)."
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
let priorContent;
|
|
60
|
+
try {
|
|
61
|
+
priorContent = readFileSync(localPath, "utf8");
|
|
62
|
+
} catch {
|
|
63
|
+
priorContent = void 0;
|
|
64
|
+
}
|
|
65
|
+
const localSchemaUrl = priorContent !== void 0 ? extractLocalSchemaUrl(priorContent) : void 0;
|
|
66
|
+
const outputJson = localSchemaUrl !== void 0 ? { $schema: localSchemaUrl, ...upstreamJson } : upstreamJson;
|
|
67
|
+
const formatted = `${JSON.stringify(outputJson, null, 2)}
|
|
68
|
+
`;
|
|
69
|
+
if (priorContent === formatted) {
|
|
70
|
+
return {
|
|
71
|
+
exitCode: 0,
|
|
72
|
+
message: `Local work-types.json already matches upstream (${localPath}).`
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
writeFileSync(localPath, formatted, "utf8");
|
|
77
|
+
} catch (error) {
|
|
78
|
+
return {
|
|
79
|
+
exitCode: 2,
|
|
80
|
+
message: `Failed to write ${localPath}: ${errorMessage(error)}`
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
exitCode: 0,
|
|
85
|
+
message: `Synced work-types.json from ${url} \u2192 ${localPath}.`
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
export {
|
|
89
|
+
syncWorkTypes
|
|
90
|
+
};
|