@williamthorsen/release-kit 9.0.1 → 9.1.1
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 +5 -3
- 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
|
@@ -730,7 +730,7 @@ export default defineConfig({
|
|
|
730
730
|
repoLabels: {
|
|
731
731
|
extends: ['common'],
|
|
732
732
|
labels: {
|
|
733
|
-
'scope:my-package': { color: '00ff96'
|
|
733
|
+
'scope:my-package': { color: '00ff96' }, // add, with no description
|
|
734
734
|
bug: { color: 'b60205', description: 'Something broken' }, // replace the preset's `bug`
|
|
735
735
|
wontfix: null, // remove the preset's `wontfix`
|
|
736
736
|
},
|
|
@@ -741,7 +741,9 @@ export default defineConfig({
|
|
|
741
741
|
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
742
|
|
|
743
743
|
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`).
|
|
744
|
+
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.
|
|
745
|
+
|
|
746
|
+
`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
747
|
|
|
746
748
|
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
749
|
|
|
@@ -750,7 +752,7 @@ Overlaps are never errors; order resolves them, and the committed `.github/label
|
|
|
750
752
|
Label configuration formerly lived in a standalone `.config/sync-labels.config.ts` (`presets` + a `labels` array). To migrate:
|
|
751
753
|
|
|
752
754
|
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
|
|
755
|
+
2. Convert each `labels` array entry to a `'name': { color }` record entry under `repoLabels.labels`, adding `description` where the label carries one.
|
|
754
756
|
3. Delete `.config/sync-labels.config.ts` and run `release-kit sync-labels generate`.
|
|
755
757
|
|
|
756
758
|
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.
|
|
3
|
+
"version": "9.1.1",
|
|
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",
|