@williamthorsen/release-kit 14.2.3 → 15.0.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.
@@ -1,4 +1,39 @@
1
1
  [
2
+ {
3
+ "version": "15.0.0",
4
+ "date": "2026-10-23",
5
+ "sections": [
6
+ {
7
+ "title": "🎉 Features",
8
+ "audience": "all",
9
+ "items": [
10
+ {
11
+ "description": "Require an isMonorepo option in deriveLabelMap to decide the root scope (#1096)",
12
+ "body": "Makes `deriveLabelMap` in `@williamthorsen/change-grammar` take a required `isMonorepo` option that decides whether the label map contains the `root` scope, replacing the inference from a non-empty workspace list. A monorepo without workspaces now receives the `root` scope, and every existing two-argument call must pass the option.",
13
+ "breaking": true,
14
+ "hash": "b12099da82b2c66fd7de23f5d8f6ecb4d29295e0"
15
+ }
16
+ ]
17
+ }
18
+ ]
19
+ },
20
+ {
21
+ "version": "14.2.4",
22
+ "date": "2026-10-21",
23
+ "sections": [
24
+ {
25
+ "title": "🐛 Bug fixes",
26
+ "audience": "all",
27
+ "items": [
28
+ {
29
+ "description": "Fixes the issue that `release-kit` did not release a workspace whose files a commit changed when that commit scoped every change-record entry to other workspaces; for that workspace, the commit's title now sets the bump and the changelog item. (#1086)",
30
+ "hash": "b518414c98f4703f2adca3840577e9f2b66e0924",
31
+ "entry": 1
32
+ }
33
+ ]
34
+ }
35
+ ]
36
+ },
2
37
  {
3
38
  "version": "14.2.3",
4
39
  "date": "2026-10-18",
@@ -183,6 +218,17 @@
183
218
  }
184
219
  ]
185
220
  },
221
+ {
222
+ "title": "🏗️ Internal features",
223
+ "audience": "dev",
224
+ "items": [
225
+ {
226
+ "description": "Resolve package roots past dual-format marker manifests (#1016)",
227
+ "body": "Replaces nmr-core's hand-written package-root and version lookups with `findPackageRoot` and `resolveSelfVersion` re-exported from `@williamthorsen/toolbelt.packaging`, which pass over the `{\"type\": \"commonjs\"}` marker manifest of a dual-format layout to reach the package that declares a `name`. `nmr`, `release-kit`, and `v11y-check` switch from `readPackageVersion` to `resolveSelfVersion`, and the boundary guard now holds `toolbelt.packaging`, like `toolbelt.terminal`, to a single importer and a single declarer in nmr-core.",
228
+ "hash": "ff6627180d9e86d083656afb2c09a3fb190fc79d"
229
+ }
230
+ ]
231
+ },
186
232
  {
187
233
  "title": "♻️ Refactoring",
188
234
  "audience": "dev",
@@ -234,6 +280,16 @@
234
280
  "description": "Makes `release-kit prepare` refuse, before it writes any file and in a dry run too, to release a publishable package that npm does not list or that it cannot look up, and prints `npm publish --access public` as the fix for each unlisted package. (#996)",
235
281
  "hash": "7b8b6d67b264af56dff761d0bf987eca8bc2f934",
236
282
  "entry": 1
283
+ },
284
+ {
285
+ "description": "Let a calling repo choose the runner of the reusable workflows (#997)",
286
+ "body": "Makes the reusable `audit`, `create-github-release`, `release`, and `sync-labels` workflows run their jobs on the runner named by the calling repository's `CI_RUNS_ON` configuration variable, such as a self-hosted runner, and keeps `ubuntu-latest` as the default when the variable is unset. Leaves the reusable `publish` workflow on `ubuntu-latest`, because npm generates provenance only on GitHub-hosted runners.",
287
+ "hash": "91573eec402f4028b766684be84c33e8739513ce"
288
+ },
289
+ {
290
+ "description": "Keep undeclared repo labels and warn about each (#1002)",
291
+ "body": "Changes the reusable sync-labels workflow to keep each repository label that `.github/labels.yaml` does not declare, rather than delete it, and to report each such label as a warning annotation on the run and on the pull request's check. A label created by hand now stays in place across syncs until the file declares it or it is deleted with `gh label delete <name>`.",
292
+ "hash": "f696a4f92064a5f2993e8bcbb6764762f161ec9d"
237
293
  }
238
294
  ]
239
295
  },
@@ -1,6 +1,6 @@
1
1
  /** @noformat -- @generated. Do not edit. Compiled by rdy. */
2
2
  /* eslint-disable */
3
- export const __readyupVersion = "0.42.0";
3
+ export const __readyupVersion = "0.43.0";
4
4
 
5
5
 
6
6
  // .readyup/kits/default.ts
@@ -234,7 +234,7 @@ var CREATE_GITHUB_RELEASE_WORKFLOW_HASH_SINGLE = "f595831ff28f8b3a01d97e6cb0cfe1
234
234
 
235
235
  // .readyup/kits/default.ts
236
236
  function getMinVersion() {
237
- const picked = { "version": "14.2.3" };
237
+ const picked = { "version": "15.0.0" };
238
238
  if (typeof picked["version"] !== "string") {
239
239
  throw new TypeError("release-kit/package.json: 'version' must be a string");
240
240
  }
@@ -1,6 +1,6 @@
1
1
  /** @noformat -- @generated. Do not edit. Compiled by rdy. */
2
2
  /* eslint-disable */
3
- export const __readyupVersion = "0.42.0";
3
+ export const __readyupVersion = "0.43.0";
4
4
 
5
5
 
6
6
  // .readyup/kits/npm-auto-publish.ts
@@ -7,7 +7,7 @@
7
7
  "checklist": "release-kit",
8
8
  "checks": [
9
9
  "@williamthorsen/release-kit in devDependencies",
10
- "@williamthorsen/release-kit >= 14.2.3",
10
+ "@williamthorsen/release-kit >= 15.0.0",
11
11
  "release.yaml workflow exists",
12
12
  "release.yaml matches template",
13
13
  "release.yaml does not reference deprecated tag ref",
@@ -63,7 +63,7 @@
63
63
  "path": "lib/workflow-hashes.ts"
64
64
  },
65
65
  {
66
- "hash": "b89073b8",
66
+ "hash": "239e734d",
67
67
  "kind": "inline",
68
68
  "path": "../package.json",
69
69
  "paths": [
@@ -98,10 +98,10 @@
98
98
  ],
99
99
  "name": "default",
100
100
  "path": "kits/default.js",
101
- "readyupVersion": "0.42.0",
101
+ "readyupVersion": "0.43.0",
102
102
  "source": "kits/default.ts",
103
103
  "sourceHash": "ad3666b8",
104
- "targetHash": "9f5a50d9"
104
+ "targetHash": "25439f31"
105
105
  },
106
106
  {
107
107
  "checklistChecks": [
@@ -157,10 +157,10 @@
157
157
  ],
158
158
  "name": "npm-auto-publish",
159
159
  "path": "kits/npm-auto-publish.js",
160
- "readyupVersion": "0.42.0",
160
+ "readyupVersion": "0.43.0",
161
161
  "source": "kits/npm-auto-publish.ts",
162
162
  "sourceHash": "682fdd4a",
163
- "targetHash": "04adb134"
163
+ "targetHash": "4aa2a346"
164
164
  }
165
165
  ]
166
166
  }
package/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## 15.0.0 — 2026-10-23
6
+
7
+ ### 🎉 Features
8
+
9
+ - 🚨 **Breaking:** Require an isMonorepo option in deriveLabelMap to decide the root scope (#1096)
10
+
11
+ Makes `deriveLabelMap` in `@williamthorsen/change-grammar` take a required `isMonorepo` option that decides whether the label map contains the `root` scope, replacing the inference from a non-empty workspace list. A monorepo without workspaces now receives the `root` scope, and every existing two-argument call must pass the option.
12
+
13
+ ## 14.2.4 — 2026-10-21
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - Fixes the issue that `release-kit` did not release a workspace whose files a commit changed when that commit scoped every change-record entry to other workspaces; for that workspace, the commit's title now sets the bump and the changelog item. (#1086)
18
+
5
19
  ## 14.2.3 — 2026-10-18
6
20
 
7
21
  ### 🐛 Bug fixes
@@ -56,6 +70,12 @@ All notable changes to this project will be documented in this file.
56
70
  - Fixes the issue that `release-kit create-github-release` did not create a GitHub Release for the project tag; a private workspace's tag is still skipped. (#1027)
57
71
  - Makes `release-kit publish` skip a project tag named by `--tags` with the warning `Skipping v0.13.0: a project tag is not an npm package.`, since the project does not have an npm package to publish. (#1027)
58
72
 
73
+ ### 🏗️ Internal features
74
+
75
+ - Resolve package roots past dual-format marker manifests (#1016)
76
+
77
+ Replaces nmr-core's hand-written package-root and version lookups with `findPackageRoot` and `resolveSelfVersion` re-exported from `@williamthorsen/toolbelt.packaging`, which pass over the `{"type": "commonjs"}` marker manifest of a dual-format layout to reach the package that declares a `name`. `nmr`, `release-kit`, and `v11y-check` switch from `readPackageVersion` to `resolveSelfVersion`, and the boundary guard now holds `toolbelt.packaging`, like `toolbelt.terminal`, to a single importer and a single declarer in nmr-core.
78
+
59
79
  ### ♻️ Refactoring
60
80
 
61
81
  - Replaces `releasePrepare`, `releasePrepareMono`, and `releasePrepareProject` with `releasePrepareTargets`, which prepares a release from one list of package and project targets resolved from any config shape. (#1026)
@@ -72,6 +92,13 @@ All notable changes to this project will be documented in this file.
72
92
  - Stops `release-kit init` from scaffolding `publish.yaml` and `create-github-release.yaml` when every package is private, even with `--force`, and removes the provenance and trusted-publisher steps from the next steps that it prints. (#995)
73
93
  - Stops `release-kit update-templates` from re-creating a deleted `publish.yaml` or `create-github-release.yaml` when every package is private, while it still refreshes either workflow that exists. (#995)
74
94
  - Makes `release-kit prepare` refuse, before it writes any file and in a dry run too, to release a publishable package that npm does not list or that it cannot look up, and prints `npm publish --access public` as the fix for each unlisted package. (#996)
95
+ - Let a calling repo choose the runner of the reusable workflows (#997)
96
+
97
+ Makes the reusable `audit`, `create-github-release`, `release`, and `sync-labels` workflows run their jobs on the runner named by the calling repository's `CI_RUNS_ON` configuration variable, such as a self-hosted runner, and keeps `ubuntu-latest` as the default when the variable is unset. Leaves the reusable `publish` workflow on `ubuntu-latest`, because npm generates provenance only on GitHub-hosted runners.
98
+
99
+ - Keep undeclared repo labels and warn about each (#1002)
100
+
101
+ Changes the reusable sync-labels workflow to keep each repository label that `.github/labels.yaml` does not declare, rather than delete it, and to report each such label as a warning annotation on the run and on the pull request's check. A label created by hand now stays in place across syncs until the file declares it or it is deleted with `gh label delete <name>`.
75
102
 
76
103
  ### 🐛 Bug fixes
77
104
 
package/README.md CHANGED
@@ -7,11 +7,13 @@ Version-bumping and changelog-generation toolkit for release workflows.
7
7
  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 the same commit history that decides each bump (with optional [editorial overrides](docs/editorial-overrides.md)).
8
8
 
9
9
  <!-- section:release-notes -->
10
- ## Release notes — v14.2.3 (2026-10-18)
10
+ ## Release notes — v15.0.0 (2026-10-23)
11
11
 
12
- ### 🐛 Bug fixes
12
+ ### 🎉 Features
13
13
 
14
- - Fixes the issue that the manifest of the `npm-auto-publish` kit recorded a different list of checks depending on the directory from which `rdy compile` ran, by declaring one fixed check, "Repo publishes at least one package", under which `rdy run` builds the npm session check and the per-package rows. (#1081)
14
+ - 🚨 **Breaking:** Require an isMonorepo option in deriveLabelMap to decide the root scope (#1096)
15
+
16
+ Makes `deriveLabelMap` in `@williamthorsen/change-grammar` take a required `isMonorepo` option that decides whether the label map contains the `root` scope, replacing the inference from a non-empty workspace list. A monorepo without workspaces now receives the `root` scope, and every existing two-argument call must pass the option.
15
17
  <!-- /section:release-notes -->
16
18
 
17
19
  ## Installation
@@ -15,10 +15,10 @@ export declare function buildChangelogEntries(config: ReleaseHistoryConfig, tag:
15
15
  * Reads a scope's release windows once and returns the changelog items of every window, with what the unreleased
16
16
  * window decides: its commits, its bump, and its diagnostics.
17
17
  *
18
- * A commit whose last `change-record` block records entries yields one item per entry routed to
19
- * `options.scope`; any other commit is classified by its title through `classifyChangelogCommit`. A `release:`
20
- * or merge subject yields nothing either way. The read records diagnostics only for the unreleased window, since the
21
- * released windows were reported when they were prepared.
18
+ * A commit whose last `change-record` block records at least one entry routed to `options.scope` yields one item per
19
+ * routed entry; any other commit, including one whose entries all route elsewhere, is classified by its title through
20
+ * `classifyChangelogCommit`. A `release:` or merge subject yields nothing either way. The read records diagnostics
21
+ * only for the unreleased window, since the released windows were reported when they were prepared.
22
22
  */
23
23
  export declare function readReleaseHistory(config: ReleaseHistoryConfig, options: GenerateChangelogOptions): ReleaseHistory;
24
24
  /** One read of a scope's release windows. */
@@ -1,5 +1,5 @@
1
1
  import { chainError } from '@williamthorsen/toolbelt.errors/candidate';
2
- import { buildEntryItem, buildTitleItem, } from "./buildChangelogItem.js";
2
+ import { buildEntryItem, buildTitleItem, hasRoutedEntry, } from "./buildChangelogItem.js";
3
3
  import { buildEmptyReleaseEntry } from "./buildEmptyReleaseEntry.js";
4
4
  import { extractVersion } from "./changelogJsonUtils.js";
5
5
  import { classifyChangelogCommit, isNonChangeSubject, isReleaseSubject } from "./classifyChangelogCommit.js";
@@ -93,7 +93,7 @@ function readCommit(commit, context, diagnostics) {
93
93
  reason: reading.reason,
94
94
  });
95
95
  }
96
- if (reading.kind === 'read' && reading.entries.length > 0) {
96
+ if (reading.kind === 'read' && reading.entries.length > 0 && hasRoutedEntry(reading.entries, context.routing)) {
97
97
  const items = reading.entries.flatMap((entry, entryIndex) => buildEntryItem(commit, entry, entryIndex + 1, reading.prNumber, context, diagnostics) ?? []);
98
98
  return { items, isUnparseable: false };
99
99
  }
@@ -38,6 +38,8 @@ export interface DerivedItem {
38
38
  item: ChangelogItem;
39
39
  signal: BumpSignal;
40
40
  }
41
+ /** Reports whether any of `entries` is routed to the workspace of `routing`; always true for a read without routing. */
42
+ export declare function hasRoutedEntry(entries: readonly ChangeRecordEntry[], routing: EntryRouting | undefined): boolean;
41
43
  /** What every window's read shares. */
42
44
  export interface ReadContext {
43
45
  breakingPolicies: NonNullable<ReleaseSettings['breakingPolicies']>;
@@ -75,6 +75,9 @@ export function buildTitleItem(commit, isParsedBreaking, workTypes) {
75
75
  item.hash = commit.hash;
76
76
  return item;
77
77
  }
78
+ export function hasRoutedEntry(entries, routing) {
79
+ return routing === undefined || entries.some((entry) => isRoutedTo(entry.scopes, routing));
80
+ }
78
81
  function extractBody(message) {
79
82
  const lines = message.split('\n').slice(1);
80
83
  let start = 0;
@@ -93,9 +93,11 @@ A workspace's window contains every commit that touches the workspace's paths. A
93
93
 
94
94
  A scope narrows the window and never widens it: A scope that names a workspace whose window does not contain the commit doesn't route the item to any workspace, and `prepare` reports it (see [Reported diagnostics](#reported-diagnostics)). A composite's changelog, such as the project changelog, and a single-package repo take every entry, whatever its scopes.
95
95
 
96
- A workspace's bump reads the items routed to it, so a breaking entry scoped to one workspace does not raise a major bump in another that the same commit touched. A workspace whose window has commits but lacks a routed item doesn't call for a release.
96
+ The window decides whether a workspace releases, and the scopes decide only what its changelog says. When none of a commit's entries routes to a workspace whose window contains the commit, release-kit reads the commit for that workspace from its title, as it reads a commit without a block: The title's type counts toward the workspace's bump, and the title's item enters its changelog. A commit with at least one entry routed to the workspace is read from its routed entries alone, even when none of them yields an item.
97
97
 
98
- Each `prepare` rebuilds a workspace's released windows with the same routing. When an earlier release recorded an item under a workspace that the item's scopes do not name, the rebuild removes the item from that workspace's past section. A path window does not follow a renamed directory, and legacy identities do not rename scopes: An entry scoped to a workspace's old directory name is routed to the workspace only through a `scopeAliases` entry from the old name to the new one, and until one exists, `prepare` reports the scope for each such commit in the unreleased window.
98
+ A workspace's bump reads the items routed to it, so a breaking entry scoped to one workspace does not raise a major bump in another workspace that has a routed entry of its own. A workspace read from the title takes the title's breaking marker; the workspace to which an entry is scoped keeps the entry's level.
99
+
100
+ Each `prepare` rebuilds a workspace's released windows with the same routing, the fallback to the title included. When an earlier release recorded an item under a workspace that the item's scopes do not name, the rebuild removes the item from that workspace's past section. A path window does not follow a renamed directory, and legacy identities do not rename scopes: An entry scoped to a workspace's old directory name is routed to the workspace only through a `scopeAliases` entry from the old name to the new one, and until one exists, `prepare` reports the scope for each such commit in the unreleased window.
99
101
 
100
102
  ## Change-record blocks
101
103
 
@@ -128,7 +130,7 @@ An entry whose type is excluded from the changelog doesn't yield an item. An ove
128
130
 
129
131
  ### Fallback to the title
130
132
 
131
- A commit is read from its title, as [Which commits appear in a changelog](#which-commits-appear-in-a-changelog) describes, when it doesn't have a block, when the block's `entries` is absent or empty, or when the block is malformed. A block is malformed when it never closes, when its payload is not valid YAML or not a mapping, when `entries` is not a list, when an entry is not a mapping, when an entry's `type` or `text` is missing, blank, or not a string, when `breaking` is not a boolean, when `scopes` is not a list of strings, when `migration` or `ticket_ref` is not a string, or when `pr_number` is not a positive integer. One defect makes the whole block malformed.
133
+ A commit is read from its title, as [Which commits appear in a changelog](#which-commits-appear-in-a-changelog) describes, when it doesn't have a block, when the block's `entries` is absent or empty, when none of its entries routes to the workspace being read (see [Routing to workspaces](#routing-to-workspaces)), or when the block is malformed. A block is malformed when it never closes, when its payload is not valid YAML or not a mapping, when `entries` is not a list, when an entry is not a mapping, when an entry's `type` or `text` is missing, blank, or not a string, when `breaking` is not a boolean, when `scopes` is not a list of strings, when `migration` or `ticket_ref` is not a string, or when `pr_number` is not a positive integer. One defect makes the whole block malformed.
132
134
 
133
135
  ### Reported diagnostics
134
136
 
@@ -139,7 +141,7 @@ A commit is read from its title, as [Which commits appear in a changelog](#which
139
141
  - **An entry that violates its type's breaking policy**, as a policy violation at the entry's position. Its item is not marked breaking.
140
142
  - **An entry scope that doesn't name a workspace in its commit's window**, with the commit's hash, the entry's position, and the scope as the entry declares it. release-kit reports it under every workspace whose window contains the commit. A scope that names nothing, or a workspace whose window does not contain the commit, is reported; `*`, `root`, and a scope that names a workspace in the window are not. Under `--only`, a scope that names a configured workspace left out of the run is not reported either. A workspace excluded with `shouldExclude: true` or a `releases` entry of `false` is not a configured workspace, so a scope that names one is reported.
141
143
 
142
- The result that `prepare` returns includes these as `malformedBlocks`, `undeclaredEntryTypes`, `policyViolations` entries whose `surface` is `'entry'`, and `unroutedEntryScopes`, whether the target releases or is skipped. Because a block whose entries are all undeclared or excluded doesn't yield an item, the commit doesn't raise a bump.
144
+ The result that `prepare` returns includes these as `malformedBlocks`, `undeclaredEntryTypes`, `policyViolations` entries whose `surface` is `'entry'`, and `unroutedEntryScopes`, whether the target releases or is skipped. Because a block whose routed entries are all undeclared or excluded doesn't yield an item, the commit doesn't raise a bump.
143
145
 
144
146
  ## Release-notes injection
145
147
 
package/docs/labels.md CHANGED
@@ -125,4 +125,4 @@ Consumers reference it from the top of their `.meta/label-map.json`:
125
125
  }
126
126
  ```
127
127
 
128
- Earlier releases published the same schema from release-kit, and URLs under existing `release-kit-v*` tags keep resolving. release-kit does not generate `.meta/label-map.json`; change-grammar's `deriveLabelMap` derives one from the taxonomy and the workspace scopes.
128
+ Earlier releases published the same schema from release-kit, and URLs under existing `release-kit-v*` tags keep resolving. release-kit does not generate `.meta/label-map.json`; change-grammar's `deriveLabelMap` derives one from the taxonomy, the workspace scopes, and the caller's statement of whether the repo is a monorepo, which decides the `root` scope.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@williamthorsen/release-kit",
3
- "version": "14.2.3",
3
+ "version": "15.0.0",
4
4
  "description": "Version-bumping and changelog-generation toolkit for release workflows",
5
5
  "keywords": [
6
6
  "changelog",
@@ -50,13 +50,13 @@
50
50
  "schemas/**"
51
51
  ],
52
52
  "dependencies": {
53
- "@williamthorsen/change-grammar": "0.2.1",
54
- "@williamthorsen/nmr-core": "0.13.4",
53
+ "@williamthorsen/change-grammar": "0.3.0",
54
+ "@williamthorsen/nmr-core": "0.13.5",
55
55
  "@williamthorsen/toolbelt.errors": "0.8.3",
56
56
  "json-stringify-pretty-compact": "4.0.0",
57
57
  "semver": "7.8.5",
58
58
  "yaml": "2.9.1",
59
- "zod": "4.6.5"
59
+ "zod": "4.6.7"
60
60
  },
61
61
  "devDependencies": {
62
62
  "@types/semver": "7.8.0",
@@ -65,9 +65,9 @@
65
65
  "@williamthorsen/toolbelt.vitest": "0.10.3",
66
66
  "actionlint": "2.0.6",
67
67
  "esbuild": "0.28.2",
68
- "readyup": "0.42.0",
68
+ "readyup": "0.43.0",
69
69
  "smol-toml": "1.9.1",
70
- "vitest": "4.1.11"
70
+ "vitest": "4.1.12"
71
71
  },
72
72
  "engines": {
73
73
  "node": ">=24.16.0"