@williamthorsen/release-kit 9.0.0 → 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 +16 -0
- package/README.md +8 -12
- 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 +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
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
|
+
|
|
13
|
+
## 9.0.1 — 2026-07-30
|
|
14
|
+
|
|
15
|
+
### ⚙️ Tooling
|
|
16
|
+
|
|
17
|
+
- Adopt Vitest projects in this repo (#530)
|
|
18
|
+
|
|
19
|
+
Simplifies test configurations so that every package exposes the same test commands, which select suites by test category: `nmr test` runs everything except integration tests, `nmr test:integration` runs only those, and `nmr test:all` runs both. `nmr test:integration` now succeeds in a package that has no integration tests instead of failing. release-kit's drift checks can now be run on their own, apart from unit and integration tests. A fresh clone now has to run `pnpm run bootstrap` before any test run, not just before `nmr` commands.
|
|
20
|
+
|
|
5
21
|
## 9.0.0 — 2026-07-28
|
|
6
22
|
|
|
7
23
|
### 🎉 Features
|
package/README.md
CHANGED
|
@@ -5,19 +5,13 @@ Version-bumping and changelog-generation toolkit for release workflows.
|
|
|
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
7
|
<!-- section:release-notes -->
|
|
8
|
-
## Release notes — v9.
|
|
8
|
+
## Release notes — v9.1.0 (2026-07-31)
|
|
9
9
|
|
|
10
10
|
### 🎉 Features
|
|
11
11
|
|
|
12
|
-
-
|
|
12
|
+
- Make label descriptions optional and generate none for scope labels (#539)
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
### 🐛 Bug fixes
|
|
17
|
-
|
|
18
|
-
- Include commits scoped to `*` in the changelog (#496)
|
|
19
|
-
|
|
20
|
-
Fixes an issue where commits scoped to `*` raised the version but were silently dropped from the generated changelog.
|
|
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.
|
|
21
15
|
<!-- /section:release-notes -->
|
|
22
16
|
|
|
23
17
|
## Installation
|
|
@@ -744,7 +738,7 @@ export default defineConfig({
|
|
|
744
738
|
repoLabels: {
|
|
745
739
|
extends: ['common'],
|
|
746
740
|
labels: {
|
|
747
|
-
'scope:my-package': { color: '00ff96'
|
|
741
|
+
'scope:my-package': { color: '00ff96' }, // add, with no description
|
|
748
742
|
bug: { color: 'b60205', description: 'Something broken' }, // replace the preset's `bug`
|
|
749
743
|
wontfix: null, // remove the preset's `wontfix`
|
|
750
744
|
},
|
|
@@ -755,7 +749,9 @@ export default defineConfig({
|
|
|
755
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:
|
|
756
750
|
|
|
757
751
|
1. Presets, in `extends` order — a later preset wins on a shared name.
|
|
758
|
-
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.
|
|
759
755
|
|
|
760
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.
|
|
761
757
|
|
|
@@ -764,7 +760,7 @@ Overlaps are never errors; order resolves them, and the committed `.github/label
|
|
|
764
760
|
Label configuration formerly lived in a standalone `.config/sync-labels.config.ts` (`presets` + a `labels` array). To migrate:
|
|
765
761
|
|
|
766
762
|
1. Move the preset list to `repoLabels.extends` in `.config/release-kit.config.ts`.
|
|
767
|
-
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.
|
|
768
764
|
3. Delete `.config/sync-labels.config.ts` and run `release-kit sync-labels generate`.
|
|
769
765
|
|
|
770
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.
|
|
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",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"semver": "7.8.5",
|
|
40
40
|
"yaml": "2.9.0",
|
|
41
41
|
"zod": "4.4.3",
|
|
42
|
-
"@williamthorsen/nmr-core": "0.7.
|
|
42
|
+
"@williamthorsen/nmr-core": "0.7.3"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/semver": "7.7.1",
|