@williamthorsen/release-kit 2.3.0 → 2.3.2

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,14 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [release-kit-v2.3.2] - 2026-03-28
6
+
7
+ ### Bug fixes
8
+
9
+ - #71 release-kit|fix: Prevent unparseable commits from being silently dropped (#76)
10
+
11
+ Prevents `releasePrepareMono` and `releasePrepare` from silently skipping components whose commits have unparseable messages. Adds ticket-prefix stripping to `parseCommitMessage` (mirroring cliff.toml's `commit_preprocessors`), a patch-floor safety net when commits exist but none parse, and unparseable-commit reporting in `reportPrepare`.
12
+
5
13
  ## [release-kit-v2.3.0] - 2026-03-28
6
14
 
7
15
  ### Features
@@ -39,7 +39,9 @@ filter_unconventional = false
39
39
  split_commits = false
40
40
  # regex for preprocessing the commit messages
41
41
  commit_preprocessors = [
42
- # Strip issue-ticket prefixes like "TOOL-123 " or "AFG-456 "
42
+ # Strip GitHub-style issue prefixes like "#8 " or "#123 "
43
+ { pattern = '^#\d+\s+', replace = "" },
44
+ # Strip Jira-style ticket prefixes like "TOOL-123 " or "AFG-456 "
43
45
  { pattern = '^[A-Z]+-\d+\s+', replace = "" },
44
46
  ]
45
47
  # regex for parsing and grouping commits
package/dist/esm/.cache CHANGED
@@ -1 +1 @@
1
- a59da8f9c8d7a95b776af266dfb8998307bdabf5e0cb68c9ca2a17b9655a5524
1
+ 894421aa6cf2eb13518310f0bb75d5751a6a68ae1fcb068600b25ed30eea9b67
@@ -0,0 +1,7 @@
1
+ import type { Commit, ReleaseType, VersionPatterns, WorkTypeConfig } from './types.ts';
2
+ export interface BumpDetermination {
3
+ releaseType: ReleaseType | undefined;
4
+ parsedCommitCount: number;
5
+ unparseableCommits: Commit[] | undefined;
6
+ }
7
+ export declare function determineBumpFromCommits(commits: Commit[], workTypes: Record<string, WorkTypeConfig>, versionPatterns: VersionPatterns, workspaceAliases: Record<string, string> | undefined): BumpDetermination;
@@ -0,0 +1,26 @@
1
+ import { determineBumpType } from "./determineBumpType.js";
2
+ import { parseCommitMessage } from "./parseCommitMessage.js";
3
+ function determineBumpFromCommits(commits, workTypes, versionPatterns, workspaceAliases) {
4
+ const parsedCommits = [];
5
+ const unparseable = [];
6
+ for (const commit of commits) {
7
+ const parsed = parseCommitMessage(commit.message, commit.hash, workTypes, workspaceAliases);
8
+ if (parsed === void 0) {
9
+ unparseable.push(commit);
10
+ } else {
11
+ parsedCommits.push(parsed);
12
+ }
13
+ }
14
+ let releaseType = determineBumpType(parsedCommits, workTypes, versionPatterns);
15
+ if (releaseType === void 0 && commits.length > 0) {
16
+ releaseType = "patch";
17
+ }
18
+ return {
19
+ releaseType,
20
+ parsedCommitCount: parsedCommits.length,
21
+ unparseableCommits: unparseable.length > 0 ? unparseable : void 0
22
+ };
23
+ }
24
+ export {
25
+ determineBumpFromCommits
26
+ };
@@ -16,7 +16,7 @@ export { determineBumpType } from './determineBumpType.ts';
16
16
  export { discoverWorkspaces } from './discoverWorkspaces.ts';
17
17
  export { generateChangelog, generateChangelogs } from './generateChangelogs.ts';
18
18
  export { getCommitsSinceTarget } from './getCommitsSinceTarget.ts';
19
- export { parseCommitMessage } from './parseCommitMessage.ts';
19
+ export { COMMIT_PREPROCESSOR_PATTERNS, parseCommitMessage } from './parseCommitMessage.ts';
20
20
  export { RELEASE_TAGS_FILE, writeReleaseTags } from './prepareCommand.ts';
21
21
  export { publish } from './publish.ts';
22
22
  export { releasePrepare } from './releasePrepare.ts';
package/dist/esm/index.js CHANGED
@@ -8,7 +8,7 @@ import { determineBumpType } from "./determineBumpType.js";
8
8
  import { discoverWorkspaces } from "./discoverWorkspaces.js";
9
9
  import { generateChangelog, generateChangelogs } from "./generateChangelogs.js";
10
10
  import { getCommitsSinceTarget } from "./getCommitsSinceTarget.js";
11
- import { parseCommitMessage } from "./parseCommitMessage.js";
11
+ import { COMMIT_PREPROCESSOR_PATTERNS, parseCommitMessage } from "./parseCommitMessage.js";
12
12
  import { RELEASE_TAGS_FILE, writeReleaseTags } from "./prepareCommand.js";
13
13
  import { publish } from "./publish.js";
14
14
  import { releasePrepare } from "./releasePrepare.js";
@@ -16,6 +16,7 @@ import { releasePrepareMono } from "./releasePrepareMono.js";
16
16
  import { reportPrepare } from "./reportPrepare.js";
17
17
  import { resolveReleaseTags } from "./resolveReleaseTags.js";
18
18
  export {
19
+ COMMIT_PREPROCESSOR_PATTERNS,
19
20
  DEFAULT_VERSION_PATTERNS,
20
21
  DEFAULT_WORK_TYPES,
21
22
  RELEASE_TAGS_FILE,
@@ -1,2 +1,3 @@
1
1
  import type { ParsedCommit, WorkTypeConfig } from './types.ts';
2
+ export declare const COMMIT_PREPROCESSOR_PATTERNS: readonly RegExp[];
2
3
  export declare function parseCommitMessage(message: string, hash: string, workTypes: Record<string, WorkTypeConfig>, workspaceAliases?: Record<string, string>): ParsedCommit | undefined;
@@ -1,5 +1,7 @@
1
+ const COMMIT_PREPROCESSOR_PATTERNS = [/^#\d+\s+/, /^[A-Z]+-\d+\s+/];
1
2
  function parseCommitMessage(message, hash, workTypes, workspaceAliases) {
2
- const match = message.match(/^(?:([^|]+)\|)?(\w+)(!)?:\s*(.*)$/);
3
+ const stripped = stripTicketPrefix(message);
4
+ const match = stripped.match(/^(?:([^|]+)\|)?(\w+)(!)?:\s*(.*)$/);
3
5
  if (!match) {
4
6
  return void 0;
5
7
  }
@@ -41,6 +43,14 @@ function resolveType(rawType, workTypes) {
41
43
  }
42
44
  return void 0;
43
45
  }
46
+ function stripTicketPrefix(message) {
47
+ let result = message;
48
+ for (const pattern of COMMIT_PREPROCESSOR_PATTERNS) {
49
+ result = result.replace(pattern, "");
50
+ }
51
+ return result;
52
+ }
44
53
  export {
54
+ COMMIT_PREPROCESSOR_PATTERNS,
45
55
  parseCommitMessage
46
56
  };
@@ -1,11 +1,10 @@
1
1
  import { execSync } from "node:child_process";
2
2
  import { bumpAllVersions } from "./bumpAllVersions.js";
3
3
  import { DEFAULT_VERSION_PATTERNS, DEFAULT_WORK_TYPES } from "./defaults.js";
4
- import { determineBumpType } from "./determineBumpType.js";
4
+ import { determineBumpFromCommits } from "./determineBumpFromCommits.js";
5
5
  import { generateChangelogs } from "./generateChangelogs.js";
6
6
  import { getCommitsSinceTarget } from "./getCommitsSinceTarget.js";
7
7
  import { hasPrettierConfig } from "./hasPrettierConfig.js";
8
- import { parseCommitMessage } from "./parseCommitMessage.js";
9
8
  function releasePrepare(config, options) {
10
9
  const { dryRun, bumpOverride } = options;
11
10
  const workTypes = config.workTypes ?? { ...DEFAULT_WORK_TYPES };
@@ -13,10 +12,12 @@ function releasePrepare(config, options) {
13
12
  const { tag, commits } = getCommitsSinceTarget(config.tagPrefix);
14
13
  let releaseType;
15
14
  let parsedCommitCount;
15
+ let unparseableCommits;
16
16
  if (bumpOverride === void 0) {
17
- const parsedCommits = commits.map((c) => parseCommitMessage(c.message, c.hash, workTypes, config.workspaceAliases)).filter((c) => c !== void 0);
18
- parsedCommitCount = parsedCommits.length;
19
- releaseType = determineBumpType(parsedCommits, workTypes, versionPatterns);
17
+ const determination = determineBumpFromCommits(commits, workTypes, versionPatterns, config.workspaceAliases);
18
+ parsedCommitCount = determination.parsedCommitCount;
19
+ unparseableCommits = determination.unparseableCommits;
20
+ releaseType = determination.releaseType;
20
21
  } else {
21
22
  releaseType = bumpOverride;
22
23
  }
@@ -28,6 +29,7 @@ function releasePrepare(config, options) {
28
29
  previousTag: tag,
29
30
  commitCount: commits.length,
30
31
  parsedCommitCount,
32
+ unparseableCommits,
31
33
  bumpedFiles: [],
32
34
  changelogFiles: [],
33
35
  skipReason: "No release-worthy changes found. Skipping."
@@ -70,7 +72,8 @@ function releasePrepare(config, options) {
70
72
  newVersion: bump.newVersion,
71
73
  tag: newTag,
72
74
  bumpedFiles: bump.files,
73
- changelogFiles
75
+ changelogFiles,
76
+ unparseableCommits
74
77
  }
75
78
  ],
76
79
  tags: [newTag],
@@ -1,11 +1,10 @@
1
1
  import { execSync } from "node:child_process";
2
2
  import { bumpAllVersions } from "./bumpAllVersions.js";
3
3
  import { DEFAULT_VERSION_PATTERNS, DEFAULT_WORK_TYPES } from "./defaults.js";
4
- import { determineBumpType } from "./determineBumpType.js";
4
+ import { determineBumpFromCommits } from "./determineBumpFromCommits.js";
5
5
  import { generateChangelog } from "./generateChangelogs.js";
6
6
  import { getCommitsSinceTarget } from "./getCommitsSinceTarget.js";
7
7
  import { hasPrettierConfig } from "./hasPrettierConfig.js";
8
- import { parseCommitMessage } from "./parseCommitMessage.js";
9
8
  function releasePrepareMono(config, options) {
10
9
  const { dryRun, force, bumpOverride } = options;
11
10
  const workTypes = config.workTypes ?? { ...DEFAULT_WORK_TYPES };
@@ -31,10 +30,12 @@ function releasePrepareMono(config, options) {
31
30
  }
32
31
  let releaseType;
33
32
  let parsedCommitCount;
33
+ let unparseableCommits;
34
34
  if (bumpOverride === void 0) {
35
- const parsedCommits = commits.map((c) => parseCommitMessage(c.message, c.hash, workTypes, config.workspaceAliases)).filter((c) => c !== void 0);
36
- parsedCommitCount = parsedCommits.length;
37
- releaseType = determineBumpType(parsedCommits, workTypes, versionPatterns);
35
+ const determination = determineBumpFromCommits(commits, workTypes, versionPatterns, config.workspaceAliases);
36
+ parsedCommitCount = determination.parsedCommitCount;
37
+ unparseableCommits = determination.unparseableCommits;
38
+ releaseType = determination.releaseType;
38
39
  } else {
39
40
  releaseType = bumpOverride;
40
41
  }
@@ -45,6 +46,7 @@ function releasePrepareMono(config, options) {
45
46
  previousTag: tag,
46
47
  commitCount: commits.length,
47
48
  parsedCommitCount,
49
+ unparseableCommits,
48
50
  bumpedFiles: [],
49
51
  changelogFiles: [],
50
52
  skipReason: `No release-worthy changes for ${name} ${since}. Skipping.`
@@ -72,7 +74,8 @@ function releasePrepareMono(config, options) {
72
74
  newVersion: bump.newVersion,
73
75
  tag: newTag,
74
76
  bumpedFiles: bump.files,
75
- changelogFiles
77
+ changelogFiles,
78
+ unparseableCommits
76
79
  });
77
80
  }
78
81
  const formatCommandStr = config.formatCommand ?? (hasPrettierConfig() ? "npx prettier --write" : void 0);
@@ -17,6 +17,7 @@ function formatSingleComponent(result) {
17
17
  if (component.parsedCommitCount !== void 0) {
18
18
  lines.push(dim(` Parsed ${component.parsedCommitCount} typed commits`));
19
19
  }
20
+ formatUnparseableWarning(lines, component);
20
21
  if (component.status === "skipped") {
21
22
  lines.push(`\u23ED\uFE0F ${component.skipReason ?? "Skipped"}`);
22
23
  return lines.join("\n");
@@ -56,6 +57,7 @@ ${sectionHeader(component.name)}`);
56
57
  if (component.parsedCommitCount !== void 0) {
57
58
  lines.push(dim(` Parsed ${component.parsedCommitCount} typed commits`));
58
59
  }
60
+ formatUnparseableWarning(lines, component, " ");
59
61
  if (component.parsedCommitCount === void 0 && component.releaseType !== void 0) {
60
62
  lines.push(` Using bump override: ${component.releaseType}`);
61
63
  }
@@ -103,6 +105,21 @@ function formatChangelogFiles(lines, component, dryRun, indent = "") {
103
105
  }
104
106
  }
105
107
  }
108
+ function formatUnparseableWarning(lines, component, indent = "") {
109
+ const unparseable = component.unparseableCommits;
110
+ if (unparseable === void 0 || unparseable.length === 0) {
111
+ return;
112
+ }
113
+ const count = unparseable.length;
114
+ const isPatchFloor = component.parsedCommitCount === 0;
115
+ const suffix = isPatchFloor ? " (defaulting to patch bump)" : "";
116
+ lines.push(`${indent} \u26A0\uFE0F ${count} commit${count === 1 ? "" : "s"} could not be parsed${suffix}`);
117
+ for (const commit of unparseable) {
118
+ const shortHash = commit.hash.slice(0, 7);
119
+ const truncatedMessage = commit.message.length > 72 ? `${commit.message.slice(0, 69)}...` : commit.message;
120
+ lines.push(`${indent} \xB7 ${shortHash} ${truncatedMessage}`);
121
+ }
122
+ }
106
123
  function formatFormatCommand(lines, result) {
107
124
  if (result.formatCommand === void 0) {
108
125
  return;
@@ -16,6 +16,7 @@ export interface ComponentPrepareResult {
16
16
  tag?: string | undefined;
17
17
  bumpedFiles: string[];
18
18
  changelogFiles: string[];
19
+ unparseableCommits?: Commit[] | undefined;
19
20
  skipReason?: string | undefined;
20
21
  }
21
22
  export interface PrepareResult {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@williamthorsen/release-kit",
3
- "version": "2.3.0",
3
+ "version": "2.3.2",
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",
@@ -34,7 +34,7 @@
34
34
  "glob": "13.0.6",
35
35
  "jiti": "2.6.1",
36
36
  "js-yaml": "4.1.1",
37
- "@williamthorsen/node-monorepo-core": "0.2.0"
37
+ "@williamthorsen/node-monorepo-core": "0.2.1"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/js-yaml": "4.0.9",