@williamthorsen/release-kit 9.0.1 → 9.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 +8 -0
- package/README.md +14 -4
- package/dist/esm/sync-labels/generateCommand.js +2 -1
- package/dist/esm/sync-labels/presets.js +9 -6
- package/dist/esm/sync-labels/resolveLabels.js +5 -1
- package/dist/esm/sync-labels/templates.js +4 -13
- package/dist/esm/sync-labels/types.d.ts +1 -1
- package/dist/esm/types.d.ts +3 -3
- package/dist/esm/types.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## 9.1.0 — 2026-07-31
|
|
6
|
+
|
|
7
|
+
### 🎉 Features
|
|
8
|
+
|
|
9
|
+
- Make label descriptions optional and generate none for scope labels (#539)
|
|
10
|
+
|
|
11
|
+
`release-kit` now supports labels without a description, and `release-kit sync-labels init` generates scope labels without one. Applying a label without a description clears any existing description the repository carries for that label.
|
|
12
|
+
|
|
5
13
|
## 9.0.1 — 2026-07-30
|
|
6
14
|
|
|
7
15
|
### ⚙️ Tooling
|
package/README.md
CHANGED
|
@@ -4,7 +4,15 @@ Version-bumping and changelog-generation toolkit for release workflows.
|
|
|
4
4
|
|
|
5
5
|
Provides a self-contained CLI that auto-discovers workspaces from `pnpm-workspace.yaml`, parses conventional commits, determines version bumps, updates `package.json` files, and generates changelogs from `git-cliff --context` output rendered in-process (with optional [editorial overrides](#editorial-overrides)).
|
|
6
6
|
|
|
7
|
-
<!-- section:release-notes
|
|
7
|
+
<!-- section:release-notes -->
|
|
8
|
+
## Release notes — v9.1.0 (2026-07-31)
|
|
9
|
+
|
|
10
|
+
### 🎉 Features
|
|
11
|
+
|
|
12
|
+
- Make label descriptions optional and generate none for scope labels (#539)
|
|
13
|
+
|
|
14
|
+
`release-kit` now supports labels without a description, and `release-kit sync-labels init` generates scope labels without one. Applying a label without a description clears any existing description the repository carries for that label.
|
|
15
|
+
<!-- /section:release-notes -->
|
|
8
16
|
|
|
9
17
|
## Installation
|
|
10
18
|
|
|
@@ -730,7 +738,7 @@ export default defineConfig({
|
|
|
730
738
|
repoLabels: {
|
|
731
739
|
extends: ['common'],
|
|
732
740
|
labels: {
|
|
733
|
-
'scope:my-package': { color: '00ff96'
|
|
741
|
+
'scope:my-package': { color: '00ff96' }, // add, with no description
|
|
734
742
|
bug: { color: 'b60205', description: 'Something broken' }, // replace the preset's `bug`
|
|
735
743
|
wontfix: null, // remove the preset's `wontfix`
|
|
736
744
|
},
|
|
@@ -741,7 +749,9 @@ export default defineConfig({
|
|
|
741
749
|
The block declares the repository's label registry — the set of labels defined on the GitHub repo, distinct from labels applied to PRs and issues. Resolution is an ordered fold with last-writer-wins:
|
|
742
750
|
|
|
743
751
|
1. Presets, in `extends` order — a later preset wins on a shared name.
|
|
744
|
-
2. The `labels` record — an entry adds a label, replaces one an earlier layer defined, or removes it (`null`).
|
|
752
|
+
2. The `labels` record — an entry adds a label, replaces one an earlier layer defined, or removes it (`null`). Replacement is wholesale: an entry omitting `description` resolves to no description rather than inheriting the one an earlier layer supplied.
|
|
753
|
+
|
|
754
|
+
`description` is optional throughout, in a preset as in the `labels` record, and `sync-labels init` generates none for a scope label. The generated file spells an absent description `''` because `github-label-sync` reads an omitted description as "leave the label's current one alone"; the empty form clears it.
|
|
745
755
|
|
|
746
756
|
Overlaps are never errors; order resolves them, and the committed `.github/labels.yaml` diff is where an unexpected change surfaces at review. The one config error is a dangling `null` — removing a name no preset defines — because that misstatement is invisible in the output diff.
|
|
747
757
|
|
|
@@ -750,7 +760,7 @@ Overlaps are never errors; order resolves them, and the committed `.github/label
|
|
|
750
760
|
Label configuration formerly lived in a standalone `.config/sync-labels.config.ts` (`presets` + a `labels` array). To migrate:
|
|
751
761
|
|
|
752
762
|
1. Move the preset list to `repoLabels.extends` in `.config/release-kit.config.ts`.
|
|
753
|
-
2. Convert each `labels` array entry to a `'name': { color
|
|
763
|
+
2. Convert each `labels` array entry to a `'name': { color }` record entry under `repoLabels.labels`, adding `description` where the label carries one.
|
|
754
764
|
3. Delete `.config/sync-labels.config.ts` and run `release-kit sync-labels generate`.
|
|
755
765
|
|
|
756
766
|
The regenerated `.github/labels.yaml` differs only in its `# Source:` header line. Name collisions with preset labels, which the old format rejected, now mean your entry replaces the preset label.
|
|
@@ -13,7 +13,8 @@ export function formatLabelsYaml(labels, presetHashes) {
|
|
|
13
13
|
for (const [name, hash] of [...presetHashes].sort(([a], [b]) => a.localeCompare(b))) {
|
|
14
14
|
headerLines.push(`# ${name} preset hash: ${hash}`);
|
|
15
15
|
}
|
|
16
|
-
const
|
|
16
|
+
const declared = labels.map((label) => ({ ...label, description: label.description ?? '' }));
|
|
17
|
+
const yamlBody = stringify(declared, { singleQuote: true, lineWidth: 0 });
|
|
17
18
|
return headerLines.join('\n') + '\n' + yamlBody;
|
|
18
19
|
}
|
|
19
20
|
export async function loadRepoLabelsConfig() {
|
|
@@ -4,10 +4,6 @@ import { resolve } from 'node:path';
|
|
|
4
4
|
import { findPackageRoot } from '@williamthorsen/nmr-core';
|
|
5
5
|
import { parse } from 'yaml';
|
|
6
6
|
import { isRecord } from "../typeGuards.js";
|
|
7
|
-
function resolvePresetPath(presetName) {
|
|
8
|
-
const root = findPackageRoot(import.meta.url);
|
|
9
|
-
return resolve(root, 'presets', 'labels', `${presetName}.yaml`);
|
|
10
|
-
}
|
|
11
7
|
export function hashPresetFile(presetName) {
|
|
12
8
|
const presetPath = resolvePresetPath(presetName);
|
|
13
9
|
if (!existsSync(presetPath)) {
|
|
@@ -38,10 +34,17 @@ export function loadPreset(presetName) {
|
|
|
38
34
|
if (!isRecord(entry)) {
|
|
39
35
|
throw new Error(`Preset "${presetName}" contains an invalid label entry: ${JSON.stringify(entry)}`);
|
|
40
36
|
}
|
|
41
|
-
|
|
37
|
+
const { description } = entry;
|
|
38
|
+
if (typeof entry.name !== 'string' ||
|
|
39
|
+
typeof entry.color !== 'string' ||
|
|
40
|
+
(description !== undefined && typeof description !== 'string')) {
|
|
42
41
|
throw new TypeError(`Preset "${presetName}" contains a label with invalid fields: ${JSON.stringify(entry)}`);
|
|
43
42
|
}
|
|
44
|
-
labels.push({ name: entry.name, color: entry.color, description
|
|
43
|
+
labels.push({ name: entry.name, color: entry.color, ...(description !== undefined && { description }) });
|
|
45
44
|
}
|
|
46
45
|
return labels;
|
|
47
46
|
}
|
|
47
|
+
function resolvePresetPath(presetName) {
|
|
48
|
+
const root = findPackageRoot(import.meta.url);
|
|
49
|
+
return resolve(root, 'presets', 'labels', `${presetName}.yaml`);
|
|
50
|
+
}
|
|
@@ -14,7 +14,11 @@ export function resolveLabels(config) {
|
|
|
14
14
|
resolved.delete(name);
|
|
15
15
|
}
|
|
16
16
|
else {
|
|
17
|
-
resolved.set(name, {
|
|
17
|
+
resolved.set(name, {
|
|
18
|
+
name,
|
|
19
|
+
color: spec.color,
|
|
20
|
+
...(spec.description !== undefined && { description: spec.description }),
|
|
21
|
+
});
|
|
18
22
|
}
|
|
19
23
|
}
|
|
20
24
|
return sortLabels([...resolved.values()]);
|
|
@@ -39,17 +39,8 @@ jobs:
|
|
|
39
39
|
`;
|
|
40
40
|
}
|
|
41
41
|
export function buildScopeLabels(workspacePaths, retiredNames = []) {
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
];
|
|
45
|
-
for (const workspacePath of workspacePaths) {
|
|
46
|
-
const name = basename(workspacePath);
|
|
47
|
-
labels.push({ name: `scope:${name}`, color: '00ff96', description: `${name} package` });
|
|
48
|
-
}
|
|
49
|
-
for (const retiredName of retiredNames) {
|
|
50
|
-
labels.push({ name: `scope:${retiredName}`, color: '00ff96', description: `${retiredName} package (retired)` });
|
|
51
|
-
}
|
|
52
|
-
return labels;
|
|
42
|
+
const scopes = ['root', ...workspacePaths.map((workspacePath) => basename(workspacePath)), ...retiredNames];
|
|
43
|
+
return scopes.map((scope) => ({ name: `scope:${scope}`, color: '00ff96' }));
|
|
53
44
|
}
|
|
54
45
|
function escapeForSingleQuotedString(value) {
|
|
55
46
|
return value.replace(/\\/g, '\\\\').replace(/'/g, String.raw `\'`);
|
|
@@ -59,8 +50,8 @@ export function renderRepoLabelsBlock(scopeLabels) {
|
|
|
59
50
|
.map((label) => {
|
|
60
51
|
const name = escapeForSingleQuotedString(label.name);
|
|
61
52
|
const color = escapeForSingleQuotedString(label.color);
|
|
62
|
-
const
|
|
63
|
-
return ` '${name}': { color: '${color}'
|
|
53
|
+
const descriptionClause = label.description === undefined ? '' : `, description: '${escapeForSingleQuotedString(label.description)}'`;
|
|
54
|
+
return ` '${name}': { color: '${color}'${descriptionClause} },`;
|
|
64
55
|
})
|
|
65
56
|
.join('\n');
|
|
66
57
|
const labelsBlock = entries === '' ? ' labels: {},' : ` labels: {\n${entries}\n },`;
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -173,14 +173,14 @@ export declare const breakingPolicyValueSchema: z.ZodEnum<{
|
|
|
173
173
|
}>;
|
|
174
174
|
export declare const labelSpecSchema: z.ZodObject<{
|
|
175
175
|
color: z.ZodString;
|
|
176
|
-
description: z.ZodString
|
|
176
|
+
description: z.ZodOptional<z.ZodString>;
|
|
177
177
|
}, z.core.$strict>;
|
|
178
178
|
export type LabelSpec = z.infer<typeof labelSpecSchema>;
|
|
179
179
|
export declare const repoLabelsSchema: z.ZodObject<{
|
|
180
180
|
extends: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
181
181
|
labels: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNullable<z.ZodObject<{
|
|
182
182
|
color: z.ZodString;
|
|
183
|
-
description: z.ZodString
|
|
183
|
+
description: z.ZodOptional<z.ZodString>;
|
|
184
184
|
}, z.core.$strict>>>>;
|
|
185
185
|
}, z.core.$strict>;
|
|
186
186
|
export type RepoLabelsConfig = z.infer<typeof repoLabelsSchema>;
|
|
@@ -207,7 +207,7 @@ export declare const releaseKitConfigSchema: z.ZodObject<{
|
|
|
207
207
|
extends: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
208
208
|
labels: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNullable<z.ZodObject<{
|
|
209
209
|
color: z.ZodString;
|
|
210
|
-
description: z.ZodString
|
|
210
|
+
description: z.ZodOptional<z.ZodString>;
|
|
211
211
|
}, z.core.$strict>>>>;
|
|
212
212
|
}, z.core.$strict>>;
|
|
213
213
|
retiredPackages: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
package/dist/esm/types.js
CHANGED
|
@@ -52,7 +52,7 @@ export const breakingPolicyValueSchema = z.enum(['forbidden', 'optional', 'requi
|
|
|
52
52
|
export const labelSpecSchema = z
|
|
53
53
|
.object({
|
|
54
54
|
color: z.string().min(1),
|
|
55
|
-
description: z.string(),
|
|
55
|
+
description: z.string().optional(),
|
|
56
56
|
})
|
|
57
57
|
.strict();
|
|
58
58
|
export const repoLabelsSchema = z
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@williamthorsen/release-kit",
|
|
3
|
-
"version": "9.0
|
|
3
|
+
"version": "9.1.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",
|