@williamthorsen/release-kit 5.3.0 → 5.3.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 CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## 5.3.1 — 2026-05-19
6
+
7
+ ### 🐛 Bug fixes
8
+
9
+ - Validate overrides against the full release history (#401)
10
+
11
+ Fixes an issue where `release-kit overrides validate` reported overrides as stale when they targeted commits in past releases, even though `release-kit prepare` correctly matched them. The two commands now agree on which overrides are stale.
12
+
13
+ ### ♻️ Refactoring
14
+
15
+ - Restructure tests and align core package directory with package name (#405)
16
+
17
+ Tests in every package are now typechecked alongside the code they cover, so type breakage in tests fails the build instead of slipping through. The `core` package's workspace directory is renamed to match its package name, so `nmr -F nmr-core ...` and `pnpm --filter nmr-core ...` now resolve where they previously failed.
18
+
5
19
  ## 5.3.0 — 2026-05-10
6
20
 
7
21
  ### 🎉 Features
package/README.md CHANGED
@@ -5,41 +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 — v5.3.0 (2026-05-10)
9
-
10
- ### 🎉 Features
11
-
12
- - Enable editorial overrides for changelog entries (#387)
13
-
14
- Allows `release-kit` consumers to skip or correct historical changelog entries by means of an overrides file.
15
-
16
- - Decentralize changelog overrides to per-scope .meta/ files (#391)
17
-
18
- Adds support for workspace-scoped editorial-override files for `release-kit`-generated changelogs. A repo-root file applies overrides to every workspace's changelog; a workspace-tier file applies only to that workspace.
19
-
20
- - Add section markers and authenticated upstream fetch (#393)
21
-
22
- A new `markers` block in `work-types.json` describes the breaking-changes emoji and label, making them available for use by consumers.
23
-
24
- `work-types check` and `work-types sync` now authenticate when `GITHUB_TOKEN` is set, so they can reach private upstream repositories.
25
-
26
- - Validate changelog overrides from the command line (#395)
27
-
28
- Adds a `release-kit overrides validate` subcommand that audits every `.meta/changelog-overrides.json` file across the project root and per-workspace scopes in one pass. The command reports schema errors, ambiguous-prefix collisions, and stale-key warnings with tiered exit codes so CI can choose its own failure threshold. The same validation is also available via a library function exported by the package.
8
+ ## Release notes — v5.3.1 (2026-05-19)
29
9
 
30
10
  ### 🐛 Bug fixes
31
11
 
32
- - Suppress git-cliff stale-version warnings on prepare (#373)
33
-
34
- Fixes an issue where `release-kit prepare` repeatedly printed git-cliff's "A new version of git-cliff is available" notice — twice per release unit, so 2 × N times for an N-package monorepo run — while never updating the locally cached git-cliff binary. Each `prepare` run now revalidates the npm cache once before any cliff work, so the binary stays current with upstream releases and the notice no longer surfaces on every per-workspace invocation.
35
-
36
- - Use synthetic changelogs for forced empty-range releases (#376)
37
-
38
- Fixes an issue where `release-kit prepare` with `--force`, `--bump=X`, or `--set-version` would invoke git-cliff against units that had no commits since their last tag, surfacing confusing `WARN git_cliff > There is already a tag (...)` lines (twice per affected unit) and silently leaving `CHANGELOG.md` and `.meta/changelog.json` stale. Empty-range bumps now write a synthetic `Notes / Forced version bump.` entry to both files instead of invoking git-cliff. Applies to all three release stages: single-package, per-workspace, and project. Prior changelog history is preserved on every path.
39
-
40
- - Accept `breakingPolicies` field in config files (#394)
12
+ - Validate overrides against the full release history (#401)
41
13
 
42
- Fixes an issue where setting `breakingPolicies` in `release-kit.config.ts` was rejected as an unknown field, leaving per-work-type breaking-policy configuration unreachable from the config file. Each entry accepts `'forbidden'`, `'optional'`, or `'required'`; an empty object opts out of enforcement.
14
+ Fixes an issue where `release-kit overrides validate` reported overrides as stale when they targeted commits in past releases, even though `release-kit prepare` correctly matched them. The two commands now agree on which overrides are stale.
43
15
  <!-- /section:release-notes -->
44
16
 
45
17
  ## Installation
@@ -495,7 +467,7 @@ The override file is validated when `release-kit prepare` loads it. Each error n
495
467
 
496
468
  #### Standalone validation: `release-kit overrides validate`
497
469
 
498
- For a fast overrides-only health check (locally or as a CI gate), run:
470
+ For a focused overrides-only health check (locally or as a CI gate), run:
499
471
 
500
472
  ```sh
501
473
  pnpm exec release-kit overrides validate
package/dist/esm/.cache CHANGED
@@ -1 +1 @@
1
- 18bd566d86a692ebdc23649a475e7d5e01562a58df99b6091e222e890cef8f80
1
+ 376cb01707ee2fb8afe186dfa7baa15e2a0c982201fae4c30632cf598d87d09c
@@ -1,4 +1,6 @@
1
+ import type { WorkspaceConfig } from './types.ts';
1
2
  export declare function buildTagPattern(tagPrefixes: readonly string[]): string;
3
+ export declare function getAllTagPrefixes(workspace: WorkspaceConfig): string[];
2
4
  export interface GenerateChangelogOptions {
3
5
  tagPattern?: string;
4
6
  includePaths?: string[];
@@ -9,9 +9,13 @@ function buildTagPattern(tagPrefixes) {
9
9
  const escaped = tagPrefixes.map(escapeRegex);
10
10
  return `(${escaped.join("|")})[0-9].*`;
11
11
  }
12
+ function getAllTagPrefixes(workspace) {
13
+ return [workspace.tagPrefix, ...workspace.legacyIdentities?.map((identity) => identity.tagPrefix) ?? []];
14
+ }
12
15
  function escapeRegex(value) {
13
16
  return value.replace(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
14
17
  }
15
18
  export {
16
- buildTagPattern
19
+ buildTagPattern,
20
+ getAllTagPrefixes
17
21
  };
@@ -19,7 +19,7 @@ import { isForwardVersion } from "./compareVersions.js";
19
19
  import { decideRelease } from "./decideRelease.js";
20
20
  import { DEFAULT_BREAKING_POLICIES, DEFAULT_VERSION_PATTERNS, DEFAULT_WORK_TYPES } from "./defaults.js";
21
21
  import { detectUndeclaredTagPrefixes } from "./detectUndeclaredTagPrefixes.js";
22
- import { buildTagPattern } from "./generateChangelogs.js";
22
+ import { buildTagPattern, getAllTagPrefixes } from "./generateChangelogs.js";
23
23
  import { getCommitsSinceTarget } from "./getCommitsSinceTarget.js";
24
24
  import { hasPrettierConfig } from "./hasPrettierConfig.js";
25
25
  import { resolveWorkTypes } from "./loadConfig.js";
@@ -550,9 +550,6 @@ function topologicalSort(releaseSet, graph) {
550
550
  }
551
551
  return { sorted, cyclicDirs };
552
552
  }
553
- function getAllTagPrefixes(workspace) {
554
- return [workspace.tagPrefix, ...workspace.legacyIdentities?.map((identity) => identity.tagPrefix) ?? []];
555
- }
556
553
  export {
557
554
  releasePrepareMono
558
555
  };
@@ -1,4 +1,5 @@
1
1
  import { type ValidateAllChangelogOverridesInputs, type ValidateAllChangelogOverridesResult } from './changelogOverrides.ts';
2
+ import type { ChangelogEntry, ReleaseConfig } from './types.ts';
2
3
  export interface ValidateOverridesCommandResult {
3
4
  exitCode: 0 | 1 | 2;
4
5
  message: string;
@@ -6,7 +7,7 @@ export interface ValidateOverridesCommandResult {
6
7
  export interface ValidateOverridesCommandDependencies {
7
8
  discoverWorkspaces?: () => Promise<string[] | undefined>;
8
9
  loadConfig?: () => Promise<unknown>;
9
- collectHashes?: (tagPrefixes: readonly string[], paths?: string[]) => readonly string[];
10
+ buildEntries?: (config: Pick<ReleaseConfig, 'cliffConfigPath' | 'changelogJson'>, tagPattern?: string, includePaths?: string[]) => ChangelogEntry[];
10
11
  validate?: (inputs: ValidateAllChangelogOverridesInputs) => ValidateAllChangelogOverridesResult;
11
12
  }
12
13
  export declare function validateOverridesCommand(dependencies?: ValidateOverridesCommandDependencies): Promise<ValidateOverridesCommandResult>;
@@ -1,15 +1,17 @@
1
+ import { buildChangelogEntries } from "./buildChangelogEntries.js";
1
2
  import {
2
3
  resolveOverridePath,
3
4
  validateAllChangelogOverrides
4
5
  } from "./changelogOverrides.js";
5
6
  import { discoverWorkspaces } from "./discoverWorkspaces.js";
6
- import { getCommitsSinceTarget } from "./getCommitsSinceTarget.js";
7
+ import { buildTagPattern, getAllTagPrefixes } from "./generateChangelogs.js";
7
8
  import { loadConfig, mergeMonorepoConfig, mergeSinglePackageConfig, readRootPackageVersion } from "./loadConfig.js";
8
9
  import { validateConfig } from "./validateConfig.js";
10
+ const SYNTHETIC_VALIDATE_TAG = "validate-only";
9
11
  async function validateOverridesCommand(dependencies = {}) {
10
12
  const discover = dependencies.discoverWorkspaces ?? discoverWorkspaces;
11
13
  const load = dependencies.loadConfig ?? loadConfig;
12
- const collect = dependencies.collectHashes ?? defaultCollectHashes;
14
+ const buildEntries = dependencies.buildEntries ?? defaultBuildEntries;
13
15
  const validate = dependencies.validate ?? validateAllChangelogOverrides;
14
16
  let userConfig;
15
17
  try {
@@ -25,7 +27,7 @@ async function validateOverridesCommand(dependencies = {}) {
25
27
  }
26
28
  let inputs;
27
29
  try {
28
- inputs = discoveredPaths === void 0 ? buildSinglePackageInputs(userConfig, collect) : buildMonorepoInputs(discoveredPaths, userConfig, collect);
30
+ inputs = discoveredPaths === void 0 ? buildSinglePackageInputs(userConfig, buildEntries) : buildMonorepoInputs(discoveredPaths, userConfig, buildEntries);
29
31
  } catch (error) {
30
32
  return { exitCode: 2, message: `Error resolving overrides scope: ${errorMessage(error)}` };
31
33
  }
@@ -60,11 +62,28 @@ function pluralize(count, noun) {
60
62
  function errorMessage(error) {
61
63
  return error instanceof Error ? error.message : String(error);
62
64
  }
63
- function defaultCollectHashes(tagPrefixes, paths) {
64
- if (tagPrefixes.length === 0) {
65
- return [];
65
+ function defaultBuildEntries(config, tagPattern, includePaths) {
66
+ const options = {};
67
+ if (tagPattern !== void 0) {
68
+ options.tagPattern = tagPattern;
66
69
  }
67
- return getCommitsSinceTarget(tagPrefixes, paths).commits.map((commit) => commit.hash);
70
+ if (includePaths !== void 0) {
71
+ options.includePaths = includePaths;
72
+ }
73
+ return buildChangelogEntries(config, SYNTHETIC_VALIDATE_TAG, options);
74
+ }
75
+ function flattenEntriesToHashes(entries) {
76
+ const hashes = [];
77
+ for (const entry of entries) {
78
+ for (const section of entry.sections) {
79
+ for (const item of section.items) {
80
+ if (item.hash !== void 0) {
81
+ hashes.push(item.hash);
82
+ }
83
+ }
84
+ }
85
+ }
86
+ return hashes;
68
87
  }
69
88
  async function loadAndValidateConfig(load) {
70
89
  let rawConfig;
@@ -83,24 +102,21 @@ async function loadAndValidateConfig(load) {
83
102
  }
84
103
  return config;
85
104
  }
86
- function buildSinglePackageInputs(userConfig, collect) {
105
+ function buildSinglePackageInputs(userConfig, buildEntries) {
87
106
  const config = mergeSinglePackageConfig(userConfig);
88
- const hashes = [...collect([config.tagPrefix])];
107
+ const hashes = flattenEntriesToHashes(buildEntries(config));
89
108
  return {
90
109
  project: { filePath: resolveOverridePath("."), hashes }
91
110
  };
92
111
  }
93
- function buildMonorepoInputs(discoveredPaths, userConfig, collect) {
112
+ function buildMonorepoInputs(discoveredPaths, userConfig, buildEntries) {
94
113
  const rootPackage = readRootPackageVersion();
95
114
  const config = mergeMonorepoConfig(discoveredPaths, userConfig, rootPackage);
96
115
  const workspaces = config.workspaces.map((workspace) => {
97
- const tagPrefixes = [
98
- workspace.tagPrefix,
99
- ...workspace.legacyIdentities?.map((identity) => identity.tagPrefix) ?? []
100
- ];
116
+ const tagPattern = buildTagPattern(getAllTagPrefixes(workspace));
101
117
  return {
102
118
  filePath: resolveOverridePath(workspace.workspacePath),
103
- hashes: [...collect(tagPrefixes, workspace.paths)]
119
+ hashes: flattenEntriesToHashes(buildEntries(config, tagPattern, workspace.paths))
104
120
  };
105
121
  });
106
122
  const project = config.project;
@@ -109,7 +125,8 @@ function buildMonorepoInputs(discoveredPaths, userConfig, collect) {
109
125
  };
110
126
  if (project !== void 0) {
111
127
  const contributingPaths = config.workspaces.flatMap((workspace) => workspace.paths);
112
- projectScope.hashes = [...collect([project.tagPrefix], contributingPaths)];
128
+ const projectTagPattern = buildTagPattern([project.tagPrefix]);
129
+ projectScope.hashes = flattenEntriesToHashes(buildEntries(config, projectTagPattern, contributingPaths));
113
130
  }
114
131
  return { project: projectScope, workspaces };
115
132
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@williamthorsen/release-kit",
3
- "version": "5.3.0",
3
+ "version": "5.3.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",
@@ -38,7 +38,7 @@
38
38
  "json-stringify-pretty-compact": "4.0.0",
39
39
  "semver": "7.8.0",
40
40
  "zod": "4.4.3",
41
- "@williamthorsen/nmr-core": "0.3.1"
41
+ "@williamthorsen/nmr-core": "0.3.2"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/js-yaml": "4.0.9",