@williamthorsen/release-kit 13.1.0 → 14.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.
Files changed (45) hide show
  1. package/.meta/changelog.json +118 -9
  2. package/.readyup/kits/default.js +4 -4
  3. package/.readyup/kits/npm-auto-publish.js +81 -41
  4. package/.readyup/manifest.json +19 -9
  5. package/CHANGELOG.md +38 -6
  6. package/README.md +14 -6
  7. package/dist/esm/assertReleasesOnNpm.d.ts +7 -0
  8. package/dist/esm/assertReleasesOnNpm.js +56 -0
  9. package/dist/esm/bin/release-kit.js +5 -2
  10. package/dist/esm/deriveWorkspaceConfig.js +2 -2
  11. package/dist/esm/enumerateReleaseWindows.d.ts +1 -1
  12. package/dist/esm/enumerateReleaseWindows.js +1 -3
  13. package/dist/esm/findTransitiveDependents.d.ts +3 -0
  14. package/dist/esm/findTransitiveDependents.js +20 -0
  15. package/dist/esm/help/prepareHelp.d.ts +1 -1
  16. package/dist/esm/help/prepareHelp.js +4 -2
  17. package/dist/esm/init/hasPublishablePackage.d.ts +10 -0
  18. package/dist/esm/init/hasPublishablePackage.js +28 -0
  19. package/dist/esm/init/initCommand.js +23 -13
  20. package/dist/esm/init/scaffold.d.ts +6 -1
  21. package/dist/esm/init/scaffold.js +10 -7
  22. package/dist/esm/init/templates.js +1 -1
  23. package/dist/esm/init/updateTemplatesCommand.d.ts +2 -1
  24. package/dist/esm/init/updateTemplatesCommand.js +12 -6
  25. package/dist/esm/isPublishableManifest.d.ts +2 -0
  26. package/dist/esm/isPublishableManifest.js +5 -0
  27. package/dist/esm/matchesTagPrefix.d.ts +2 -0
  28. package/dist/esm/matchesTagPrefix.js +4 -0
  29. package/dist/esm/npmRegistry.d.ts +35 -0
  30. package/dist/esm/npmRegistry.js +58 -0
  31. package/dist/esm/prepareCommand.d.ts +1 -0
  32. package/dist/esm/prepareCommand.js +52 -61
  33. package/dist/esm/previewTagPrefixes.js +2 -1
  34. package/dist/esm/releasePrepare.d.ts +2 -16
  35. package/dist/esm/releasePrepareMono.d.ts +17 -2
  36. package/dist/esm/releasePrepareMono.js +23 -11
  37. package/dist/esm/sync-labels/templates.js +1 -1
  38. package/docs/configuration.md +1 -1
  39. package/docs/labels.md +5 -3
  40. package/docs/project-releases.md +2 -2
  41. package/docs/readiness-checks.md +2 -2
  42. package/docs/releasing.md +20 -10
  43. package/package.json +8 -8
  44. package/dist/esm/validateOnlyExcludesStrandedDependents.d.ts +0 -37
  45. package/dist/esm/validateOnlyExcludesStrandedDependents.js +0 -115
@@ -1,4 +1,106 @@
1
1
  [
2
+ {
3
+ "version": "14.1.0",
4
+ "date": "2026-10-11",
5
+ "sections": [
6
+ {
7
+ "title": "🎉 Features",
8
+ "audience": "all",
9
+ "items": [
10
+ {
11
+ "description": "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)",
12
+ "hash": "93355490219535fac282ab2256179094ca920e47",
13
+ "entry": 1
14
+ },
15
+ {
16
+ "description": "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)",
17
+ "hash": "93355490219535fac282ab2256179094ca920e47",
18
+ "entry": 2
19
+ },
20
+ {
21
+ "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)",
22
+ "hash": "7b8b6d67b264af56dff761d0bf987eca8bc2f934",
23
+ "entry": 1
24
+ }
25
+ ]
26
+ },
27
+ {
28
+ "title": "🐛 Bug fixes",
29
+ "audience": "all",
30
+ "items": [
31
+ {
32
+ "description": "Fixes the issue that the `npm-auto-publish` kit's \"published to npm\" check reported a failed lookup, such as an unreachable registry or an authentication error, as an unpublished package and ignored `publishConfig.registry`. (#996)",
33
+ "hash": "7b8b6d67b264af56dff761d0bf987eca8bc2f934",
34
+ "entry": 2
35
+ },
36
+ {
37
+ "description": "Fixes the issue that release-kit's changelog generation treated a floating major tag such as `v1` as a release and added a spurious release to the history, by counting a tag only when the rest of its name after the prefix is a complete SemVer version such as `1.0.0` or `1.0.0-beta.1`. (#1000)",
38
+ "hash": "712e7500109b4bb3db579b60c72e4bf85b308098",
39
+ "entry": 1
40
+ },
41
+ {
42
+ "description": "Fixes the issue that `release-kit show-tag-prefixes` counted every tag that starts with a workspace's prefix, including floating pointers such as `core-v1` and non-release tags such as `core-vnext`, rather than only the tags that release-kit counts as releases. (#1000)",
43
+ "hash": "712e7500109b4bb3db579b60c72e4bf85b308098",
44
+ "entry": 2
45
+ }
46
+ ]
47
+ }
48
+ ]
49
+ },
50
+ {
51
+ "version": "14.0.0",
52
+ "date": "2026-10-08",
53
+ "sections": [
54
+ {
55
+ "title": "🎉 Features",
56
+ "audience": "all",
57
+ "items": [
58
+ {
59
+ "description": "Changes monorepo `--set-version` to take comma-separated `<workspace>@N.N.N` entries, which `prepare` accepts without `--only` and together with `--bump` and `--force`, and which release each named workspace's dependents. (#970)",
60
+ "breaking": true,
61
+ "migration": "Replace `--only arrays --set-version 1.0.0` with `--set-version arrays@1.0.0 --only arrays`, and keep `--only` unless the release should cover every workspace, because a `--set-version` run without `--only` is a full run.",
62
+ "hash": "07e3e99cb03e6031619bd58dac6ee339ceac0e89",
63
+ "entry": 2
64
+ }
65
+ ]
66
+ },
67
+ {
68
+ "title": "🐛 Bug fixes",
69
+ "audience": "all",
70
+ "items": [
71
+ {
72
+ "description": "Fixes the issue that `release-kit prepare --only` left each dependent of the named workspaces published against the old version range of its dependency, by also releasing every transitive dependent: a dependent with commits of its own at its own bump, and one without at a propagated patch bump. (#970)",
73
+ "breaking": true,
74
+ "migration": "Expect each `--only` run to release the dependents of the named workspaces as well, and remove any dependents that were added to `--only` only to get past the stranded-dependents error, which `prepare` no longer reports.",
75
+ "hash": "07e3e99cb03e6031619bd58dac6ee339ceac0e89",
76
+ "entry": 1
77
+ }
78
+ ]
79
+ },
80
+ {
81
+ "title": "🧪 Tests",
82
+ "audience": "dev",
83
+ "items": [
84
+ {
85
+ "description": "Replaces the hand-rolled `console.warn` spies in release-kit's config-warning and push tests with `silenceConsole`, which restores the method when its `using` scope exits. (#964)",
86
+ "hash": "38cc179b4116bcd5aba8371b17b0816150c9e9a3",
87
+ "entry": 4
88
+ }
89
+ ]
90
+ },
91
+ {
92
+ "title": "📚 Documentation",
93
+ "audience": "dev",
94
+ "items": [
95
+ {
96
+ "description": "Updates the readyup examples in the `nmr`, `release-kit`, and `v11y-check` READMEs and the `nmr` rulebook to use the `sources` key and `rdy run --sources`, and states that `--sources` needs readyup 0.40 or later. (#977)",
97
+ "hash": "41177f8f562d59a4cf0aff9d24199dbd370bb1a1",
98
+ "entry": 4
99
+ }
100
+ ]
101
+ }
102
+ ]
103
+ },
2
104
  {
3
105
  "version": "13.1.0",
4
106
  "date": "2026-10-02",
@@ -1923,10 +2025,22 @@
1923
2025
  "audience": "all",
1924
2026
  "items": [
1925
2027
  {
1926
- "description": "Migrate release-kit from toolbelt (#18)",
1927
- "body": "Migrates the complete `@williamthorsen/release-kit` package (v1.0.1) from `williamthorsen/toolbelt` into `packages/release-kit/`, adds shebang preservation to the shared esbuild plugin for CLI binaries, and sets up dogfooding infrastructure so this monorepo uses release-kit for its own releases.",
1928
- "hash": "da3a61fd335fb3e05ce20a0bffcdc1647d7aeb45"
1929
- },
2028
+ "description": "Allow git-cliff to be used without config (#31)",
2029
+ "body": "Adds a `resolveCliffConfigPath()` function that searches for a git-cliff config in a 4-step cascade (explicit path → `.config/git-cliff.toml` → `cliff.toml` → bundled `cliff.toml.template`), eliminating the requirement for consuming repos to maintain a cliff config copy. Restructures the `init` command to scaffold only the workflow file by default, with new `--with-config` and `--force` flags. Moves `.release-tags` from `/tmp/release-kit/` to project-local `tmp/` for predictable behavior in local runs.",
2030
+ "hash": "0cbd2605d6f5f53d1efe3dda8dd0f6e4376b499f"
2031
+ }
2032
+ ]
2033
+ }
2034
+ ]
2035
+ },
2036
+ {
2037
+ "version": "2.0.0",
2038
+ "date": "2026-03-16",
2039
+ "sections": [
2040
+ {
2041
+ "title": "🎉 Features",
2042
+ "audience": "all",
2043
+ "items": [
1930
2044
  {
1931
2045
  "description": "Slim down release workflow by removing unnecessary pnpm install (#21)",
1932
2046
  "body": "Make release-kit self-contained by invoking git-cliff via `npx --yes` instead of requiring it on PATH, and by appending modified file paths to the format command so lightweight formatters like `npx prettier --write` work without a full `pnpm install`. Update init templates, README, and consuming repo config/workflow to reference workflow v3.",
@@ -1943,11 +2057,6 @@
1943
2057
  "body": "Moves the reusable release workflow from `williamthorsen/.github` into this repo as `release-workflow.yaml`, stripping all pnpm-related steps since release-kit now runs git-cliff and prettier via `npx` internally. Updates this repo's caller workflow to use a relative path and update init templates to reference the new location. Establishes a naming convention (`{name}-workflow.yaml` for reusable, `{name}.yaml` for callers) and independent versioning strategy (`{name}-workflow-v{major}` tags), documented in `.github/workflows/README.md`.",
1944
2058
  "breaking": true,
1945
2059
  "hash": "f3416120cfe5bf0e7dbf51389e0c2647ccf41d30"
1946
- },
1947
- {
1948
- "description": "Allow git-cliff to be used without config (#31)",
1949
- "body": "Adds a `resolveCliffConfigPath()` function that searches for a git-cliff config in a 4-step cascade (explicit path → `.config/git-cliff.toml` → `cliff.toml` → bundled `cliff.toml.template`), eliminating the requirement for consuming repos to maintain a cliff config copy. Restructures the `init` command to scaffold only the workflow file by default, with new `--with-config` and `--force` flags. Moves `.release-tags` from `/tmp/release-kit/` to project-local `tmp/` for predictable behavior in local runs.",
1950
- "hash": "0cbd2605d6f5f53d1efe3dda8dd0f6e4376b499f"
1951
2060
  }
1952
2061
  ]
1953
2062
  },
@@ -1,6 +1,6 @@
1
1
  /** @noformat -- @generated. Do not edit. Compiled by rdy. */
2
2
  /* eslint-disable */
3
- export const __readyupVersion = "0.38.0";
3
+ export const __readyupVersion = "0.40.0";
4
4
 
5
5
 
6
6
  // .readyup/kits/default.ts
@@ -98,7 +98,7 @@ function translateGlob(pattern) {
98
98
 
99
99
  // .readyup/kits/default.ts
100
100
  function getMinVersion() {
101
- const picked = { "version": "13.1.0" };
101
+ const picked = { "version": "14.1.0" };
102
102
  if (typeof picked["version"] !== "string") {
103
103
  throw new TypeError("release-kit/package.json: 'version' must be a string");
104
104
  }
@@ -115,8 +115,8 @@ var CONFIG_EXPORT_PATTERNS = [
115
115
  /export\s*\*/
116
116
  ];
117
117
  var COMMON_PRESET_HASH = "618bb5cbcbdd4f4f0c2a777add7e14d992d48483350dd9651bf8cf4c1fa9d418";
118
- var SYNC_LABELS_WORKFLOW_HASH = "6b50015e2919ffa4b16d0b4a1f340e585ff3e5b779a2256010d3c62fa07ff8f2";
119
- var RELEASE_WORKFLOW_HASH_MONOREPO = "f35503bcfc5c131a8309305fed27f8127cb901f422cb8b62978a34b86dda7f7a";
118
+ var SYNC_LABELS_WORKFLOW_HASH = "0a88829d154c0de7cdd4246fede99e9311a89ab4199034dbe601663d641ab1e4";
119
+ var RELEASE_WORKFLOW_HASH_MONOREPO = "4a20ff3315155500f64a27133c54eb1bc1f07a4acc370ec0d83176109041c60d";
120
120
  var RELEASE_WORKFLOW_HASH_SINGLE = "d26dd4ca186632b388ea46bd885721124c03d19918e511baa40821d2d760da69";
121
121
  var PUBLISH_WORKFLOW_HASH_MONOREPO = "0afa9ffe914f3dc8f043e68252ebc604c8cc1a953422fcea37a909a4def370ee";
122
122
  var PUBLISH_WORKFLOW_HASH_SINGLE = "6f31183e0a1e66be791a19266c3b028dadbd9fe010f7fc4452f3f8970c937b43";
@@ -1,6 +1,6 @@
1
1
  /** @noformat -- @generated. Do not edit. Compiled by rdy. */
2
2
  /* eslint-disable */
3
- export const __readyupVersion = "0.38.0";
3
+ export const __readyupVersion = "0.40.0";
4
4
 
5
5
 
6
6
  // .readyup/kits/npm-auto-publish.ts
@@ -13,7 +13,7 @@ import {
13
13
  fileContains,
14
14
  fileExists,
15
15
  getJsonValue,
16
- isRecord,
16
+ isRecord as isRecord2,
17
17
  readFile,
18
18
  readJsonFile
19
19
  } from "readyup/check-utils";
@@ -23,10 +23,16 @@ function hasProvenance(workflowContent) {
23
23
  return /^[^#]*provenance:\s*['"]?true['"]?/im.test(workflowContent);
24
24
  }
25
25
 
26
- // .readyup/kits/npm-auto-publish.ts
26
+ // src/npmRegistry.ts
27
+ import { execFileSync } from "node:child_process";
28
+
29
+ // src/typeGuards.ts
30
+ function isRecord(value) {
31
+ return typeof value === "object" && value !== null && !Array.isArray(value);
32
+ }
33
+
34
+ // src/npmRegistry.ts
27
35
  var AUTH_ERROR_CODES = /* @__PURE__ */ new Set(["E401", "ENEEDAUTH"]);
28
- var OTP_ERROR_CODE = "EOTP";
29
- var PUBLISH_WORKFLOW_FILE = "publish.yaml";
30
36
  var UNREACHABLE_ERROR_CODES = /* @__PURE__ */ new Set([
31
37
  "EAI_AGAIN",
32
38
  "ECONNREFUSED",
@@ -36,6 +42,54 @@ var UNREACHABLE_ERROR_CODES = /* @__PURE__ */ new Set([
36
42
  "ERR_SOCKET_TIMEOUT",
37
43
  "ETIMEDOUT"
38
44
  ]);
45
+ function classifyNpmPackageLookup(result) {
46
+ if (result.exitOk) {
47
+ return { status: "published" };
48
+ }
49
+ const error = readNpmError(result.stdout);
50
+ if (error === void 0) {
51
+ return { status: "unverifiable", detail: "The npm registry query failed without a readable error payload" };
52
+ }
53
+ if (error.code === "E404") {
54
+ return { status: "unpublished" };
55
+ }
56
+ if (AUTH_ERROR_CODES.has(error.code)) {
57
+ return { status: "unverifiable", detail: `The npm registry rejected the session (${error.code})` };
58
+ }
59
+ if (UNREACHABLE_ERROR_CODES.has(error.code)) {
60
+ return { status: "unverifiable", detail: `Cannot reach the npm registry (${error.code})` };
61
+ }
62
+ return { status: "unverifiable", detail: `The npm registry query failed (${error.code}): ${error.summary}` };
63
+ }
64
+ function lookUpNpmPackage(packageName, registry) {
65
+ const args = ["view", packageName, "version", "--json", ...registry === void 0 ? [] : ["--registry", registry]];
66
+ return classifyNpmPackageLookup(runNpmJson(args));
67
+ }
68
+ function readNpmError(stdout) {
69
+ let parsed;
70
+ try {
71
+ parsed = JSON.parse(stdout);
72
+ } catch {
73
+ return void 0;
74
+ }
75
+ if (!isRecord(parsed) || !isRecord(parsed["error"]) || typeof parsed["error"]["code"] !== "string") {
76
+ return void 0;
77
+ }
78
+ const { code, summary } = parsed["error"];
79
+ return { code, summary: typeof summary === "string" ? summary : "" };
80
+ }
81
+ function runNpmJson(args) {
82
+ try {
83
+ return { exitOk: true, stdout: execFileSync("npm", args, { encoding: "utf8", stdio: "pipe" }) };
84
+ } catch (error) {
85
+ const stdout = isRecord(error) && typeof error["stdout"] === "string" ? error["stdout"] : "";
86
+ return { exitOk: false, stdout };
87
+ }
88
+ }
89
+
90
+ // .readyup/kits/npm-auto-publish.ts
91
+ var OTP_ERROR_CODE = "EOTP";
92
+ var PUBLISH_WORKFLOW_FILE = "publish.yaml";
39
93
  var repoChecklist = defineRdyChecklist({
40
94
  name: "repo",
41
95
  checks: [
@@ -149,7 +203,7 @@ function buildWorkspaceCheck(workspace) {
149
203
  const auth = getCachedNpmAuthStatus();
150
204
  return auth.status === "unreachable" ? auth.detail : false;
151
205
  },
152
- check: () => isPublishedToNpm(displayName),
206
+ check: () => describeNpmPackageLookup(lookUpNpmPackage(displayName, readPublishRegistry(workspace))),
153
207
  fix: `Run "npm publish --access public" from ${workspace.dir} to bootstrap the package on npm`,
154
208
  checks: [
155
209
  {
@@ -290,6 +344,19 @@ function classifyTrustQuery(result, expectedRepo, expectedFile) {
290
344
  detail: `Expected github ${expectedRepo} (${expectedFile}); found ${describeTrustRelationships(relationships)}`
291
345
  };
292
346
  }
347
+ function describeNpmPackageLookup(lookup) {
348
+ if (lookup.status === "published") {
349
+ return { ok: true };
350
+ }
351
+ if (lookup.status === "unpublished") {
352
+ return { ok: false, detail: "The npm registry does not list this package" };
353
+ }
354
+ return {
355
+ ok: false,
356
+ detail: lookup.detail,
357
+ fix: "Restore access to the npm registry, then run this check again"
358
+ };
359
+ }
293
360
  function describeTrustRelationships(relationships) {
294
361
  return relationships.map((relationship) => {
295
362
  const type = typeof relationship.type === "string" ? relationship.type : "(unknown)";
@@ -300,7 +367,7 @@ function describeTrustRelationships(relationships) {
300
367
  }
301
368
  var getCachedNpmAuthStatus = /* @__PURE__ */ (() => {
302
369
  let cached;
303
- return () => cached ??= classifyNpmAuth(runNpmJson("npm whoami --json"));
370
+ return () => cached ??= classifyNpmAuth(runNpmJson(["whoami", "--json"]));
304
371
  })();
305
372
  var getCachedOwnerRepo = /* @__PURE__ */ (() => {
306
373
  let cached;
@@ -331,7 +398,7 @@ function getTrustQueryResult(packageName) {
331
398
  return cached;
332
399
  }
333
400
  const result = classifyTrustQuery(
334
- runNpmJson(`npm trust list ${packageName} --json`),
401
+ runNpmJson(["trust", "list", packageName, "--json"]),
335
402
  getCachedOwnerRepo(),
336
403
  PUBLISH_WORKFLOW_FILE
337
404
  );
@@ -352,17 +419,6 @@ function hasTokenReferences() {
352
419
  }
353
420
  return false;
354
421
  }
355
- function isPublishedToNpm(packageName) {
356
- try {
357
- execSync(`npm view ${packageName} version`, {
358
- encoding: "utf8",
359
- stdio: "pipe"
360
- });
361
- return true;
362
- } catch {
363
- return false;
364
- }
365
- }
366
422
  function isRepoPrivate() {
367
423
  const ownerRepo = getOwnerRepo();
368
424
  const result = execSync(`gh api repos/${ownerRepo} --jq .private`, {
@@ -374,18 +430,9 @@ function probeTrustQuery() {
374
430
  const probeName = selectProbeName();
375
431
  return probeName === void 0 ? void 0 : getTrustQueryResult(probeName);
376
432
  }
377
- function readNpmError(stdout) {
378
- let parsed;
379
- try {
380
- parsed = JSON.parse(stdout);
381
- } catch {
382
- return void 0;
383
- }
384
- if (!isRecord(parsed) || !isRecord(parsed["error"]) || typeof parsed["error"]["code"] !== "string") {
385
- return void 0;
386
- }
387
- const { code, summary } = parsed["error"];
388
- return { code, summary: typeof summary === "string" ? summary : "" };
433
+ function readPublishRegistry(workspace) {
434
+ const registry = getJsonValue(workspace.packageJson, "publishConfig", "registry");
435
+ return typeof registry === "string" ? registry : void 0;
389
436
  }
390
437
  function readTrustRelationships(stdout) {
391
438
  let parsed;
@@ -395,9 +442,9 @@ function readTrustRelationships(stdout) {
395
442
  return void 0;
396
443
  }
397
444
  if (Array.isArray(parsed)) {
398
- return parsed.filter((entry) => isRecord(entry) && "type" in entry);
445
+ return parsed.filter((entry) => isRecord2(entry) && "type" in entry);
399
446
  }
400
- if (!isRecord(parsed)) {
447
+ if (!isRecord2(parsed)) {
401
448
  return void 0;
402
449
  }
403
450
  return "type" in parsed ? [parsed] : [];
@@ -406,14 +453,6 @@ function resolveTrustCapability() {
406
453
  const auth = getCachedNpmAuthStatus();
407
454
  return classifyTrustCapability(auth, auth.status === "authenticated" ? probeTrustQuery() : void 0);
408
455
  }
409
- function runNpmJson(command) {
410
- try {
411
- return { exitOk: true, stdout: execSync(command, { encoding: "utf8", stdio: "pipe" }) };
412
- } catch (error) {
413
- const stdout = isRecord(error) && typeof error["stdout"] === "string" ? error["stdout"] : "";
414
- return { exitOk: false, stdout };
415
- }
416
- }
417
456
  function selectProbeName() {
418
457
  return discoverWorkspaces({ filter: (workspace) => workspace.isPackage }).find(
419
458
  (workspace) => workspace.name !== void 0
@@ -433,6 +472,7 @@ export {
433
472
  classifyTrustCapability,
434
473
  classifyTrustQuery,
435
474
  npm_auto_publish_default as default,
475
+ describeNpmPackageLookup,
436
476
  packagesChecklist,
437
477
  selectProbeName,
438
478
  skipIfNotPublishable,
@@ -8,12 +8,12 @@
8
8
  "esbuildVersion": "0.28.2",
9
9
  "inputs": [
10
10
  {
11
- "hash": "91bc3638",
11
+ "hash": "dee24ab4",
12
12
  "kind": "module",
13
13
  "path": "kits/default.ts"
14
14
  },
15
15
  {
16
- "hash": "fd27a632",
16
+ "hash": "4aa0967e",
17
17
  "kind": "inline",
18
18
  "path": "../package.json",
19
19
  "paths": [
@@ -43,10 +43,10 @@
43
43
  ],
44
44
  "name": "default",
45
45
  "path": "kits/default.js",
46
- "readyupVersion": "0.38.0",
46
+ "readyupVersion": "0.40.0",
47
47
  "source": "kits/default.ts",
48
- "sourceHash": "91bc3638",
49
- "targetHash": "a3266cf2"
48
+ "sourceHash": "dee24ab4",
49
+ "targetHash": "25d17b58"
50
50
  },
51
51
  {
52
52
  "checklists": [
@@ -56,7 +56,7 @@
56
56
  "esbuildVersion": "0.28.2",
57
57
  "inputs": [
58
58
  {
59
- "hash": "d9910d60",
59
+ "hash": "70c0bb99",
60
60
  "kind": "module",
61
61
  "path": "kits/npm-auto-publish.ts"
62
62
  },
@@ -64,14 +64,24 @@
64
64
  "hash": "d360febf",
65
65
  "kind": "module",
66
66
  "path": "../src/init/hasProvenance.ts"
67
+ },
68
+ {
69
+ "hash": "3e39b9be",
70
+ "kind": "module",
71
+ "path": "../src/npmRegistry.ts"
72
+ },
73
+ {
74
+ "hash": "1424b343",
75
+ "kind": "module",
76
+ "path": "../src/typeGuards.ts"
67
77
  }
68
78
  ],
69
79
  "name": "npm-auto-publish",
70
80
  "path": "kits/npm-auto-publish.js",
71
- "readyupVersion": "0.38.0",
81
+ "readyupVersion": "0.40.0",
72
82
  "source": "kits/npm-auto-publish.ts",
73
- "sourceHash": "d9910d60",
74
- "targetHash": "8bdbefef"
83
+ "sourceHash": "70c0bb99",
84
+ "targetHash": "d97d6123"
75
85
  }
76
86
  ]
77
87
  }
package/CHANGELOG.md CHANGED
@@ -2,6 +2,38 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## 14.1.0 — 2026-10-11
6
+
7
+ ### 🎉 Features
8
+
9
+ - 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)
10
+ - 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)
11
+ - 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)
12
+
13
+ ### 🐛 Bug fixes
14
+
15
+ - Fixes the issue that the `npm-auto-publish` kit's "published to npm" check reported a failed lookup, such as an unreachable registry or an authentication error, as an unpublished package and ignored `publishConfig.registry`. (#996)
16
+ - Fixes the issue that release-kit's changelog generation treated a floating major tag such as `v1` as a release and added a spurious release to the history, by counting a tag only when the rest of its name after the prefix is a complete SemVer version such as `1.0.0` or `1.0.0-beta.1`. (#1000)
17
+ - Fixes the issue that `release-kit show-tag-prefixes` counted every tag that starts with a workspace's prefix, including floating pointers such as `core-v1` and non-release tags such as `core-vnext`, rather than only the tags that release-kit counts as releases. (#1000)
18
+
19
+ ## 14.0.0 — 2026-10-08
20
+
21
+ ### 🎉 Features
22
+
23
+ - 🚨 **Breaking:** Changes monorepo `--set-version` to take comma-separated `<workspace>@N.N.N` entries, which `prepare` accepts without `--only` and together with `--bump` and `--force`, and which release each named workspace's dependents. (#970)
24
+
25
+ ### 🐛 Bug fixes
26
+
27
+ - 🚨 **Breaking:** Fixes the issue that `release-kit prepare --only` left each dependent of the named workspaces published against the old version range of its dependency, by also releasing every transitive dependent: a dependent with commits of its own at its own bump, and one without at a propagated patch bump. (#970)
28
+
29
+ ### 🧪 Tests
30
+
31
+ - Replaces the hand-rolled `console.warn` spies in release-kit's config-warning and push tests with `silenceConsole`, which restores the method when its `using` scope exits. (#964)
32
+
33
+ ### 📚 Documentation
34
+
35
+ - Updates the readyup examples in the `nmr`, `release-kit`, and `v11y-check` READMEs and the `nmr` rulebook to use the `sources` key and `rdy run --sources`, and states that `--sources` needs readyup 0.40 or later. (#977)
36
+
5
37
  ## 13.1.0 — 2026-10-02
6
38
 
7
39
  ### 🎉 Features
@@ -1111,9 +1143,13 @@ All notable changes to this project will be documented in this file.
1111
1143
 
1112
1144
  ### 🎉 Features
1113
1145
 
1114
- - Migrate release-kit from toolbelt (#18)
1146
+ - Allow git-cliff to be used without config (#31)
1115
1147
 
1116
- Migrates the complete `@williamthorsen/release-kit` package (v1.0.1) from `williamthorsen/toolbelt` into `packages/release-kit/`, adds shebang preservation to the shared esbuild plugin for CLI binaries, and sets up dogfooding infrastructure so this monorepo uses release-kit for its own releases.
1148
+ Adds a `resolveCliffConfigPath()` function that searches for a git-cliff config in a 4-step cascade (explicit path → `.config/git-cliff.toml` → `cliff.toml` → bundled `cliff.toml.template`), eliminating the requirement for consuming repos to maintain a cliff config copy. Restructures the `init` command to scaffold only the workflow file by default, with new `--with-config` and `--force` flags. Moves `.release-tags` from `/tmp/release-kit/` to project-local `tmp/` for predictable behavior in local runs.
1149
+
1150
+ ## 2.0.0 — 2026-03-16
1151
+
1152
+ ### 🎉 Features
1117
1153
 
1118
1154
  - 🚨 **Breaking:** Slim down release workflow by removing unnecessary pnpm install (#21)
1119
1155
 
@@ -1127,10 +1163,6 @@ All notable changes to this project will be documented in this file.
1127
1163
 
1128
1164
  Moves the reusable release workflow from `williamthorsen/.github` into this repo as `release-workflow.yaml`, stripping all pnpm-related steps since release-kit now runs git-cliff and prettier via `npx` internally. Updates this repo's caller workflow to use a relative path and update init templates to reference the new location. Establishes a naming convention (`{name}-workflow.yaml` for reusable, `{name}.yaml` for callers) and independent versioning strategy (`{name}-workflow-v{major}` tags), documented in `.github/workflows/README.md`.
1129
1165
 
1130
- - Allow git-cliff to be used without config (#31)
1131
-
1132
- Adds a `resolveCliffConfigPath()` function that searches for a git-cliff config in a 4-step cascade (explicit path → `.config/git-cliff.toml` → `cliff.toml` → bundled `cliff.toml.template`), eliminating the requirement for consuming repos to maintain a cliff config copy. Restructures the `init` command to scaffold only the workflow file by default, with new `--with-config` and `--force` flags. Moves `.release-tags` from `/tmp/release-kit/` to project-local `tmp/` for predictable behavior in local runs.
1133
-
1134
1166
  ### ♻️ Refactoring
1135
1167
 
1136
1168
  - Clean up release-kit post-migration issues (#19)
package/README.md CHANGED
@@ -7,11 +7,19 @@ 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 — v13.1.0 (2026-10-02)
10
+ ## Release notes — v14.1.0 (2026-10-11)
11
11
 
12
12
  ### 🎉 Features
13
13
 
14
- - Adds an `archived: true` mark to `repoLabels.labels` entries, which `sync-labels generate` writes to `.github/labels.yaml` without changing the output for unmarked labels. (#955)
14
+ - 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)
15
+ - 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)
16
+ - 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)
17
+
18
+ ### 🐛 Bug fixes
19
+
20
+ - Fixes the issue that the `npm-auto-publish` kit's "published to npm" check reported a failed lookup, such as an unreachable registry or an authentication error, as an unpublished package and ignored `publishConfig.registry`. (#996)
21
+ - Fixes the issue that release-kit's changelog generation treated a floating major tag such as `v1` as a release and added a spurious release to the history, by counting a tag only when the rest of its name after the prefix is a complete SemVer version such as `1.0.0` or `1.0.0-beta.1`. (#1000)
22
+ - Fixes the issue that `release-kit show-tag-prefixes` counted every tag that starts with a workspace's prefix, including floating pointers such as `core-v1` and non-release tags such as `core-vnext`, rather than only the tags that release-kit counts as releases. (#1000)
15
23
  <!-- /section:release-notes -->
16
24
 
17
25
  ## Installation
@@ -57,7 +65,7 @@ Commit the scaffolded workflows, then start a release from the `release` workflo
57
65
 
58
66
  ```sh
59
67
  gh workflow run release.yaml # every workspace with release-worthy changes
60
- gh workflow run release.yaml -f only=arrays # the named workspaces alone
68
+ gh workflow run release.yaml -f only=arrays # the named workspaces and their dependents
61
69
  ```
62
70
 
63
71
  After upgrading release-kit, run `release-kit update-templates` to update the scaffolded workflows to its templates; it leaves the config files alone.
@@ -136,15 +144,15 @@ release-kit publishes two [readyup](https://www.npmjs.com/package/readyup) kits:
136
144
  import { defineRdyConfig } from 'readyup';
137
145
 
138
146
  export default defineRdyConfig({
139
- packages: ['@williamthorsen/release-kit'],
147
+ sources: ['npm:@williamthorsen/release-kit'],
140
148
  });
141
149
  ```
142
150
 
143
151
  ```bash
144
- rdy run --packages
152
+ rdy run --sources
145
153
  ```
146
154
 
147
- `rdy run` needs `readyup` 0.23 or later, and `@williamthorsen/release-kit` as a _direct_ devDependency: A strict pnpm layout links nothing else into the project; therefore, a transitive copy is unreachable.
155
+ `rdy run --sources` needs `readyup` 0.40 or later, and `@williamthorsen/release-kit` as a _direct_ devDependency: A strict pnpm layout links nothing else into the project; therefore, a transitive copy is unreachable.
148
156
 
149
157
  [Readiness checks](docs/readiness-checks.md) covers running each kit alone and what `npm-auto-publish` needs from the npm session.
150
158
 
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Throws when npm doesn't list, or can't confirm, a publishable package among the given workspaces.
3
+ *
4
+ * Each workspace path is repo-relative and is read from the current directory. A private package is not looked up,
5
+ * and an empty list doesn't call the registry at all.
6
+ */
7
+ export declare function assertReleasesOnNpm(workspacePaths: readonly string[]): void;
@@ -0,0 +1,56 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { isPublishableManifest } from "./isPublishableManifest.js";
4
+ import { lookUpNpmPackage } from "./npmRegistry.js";
5
+ import { isRecord } from "./typeGuards.js";
6
+ export function assertReleasesOnNpm(workspacePaths) {
7
+ const unpublished = [];
8
+ const unverifiable = [];
9
+ for (const workspacePath of workspacePaths) {
10
+ const candidate = buildNpmCandidate(workspacePath);
11
+ if (candidate === undefined) {
12
+ continue;
13
+ }
14
+ const lookup = lookUpNpmPackage(candidate.name, candidate.registry);
15
+ if (lookup.status === 'unpublished') {
16
+ unpublished.push(candidate);
17
+ }
18
+ else if (lookup.status === 'unverifiable') {
19
+ unverifiable.push({ candidate, detail: lookup.detail });
20
+ }
21
+ }
22
+ if (unpublished.length === 0 && unverifiable.length === 0) {
23
+ return;
24
+ }
25
+ throw new Error(describeRefusal(unpublished, unverifiable));
26
+ }
27
+ function buildNpmCandidate(workspacePath) {
28
+ const manifest = JSON.parse(readFileSync(join(workspacePath, 'package.json'), 'utf8'));
29
+ if (!isRecord(manifest) || !isPublishableManifest(manifest) || typeof manifest['name'] !== 'string') {
30
+ return undefined;
31
+ }
32
+ const publishConfig = manifest['publishConfig'];
33
+ const registry = isRecord(publishConfig) ? publishConfig['registry'] : undefined;
34
+ return {
35
+ name: manifest['name'],
36
+ workspacePath,
37
+ ...(typeof registry === 'string' && { registry }),
38
+ };
39
+ }
40
+ function describeRefusal(unpublished, unverifiable) {
41
+ const sections = ['Cannot prepare the release: npm must already have every package that the release publishes.'];
42
+ if (unpublished.length > 0) {
43
+ sections.push([
44
+ 'Not on npm yet:',
45
+ ...unpublished.map(({ name, workspacePath }) => ` - ${name}: run "npm publish --access public" from ${workspacePath}`),
46
+ 'Then register each one as a trusted publisher; "rdy run --from npm:@williamthorsen/release-kit npm-auto-publish" prints the command.',
47
+ ].join('\n'));
48
+ }
49
+ if (unverifiable.length > 0) {
50
+ sections.push([
51
+ 'Could not confirm on npm:',
52
+ ...unverifiable.map(({ candidate, detail }) => ` - ${candidate.name}: ${detail}`),
53
+ ].join('\n'));
54
+ }
55
+ return sections.join('\n\n');
56
+ }
@@ -108,7 +108,8 @@ function showInitHelp() {
108
108
  Usage: release-kit init [options]
109
109
 
110
110
  Initialize release-kit in the current repository.
111
- By default, scaffolds only the GitHub Actions workflow files.
111
+ By default, scaffolds only the GitHub Actions workflow files. When every
112
+ package is private, skips publish.yaml and create-github-release.yaml.
112
113
 
113
114
  Options:
114
115
  --with-config Also scaffold .config/release-kit.config.ts
@@ -124,7 +125,9 @@ Usage: release-kit update-templates [options]
124
125
  Update the GitHub Actions workflows that release-kit scaffolds to the templates
125
126
  of the installed version, showing what changed in each file. Creates a missing
126
127
  workflow that \`init\` would create, and refreshes sync-labels.yaml only when it
127
- exists. Keeps publish.yaml's provenance setting. Never touches config files.
128
+ exists. When every package is private, refreshes publish.yaml and
129
+ create-github-release.yaml only when they exist. Keeps publish.yaml's provenance
130
+ setting. Never touches config files.
128
131
 
129
132
  Options:
130
133
  --dry-run Preview changes without writing files