@williamthorsen/release-kit 0.2.2 → 1.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 (47) hide show
  1. package/CHANGELOG.md +43 -1
  2. package/README.md +202 -219
  3. package/dist/esm/.cache +1 -1
  4. package/dist/esm/bin/release-kit.d.ts +2 -0
  5. package/dist/esm/bin/release-kit.js +73 -0
  6. package/dist/esm/component.d.ts +2 -0
  7. package/dist/esm/component.js +15 -0
  8. package/dist/esm/defaults.d.ts +3 -2
  9. package/dist/esm/defaults.js +17 -12
  10. package/dist/esm/determineBumpType.d.ts +2 -2
  11. package/dist/esm/determineBumpType.js +11 -13
  12. package/dist/esm/discoverWorkspaces.d.ts +1 -0
  13. package/dist/esm/discoverWorkspaces.js +45 -0
  14. package/dist/esm/getCommitsSinceTarget.js +2 -1
  15. package/dist/esm/index.d.ts +5 -2
  16. package/dist/esm/index.js +11 -2
  17. package/dist/esm/init/checks.d.ts +9 -0
  18. package/dist/esm/init/checks.js +56 -0
  19. package/dist/esm/init/detectRepoType.d.ts +2 -0
  20. package/dist/esm/init/detectRepoType.js +19 -0
  21. package/dist/esm/init/initCommand.d.ts +5 -0
  22. package/dist/esm/init/initCommand.js +65 -0
  23. package/dist/esm/init/parseJsonRecord.d.ts +1 -0
  24. package/dist/esm/init/parseJsonRecord.js +15 -0
  25. package/dist/esm/init/prompt.d.ts +5 -0
  26. package/dist/esm/init/prompt.js +30 -0
  27. package/dist/esm/init/scaffold.d.ts +9 -0
  28. package/dist/esm/init/scaffold.js +65 -0
  29. package/dist/esm/init/templates.d.ts +3 -0
  30. package/dist/esm/init/templates.js +105 -0
  31. package/dist/esm/loadConfig.d.ts +5 -0
  32. package/dist/esm/loadConfig.js +91 -0
  33. package/dist/esm/parseCommitMessage.d.ts +1 -1
  34. package/dist/esm/parseCommitMessage.js +4 -4
  35. package/dist/esm/prepareCommand.d.ts +1 -0
  36. package/dist/esm/prepareCommand.js +77 -0
  37. package/dist/esm/releasePrepare.d.ts +1 -1
  38. package/dist/esm/releasePrepare.js +7 -3
  39. package/dist/esm/releasePrepareMono.d.ts +1 -1
  40. package/dist/esm/releasePrepareMono.js +16 -10
  41. package/dist/esm/runReleasePrepare.d.ts +9 -0
  42. package/dist/esm/runReleasePrepare.js +112 -0
  43. package/dist/esm/types.d.ts +22 -4
  44. package/dist/esm/validateConfig.d.ts +5 -0
  45. package/dist/esm/validateConfig.js +143 -0
  46. package/dist/tsconfig.generate-typings.tsbuildinfo +1 -1
  47. package/package.json +12 -4
package/CHANGELOG.md CHANGED
@@ -2,7 +2,49 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
- ## [release-kit-v0.2.2] - 2026-03-10
5
+ ## [release-kit-v1.0.0] - 2026-03-12
6
+
7
+ ### Features
8
+
9
+ - #28 release-kit|feat!: Migrate to CLI-driven release preparation with auto-discovery (#31)
10
+
11
+ Replaces release-kit's script-based release preparation with a self-contained CLI (`npx @williamthorsen/release-kit prepare`) that auto-discovers workspaces from `pnpm-workspace.yaml`. Adds workspace auto-discovery, TypeScript config loading via jiti, config validation, and a `component()` factory that accepts full workspace-relative paths. Refactors the type system: `WorkTypeConfig` becomes a record keyed by type name, version-bump rules move to a separate `VersionPatterns` structure, and `ComponentConfig` gains a `dir` field for canonical directory identity.
12
+
13
+ ### Refactoring
14
+
15
+ - #28 release-kit|refactor: Adjust location of config & tags file
16
+
17
+ ### Tooling
18
+
19
+ - #28 release-kit|tooling: Remove legacy release-kit scripts
20
+
21
+ ## [release-kit-v0.3.0] - 2026-03-11
22
+
23
+ ### Features
24
+
25
+ - Release-kit|feat: Extract CLI runner into release-kit and co-locate scripts with workflow
26
+
27
+ Move release script logic (arg parsing, validation, component filtering) into a reusable `runReleasePrepare` function in the release-kit package. Consuming repos now provide only their config and call `runReleasePrepare(config)`.
28
+
29
+ Relocate release-prepare.ts and release.config.ts from scripts/ to .github/scripts/ so they live alongside the workflow they serve.
30
+
31
+ - #20 release-kit|feat: Add release-kit init CLI command for automated repo setup (#22)
32
+
33
+ Add an interactive `npx release-kit init` CLI command that checks repo eligibility, detects monorepo vs single-package layout, scaffolds workflow/scripts/config files, and updates `package.json` with release scripts.
34
+
35
+ Also expand `runReleasePrepare` to polymorphically handle both `MonorepoReleaseConfig` and `ReleaseConfig`, and update the esbuild plugin to preserve shebangs during compilation.
36
+
37
+ - #24 release-kit|feat: Return computed tags from release prepare and write .release-tags (#27)
38
+
39
+ `releasePrepare` and `releasePrepareMono` now return `string[]` of computed tag names instead of `void`. `runReleasePrepare` writes these tags to a `.release-tags` file (one tag per line) so the CI workflow can read them instead of independently deriving tag names from `git diff`. In dry-run mode the file is not written. This makes `tagPrefix` the single source of truth for tag names, eliminating the mismatch between TypeScript-computed tags and workflow-derived tags.
40
+
41
+ ## [strings-v3.1.1] - 2026-03-10
42
+
43
+ ### Tooling
44
+
45
+ - \*|tooling: Change package registry from github to npmjs
46
+
47
+ ## [tools-v3.0.1] - 2026-03-10
6
48
 
7
49
  ### Tooling
8
50
 
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Version-bumping and changelog-generation toolkit for release workflows.
4
4
 
5
- This package extracts the shared release-preparation logic from the `skypilot-site` and `devtools/afg` repositories into a reusable library. It provides functions for parsing commits, determining version bumps, updating `package.json` files, and generating changelogs with `git-cliff`.
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 with `git-cliff`.
6
6
 
7
7
  ## Installation
8
8
 
@@ -10,254 +10,195 @@ This package extracts the shared release-preparation logic from the `skypilot-si
10
10
  pnpm add -D @williamthorsen/release-kit git-cliff
11
11
  ```
12
12
 
13
- Since this package is published to GitHub Packages, configure your `.npmrc`:
13
+ ## Quick start
14
+
15
+ ```bash
16
+ # 1. Set up release-kit in your repo (scaffolds workflow + optional config)
17
+ npx @williamthorsen/release-kit init
14
18
 
15
- ```ini
16
- @williamthorsen:registry=https://npm.pkg.github.com
19
+ # 2. Preview what a release would do
20
+ npx @williamthorsen/release-kit prepare --dry-run
21
+
22
+ # 3. Copy cliff.toml.template to your repo root (if init didn't create one)
23
+ cp node_modules/@williamthorsen/release-kit/cliff.toml.template cliff.toml
17
24
  ```
18
25
 
19
- ## Quick start
26
+ That's it for most repos. The CLI auto-discovers workspaces and applies sensible defaults. Customize only what you need via `.config/release-kit.config.ts`.
20
27
 
21
- 1. Install `@williamthorsen/release-kit` and `git-cliff` as dev dependencies
22
- 2. Create `scripts/release-prepare.ts` and `scripts/release.config.ts` (see examples below)
23
- 3. Add `release:prepare` scripts to `package.json`
24
- 4. Copy `cliff.toml.template` to your repo root as `cliff.toml`
25
- 5. Add the GitHub Actions release workflow
28
+ ## How it works
26
29
 
27
- ## Configuration
30
+ 1. **Workspace discovery**: reads `pnpm-workspace.yaml` and resolves its `packages` globs to find workspace directories. Each directory containing a `package.json` becomes a component. If no workspace file is found, the repo is treated as a single-package project.
31
+ 2. **Config loading**: loads `.config/release-kit.config.ts` (if present) via [jiti](https://github.com/unjs/jiti) and merges it with discovered defaults.
32
+ 3. **Commit analysis**: for each component, finds commits since the last version tag, parses them for type and scope, and determines the appropriate version bump.
33
+ 4. **Version bump + changelog**: bumps `package.json` versions and generates changelogs via `git-cliff`.
34
+ 5. **Release tags file**: writes computed tags to `/tmp/release-kit/.release-tags` for CI consumption.
28
35
 
29
- ### Single-package repo
36
+ ## CLI reference
30
37
 
31
- Create `scripts/release.config.ts`:
38
+ ### `release-kit prepare`
39
+
40
+ Run release preparation with automatic workspace discovery.
32
41
 
33
- ```typescript
34
- import { DEFAULT_WORK_TYPES } from '@williamthorsen/release-kit';
35
- import type { ReleaseConfig } from '@williamthorsen/release-kit';
36
-
37
- export const config: ReleaseConfig = {
38
- tagPrefix: 'v',
39
- packageFiles: ['package.json'],
40
- changelogPaths: ['.'],
41
- workTypes: [...DEFAULT_WORK_TYPES],
42
- formatCommand: 'pnpm run fmt',
43
- };
44
42
  ```
43
+ Usage: release-kit prepare [options]
45
44
 
46
- ### Monorepo
45
+ Options:
46
+ --dry-run Preview changes without writing files
47
+ --bump=major|minor|patch Override the bump type for all components
48
+ --only=name1,name2 Only process the named components (monorepo only)
49
+ --help, -h Show help
50
+ ```
47
51
 
48
- Create `scripts/release.config.ts`:
52
+ Component names for `--only` match the package directory name (e.g., `arrays`, `release-kit`).
49
53
 
50
- ```typescript
51
- import { DEFAULT_WORK_TYPES } from '@williamthorsen/release-kit';
52
- import type { MonorepoReleaseConfig } from '@williamthorsen/release-kit';
54
+ **Self-hosting note**: in a repo where `release-kit` is a workspace package (e.g., this monorepo), `npx` and `pnpm exec` may not resolve the binary. Run the built entry point directly:
53
55
 
54
- function component(dir: string) {
55
- return {
56
- tagPrefix: `${dir}-v`,
57
- packageFiles: [`packages/${dir}/package.json`],
58
- changelogPaths: [`packages/${dir}`],
59
- paths: [`packages/${dir}/**`],
60
- };
61
- }
62
-
63
- export const config: MonorepoReleaseConfig = {
64
- components: [component('my-lib'), component('my-cli')],
65
- workTypes: [...DEFAULT_WORK_TYPES],
66
- formatCommand: 'pnpm run fmt',
67
- };
56
+ ```bash
57
+ node packages/release-kit/dist/esm/bin/release-kit.js prepare --dry-run
68
58
  ```
69
59
 
70
- ### ReleaseConfig reference
60
+ ### `release-kit init`
61
+
62
+ Initialize release-kit in the current repository. Scaffolds a GitHub Actions workflow and an optional config file.
63
+
64
+ ```
65
+ Usage: release-kit init [options]
71
66
 
72
- | Field | Type | Required | Description |
73
- | ----------------- | ------------------ | -------- | ------------------------------------------------------------------ |
74
- | `tagPrefix` | `string` | Yes | Git tag prefix for version tags (e.g., `'v'`) |
75
- | `packageFiles` | `string[]` | Yes | Paths to `package.json` files to bump |
76
- | `changelogPaths` | `string[]` | Yes | Directories in which to generate changelogs |
77
- | `workTypes` | `WorkTypeConfig[]` | Yes | Ordered list of work type configurations for commit categorization |
78
- | `formatCommand` | `string` | No | Shell command to run after changelog generation |
79
- | `cliffConfigPath` | `string` | No | Path to `cliff.toml` (defaults to `'cliff.toml'`) |
67
+ Options:
68
+ --dry-run Preview changes without writing files
69
+ --help, -h Show help
70
+ ```
80
71
 
81
- ### MonorepoReleaseConfig reference
72
+ Scaffolded files:
82
73
 
83
- | Field | Type | Required | Description |
84
- | --------------- | ------------------- | -------- | ----------------------------------------------------- |
85
- | `components` | `ComponentConfig[]` | Yes | Per-component config (tagPrefix, packageFiles, paths) |
86
- | `workTypes` | `WorkTypeConfig[]` | Yes | Shared work type configurations |
87
- | `formatCommand` | `string` | No | Shell command to run after changelog generation |
74
+ - `.config/release-kit.config.ts` starter config with commented-out customization examples
75
+ - `.github/workflows/release.yaml` workflow that delegates to a reusable release workflow
76
+ - `cliff.toml` copied from the bundled template (prompted if missing)
88
77
 
89
- ## Release script
78
+ ## Configuration
90
79
 
91
- Create `scripts/release-prepare.ts`:
80
+ 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.
92
81
 
93
- ### Single-package version
82
+ ### Config file
94
83
 
95
84
  ```typescript
96
- import { releasePrepare } from '@williamthorsen/release-kit';
97
- import type { ReleaseType } from '@williamthorsen/release-kit';
98
- import { config } from './release.config.ts';
85
+ import type { ReleaseKitConfig } from '@williamthorsen/release-kit';
99
86
 
100
- const VALID_BUMP_TYPES: readonly string[] = ['major', 'minor', 'patch'];
101
-
102
- function parseArgs(): { dryRun: boolean; bumpOverride?: ReleaseType } {
103
- const args = process.argv.slice(2);
104
- let dryRun = false;
105
- let bumpOverride: ReleaseType | undefined;
106
-
107
- for (const arg of args) {
108
- if (arg === '--dry-run') dryRun = true;
109
- else if (arg.startsWith('--bump=')) {
110
- const value = arg.slice('--bump='.length);
111
- if (!VALID_BUMP_TYPES.includes(value)) {
112
- console.error(`Invalid bump type "${value}". Must be: ${VALID_BUMP_TYPES.join(', ')}`);
113
- process.exit(1);
114
- }
115
- bumpOverride = value as ReleaseType;
116
- }
117
- }
118
-
119
- return { dryRun, bumpOverride };
120
- }
87
+ const config: ReleaseKitConfig = {
88
+ // Exclude a component from release processing
89
+ components: [{ dir: 'internal-tools', shouldExclude: true }],
90
+
91
+ // Run a formatter after changelog generation
92
+ formatCommand: 'pnpm run fmt',
93
+
94
+ // Override the default version patterns
95
+ versionPatterns: { major: ['!'], minor: ['feat', 'feature'] },
96
+
97
+ // Add or override work types (merged with defaults by key)
98
+ workTypes: { perf: { header: 'Performance' } },
99
+ };
121
100
 
122
- const { dryRun, bumpOverride } = parseArgs();
123
- releasePrepare(config, { dryRun, ...(bumpOverride ? { bumpOverride } : {}) });
101
+ export default config;
124
102
  ```
125
103
 
126
- ### Monorepo version
104
+ The config file supports both `export default config` and `export const config = { ... }`.
127
105
 
128
- ```typescript
129
- import { releasePrepareMono } from '@williamthorsen/release-kit';
130
- import type { ReleaseType } from '@williamthorsen/release-kit';
131
- import { config } from './release.config.ts';
106
+ ### `ReleaseKitConfig` reference
132
107
 
133
- const VALID_BUMP_TYPES: readonly string[] = ['major', 'minor', 'patch'];
134
-
135
- function parseArgs(): { dryRun: boolean; bumpOverride?: ReleaseType; only?: string[] } {
136
- const args = process.argv.slice(2);
137
- let dryRun = false;
138
- let bumpOverride: ReleaseType | undefined;
139
- let only: string[] | undefined;
140
-
141
- for (const arg of args) {
142
- if (arg === '--dry-run') dryRun = true;
143
- else if (arg.startsWith('--bump=')) {
144
- const value = arg.slice('--bump='.length);
145
- if (!VALID_BUMP_TYPES.includes(value)) {
146
- console.error(`Invalid bump type "${value}". Must be: ${VALID_BUMP_TYPES.join(', ')}`);
147
- process.exit(1);
148
- }
149
- bumpOverride = value as ReleaseType;
150
- } else if (arg.startsWith('--only=')) {
151
- only = arg.slice('--only='.length).split(',');
152
- }
153
- }
154
-
155
- return { dryRun, bumpOverride, only };
156
- }
108
+ | Field | Type | Description |
109
+ | ------------------ | -------------------------------- | ------------------------------------------------------------ |
110
+ | `cliffConfigPath` | `string` | Path to `cliff.toml` (defaults to `'cliff.toml'`) |
111
+ | `components` | `ComponentOverride[]` | Override or exclude discovered components (matched by `dir`) |
112
+ | `formatCommand` | `string` | Shell command to run after changelog generation |
113
+ | `versionPatterns` | `VersionPatterns` | Rules for which commit types trigger major/minor bumps |
114
+ | `workspaceAliases` | `Record<string, string>` | Maps shorthand workspace names to canonical names in commits |
115
+ | `workTypes` | `Record<string, WorkTypeConfig>` | Work type definitions, merged with defaults by key |
157
116
 
158
- const { dryRun, bumpOverride, only } = parseArgs();
117
+ All fields are optional.
159
118
 
160
- let effectiveConfig = config;
161
- if (only) {
162
- const filtered = config.components.filter((c) => {
163
- const name = c.tagPrefix.replace(/-v$/, '');
164
- return only.includes(name);
165
- });
166
- effectiveConfig = { ...config, components: filtered };
167
- }
119
+ ### `ComponentOverride`
168
120
 
169
- releasePrepareMono(effectiveConfig, { dryRun, ...(bumpOverride ? { bumpOverride } : {}) });
121
+ ```typescript
122
+ interface ComponentOverride {
123
+ dir: string; // Package directory name (e.g., 'arrays')
124
+ tagPrefix?: string; // Custom git tag prefix (defaults to '${dir}-v')
125
+ shouldExclude?: boolean; // If true, exclude from release processing
126
+ }
170
127
  ```
171
128
 
172
- ### package.json scripts
129
+ ### `VersionPatterns`
173
130
 
174
- ```json
175
- {
176
- "scripts": {
177
- "release:prepare": "tsx scripts/release-prepare.ts",
178
- "release:prepare:dry": "tsx scripts/release-prepare.ts --dry-run"
179
- }
131
+ Defines which commit types trigger major or minor bumps. Any recognized type not listed defaults to a patch bump.
132
+
133
+ ```typescript
134
+ interface VersionPatterns {
135
+ major: string[]; // Patterns triggering a major bump ('!' = any breaking change)
136
+ minor: string[]; // Commit types triggering a minor bump
180
137
  }
181
138
  ```
182
139
 
183
- ## GitHub Actions workflow
140
+ Default: `{ major: ['!'], minor: ['feat', 'feature'] }`
184
141
 
185
- ### Single-package repo
142
+ ### Default work types
186
143
 
187
- ```yaml
188
- # .github/workflows/release.yaml
189
- name: Release
144
+ | Key | Header | Aliases |
145
+ | ---------- | ------------- | --------- |
146
+ | `fix` | Bug fixes | `bugfix` |
147
+ | `feat` | Features | `feature` |
148
+ | `internal` | Internal | |
149
+ | `refactor` | Refactoring | |
150
+ | `tests` | Tests | `test` |
151
+ | `tooling` | Tooling | |
152
+ | `ci` | CI | |
153
+ | `deps` | Dependencies | `dep` |
154
+ | `docs` | Documentation | `doc` |
155
+ | `fmt` | Formatting | |
190
156
 
191
- on:
192
- workflow_dispatch:
193
- inputs:
194
- bump:
195
- description: 'Override bump type (leave empty to auto-detect)'
196
- required: false
197
- type: choice
198
- options:
199
- - ''
200
- - patch
201
- - minor
202
- - major
157
+ Work types from your config are merged with these defaults by key — your entries override or extend, they don't replace the full set.
203
158
 
204
- permissions:
205
- contents: write
159
+ ## Commit format
206
160
 
207
- jobs:
208
- release:
209
- runs-on: ubuntu-latest
210
- steps:
211
- - uses: actions/checkout@v4
212
- with:
213
- fetch-depth: 0
214
- token: ${{ secrets.GITHUB_TOKEN }}
161
+ release-kit parses commits in these formats:
215
162
 
216
- - uses: pnpm/action-setup@v4
163
+ ```
164
+ type: description # e.g., feat: add utility
165
+ type(scope): description # e.g., fix(parser): handle edge case
166
+ workspace|type: description # e.g., arrays|feat: add compact function
167
+ !type: description # breaking change (triggers major bump)
168
+ ```
217
169
 
218
- - uses: actions/setup-node@v4
219
- with:
220
- node-version: '24'
221
- cache: 'pnpm'
170
+ 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.
222
171
 
223
- - run: pnpm install
172
+ ## Using `component()` for manual configuration
224
173
 
225
- - name: Run release preparation
226
- id: prepare
227
- run: |
228
- ARGS=""
229
- if [ -n "${{ inputs.bump }}" ]; then
230
- ARGS="--bump=${{ inputs.bump }}"
231
- fi
232
- pnpm run release:prepare $ARGS
233
- VERSION=$(node -p "require('./package.json').version")
234
- echo "version=$VERSION" >> "$GITHUB_OUTPUT"
174
+ If you need to build a `MonorepoReleaseConfig` manually (e.g., for the legacy script-based approach), the exported `component()` helper creates a `ComponentConfig` from a workspace-relative path:
235
175
 
236
- - name: Check for changes
237
- id: check
238
- run: |
239
- if git diff --quiet; then
240
- echo "changed=false" >> "$GITHUB_OUTPUT"
241
- echo "No release-worthy changes found."
242
- else
243
- echo "changed=true" >> "$GITHUB_OUTPUT"
244
- fi
245
-
246
- - name: Commit, tag, and push
247
- if: steps.check.outputs.changed == 'true'
248
- run: |
249
- git config user.name "github-actions[bot]"
250
- git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
251
- git add -A
252
- git commit -m "release: v${{ steps.prepare.outputs.version }}"
253
- git tag "v${{ steps.prepare.outputs.version }}"
254
- git push origin main "v${{ steps.prepare.outputs.version }}"
176
+ ```typescript
177
+ import { component } from '@williamthorsen/release-kit';
178
+
179
+ // Accepts the full workspace-relative path
180
+ component('packages/arrays');
181
+ // => {
182
+ // dir: 'arrays',
183
+ // tagPrefix: 'arrays-v',
184
+ // packageFiles: ['packages/arrays/package.json'],
185
+ // changelogPaths: ['packages/arrays'],
186
+ // paths: ['packages/arrays/**'],
187
+ // }
188
+
189
+ // Custom tag prefix
190
+ component('libs/core', 'core-v');
255
191
  ```
256
192
 
193
+ The `dir` field is derived from `path.basename()`, so `packages/arrays` and `libs/arrays` both produce `dir: 'arrays'`.
194
+
195
+ ## GitHub Actions workflow
196
+
197
+ The `init` command scaffolds a workflow that delegates to a reusable release workflow. For repos that need a self-contained workflow:
198
+
257
199
  ### Monorepo
258
200
 
259
201
  ```yaml
260
- # .github/workflows/release.yaml
261
202
  name: Release
262
203
 
263
204
  on:
@@ -293,7 +234,7 @@ jobs:
293
234
 
294
235
  - uses: actions/setup-node@v4
295
236
  with:
296
- node-version: '24'
237
+ node-version: '22'
297
238
  cache: 'pnpm'
298
239
 
299
240
  - run: pnpm install
@@ -307,7 +248,7 @@ jobs:
307
248
  if [ -n "${{ inputs.bump }}" ]; then
308
249
  ARGS="$ARGS --bump=${{ inputs.bump }}"
309
250
  fi
310
- pnpm run release:prepare $ARGS
251
+ npx @williamthorsen/release-kit prepare $ARGS
311
252
 
312
253
  - name: Check for changes
313
254
  id: check
@@ -319,18 +260,13 @@ jobs:
319
260
  echo "changed=true" >> "$GITHUB_OUTPUT"
320
261
  fi
321
262
 
322
- - name: Determine release tags
263
+ - name: Read release tags
323
264
  if: steps.check.outputs.changed == 'true'
324
265
  id: tags
325
266
  run: |
326
- TAGS=""
327
- for pkg in $(git diff --name-only -- 'packages/*/package.json'); do
328
- DIR=$(echo "$pkg" | cut -d/ -f2)
329
- VERSION=$(node -p "require('./$pkg').version")
330
- TAGS="$TAGS ${DIR}-v${VERSION}"
331
- done
267
+ TAGS=$(cat /tmp/release-kit/.release-tags | tr '\n' ' ')
332
268
  echo "tags=$TAGS" >> "$GITHUB_OUTPUT"
333
- echo "Releasing:$TAGS"
269
+ echo "Releasing: $TAGS"
334
270
 
335
271
  - name: Commit, tag, and push
336
272
  if: steps.check.outputs.changed == 'true'
@@ -345,21 +281,44 @@ jobs:
345
281
  git push origin main ${{ steps.tags.outputs.tags }}
346
282
  ```
347
283
 
284
+ ### Single-package repo
285
+
286
+ The same workflow without the `only` input. Replace the prepare step with:
287
+
288
+ ```yaml
289
+ - name: Run release preparation
290
+ run: |
291
+ ARGS=""
292
+ if [ -n "${{ inputs.bump }}" ]; then
293
+ ARGS="--bump=${{ inputs.bump }}"
294
+ fi
295
+ npx @williamthorsen/release-kit prepare $ARGS
296
+ ```
297
+
298
+ And the tag step with:
299
+
300
+ ```yaml
301
+ - name: Read release tag
302
+ if: steps.check.outputs.changed == 'true'
303
+ id: tags
304
+ run: |
305
+ TAG=$(cat /tmp/release-kit/.release-tags)
306
+ echo "tag=$TAG" >> "$GITHUB_OUTPUT"
307
+ ```
308
+
348
309
  ## Triggering a release
349
310
 
350
311
  ```sh
351
- # Single-package repo
352
- gh workflow run release.yaml
353
- gh workflow run release.yaml -f bump=minor
354
-
355
- # Monorepo: all components
312
+ # All components
356
313
  gh workflow run release.yaml
357
314
 
358
- # Monorepo: specific component(s)
359
- gh workflow run release.yaml -f only=my-lib
360
- gh workflow run release.yaml -f only=my-lib,my-cli -f bump=minor
315
+ # Specific component(s)
316
+ gh workflow run release.yaml -f only=arrays
317
+ gh workflow run release.yaml -f only=arrays,strings -f bump=minor
361
318
  ```
362
319
 
320
+ Or use the GitHub UI: Actions > Release > Run workflow.
321
+
363
322
  ## cliff.toml setup
364
323
 
365
324
  The package includes a `cliff.toml.template` with a generic git-cliff configuration that:
@@ -383,13 +342,37 @@ This package shells out to two external tools:
383
342
  - **`git`** — must be available on `PATH`. Used to find tags and retrieve commit history.
384
343
  - **`git-cliff`** — must be available on `PATH`. Add `git-cliff` as a dev dependency to make it available in CI.
385
344
 
345
+ ## Legacy script-based approach
346
+
347
+ The CLI-driven approach is recommended for new setups. The script-based approach (using `runReleasePrepare` with a manually maintained config) is still supported for backward compatibility.
348
+
349
+ ```typescript
350
+ // .github/scripts/release.config.ts
351
+ import type { MonorepoReleaseConfig } from '@williamthorsen/release-kit';
352
+ import { component } from '@williamthorsen/release-kit';
353
+
354
+ export const config: MonorepoReleaseConfig = {
355
+ components: [component('packages/arrays'), component('packages/strings')],
356
+ formatCommand: 'pnpm run fmt',
357
+ };
358
+ ```
359
+
360
+ ```typescript
361
+ // .github/scripts/release-prepare.ts
362
+ import { runReleasePrepare } from '@williamthorsen/release-kit';
363
+ import { config } from './release.config.ts';
364
+
365
+ runReleasePrepare(config);
366
+ ```
367
+
368
+ The key difference: the script-based approach requires manually listing every component, while the CLI auto-discovers them from `pnpm-workspace.yaml`.
369
+
386
370
  ## Migration from changesets
387
371
 
388
372
  1. Add `@williamthorsen/release-kit` and `git-cliff` as dev dependencies.
389
373
  2. Remove `@changesets/cli` from dev dependencies.
390
374
  3. Delete the `.changeset/` directory.
391
- 4. Create `scripts/release-prepare.ts` and `scripts/release.config.ts` (see examples above).
392
- 5. Replace `changeset:*` scripts in `package.json` with `release:prepare` scripts.
393
- 6. Copy `cliff.toml.template` to your repo root as `cliff.toml`.
394
- 7. Add the GitHub Actions release workflow.
395
- 8. Create an initial version tag for each package (e.g., `git tag v1.0.0` or `git tag my-lib-v1.0.0`).
375
+ 4. Run `npx @williamthorsen/release-kit init` to scaffold workflow and config files.
376
+ 5. Remove `changeset:*` scripts from `package.json` (no replacement needed — the CLI handles everything).
377
+ 6. Copy `cliff.toml.template` to your repo root as `cliff.toml` (if `init` didn't create one).
378
+ 7. Create an initial version tag for each package (e.g., `git tag v1.0.0` or `git tag arrays-v1.0.0`).
package/dist/esm/.cache CHANGED
@@ -1 +1 @@
1
- d1399daa908608c433b1235b6ea87a53fa94d624e116cc5a5aa3da8a5790358b
1
+ f68170895c39861df163752e69beb845e3033a1334b92a564a1dc3b95ff170ca
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env node
2
+ import { initCommand } from "../init/initCommand.js";
3
+ import { prepareCommand } from "../prepareCommand.js";
4
+ function showUsage() {
5
+ console.info(`
6
+ Usage: release-kit <command> [options]
7
+
8
+ Commands:
9
+ prepare Run release preparation (auto-discovers workspaces)
10
+ init Initialize release-kit in the current repository
11
+
12
+ Options:
13
+ --dry-run Preview changes without writing files
14
+ --help, -h Show this help message
15
+ `);
16
+ }
17
+ function showInitHelp() {
18
+ console.info(`
19
+ Usage: release-kit init [options]
20
+
21
+ Initialize release-kit in the current repository.
22
+ Scaffolds workflow and config files.
23
+
24
+ Options:
25
+ --dry-run Preview changes without writing files
26
+ --help, -h Show this help message
27
+ `);
28
+ }
29
+ function showPrepareHelp() {
30
+ console.info(`
31
+ Usage: release-kit prepare [options]
32
+
33
+ Run release preparation with automatic workspace discovery.
34
+
35
+ Options:
36
+ --dry-run Run without modifying any files
37
+ --bump=major|minor|patch Override the bump type for all components
38
+ --only=name1,name2 Only process the named components (comma-separated, monorepo only)
39
+ --help, -h Show this help message
40
+ `);
41
+ }
42
+ const args = process.argv.slice(2);
43
+ const command = args[0];
44
+ const flags = args.slice(1);
45
+ if (command === "--help" || command === "-h" || command === void 0) {
46
+ showUsage();
47
+ process.exit(0);
48
+ }
49
+ if (command === "prepare") {
50
+ if (flags.some((f) => f === "--help" || f === "-h")) {
51
+ showPrepareHelp();
52
+ process.exit(0);
53
+ }
54
+ await prepareCommand(flags);
55
+ process.exit(0);
56
+ }
57
+ if (command === "init") {
58
+ if (flags.some((f) => f === "--help" || f === "-h")) {
59
+ showInitHelp();
60
+ process.exit(0);
61
+ }
62
+ const unknownFlags = flags.filter((f) => f !== "--dry-run" && f !== "--help" && f !== "-h");
63
+ if (unknownFlags.length > 0) {
64
+ console.error(`Error: Unknown option: ${unknownFlags[0]}`);
65
+ process.exit(1);
66
+ }
67
+ const dryRun = flags.includes("--dry-run");
68
+ const exitCode = await initCommand({ dryRun });
69
+ process.exit(exitCode);
70
+ }
71
+ console.error(`Error: Unknown command: ${command}`);
72
+ showUsage();
73
+ process.exit(1);