@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
package/dist/esm/types.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export type ChangelogAudience = 'all' | 'dev';
|
|
|
3
3
|
export interface ChangelogItem {
|
|
4
4
|
description: string;
|
|
5
5
|
body?: string;
|
|
6
|
+
breaking?: boolean;
|
|
6
7
|
}
|
|
7
8
|
export interface ChangelogSection {
|
|
8
9
|
title: string;
|
|
@@ -22,6 +23,12 @@ export interface ChangelogJsonConfig {
|
|
|
22
23
|
export interface ReleaseNotesConfig {
|
|
23
24
|
shouldInjectIntoReadme: boolean;
|
|
24
25
|
}
|
|
26
|
+
export interface ProjectConfig {
|
|
27
|
+
tagPrefix?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface ResolvedProjectConfig {
|
|
30
|
+
tagPrefix: string;
|
|
31
|
+
}
|
|
25
32
|
export interface PropagationSource {
|
|
26
33
|
packageName: string;
|
|
27
34
|
newVersion: string;
|
|
@@ -31,24 +38,68 @@ export interface BumpResult {
|
|
|
31
38
|
newVersion: string;
|
|
32
39
|
files: string[];
|
|
33
40
|
}
|
|
34
|
-
export interface
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
41
|
+
export interface PolicyViolation {
|
|
42
|
+
commitHash: string;
|
|
43
|
+
commitSubject: string;
|
|
44
|
+
type: string;
|
|
45
|
+
surface: 'prefix' | 'body';
|
|
46
|
+
}
|
|
47
|
+
export interface ReleasedWorkspaceResult {
|
|
48
|
+
status: 'released';
|
|
49
|
+
name?: string;
|
|
50
|
+
previousTag?: string;
|
|
38
51
|
commitCount: number;
|
|
39
|
-
parsedCommitCount?: number
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
52
|
+
parsedCommitCount?: number;
|
|
53
|
+
unparseableCommits?: Commit[];
|
|
54
|
+
policyViolations?: PolicyViolation[];
|
|
55
|
+
releaseType?: ReleaseType;
|
|
56
|
+
currentVersion: string;
|
|
57
|
+
newVersion: string;
|
|
58
|
+
tag: string;
|
|
44
59
|
bumpedFiles: string[];
|
|
45
60
|
changelogFiles: string[];
|
|
46
|
-
commits?: Commit[]
|
|
47
|
-
|
|
48
|
-
propagatedFrom?: PropagationSource[]
|
|
49
|
-
|
|
50
|
-
|
|
61
|
+
commits?: Commit[];
|
|
62
|
+
bumpOverride?: ReleaseType;
|
|
63
|
+
propagatedFrom?: PropagationSource[];
|
|
64
|
+
setVersion?: string;
|
|
65
|
+
}
|
|
66
|
+
export interface SkippedWorkspaceResult {
|
|
67
|
+
status: 'skipped';
|
|
68
|
+
name?: string;
|
|
69
|
+
previousTag?: string;
|
|
70
|
+
commitCount: number;
|
|
71
|
+
parsedCommitCount?: number;
|
|
72
|
+
unparseableCommits?: Commit[];
|
|
73
|
+
policyViolations?: PolicyViolation[];
|
|
74
|
+
skipReason: string;
|
|
75
|
+
}
|
|
76
|
+
export type WorkspacePrepareResult = ReleasedWorkspaceResult | SkippedWorkspaceResult;
|
|
77
|
+
export interface ReleasedProjectResult {
|
|
78
|
+
status: 'released';
|
|
79
|
+
previousTag?: string;
|
|
80
|
+
commitCount: number;
|
|
81
|
+
parsedCommitCount: number;
|
|
82
|
+
unparseableCommits?: Commit[];
|
|
83
|
+
policyViolations?: PolicyViolation[];
|
|
84
|
+
releaseType: ReleaseType;
|
|
85
|
+
currentVersion: string;
|
|
86
|
+
newVersion: string;
|
|
87
|
+
tag: string;
|
|
88
|
+
bumpedFiles: string[];
|
|
89
|
+
changelogFiles: string[];
|
|
90
|
+
commits: Commit[];
|
|
91
|
+
bumpOverride?: ReleaseType;
|
|
92
|
+
}
|
|
93
|
+
export interface SkippedProjectResult {
|
|
94
|
+
status: 'skipped';
|
|
95
|
+
previousTag?: string;
|
|
96
|
+
commitCount: number;
|
|
97
|
+
parsedCommitCount: number;
|
|
98
|
+
unparseableCommits?: Commit[];
|
|
99
|
+
policyViolations?: PolicyViolation[];
|
|
100
|
+
skipReason: string;
|
|
51
101
|
}
|
|
102
|
+
export type ProjectPrepareResult = ReleasedProjectResult | SkippedProjectResult;
|
|
52
103
|
export interface PrepareResult {
|
|
53
104
|
workspaces: WorkspacePrepareResult[];
|
|
54
105
|
tags: string[];
|
|
@@ -59,6 +110,7 @@ export interface PrepareResult {
|
|
|
59
110
|
} | undefined;
|
|
60
111
|
dryRun: boolean;
|
|
61
112
|
warnings?: string[] | undefined;
|
|
113
|
+
project?: ProjectPrepareResult | undefined;
|
|
62
114
|
}
|
|
63
115
|
export interface WorkTypeConfig {
|
|
64
116
|
header: string;
|
|
@@ -72,12 +124,14 @@ export interface ReleaseKitConfig {
|
|
|
72
124
|
workspaces?: WorkspaceOverride[];
|
|
73
125
|
versionPatterns?: VersionPatterns;
|
|
74
126
|
workTypes?: Record<string, WorkTypeConfig>;
|
|
127
|
+
breakingPolicies?: Record<string, 'forbidden' | 'optional' | 'required'>;
|
|
75
128
|
formatCommand?: string;
|
|
76
129
|
cliffConfigPath?: string;
|
|
77
130
|
scopeAliases?: Record<string, string>;
|
|
78
131
|
changelogJson?: Partial<ChangelogJsonConfig>;
|
|
79
132
|
releaseNotes?: Partial<ReleaseNotesConfig>;
|
|
80
133
|
retiredPackages?: RetiredPackage[];
|
|
134
|
+
project?: ProjectConfig;
|
|
81
135
|
}
|
|
82
136
|
export interface LegacyIdentity {
|
|
83
137
|
name: string;
|
|
@@ -110,6 +164,7 @@ export interface WorkspaceConfig {
|
|
|
110
164
|
name: string;
|
|
111
165
|
tagPrefix: string;
|
|
112
166
|
workspacePath: string;
|
|
167
|
+
isPublishable: boolean;
|
|
113
168
|
packageFiles: string[];
|
|
114
169
|
changelogPaths: string[];
|
|
115
170
|
paths: string[];
|
|
@@ -119,11 +174,13 @@ export interface MonorepoReleaseConfig {
|
|
|
119
174
|
workspaces: WorkspaceConfig[];
|
|
120
175
|
workTypes?: Record<string, WorkTypeConfig>;
|
|
121
176
|
versionPatterns?: VersionPatterns;
|
|
177
|
+
breakingPolicies?: Record<string, 'forbidden' | 'optional' | 'required'>;
|
|
122
178
|
formatCommand?: string;
|
|
123
179
|
cliffConfigPath?: string;
|
|
124
180
|
scopeAliases?: Record<string, string>;
|
|
125
181
|
changelogJson: ChangelogJsonConfig;
|
|
126
182
|
releaseNotes: ReleaseNotesConfig;
|
|
183
|
+
project?: ResolvedProjectConfig;
|
|
127
184
|
}
|
|
128
185
|
export interface ReleaseConfig {
|
|
129
186
|
tagPrefix: string;
|
|
@@ -131,6 +188,7 @@ export interface ReleaseConfig {
|
|
|
131
188
|
changelogPaths: string[];
|
|
132
189
|
workTypes?: Record<string, WorkTypeConfig>;
|
|
133
190
|
versionPatterns?: VersionPatterns;
|
|
191
|
+
breakingPolicies?: Record<string, 'forbidden' | 'optional' | 'required'>;
|
|
134
192
|
formatCommand?: string;
|
|
135
193
|
cliffConfigPath?: string;
|
|
136
194
|
scopeAliases?: Record<string, string>;
|
|
@@ -9,6 +9,7 @@ function validateConfig(raw) {
|
|
|
9
9
|
"changelogJson",
|
|
10
10
|
"cliffConfigPath",
|
|
11
11
|
"formatCommand",
|
|
12
|
+
"project",
|
|
12
13
|
"releaseNotes",
|
|
13
14
|
"retiredPackages",
|
|
14
15
|
"scopeAliases",
|
|
@@ -30,6 +31,7 @@ function validateConfig(raw) {
|
|
|
30
31
|
validateStringField("cliffConfigPath", raw.cliffConfigPath, config, errors);
|
|
31
32
|
validateScopeAliases(raw.scopeAliases, config, errors);
|
|
32
33
|
validateRetiredPackages(raw.retiredPackages, config, errors);
|
|
34
|
+
validateProjectConfig(raw.project, config, errors);
|
|
33
35
|
const warnings = [];
|
|
34
36
|
const changelogJsonEnabled = config.changelogJson?.enabled ?? true;
|
|
35
37
|
if (!changelogJsonEnabled && config.releaseNotes?.shouldInjectIntoReadme) {
|
|
@@ -75,6 +77,30 @@ function validateChangelogJson(value, config, errors) {
|
|
|
75
77
|
}
|
|
76
78
|
config.changelogJson = result;
|
|
77
79
|
}
|
|
80
|
+
function validateProjectConfig(value, config, errors) {
|
|
81
|
+
if (value === void 0) return;
|
|
82
|
+
if (!isRecord(value)) {
|
|
83
|
+
errors.push("'project' must be an object");
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const knownProjectFields = /* @__PURE__ */ new Set(["tagPrefix"]);
|
|
87
|
+
for (const key of Object.keys(value)) {
|
|
88
|
+
if (!knownProjectFields.has(key)) {
|
|
89
|
+
errors.push(`project: unknown field '${key}'`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const result = {};
|
|
93
|
+
if (value.tagPrefix !== void 0) {
|
|
94
|
+
if (typeof value.tagPrefix !== "string") {
|
|
95
|
+
errors.push("project.tagPrefix: must be a string");
|
|
96
|
+
} else if (value.tagPrefix === "") {
|
|
97
|
+
errors.push("project.tagPrefix: must be a non-empty string");
|
|
98
|
+
} else {
|
|
99
|
+
result.tagPrefix = value.tagPrefix;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
config.project = result;
|
|
103
|
+
}
|
|
78
104
|
function validateReleaseNotes(value, config, errors) {
|
|
79
105
|
if (value === void 0) return;
|
|
80
106
|
if (!isRecord(value)) {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { DependencyGraph } from './buildDependencyGraph.ts';
|
|
2
|
+
import type { WorkspaceConfig } from './types.ts';
|
|
3
|
+
export interface CommitsProbeResult {
|
|
4
|
+
has: boolean;
|
|
5
|
+
tag: string | undefined;
|
|
6
|
+
}
|
|
7
|
+
export interface StrandedDependentViolation {
|
|
8
|
+
dir: string;
|
|
9
|
+
downstreamOf: string;
|
|
10
|
+
tag: string | undefined;
|
|
11
|
+
}
|
|
12
|
+
type CommitsProbe = (workspace: WorkspaceConfig) => CommitsProbeResult;
|
|
13
|
+
export declare function validateOnlyExcludesStrandedDependents(workspaces: readonly WorkspaceConfig[], only: readonly string[], graph: DependencyGraph, hasCommits: CommitsProbe): StrandedDependentViolation[] | undefined;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
function validateOnlyExcludesStrandedDependents(workspaces, only, graph, hasCommits) {
|
|
2
|
+
const probeCommits = memoizeCommitsProbe(hasCommits);
|
|
3
|
+
const workspaceByDir = new Map(workspaces.map((w) => [w.dir, w]));
|
|
4
|
+
const released = computeReleasedSet(only, workspaceByDir, graph, probeCommits);
|
|
5
|
+
const violations = collectStrandedViolations(released, new Set(only), graph, probeCommits);
|
|
6
|
+
if (violations.length === 0) return void 0;
|
|
7
|
+
violations.sort((a, b) => a.dir.localeCompare(b.dir));
|
|
8
|
+
return violations;
|
|
9
|
+
}
|
|
10
|
+
function memoizeCommitsProbe(probe) {
|
|
11
|
+
const cache = /* @__PURE__ */ new Map();
|
|
12
|
+
return (workspace) => {
|
|
13
|
+
const cached = cache.get(workspace.dir);
|
|
14
|
+
if (cached !== void 0) return cached;
|
|
15
|
+
const result = probe(workspace);
|
|
16
|
+
cache.set(workspace.dir, result);
|
|
17
|
+
return result;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function computeReleasedSet(only, workspaceByDir, graph, probeCommits) {
|
|
21
|
+
const released = /* @__PURE__ */ new Set();
|
|
22
|
+
for (const dir of only) {
|
|
23
|
+
const workspace = workspaceByDir.get(dir);
|
|
24
|
+
if (workspace !== void 0 && probeCommits(workspace).has) {
|
|
25
|
+
released.add(dir);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
let changed = true;
|
|
29
|
+
while (changed) {
|
|
30
|
+
changed = false;
|
|
31
|
+
for (const dir of only) {
|
|
32
|
+
if (released.has(dir)) continue;
|
|
33
|
+
if (hasDependencyIn(dir, graph, released)) {
|
|
34
|
+
released.add(dir);
|
|
35
|
+
changed = true;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return released;
|
|
40
|
+
}
|
|
41
|
+
function hasDependencyIn(dir, graph, released) {
|
|
42
|
+
const forwardDeps = graph.dependenciesOf.get(dir);
|
|
43
|
+
if (forwardDeps === void 0) return false;
|
|
44
|
+
for (const depPackageName of forwardDeps) {
|
|
45
|
+
const depDir = graph.packageNameToDir.get(depPackageName);
|
|
46
|
+
if (depDir !== void 0 && released.has(depDir)) return true;
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
function collectStrandedViolations(released, onlySet, graph, probeCommits) {
|
|
51
|
+
const violations = [];
|
|
52
|
+
const violationDirs = /* @__PURE__ */ new Set();
|
|
53
|
+
const visited = /* @__PURE__ */ new Set();
|
|
54
|
+
const queue = [];
|
|
55
|
+
for (const dir of released) {
|
|
56
|
+
const packageName = graph.dirToPackageName.get(dir);
|
|
57
|
+
if (packageName !== void 0) {
|
|
58
|
+
queue.push({ packageName, attributionRoot: dir });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
while (queue.length > 0) {
|
|
62
|
+
const item = queue.shift();
|
|
63
|
+
if (item === void 0) break;
|
|
64
|
+
if (visited.has(item.packageName)) continue;
|
|
65
|
+
visited.add(item.packageName);
|
|
66
|
+
const dependents = graph.dependentsOf.get(item.packageName);
|
|
67
|
+
if (dependents === void 0) continue;
|
|
68
|
+
for (const dependent of dependents) {
|
|
69
|
+
visitDependent(dependent, item.attributionRoot, {
|
|
70
|
+
released,
|
|
71
|
+
onlySet,
|
|
72
|
+
graph,
|
|
73
|
+
probeCommits,
|
|
74
|
+
queue,
|
|
75
|
+
violations,
|
|
76
|
+
violationDirs
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return violations;
|
|
81
|
+
}
|
|
82
|
+
function visitDependent(dependent, attributionRoot, ctx) {
|
|
83
|
+
if (ctx.onlySet.has(dependent.dir)) {
|
|
84
|
+
if (ctx.released.has(dependent.dir)) {
|
|
85
|
+
enqueueDependent(dependent, ctx);
|
|
86
|
+
}
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const probe = ctx.probeCommits(dependent);
|
|
90
|
+
if (!probe.has) return;
|
|
91
|
+
if (!ctx.violationDirs.has(dependent.dir)) {
|
|
92
|
+
ctx.violationDirs.add(dependent.dir);
|
|
93
|
+
ctx.violations.push({
|
|
94
|
+
dir: dependent.dir,
|
|
95
|
+
downstreamOf: attributionRoot,
|
|
96
|
+
tag: probe.tag
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
enqueueDependent(dependent, ctx);
|
|
100
|
+
}
|
|
101
|
+
function enqueueDependent(dependent, ctx) {
|
|
102
|
+
const packageName = ctx.graph.dirToPackageName.get(dependent.dir);
|
|
103
|
+
if (packageName !== void 0) {
|
|
104
|
+
ctx.queue.push({ packageName, attributionRoot: dependent.dir });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
export {
|
|
108
|
+
validateOnlyExcludesStrandedDependents
|
|
109
|
+
};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./work-types.schema.json",
|
|
3
|
+
"tiers": ["Public", "Internal", "Process"],
|
|
4
|
+
"types": [
|
|
5
|
+
{
|
|
6
|
+
"tier": "Public",
|
|
7
|
+
"key": "feat",
|
|
8
|
+
"aliases": ["feature"],
|
|
9
|
+
"emoji": "🎉",
|
|
10
|
+
"label": "Features",
|
|
11
|
+
"breakingPolicy": "optional"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"tier": "Public",
|
|
15
|
+
"key": "drop",
|
|
16
|
+
"aliases": [],
|
|
17
|
+
"emoji": "🪦",
|
|
18
|
+
"label": "Removed",
|
|
19
|
+
"breakingPolicy": "required"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"tier": "Public",
|
|
23
|
+
"key": "deprecate",
|
|
24
|
+
"aliases": [],
|
|
25
|
+
"emoji": "🗑️",
|
|
26
|
+
"label": "Deprecated",
|
|
27
|
+
"breakingPolicy": "forbidden"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"tier": "Public",
|
|
31
|
+
"key": "fix",
|
|
32
|
+
"aliases": ["bugfix"],
|
|
33
|
+
"emoji": "🐛",
|
|
34
|
+
"label": "Bug fixes",
|
|
35
|
+
"breakingPolicy": "forbidden"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"tier": "Public",
|
|
39
|
+
"key": "sec",
|
|
40
|
+
"aliases": ["security"],
|
|
41
|
+
"emoji": "🔒",
|
|
42
|
+
"label": "Security",
|
|
43
|
+
"breakingPolicy": "optional"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"tier": "Public",
|
|
47
|
+
"key": "perf",
|
|
48
|
+
"aliases": ["performance"],
|
|
49
|
+
"emoji": "⚡",
|
|
50
|
+
"label": "Performance",
|
|
51
|
+
"breakingPolicy": "forbidden"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"tier": "Internal",
|
|
55
|
+
"key": "internal",
|
|
56
|
+
"aliases": ["utility"],
|
|
57
|
+
"emoji": "🏗️",
|
|
58
|
+
"label": "Internal features",
|
|
59
|
+
"breakingPolicy": "forbidden"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"tier": "Internal",
|
|
63
|
+
"key": "refactor",
|
|
64
|
+
"aliases": [],
|
|
65
|
+
"emoji": "♻️",
|
|
66
|
+
"label": "Refactoring",
|
|
67
|
+
"breakingPolicy": "forbidden"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"tier": "Internal",
|
|
71
|
+
"key": "tests",
|
|
72
|
+
"aliases": ["test"],
|
|
73
|
+
"emoji": "🧪",
|
|
74
|
+
"label": "Tests",
|
|
75
|
+
"breakingPolicy": "forbidden"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"tier": "Process",
|
|
79
|
+
"key": "tooling",
|
|
80
|
+
"aliases": [],
|
|
81
|
+
"emoji": "⚙️",
|
|
82
|
+
"label": "Tooling",
|
|
83
|
+
"breakingPolicy": "forbidden"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"tier": "Process",
|
|
87
|
+
"key": "ci",
|
|
88
|
+
"aliases": [],
|
|
89
|
+
"emoji": "👷",
|
|
90
|
+
"label": "CI",
|
|
91
|
+
"breakingPolicy": "forbidden"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"tier": "Process",
|
|
95
|
+
"key": "deps",
|
|
96
|
+
"aliases": ["dep"],
|
|
97
|
+
"emoji": "📦",
|
|
98
|
+
"label": "Dependencies",
|
|
99
|
+
"breakingPolicy": "forbidden"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"tier": "Process",
|
|
103
|
+
"key": "ai",
|
|
104
|
+
"aliases": [],
|
|
105
|
+
"emoji": "🤖",
|
|
106
|
+
"label": "Agentic support",
|
|
107
|
+
"breakingPolicy": "forbidden"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"tier": "Process",
|
|
111
|
+
"key": "docs",
|
|
112
|
+
"aliases": ["doc"],
|
|
113
|
+
"emoji": "📚",
|
|
114
|
+
"label": "Documentation",
|
|
115
|
+
"breakingPolicy": "forbidden"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"tier": "Process",
|
|
119
|
+
"key": "fmt",
|
|
120
|
+
"aliases": [],
|
|
121
|
+
"emoji": "🎨",
|
|
122
|
+
"label": "Formatting",
|
|
123
|
+
"breakingPolicy": "forbidden",
|
|
124
|
+
"excludedFromChangelog": true
|
|
125
|
+
}
|
|
126
|
+
]
|
|
127
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://github.com/williamthorsen/node-monorepo-tools/raw/main/packages/release-kit/src/work-types.schema.json",
|
|
4
|
+
"title": "Work types",
|
|
5
|
+
"description": "Canonical taxonomy of commit work types used by release-kit. Drives section ordering, audience classification, and the `!` (breaking) policy. The structured single-source-of-truth for everything that previously lived in `DEFAULT_WORK_TYPES` and `cliff.toml.template` group definitions.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["tiers", "types"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"$schema": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"description": "Optional reference to this schema for IDE validation."
|
|
13
|
+
},
|
|
14
|
+
"tiers": {
|
|
15
|
+
"type": "array",
|
|
16
|
+
"description": "Render tiers in canonical order. Each entry's `tier` field must reference one of these names.",
|
|
17
|
+
"items": { "type": "string", "minLength": 1 },
|
|
18
|
+
"minItems": 1,
|
|
19
|
+
"uniqueItems": true
|
|
20
|
+
},
|
|
21
|
+
"types": {
|
|
22
|
+
"type": "array",
|
|
23
|
+
"description": "Work-type entries in canonical render order (tier order, then row order within tier).",
|
|
24
|
+
"items": { "$ref": "#/definitions/workType" },
|
|
25
|
+
"minItems": 1
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"definitions": {
|
|
29
|
+
"workType": {
|
|
30
|
+
"type": "object",
|
|
31
|
+
"required": ["tier", "key", "aliases", "emoji", "label", "breakingPolicy"],
|
|
32
|
+
"additionalProperties": false,
|
|
33
|
+
"properties": {
|
|
34
|
+
"tier": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"description": "Render tier this entry belongs to. Must match one of the names in the top-level `tiers` array."
|
|
37
|
+
},
|
|
38
|
+
"key": {
|
|
39
|
+
"type": "string",
|
|
40
|
+
"description": "Canonical work-type identifier (e.g. `feat`, `fix`). Must be globally unique across all entries.",
|
|
41
|
+
"minLength": 1,
|
|
42
|
+
"pattern": "^[a-z][a-z0-9]*$"
|
|
43
|
+
},
|
|
44
|
+
"aliases": {
|
|
45
|
+
"type": "array",
|
|
46
|
+
"description": "Alternate identifiers that resolve to this work type. Must be globally unique and not collide with any `key`.",
|
|
47
|
+
"items": { "type": "string", "minLength": 1, "pattern": "^[a-z][a-z0-9]*$" },
|
|
48
|
+
"uniqueItems": true
|
|
49
|
+
},
|
|
50
|
+
"emoji": {
|
|
51
|
+
"type": "string",
|
|
52
|
+
"description": "Decorative emoji rendered as the prefix of the section heading.",
|
|
53
|
+
"minLength": 1
|
|
54
|
+
},
|
|
55
|
+
"label": {
|
|
56
|
+
"type": "string",
|
|
57
|
+
"description": "Human-readable section label (rendered as `${emoji} ${label}` in changelogs and release notes).",
|
|
58
|
+
"minLength": 1
|
|
59
|
+
},
|
|
60
|
+
"breakingPolicy": {
|
|
61
|
+
"type": "string",
|
|
62
|
+
"description": "Policy governing the `!` (breaking) marker for this type. `forbidden`: `!` is a policy violation. `optional`: `!` is allowed. `required`: bare type is a policy violation; only the `!` form is accepted.",
|
|
63
|
+
"enum": ["forbidden", "optional", "required"]
|
|
64
|
+
},
|
|
65
|
+
"excludedFromChangelog": {
|
|
66
|
+
"type": "boolean",
|
|
67
|
+
"description": "When `true`, this type's commits never appear in any changelog or release note. Currently only `fmt` sets this. Default: `false`.",
|
|
68
|
+
"default": false
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface WorkTypeEntry {
|
|
2
|
+
tier: string;
|
|
3
|
+
key: string;
|
|
4
|
+
aliases: string[];
|
|
5
|
+
emoji: string;
|
|
6
|
+
label: string;
|
|
7
|
+
breakingPolicy: 'forbidden' | 'optional' | 'required';
|
|
8
|
+
excludedFromChangelog?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface WorkTypesData {
|
|
11
|
+
tiers: string[];
|
|
12
|
+
types: WorkTypeEntry[];
|
|
13
|
+
}
|
|
14
|
+
export declare const WORK_TYPES_DATA: WorkTypesData;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const WORK_TYPES_DATA = {
|
|
2
|
+
tiers: ["Public", "Internal", "Process"],
|
|
3
|
+
types: [
|
|
4
|
+
{ tier: "Public", key: "feat", aliases: ["feature"], emoji: "\u{1F389}", label: "Features", breakingPolicy: "optional" },
|
|
5
|
+
{ tier: "Public", key: "drop", aliases: [], emoji: "\u{1FAA6}", label: "Removed", breakingPolicy: "required" },
|
|
6
|
+
{ tier: "Public", key: "deprecate", aliases: [], emoji: "\u{1F5D1}\uFE0F", label: "Deprecated", breakingPolicy: "forbidden" },
|
|
7
|
+
{ tier: "Public", key: "fix", aliases: ["bugfix"], emoji: "\u{1F41B}", label: "Bug fixes", breakingPolicy: "forbidden" },
|
|
8
|
+
{ tier: "Public", key: "sec", aliases: ["security"], emoji: "\u{1F512}", label: "Security", breakingPolicy: "optional" },
|
|
9
|
+
{
|
|
10
|
+
tier: "Public",
|
|
11
|
+
key: "perf",
|
|
12
|
+
aliases: ["performance"],
|
|
13
|
+
emoji: "\u26A1",
|
|
14
|
+
label: "Performance",
|
|
15
|
+
breakingPolicy: "forbidden"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
tier: "Internal",
|
|
19
|
+
key: "internal",
|
|
20
|
+
aliases: ["utility"],
|
|
21
|
+
emoji: "\u{1F3D7}\uFE0F",
|
|
22
|
+
label: "Internal features",
|
|
23
|
+
breakingPolicy: "forbidden"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
tier: "Internal",
|
|
27
|
+
key: "refactor",
|
|
28
|
+
aliases: [],
|
|
29
|
+
emoji: "\u267B\uFE0F",
|
|
30
|
+
label: "Refactoring",
|
|
31
|
+
breakingPolicy: "forbidden"
|
|
32
|
+
},
|
|
33
|
+
{ tier: "Internal", key: "tests", aliases: ["test"], emoji: "\u{1F9EA}", label: "Tests", breakingPolicy: "forbidden" },
|
|
34
|
+
{ tier: "Process", key: "tooling", aliases: [], emoji: "\u2699\uFE0F", label: "Tooling", breakingPolicy: "forbidden" },
|
|
35
|
+
{ tier: "Process", key: "ci", aliases: [], emoji: "\u{1F477}", label: "CI", breakingPolicy: "forbidden" },
|
|
36
|
+
{ tier: "Process", key: "deps", aliases: ["dep"], emoji: "\u{1F4E6}", label: "Dependencies", breakingPolicy: "forbidden" },
|
|
37
|
+
{ tier: "Process", key: "ai", aliases: [], emoji: "\u{1F916}", label: "Agentic support", breakingPolicy: "forbidden" },
|
|
38
|
+
{
|
|
39
|
+
tier: "Process",
|
|
40
|
+
key: "docs",
|
|
41
|
+
aliases: ["doc"],
|
|
42
|
+
emoji: "\u{1F4DA}",
|
|
43
|
+
label: "Documentation",
|
|
44
|
+
breakingPolicy: "forbidden"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
tier: "Process",
|
|
48
|
+
key: "fmt",
|
|
49
|
+
aliases: [],
|
|
50
|
+
emoji: "\u{1F3A8}",
|
|
51
|
+
label: "Formatting",
|
|
52
|
+
breakingPolicy: "forbidden",
|
|
53
|
+
excludedFromChangelog: true
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
};
|
|
57
|
+
export {
|
|
58
|
+
WORK_TYPES_DATA
|
|
59
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
function hasExpectedTopLevelShape(value) {
|
|
2
|
+
if (typeof value !== "object" || value === null) {
|
|
3
|
+
return false;
|
|
4
|
+
}
|
|
5
|
+
if (!("tiers" in value) || !("types" in value)) {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
return Array.isArray(value.tiers) && Array.isArray(value.types);
|
|
9
|
+
}
|
|
10
|
+
function errorMessage(error) {
|
|
11
|
+
return error instanceof Error ? error.message : String(error);
|
|
12
|
+
}
|
|
13
|
+
export {
|
|
14
|
+
errorMessage,
|
|
15
|
+
hasExpectedTopLevelShape
|
|
16
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@williamthorsen/release-kit",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.2.0",
|
|
4
4
|
"description": "Version-bumping and changelog-generation toolkit for release workflows",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"homepage": "https://github.com/williamthorsen/node-monorepo-tools/tree/main/packages/release-kit#readme",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"dist/*",
|
|
29
29
|
"cliff.toml.template",
|
|
30
30
|
"presets/**",
|
|
31
|
+
"schemas/**",
|
|
31
32
|
"CHANGELOG.md"
|
|
32
33
|
],
|
|
33
34
|
"dependencies": {
|
|
@@ -35,10 +36,12 @@
|
|
|
35
36
|
"jiti": "2.6.1",
|
|
36
37
|
"js-yaml": "4.1.1",
|
|
37
38
|
"json-stringify-pretty-compact": "4.0.0",
|
|
38
|
-
"
|
|
39
|
+
"semver": "7.7.4",
|
|
40
|
+
"@williamthorsen/nmr-core": "0.3.1"
|
|
39
41
|
},
|
|
40
42
|
"devDependencies": {
|
|
41
43
|
"@types/js-yaml": "4.0.9",
|
|
44
|
+
"@types/semver": "7.7.1",
|
|
42
45
|
"smol-toml": "1.6.1"
|
|
43
46
|
},
|
|
44
47
|
"engines": {
|
|
@@ -49,9 +52,12 @@
|
|
|
49
52
|
"registry": "https://registry.npmjs.org"
|
|
50
53
|
},
|
|
51
54
|
"scripts": {
|
|
55
|
+
"build:post": "node scripts/copy-work-types.js",
|
|
52
56
|
"test": "pnpm exec vitest --config=vitest.standalone.config.ts",
|
|
53
57
|
"test:coverage": "pnpm exec vitest --config=vitest.standalone.config.ts --coverage",
|
|
54
58
|
"test:integration": "pnpm exec vitest --config=vitest.integration.config.ts",
|
|
55
|
-
"test:watch": "pnpm exec vitest --config=vitest.standalone.config.ts --watch"
|
|
59
|
+
"test:watch": "pnpm exec vitest --config=vitest.standalone.config.ts --watch",
|
|
60
|
+
"work-types:check": "pnpm exec release-kit work-types check",
|
|
61
|
+
"work-types:sync": "pnpm exec release-kit work-types sync"
|
|
56
62
|
}
|
|
57
63
|
}
|