@williamthorsen/release-kit 2.3.2 → 4.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.
Files changed (43) hide show
  1. package/CHANGELOG.md +54 -38
  2. package/README.md +29 -12
  3. package/cliff.toml.template +13 -12
  4. package/dist/esm/.cache +1 -1
  5. package/dist/esm/bin/release-kit.js +28 -0
  6. package/dist/esm/buildDependencyGraph.d.ts +7 -0
  7. package/dist/esm/buildDependencyGraph.js +59 -0
  8. package/dist/esm/buildReleaseSummary.d.ts +2 -0
  9. package/dist/esm/buildReleaseSummary.js +22 -0
  10. package/dist/esm/commitCommand.d.ts +1 -0
  11. package/dist/esm/commitCommand.js +55 -0
  12. package/dist/esm/createTags.js +12 -1
  13. package/dist/esm/determineBumpFromCommits.d.ts +1 -1
  14. package/dist/esm/determineBumpFromCommits.js +2 -2
  15. package/dist/esm/generateChangelogs.d.ts +2 -0
  16. package/dist/esm/generateChangelogs.js +9 -1
  17. package/dist/esm/index.d.ts +4 -1
  18. package/dist/esm/index.js +8 -1
  19. package/dist/esm/init/initCommand.js +4 -2
  20. package/dist/esm/init/scaffold.js +3 -2
  21. package/dist/esm/init/templates.d.ts +1 -0
  22. package/dist/esm/init/templates.js +24 -2
  23. package/dist/esm/loadConfig.js +6 -6
  24. package/dist/esm/parseCommitMessage.d.ts +1 -1
  25. package/dist/esm/parseCommitMessage.js +9 -7
  26. package/dist/esm/prepareCommand.d.ts +1 -0
  27. package/dist/esm/prepareCommand.js +20 -0
  28. package/dist/esm/propagateBumps.d.ts +8 -0
  29. package/dist/esm/propagateBumps.js +54 -0
  30. package/dist/esm/publish.d.ts +1 -0
  31. package/dist/esm/publish.js +6 -3
  32. package/dist/esm/publishCommand.js +3 -2
  33. package/dist/esm/releasePrepare.js +2 -1
  34. package/dist/esm/releasePrepareMono.js +237 -48
  35. package/dist/esm/reportPrepare.js +29 -3
  36. package/dist/esm/stripScope.d.ts +1 -0
  37. package/dist/esm/stripScope.js +24 -0
  38. package/dist/esm/sync-labels/templates.js +1 -1
  39. package/dist/esm/types.d.ts +11 -4
  40. package/dist/esm/validateConfig.js +6 -6
  41. package/dist/esm/writeSyntheticChangelog.d.ts +9 -0
  42. package/dist/esm/writeSyntheticChangelog.js +27 -0
  43. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,40 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [release-kit-v3.0.0] - 2026-03-29
6
+
7
+ ### Bug fixes
8
+
9
+ - #68 release-kit|fix: Pass tag pattern to git-cliff based on tagPrefix (#77)
10
+
11
+ Fixes the issue that git-cliff was processing the entire commit history on every run instead of only commits since the last release.
12
+
13
+ Constructs the pattern from `tagPrefix` at invocation time (e.g., `release-kit-v` → `release-kit-v[0-9].*`) and pass it via `--tag-pattern`, which overrides the config file default.
14
+
15
+ - #72 release-kit|fix: Propagate version bumps to workspace dependents (#80)
16
+
17
+ Restructures `releasePrepareMono` from a single-pass loop into a phased pipeline that automatically patch-bumps workspace dependents when a component is released. A reverse dependency graph is built from `workspace:` references in `dependencies` and `peerDependencies`, then BFS propagation walks upward from bumped components to their dependents. Propagated-only components receive synthetic changelog entries instead of git-cliff invocations.
18
+
19
+ ### Features
20
+
21
+ - #73 release-kit|feat!: Support conventional-commit format in commit parsing (#85)
22
+
23
+ Adds support for the conventional commits format (`type(scope): description`) alongside the existing pipe-prefixed format (`scope|type: description`) in release-kit's commit parser. Renames `workspace` to `scope` throughout release-kit types, config, validation, and consumers.
24
+
25
+ - #70 release-kit|feat: Add commit command for local release flow (#89)
26
+
27
+ Adds a `release-kit commit` command that centralizes the release commit step between `prepare` and `tag`. The command reads tag names and a per-component commit summary from temporary files written by `prepare`, stages all changes, and creates a formatted commit. Two new utilities — `stripScope` and `buildReleaseSummary` — support building the commit body by stripping redundant scope indicators and formatting commits under their component headings. The CI workflow is simplified to use `release-kit commit` and `release-kit tag` instead of inline shell logic.
28
+
29
+ - #69 release-kit|feat: Add CI publish workflow with OIDC trusted publishing (#90)
30
+
31
+ Adds automated npm publication via a tag-push-triggered GitHub Actions workflow using OIDC trusted publishing. Extends `release-kit publish` with a `--provenance` flag and `release-kit init` with publish workflow scaffolding.
32
+
33
+ - #91 release-kit|feat: Make --provenance opt-in to support private repos (#94)
34
+
35
+ Adds a `provenance` boolean input (default `false`) to the reusable `publish-workflow.yaml` so private repos using OIDC trusted publishing no longer fail at publish time. The `--provenance` flag is only passed to `release-kit publish` when the caller sets `provenance: true`.
36
+
37
+ Updates the scaffolded `publish.yaml` template to include `provenance: false` with an inline comment guiding public repos to opt in. Expand the `release-kit init` next-steps output with hints about the provenance setting and trusted publisher registration. Set `provenance: true` in this repo's own `publish.yaml` since it is public.
38
+
5
39
  ## [release-kit-v2.3.2] - 2026-03-28
6
40
 
7
41
  ### Bug fixes
@@ -14,10 +48,6 @@ Prevents `releasePrepareMono` and `releasePrepare` from silently skipping compon
14
48
 
15
49
  ### Features
16
50
 
17
- - #8 feat: Add shared writeFileWithCheck utility and overwrite reporting (#66)
18
-
19
- Extracts three duplicated `writeIfAbsent` implementations and two duplicated terminal helper sets into shared utilities in `@williamthorsen/node-monorepo-core`, then migrates all consumers (`release-kit init`, `preflight init`, `sync-labels`) to use them. All init commands now report which files were created, overwritten, skipped, or failed — including when `--force` replaces existing files.
20
-
21
51
  - #11 release-kit|feat: Separate tag-write errors from release preparation errors (#67)
22
52
 
23
53
  When tag-file writing fails, the error message now reads "Error writing release tags:" instead of the misleading "Error preparing release:", which only appeared because both operations shared a single try/catch.
@@ -40,8 +70,28 @@ Adds three unit tests to `releasePrepare.unit.test.ts` covering previously untes
40
70
 
41
71
  ## [release-kit-v2.2.0] - 2026-03-27
42
72
 
73
+ ### Dependencies
74
+
75
+ - Root|deps: Add release-kit as root devDependency
76
+
77
+ Make `npx release-kit` and `pnpm exec release-kit` resolve within
78
+ this repo by adding a `workspace:*` dependency that symlinks the bin.
79
+
43
80
  ### Features
44
81
 
82
+ - #23 release-kit|feat: Add sync-labels command (#33)
83
+
84
+ Add a `release-kit sync-labels` command group with three subcommands (`init`, `generate`, `sync`) for declarative GitHub label management in monorepos. Bundle a reusable GitHub Actions workflow and composable label presets with the release-kit package. Introduce a `findPackageRoot` utility to replace fragile hardcoded path resolutions across the codebase.
85
+
86
+ - #34 release-kit|feat: Report up-to-date status for unchanged init files (#35)
87
+
88
+ `release-kit init` now compares existing file content against the default before reporting status. When an existing file is identical to the default (after normalizing trailing whitespace), it reports `✅ (up to date)` instead of the misleading `⚠️ (already exists)`.
89
+
90
+ - Release-workflow|feat: Accept force input
91
+
92
+ Pass `--force` to the prepare command so callers can force a version
93
+ bump even when there are no release-worthy changes.
94
+
45
95
  - #27 release-kit|feat: Auto-detect Prettier for CHANGELOG formatting (#36)
46
96
 
47
97
  When `formatCommand` is not configured, release-kit now auto-detects whether the repo uses Prettier by checking for config files (`.prettierrc*`, `prettier.config.*`) or a `"prettier"` key in root `package.json`. If found, it defaults to `npx prettier --write` on generated files. If not found, formatting is skipped.
@@ -62,18 +112,8 @@ Removes the ability to customize `tagPrefix` per component, enforcing the determ
62
112
 
63
113
  Adds ANSI formatting and emoji markers to the `release-kit prepare` command output. Progress chatter is dimmed, key results (version bumps, release tags, completion status) are highlighted with bold text and emoji, and monorepo components are separated by box-drawing section headers.
64
114
 
65
- - #59 feat: Extract nmr CLI from core package (#61)
66
-
67
- Extracts all nmr CLI code from `packages/core` into a new `packages/nmr` package (`@williamthorsen/nmr`). Core is reduced to an empty shared-library shell ready for cross-cutting utilities. All internal references are rewired and the full build/test pipeline passes.
68
-
69
- Scopes: core, nmr
70
-
71
115
  ### Refactoring
72
116
 
73
- - #43 refactor: Replace dist bin targets with thin wrapper scripts (#48)
74
-
75
- The `bin` entries in `packages/core` and `packages/release-kit` pointed directly into `dist/esm/`, causing `pnpm install` to emit "Failed to create bin" warnings in fresh worktrees where `dist/` does not yet exist. Each bin entry now points to a committed wrapper script in `bin/` that dynamically imports the real entry point. The `files` field in both packages includes `bin` so the wrappers are published.
76
-
77
117
  - #53 release-kit|refactor: Separate presentation from logic in prepare workflow (#57)
78
118
 
79
119
  Extracts all `console.info` calls from the prepare workflow's logic functions (`bumpAllVersions`, `generateChangelogs`, `releasePrepare`, `releasePrepareMono`) into a dedicated `reportPrepare` formatter. Logic functions now return structured result types (`BumpResult`, `ComponentPrepareResult`, `PrepareResult`). The legacy `runReleasePrepare` entry point is retired, with its utilities absorbed into `prepareCommand`.
@@ -96,30 +136,6 @@ Also adds a `findAllCliffOutputPaths()` test helper that collects the `--output`
96
136
 
97
137
  Replaces the legacy workspace script runner and ~25 root `package.json` scripts with `nmr`, the monorepo's own context-aware script runner. Root scripts are reduced to 4 (`prepare`, `postinstall`, `ci`, `bootstrap`), packages use direct build commands for bootstrap, and release-kit declares tier-3 test overrides for its integration test configs.
98
138
 
99
- ## [release-workflow-v1] - 2026-03-19
100
-
101
- ### Dependencies
102
-
103
- - Root|deps: Add release-kit as root devDependency
104
-
105
- Make `npx release-kit` and `pnpm exec release-kit` resolve within
106
- this repo by adding a `workspace:*` dependency that symlinks the bin.
107
-
108
- ### Features
109
-
110
- - #23 release-kit|feat: Add sync-labels command (#33)
111
-
112
- Add a `release-kit sync-labels` command group with three subcommands (`init`, `generate`, `sync`) for declarative GitHub label management in monorepos. Bundle a reusable GitHub Actions workflow and composable label presets with the release-kit package. Introduce a `findPackageRoot` utility to replace fragile hardcoded path resolutions across the codebase.
113
-
114
- - #34 release-kit|feat: Report up-to-date status for unchanged init files (#35)
115
-
116
- `release-kit init` now compares existing file content against the default before reporting status. When an existing file is identical to the default (after normalizing trailing whitespace), it reports `✅ (up to date)` instead of the misleading `⚠️ (already exists)`.
117
-
118
- - Release-workflow|feat: Accept force input
119
-
120
- Pass `--force` to the prepare command so callers can force a version
121
- bump even when there are no release-worthy changes.
122
-
123
139
  ## [release-kit-v2.1.0] - 2026-03-17
124
140
 
125
141
  ### Features
package/README.md CHANGED
@@ -68,6 +68,21 @@ Scaffolded files:
68
68
  - `.config/release-kit.config.ts` — starter config with commented-out customization examples (with `--with-config`)
69
69
  - `.config/git-cliff.toml` — copied from the bundled template (with `--with-config`)
70
70
 
71
+ ### `release-kit sync-labels`
72
+
73
+ Manage GitHub label definitions via config-driven YAML files.
74
+
75
+ ```
76
+ Usage: release-kit sync-labels <command> [options]
77
+
78
+ Commands:
79
+ init Scaffold sync-labels config and caller workflow
80
+ generate Generate .github/labels.yaml from config
81
+ sync Generate labels and push to GitHub (runs generate + gh label sync)
82
+ ```
83
+
84
+ `init` scaffolds `.config/sync-labels.config.ts` with auto-detected workspace scope labels and a `.github/workflows/sync-labels.yaml` caller workflow. `generate` reads the config and writes `.github/labels.yaml`. `sync` runs `generate` and then applies labels to the GitHub repo.
85
+
71
86
  ## Configuration
72
87
 
73
88
  Configuration is optional. The CLI works out of the box by auto-discovering workspaces and applying defaults. Create `.config/release-kit.config.ts` only when you need to customize behavior.
@@ -98,14 +113,14 @@ The config file supports both `export default config` and `export const config =
98
113
 
99
114
  ### `ReleaseKitConfig` reference
100
115
 
101
- | Field | Type | Description |
102
- | ------------------ | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
103
- | `cliffConfigPath` | `string` | Explicit path to cliff config. If omitted, resolved automatically: `.config/git-cliff.toml` → `cliff.toml` → bundled template |
104
- | `components` | `ComponentOverride[]` | Override or exclude discovered components (matched by `dir`) |
105
- | `formatCommand` | `string` | Shell command to run after changelog generation; modified file paths are appended as arguments |
106
- | `versionPatterns` | `VersionPatterns` | Rules for which commit types trigger major/minor bumps |
107
- | `workspaceAliases` | `Record<string, string>` | Maps shorthand workspace names to canonical names in commits |
108
- | `workTypes` | `Record<string, WorkTypeConfig>` | Work type definitions, merged with defaults by key |
116
+ | Field | Type | Description |
117
+ | ----------------- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
118
+ | `cliffConfigPath` | `string` | Explicit path to cliff config. If omitted, resolved automatically: `.config/git-cliff.toml` → `cliff.toml` → bundled template |
119
+ | `components` | `ComponentOverride[]` | Override or exclude discovered components (matched by `dir`) |
120
+ | `formatCommand` | `string` | Shell command to run after changelog generation; modified file paths are appended as arguments |
121
+ | `versionPatterns` | `VersionPatterns` | Rules for which commit types trigger major/minor bumps |
122
+ | `scopeAliases` | `Record<string, string>` | Maps shorthand scope names to canonical names in commits |
123
+ | `workTypes` | `Record<string, WorkTypeConfig>` | Work type definitions, merged with defaults by key |
109
124
 
110
125
  All fields are optional.
111
126
 
@@ -154,12 +169,14 @@ release-kit parses commits in these formats:
154
169
 
155
170
  ```
156
171
  type: description # e.g., feat: add utility
157
- type(scope): description # e.g., fix(parser): handle edge case
158
- workspace|type: description # e.g., arrays|feat: add compact function
159
- !type: description # breaking change (triggers major bump)
172
+ scope|type: description # e.g., arrays|feat: add compact function
173
+ type(scope): description # e.g., feat(arrays): add compact function
174
+ type!: description # breaking change (triggers major bump)
175
+ scope|type!: description # scoped breaking change
176
+ type(scope)!: description # conventional scoped breaking change
160
177
  ```
161
178
 
162
- The `workspace|type:` format scopes a commit to a specific workspace in a monorepo. Use `workspaceAliases` in your config to map shorthand names to canonical workspace names.
179
+ The `scope|type:` format scopes a commit to a specific component in a monorepo. Use `scopeAliases` in your config to map shorthand names to canonical scope names.
163
180
 
164
181
  ## Using `component()` for manual configuration
165
182
 
@@ -45,19 +45,20 @@ commit_preprocessors = [
45
45
  { pattern = '^[A-Z]+-\d+\s+', replace = "" },
46
46
  ]
47
47
  # regex for parsing and grouping commits
48
- # Supports both "type: description" and "workspace|type: description" formats
48
+ # Supports "type: desc", "scope|type: desc", and "type(scope): desc" formats
49
49
  commit_parsers = [
50
- { message = "^.*\\|?fix(!)?:", group = "Bug fixes" },
51
- { message = "^.*\\|?feat(!)?:", group = "Features" },
52
- { message = "^.*\\|?feature(!)?:", group = "Features" },
53
- { message = "^.*\\|?internal(!)?:", group = "Internal" },
54
- { message = "^.*\\|?refactor(!)?:", group = "Refactoring" },
55
- { message = "^.*\\|?tests?(!)?:", group = "Tests" },
56
- { message = "^.*\\|?tooling(!)?:", group = "Tooling" },
57
- { message = "^.*\\|?ci(!)?:", group = "CI" },
58
- { message = "^.*\\|?deps?(!)?:", group = "Dependencies" },
59
- { message = "^.*\\|?docs?(!)?:", group = "Documentation" },
60
- { message = "^.*\\|?fmt(!)?:", group = "Formatting" },
50
+ { message = "^(.*\\|)?fix(\\(.*\\))?(!)?:", group = "Bug fixes" },
51
+ { message = "^(.*\\|)?bugfix(\\(.*\\))?(!)?:", group = "Bug fixes" },
52
+ { message = "^(.*\\|)?feat(\\(.*\\))?(!)?:", group = "Features" },
53
+ { message = "^(.*\\|)?feature(\\(.*\\))?(!)?:", group = "Features" },
54
+ { message = "^(.*\\|)?internal(\\(.*\\))?(!)?:", group = "Internal" },
55
+ { message = "^(.*\\|)?refactor(\\(.*\\))?(!)?:", group = "Refactoring" },
56
+ { message = "^(.*\\|)?tests?(\\(.*\\))?(!)?:", group = "Tests" },
57
+ { message = "^(.*\\|)?tooling(\\(.*\\))?(!)?:", group = "Tooling" },
58
+ { message = "^(.*\\|)?ci(\\(.*\\))?(!)?:", group = "CI" },
59
+ { message = "^(.*\\|)?deps?(\\(.*\\))?(!)?:", group = "Dependencies" },
60
+ { message = "^(.*\\|)?docs?(\\(.*\\))?(!)?:", group = "Documentation" },
61
+ { message = "^(.*\\|)?fmt(\\(.*\\))?(!)?:", group = "Formatting" },
61
62
  ]
62
63
  # protect breaking changes from being skipped due to matching a skipping commit_parser
63
64
  protect_breaking_commits = false
package/dist/esm/.cache CHANGED
@@ -1 +1 @@
1
- 894421aa6cf2eb13518310f0bb75d5751a6a68ae1fcb068600b25ed30eea9b67
1
+ eda8d9c3d3574c46e1afbeccbda04ffac0675bd9d30270ab8644fc48477ace14
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { commitCommand } from "../commitCommand.js";
2
3
  import { initCommand } from "../init/initCommand.js";
3
4
  import { prepareCommand } from "../prepareCommand.js";
4
5
  import { publishCommand } from "../publishCommand.js";
@@ -12,6 +13,7 @@ Usage: release-kit <command> [options]
12
13
 
13
14
  Commands:
14
15
  prepare Run release preparation (auto-discovers workspaces)
16
+ commit Stage changes and create the release commit
15
17
  tag Create annotated git tags from the tags file
16
18
  publish Publish packages with release tags on HEAD
17
19
  init Initialize release-kit in the current repository
@@ -97,6 +99,18 @@ Options:
97
99
  --help, -h Show this help message
98
100
  `);
99
101
  }
102
+ function showCommitHelp() {
103
+ console.info(`
104
+ Usage: release-kit commit [options]
105
+
106
+ Stage all changes and create the release commit using tags and summary
107
+ produced by \`prepare\`.
108
+
109
+ Options:
110
+ --dry-run Preview the commit message without creating it
111
+ --help, -h Show this help message
112
+ `);
113
+ }
100
114
  function showTagHelp() {
101
115
  console.info(`
102
116
  Usage: release-kit tag [options]
@@ -119,6 +133,7 @@ Options:
119
133
  --dry-run Preview without publishing
120
134
  --no-git-checks Skip git checks (pnpm only)
121
135
  --only=name1,name2 Only publish the named packages (comma-separated, monorepo only)
136
+ --provenance Generate provenance statement (requires OIDC, not supported by classic yarn)
122
137
  --help, -h Show this help message
123
138
  `);
124
139
  }
@@ -137,6 +152,19 @@ if (command === "prepare") {
137
152
  await prepareCommand(flags);
138
153
  process.exit(0);
139
154
  }
155
+ if (command === "commit") {
156
+ if (flags.some((f) => f === "--help" || f === "-h")) {
157
+ showCommitHelp();
158
+ process.exit(0);
159
+ }
160
+ try {
161
+ commitCommand(flags);
162
+ } catch (error) {
163
+ console.error(error instanceof Error ? error.message : String(error));
164
+ process.exit(1);
165
+ }
166
+ process.exit(0);
167
+ }
140
168
  if (command === "tag") {
141
169
  if (flags.some((f) => f === "--help" || f === "-h")) {
142
170
  showTagHelp();
@@ -0,0 +1,7 @@
1
+ import type { ComponentConfig } from './types.ts';
2
+ export interface DependencyGraph {
3
+ packageNameToDir: Map<string, string>;
4
+ dirToPackageName: Map<string, string>;
5
+ dependentsOf: Map<string, ComponentConfig[]>;
6
+ }
7
+ export declare function buildDependencyGraph(components: readonly ComponentConfig[]): DependencyGraph;
@@ -0,0 +1,59 @@
1
+ import { readFileSync } from "node:fs";
2
+ function isPackageJsonSubset(value) {
3
+ return typeof value === "object" && value !== null;
4
+ }
5
+ function buildDependencyGraph(components) {
6
+ const packageNameToDir = /* @__PURE__ */ new Map();
7
+ const dirToPackageName = /* @__PURE__ */ new Map();
8
+ const dependentsOf = /* @__PURE__ */ new Map();
9
+ const componentPackages = /* @__PURE__ */ new Map();
10
+ for (const component of components) {
11
+ const primaryPackageFile = component.packageFiles[0];
12
+ if (primaryPackageFile === void 0) {
13
+ continue;
14
+ }
15
+ const pkg = readPackageJsonSubset(primaryPackageFile);
16
+ componentPackages.set(component, pkg);
17
+ if (pkg.name === void 0) {
18
+ continue;
19
+ }
20
+ packageNameToDir.set(pkg.name, component.dir);
21
+ dirToPackageName.set(component.dir, pkg.name);
22
+ }
23
+ for (const [component, pkg] of componentPackages) {
24
+ const allDeps = { ...pkg.dependencies, ...pkg.peerDependencies };
25
+ for (const [depName, depVersion] of Object.entries(allDeps)) {
26
+ if (typeof depVersion !== "string" || !depVersion.startsWith("workspace:")) {
27
+ continue;
28
+ }
29
+ const existing = dependentsOf.get(depName);
30
+ if (existing === void 0) {
31
+ dependentsOf.set(depName, [component]);
32
+ } else {
33
+ existing.push(component);
34
+ }
35
+ }
36
+ }
37
+ return { packageNameToDir, dirToPackageName, dependentsOf };
38
+ }
39
+ function readPackageJsonSubset(filePath) {
40
+ let content;
41
+ try {
42
+ content = readFileSync(filePath, "utf8");
43
+ } catch (error) {
44
+ throw new Error(`Failed to read ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
45
+ }
46
+ let parsed;
47
+ try {
48
+ parsed = JSON.parse(content);
49
+ } catch (error) {
50
+ throw new Error(`Failed to parse JSON in ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
51
+ }
52
+ if (!isPackageJsonSubset(parsed)) {
53
+ throw new Error(`Invalid package.json at ${filePath}`);
54
+ }
55
+ return parsed;
56
+ }
57
+ export {
58
+ buildDependencyGraph
59
+ };
@@ -0,0 +1,2 @@
1
+ import type { PrepareResult } from './types.ts';
2
+ export declare function buildReleaseSummary(result: PrepareResult): string;
@@ -0,0 +1,22 @@
1
+ import { stripScope } from "./stripScope.js";
2
+ function buildReleaseSummary(result) {
3
+ const sections = [];
4
+ for (const component of result.components) {
5
+ if (component.status !== "released" || component.tag === void 0) {
6
+ continue;
7
+ }
8
+ const commits = component.commits;
9
+ if (commits === void 0 || commits.length === 0) {
10
+ continue;
11
+ }
12
+ const lines = [component.tag];
13
+ for (const commit of commits) {
14
+ lines.push(`- ${stripScope(commit.message)}`);
15
+ }
16
+ sections.push(lines.join("\n"));
17
+ }
18
+ return sections.join("\n\n");
19
+ }
20
+ export {
21
+ buildReleaseSummary
22
+ };
@@ -0,0 +1 @@
1
+ export declare function commitCommand(argv: string[]): void;
@@ -0,0 +1,55 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { readFileSync } from "node:fs";
3
+ import { RELEASE_SUMMARY_FILE, RELEASE_TAGS_FILE } from "./prepareCommand.js";
4
+ function commitCommand(argv) {
5
+ const knownFlags = /* @__PURE__ */ new Set(["--dry-run"]);
6
+ const unknownFlags = argv.filter((f) => !knownFlags.has(f));
7
+ if (unknownFlags.length > 0) {
8
+ console.error(`Error: Unknown option: ${unknownFlags[0]}`);
9
+ process.exit(1);
10
+ }
11
+ const dryRun = argv.includes("--dry-run");
12
+ let tagsContent;
13
+ try {
14
+ tagsContent = readFileSync(RELEASE_TAGS_FILE, "utf8");
15
+ } catch {
16
+ throw new Error("No tags file found. Run `release-kit prepare` first.");
17
+ }
18
+ const tags = tagsContent.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
19
+ if (tags.length === 0) {
20
+ throw new Error("Tags file is empty. Run `release-kit prepare` first.");
21
+ }
22
+ let summary = "";
23
+ try {
24
+ summary = readFileSync(RELEASE_SUMMARY_FILE, "utf8").trim();
25
+ } catch (error) {
26
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
27
+ } else {
28
+ throw error;
29
+ }
30
+ }
31
+ const title = `release: ${tags.join(" ")}`;
32
+ const message = summary.length > 0 ? `${title}
33
+
34
+ ${summary}` : title;
35
+ if (dryRun) {
36
+ console.info("[dry-run] Would create commit with message:\n");
37
+ console.info(message);
38
+ try {
39
+ const status = execFileSync("git", ["status", "--porcelain"], { encoding: "utf8" });
40
+ if (status.trim().length > 0) {
41
+ console.info("\nUncommitted changes:");
42
+ console.info(status.trimEnd());
43
+ }
44
+ } catch {
45
+ console.info("(Could not determine uncommitted changes)");
46
+ }
47
+ return;
48
+ }
49
+ execFileSync("git", ["add", "-A"]);
50
+ execFileSync("git", ["commit", "-m", message]);
51
+ console.info(`Created release commit: ${title}`);
52
+ }
53
+ export {
54
+ commitCommand
55
+ };
@@ -1,6 +1,6 @@
1
1
  import { execFileSync } from "node:child_process";
2
2
  import { readFileSync, unlinkSync } from "node:fs";
3
- import { RELEASE_TAGS_FILE } from "./prepareCommand.js";
3
+ import { RELEASE_SUMMARY_FILE, RELEASE_TAGS_FILE } from "./prepareCommand.js";
4
4
  function createTags(options) {
5
5
  const { dryRun, noGitChecks } = options;
6
6
  let content;
@@ -43,6 +43,7 @@ function createTags(options) {
43
43
  console.info(`\u{1F3F7}\uFE0F ${tag}`);
44
44
  }
45
45
  deleteTagsFile();
46
+ deleteSummaryFile();
46
47
  return tags;
47
48
  }
48
49
  function deleteTagsFile() {
@@ -55,6 +56,16 @@ function deleteTagsFile() {
55
56
  throw error;
56
57
  }
57
58
  }
59
+ function deleteSummaryFile() {
60
+ try {
61
+ unlinkSync(RELEASE_SUMMARY_FILE);
62
+ } catch (error) {
63
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
64
+ return;
65
+ }
66
+ throw error;
67
+ }
68
+ }
58
69
  function assertCleanWorkingTree() {
59
70
  try {
60
71
  execFileSync("git", ["diff", "--quiet"]);
@@ -4,4 +4,4 @@ export interface BumpDetermination {
4
4
  parsedCommitCount: number;
5
5
  unparseableCommits: Commit[] | undefined;
6
6
  }
7
- export declare function determineBumpFromCommits(commits: Commit[], workTypes: Record<string, WorkTypeConfig>, versionPatterns: VersionPatterns, workspaceAliases: Record<string, string> | undefined): BumpDetermination;
7
+ export declare function determineBumpFromCommits(commits: Commit[], workTypes: Record<string, WorkTypeConfig>, versionPatterns: VersionPatterns, scopeAliases: Record<string, string> | undefined): BumpDetermination;
@@ -1,10 +1,10 @@
1
1
  import { determineBumpType } from "./determineBumpType.js";
2
2
  import { parseCommitMessage } from "./parseCommitMessage.js";
3
- function determineBumpFromCommits(commits, workTypes, versionPatterns, workspaceAliases) {
3
+ function determineBumpFromCommits(commits, workTypes, versionPatterns, scopeAliases) {
4
4
  const parsedCommits = [];
5
5
  const unparseable = [];
6
6
  for (const commit of commits) {
7
- const parsed = parseCommitMessage(commit.message, commit.hash, workTypes, workspaceAliases);
7
+ const parsed = parseCommitMessage(commit.message, commit.hash, workTypes, scopeAliases);
8
8
  if (parsed === void 0) {
9
9
  unparseable.push(commit);
10
10
  } else {
@@ -1,5 +1,7 @@
1
1
  import type { ReleaseConfig } from './types.ts';
2
+ export declare function buildTagPattern(tagPrefix: string): string;
2
3
  export interface GenerateChangelogOptions {
4
+ tagPattern?: string;
3
5
  includePaths?: string[];
4
6
  }
5
7
  export declare function generateChangelog(config: Pick<ReleaseConfig, 'cliffConfigPath'>, changelogPath: string, tag: string, dryRun: boolean, options?: GenerateChangelogOptions): string[];
@@ -1,9 +1,15 @@
1
1
  import { execFileSync } from "node:child_process";
2
2
  import { resolveCliffConfigPath } from "./resolveCliffConfigPath.js";
3
+ function buildTagPattern(tagPrefix) {
4
+ return `${tagPrefix}[0-9].*`;
5
+ }
3
6
  function generateChangelog(config, changelogPath, tag, dryRun, options) {
4
7
  const cliffConfigPath = resolveCliffConfigPath(config.cliffConfigPath, import.meta.url);
5
8
  const outputFile = `${changelogPath}/CHANGELOG.md`;
6
9
  const args = ["--config", cliffConfigPath, "--output", outputFile, "--tag", tag];
10
+ if (options?.tagPattern !== void 0) {
11
+ args.push("--tag-pattern", options.tagPattern);
12
+ }
7
13
  for (const includePath of options?.includePaths ?? []) {
8
14
  args.push("--include-path", includePath);
9
15
  }
@@ -19,13 +25,15 @@ function generateChangelog(config, changelogPath, tag, dryRun, options) {
19
25
  return [outputFile];
20
26
  }
21
27
  function generateChangelogs(config, tag, dryRun) {
28
+ const tagPattern = buildTagPattern(config.tagPrefix);
22
29
  const results = [];
23
30
  for (const changelogPath of config.changelogPaths) {
24
- results.push(...generateChangelog(config, changelogPath, tag, dryRun));
31
+ results.push(...generateChangelog(config, changelogPath, tag, dryRun, { tagPattern }));
25
32
  }
26
33
  return results;
27
34
  }
28
35
  export {
36
+ buildTagPattern,
29
37
  generateChangelog,
30
38
  generateChangelogs
31
39
  };
@@ -7,8 +7,10 @@ export type { ResolvedTag } from './resolveReleaseTags.ts';
7
7
  export type { LabelDefinition, SyncLabelsConfig } from './sync-labels/types.ts';
8
8
  export type { BumpResult, Commit, ComponentConfig, ComponentOverride, ComponentPrepareResult, MonorepoReleaseConfig, ParsedCommit, PrepareResult, ReleaseConfig, ReleaseKitConfig, ReleaseType, VersionPatterns, WorkTypeConfig, } from './types.ts';
9
9
  export { DEFAULT_VERSION_PATTERNS, DEFAULT_WORK_TYPES } from './defaults.ts';
10
+ export { buildReleaseSummary } from './buildReleaseSummary.ts';
10
11
  export { bumpAllVersions } from './bumpAllVersions.ts';
11
12
  export { bumpVersion } from './bumpVersion.ts';
13
+ export { commitCommand } from './commitCommand.ts';
12
14
  export { component } from './component.ts';
13
15
  export { createTags } from './createTags.ts';
14
16
  export { detectPackageManager } from './detectPackageManager.ts';
@@ -17,9 +19,10 @@ export { discoverWorkspaces } from './discoverWorkspaces.ts';
17
19
  export { generateChangelog, generateChangelogs } from './generateChangelogs.ts';
18
20
  export { getCommitsSinceTarget } from './getCommitsSinceTarget.ts';
19
21
  export { COMMIT_PREPROCESSOR_PATTERNS, parseCommitMessage } from './parseCommitMessage.ts';
20
- export { RELEASE_TAGS_FILE, writeReleaseTags } from './prepareCommand.ts';
22
+ export { RELEASE_SUMMARY_FILE, RELEASE_TAGS_FILE, writeReleaseTags } from './prepareCommand.ts';
21
23
  export { publish } from './publish.ts';
22
24
  export { releasePrepare } from './releasePrepare.ts';
23
25
  export { releasePrepareMono } from './releasePrepareMono.ts';
24
26
  export { reportPrepare } from './reportPrepare.ts';
25
27
  export { resolveReleaseTags } from './resolveReleaseTags.ts';
28
+ export { stripScope } from './stripScope.ts';
package/dist/esm/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import { DEFAULT_VERSION_PATTERNS, DEFAULT_WORK_TYPES } from "./defaults.js";
2
+ import { buildReleaseSummary } from "./buildReleaseSummary.js";
2
3
  import { bumpAllVersions } from "./bumpAllVersions.js";
3
4
  import { bumpVersion } from "./bumpVersion.js";
5
+ import { commitCommand } from "./commitCommand.js";
4
6
  import { component } from "./component.js";
5
7
  import { createTags } from "./createTags.js";
6
8
  import { detectPackageManager } from "./detectPackageManager.js";
@@ -9,19 +11,23 @@ import { discoverWorkspaces } from "./discoverWorkspaces.js";
9
11
  import { generateChangelog, generateChangelogs } from "./generateChangelogs.js";
10
12
  import { getCommitsSinceTarget } from "./getCommitsSinceTarget.js";
11
13
  import { COMMIT_PREPROCESSOR_PATTERNS, parseCommitMessage } from "./parseCommitMessage.js";
12
- import { RELEASE_TAGS_FILE, writeReleaseTags } from "./prepareCommand.js";
14
+ import { RELEASE_SUMMARY_FILE, RELEASE_TAGS_FILE, writeReleaseTags } from "./prepareCommand.js";
13
15
  import { publish } from "./publish.js";
14
16
  import { releasePrepare } from "./releasePrepare.js";
15
17
  import { releasePrepareMono } from "./releasePrepareMono.js";
16
18
  import { reportPrepare } from "./reportPrepare.js";
17
19
  import { resolveReleaseTags } from "./resolveReleaseTags.js";
20
+ import { stripScope } from "./stripScope.js";
18
21
  export {
19
22
  COMMIT_PREPROCESSOR_PATTERNS,
20
23
  DEFAULT_VERSION_PATTERNS,
21
24
  DEFAULT_WORK_TYPES,
25
+ RELEASE_SUMMARY_FILE,
22
26
  RELEASE_TAGS_FILE,
27
+ buildReleaseSummary,
23
28
  bumpAllVersions,
24
29
  bumpVersion,
30
+ commitCommand,
25
31
  component,
26
32
  createTags,
27
33
  detectPackageManager,
@@ -36,5 +42,6 @@ export {
36
42
  releasePrepareMono,
37
43
  reportPrepare,
38
44
  resolveReleaseTags,
45
+ stripScope,
39
46
  writeReleaseTags
40
47
  };
@@ -56,8 +56,10 @@ function initCommand({ dryRun, force, withConfig }) {
56
56
  const configHint = withConfig ? "1. (Optional) Customize .config/release-kit.config.ts and .config/git-cliff.toml." : "1. (Optional) Run again with --with-config to scaffold config files.";
57
57
  console.info(`
58
58
  ${configHint}
59
- 2. Test by running: npx @williamthorsen/release-kit prepare --dry-run
60
- 3. Commit the generated files.
59
+ 2. If this is a public repo, set provenance: true in .github/workflows/publish.yaml.
60
+ 3. Test by running: npx @williamthorsen/release-kit prepare --dry-run
61
+ 4. Commit the generated files.
62
+ 5. Register each package as a trusted publisher on npmjs.com.
61
63
  `);
62
64
  return 0;
63
65
  }
@@ -2,7 +2,7 @@ import { existsSync, readFileSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
3
  import { writeFileWithCheck } from "@williamthorsen/node-monorepo-core";
4
4
  import { findPackageRoot } from "../findPackageRoot.js";
5
- import { releaseConfigScript, releaseWorkflow } from "./templates.js";
5
+ import { publishWorkflow, releaseConfigScript, releaseWorkflow } from "./templates.js";
6
6
  function copyCliffTemplate(dryRun, overwrite) {
7
7
  const destPath = ".config/git-cliff.toml";
8
8
  const root = findPackageRoot(import.meta.url);
@@ -21,7 +21,8 @@ function copyCliffTemplate(dryRun, overwrite) {
21
21
  }
22
22
  function scaffoldFiles({ repoType, dryRun, overwrite, withConfig }) {
23
23
  const results = [
24
- writeFileWithCheck(".github/workflows/release.yaml", releaseWorkflow(repoType), { dryRun, overwrite })
24
+ writeFileWithCheck(".github/workflows/release.yaml", releaseWorkflow(repoType), { dryRun, overwrite }),
25
+ writeFileWithCheck(".github/workflows/publish.yaml", publishWorkflow(repoType), { dryRun, overwrite })
25
26
  ];
26
27
  if (withConfig) {
27
28
  results.push(