@williamthorsen/release-kit 4.0.0 → 4.4.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.
- package/CHANGELOG.md +50 -0
- package/README.md +115 -182
- package/bin/release-kit.js +10 -1
- package/dist/esm/.cache +1 -1
- package/dist/esm/bin/release-kit.js +27 -13
- package/dist/esm/commitCommand.js +10 -5
- package/dist/esm/createTags.js +4 -23
- package/dist/esm/deleteFileIfExists.d.ts +1 -0
- package/dist/esm/deleteFileIfExists.js +14 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/prepareCommand.js +44 -34
- package/dist/esm/publishCommand.js +14 -9
- package/dist/esm/tagCommand.js +11 -6
- package/dist/esm/version.d.ts +1 -0
- package/dist/esm/version.js +4 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,42 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [release-kit-v4.4.0] - 2026-04-04
|
|
6
|
+
|
|
7
|
+
### Documentation
|
|
8
|
+
|
|
9
|
+
- #135 release-kit|docs: Refine README to match preflight documentation standard (#138)
|
|
10
|
+
|
|
11
|
+
Restructures the release-kit README to match the documentation standard established by the preflight README (#114). Reorders sections to follow the cross-package convention, converts CLI flag listings from code blocks to tables, adds representative `prepare --dry-run` output to the quick start, and condenses ~90 lines of inline workflow YAML into a summary with an inputs table and trigger examples. Fixes several accuracy gaps found by verifying documentation against source.
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
- #142 feat: Add --version flag to nmr and release-kit (#143)
|
|
16
|
+
|
|
17
|
+
Adds `--version` / `-V` support to the `nmr` and `release-kit` CLIs, matching the existing `preflight` behavior. Moves the build-time version generation script to the shared `config/` directory so all three packages use a single `generateVersion.ts`.
|
|
18
|
+
|
|
19
|
+
- #150 feat: Detect and report missing build output in bin wrappers (#152)
|
|
20
|
+
|
|
21
|
+
Adds try/catch with `ERR_MODULE_NOT_FOUND` detection to all six bin wrappers across `nmr`, `preflight`, and `release-kit`. Previously, five of the six wrappers used bare `import()` calls that produced cryptic unhandled rejections when `dist/` was missing, and `preflight`'s existing try/catch gave no actionable guidance.
|
|
22
|
+
|
|
23
|
+
### Refactoring
|
|
24
|
+
|
|
25
|
+
- #88 release-kit|refactor: Extract deleteFileIfExists helper (#136)
|
|
26
|
+
|
|
27
|
+
Replaces the duplicate `deleteTagsFile` and `deleteSummaryFile` functions in `createTags.ts` with a single parameterized `deleteFileIfExists(path)` utility. The new helper lives in its own module and is exported from the package barrel for reuse.
|
|
28
|
+
|
|
29
|
+
- #145 refactor: Extract shared CLI argument-parsing utility into core (#151)
|
|
30
|
+
|
|
31
|
+
Add a schema-driven `parseArgs` function to `@williamthorsen/node-monorepo-core` that handles boolean flags, string flags (both `--flag=value` and `--flag value`), short aliases, positional collection, the `--` delimiter, and unknown-flag errors. Migrate all CLI argument-parsing sites in preflight (3 sites) and release-kit (5 sites) to use it. A companion `translateParseError` helper normalizes internal error messages for consistent user-facing output.
|
|
32
|
+
|
|
33
|
+
## [release-kit-v4.0.0] - 2026-04-02
|
|
34
|
+
|
|
35
|
+
### Features
|
|
36
|
+
|
|
37
|
+
- #125 feat: Rename reusable workflows to .reusable.yaml convention (#129)
|
|
38
|
+
|
|
39
|
+
Renames all three reusable GitHub Actions workflow files from the inconsistent `-workflow.yaml`/bare `.yaml` convention to a uniform `.reusable.yaml` suffix. Updates all references across caller workflows, release-kit templates, tests, preflight collection, and documentation. Scaffolds the sync-labels caller workflow and labels file for this repo. Deletes superseded legacy files.
|
|
40
|
+
|
|
5
41
|
## [release-kit-v3.0.0] - 2026-03-29
|
|
6
42
|
|
|
7
43
|
### Bug fixes
|
|
@@ -48,6 +84,10 @@ Prevents `releasePrepareMono` and `releasePrepare` from silently skipping compon
|
|
|
48
84
|
|
|
49
85
|
### Features
|
|
50
86
|
|
|
87
|
+
- #8 feat: Add shared writeFileWithCheck utility and overwrite reporting (#66)
|
|
88
|
+
|
|
89
|
+
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.
|
|
90
|
+
|
|
51
91
|
- #11 release-kit|feat: Separate tag-write errors from release preparation errors (#67)
|
|
52
92
|
|
|
53
93
|
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.
|
|
@@ -112,8 +152,18 @@ Removes the ability to customize `tagPrefix` per component, enforcing the determ
|
|
|
112
152
|
|
|
113
153
|
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.
|
|
114
154
|
|
|
155
|
+
- #59 feat: Extract nmr CLI from core package (#61)
|
|
156
|
+
|
|
157
|
+
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.
|
|
158
|
+
|
|
159
|
+
Scopes: core, nmr
|
|
160
|
+
|
|
115
161
|
### Refactoring
|
|
116
162
|
|
|
163
|
+
- #43 refactor: Replace dist bin targets with thin wrapper scripts (#48)
|
|
164
|
+
|
|
165
|
+
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.
|
|
166
|
+
|
|
117
167
|
- #53 release-kit|refactor: Separate presentation from logic in prepare workflow (#57)
|
|
118
168
|
|
|
119
169
|
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`.
|
package/README.md
CHANGED
|
@@ -20,6 +20,36 @@ npx @williamthorsen/release-kit init
|
|
|
20
20
|
npx @williamthorsen/release-kit prepare --dry-run
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
Example output from `prepare --dry-run` in a monorepo:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
🔍 DRY RUN — no files will be modified
|
|
27
|
+
|
|
28
|
+
── arrays ──────────────────────────────────────
|
|
29
|
+
Found 4 commits since arrays-v1.2.0
|
|
30
|
+
Parsed 3 typed commits
|
|
31
|
+
Bumping versions (minor)...
|
|
32
|
+
📦 1.2.0 → 1.3.0 (minor)
|
|
33
|
+
[dry-run] Would bump packages/arrays/package.json
|
|
34
|
+
Generating changelogs...
|
|
35
|
+
[dry-run] Would run: npx --yes git-cliff ... --output packages/arrays/CHANGELOG.md
|
|
36
|
+
🏷️ arrays-v1.3.0
|
|
37
|
+
|
|
38
|
+
── strings ─────────────────────────────────────
|
|
39
|
+
Found 2 commits since strings-v0.5.1
|
|
40
|
+
Parsed 2 typed commits
|
|
41
|
+
Bumping versions (patch)...
|
|
42
|
+
📦 0.5.1 → 0.5.2 (patch)
|
|
43
|
+
[dry-run] Would bump packages/strings/package.json
|
|
44
|
+
Generating changelogs...
|
|
45
|
+
[dry-run] Would run: npx --yes git-cliff ... --output packages/strings/CHANGELOG.md
|
|
46
|
+
🏷️ strings-v0.5.2
|
|
47
|
+
|
|
48
|
+
✅ Release preparation complete.
|
|
49
|
+
🏷️ arrays-v1.3.0
|
|
50
|
+
🏷️ strings-v0.5.2
|
|
51
|
+
```
|
|
52
|
+
|
|
23
53
|
That's it for most repos. The CLI auto-discovers workspaces and applies sensible defaults. The bundled `cliff.toml.template` is used automatically — no need to copy it. Customize only what you need via `.config/release-kit.config.ts`.
|
|
24
54
|
|
|
25
55
|
## How it works
|
|
@@ -30,58 +60,20 @@ That's it for most repos. The CLI auto-discovers workspaces and applies sensible
|
|
|
30
60
|
4. **Version bump + changelog**: bumps `package.json` versions and generates changelogs via `git-cliff`.
|
|
31
61
|
5. **Release tags file**: writes computed tags to `tmp/.release-tags` for the release workflow to read when tagging and pushing.
|
|
32
62
|
|
|
33
|
-
##
|
|
34
|
-
|
|
35
|
-
### `release-kit prepare`
|
|
36
|
-
|
|
37
|
-
Run release preparation with automatic workspace discovery.
|
|
38
|
-
|
|
39
|
-
```
|
|
40
|
-
Usage: release-kit prepare [options]
|
|
41
|
-
|
|
42
|
-
Options:
|
|
43
|
-
--dry-run Preview changes without writing files
|
|
44
|
-
--bump=major|minor|patch Override the bump type for all components
|
|
45
|
-
--only=name1,name2 Only process the named components (monorepo only)
|
|
46
|
-
--help, -h Show help
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
Component names for `--only` match the package directory name (e.g., `arrays`, `release-kit`).
|
|
50
|
-
|
|
51
|
-
### `release-kit init`
|
|
52
|
-
|
|
53
|
-
Initialize release-kit in the current repository. By default, scaffolds only the GitHub Actions workflow file. Use `--with-config` to also scaffold configuration files.
|
|
54
|
-
|
|
55
|
-
```
|
|
56
|
-
Usage: release-kit init [options]
|
|
57
|
-
|
|
58
|
-
Options:
|
|
59
|
-
--with-config Also scaffold .config/release-kit.config.ts and .config/git-cliff.toml
|
|
60
|
-
--force Overwrite existing files instead of skipping them
|
|
61
|
-
--dry-run Preview changes without writing files
|
|
62
|
-
--help, -h Show help
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
Scaffolded files:
|
|
66
|
-
|
|
67
|
-
- `.github/workflows/release.yaml` — workflow that delegates to a reusable release workflow
|
|
68
|
-
- `.config/release-kit.config.ts` — starter config with commented-out customization examples (with `--with-config`)
|
|
69
|
-
- `.config/git-cliff.toml` — copied from the bundled template (with `--with-config`)
|
|
70
|
-
|
|
71
|
-
### `release-kit sync-labels`
|
|
63
|
+
## Commit format
|
|
72
64
|
|
|
73
|
-
|
|
65
|
+
release-kit parses commits in these formats:
|
|
74
66
|
|
|
75
67
|
```
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
68
|
+
type: description # e.g., feat: add utility
|
|
69
|
+
scope|type: description # e.g., arrays|feat: add compact function
|
|
70
|
+
type(scope): description # e.g., feat(arrays): add compact function
|
|
71
|
+
type!: description # breaking change (triggers major bump)
|
|
72
|
+
scope|type!: description # scoped breaking change
|
|
73
|
+
type(scope)!: description # conventional scoped breaking change
|
|
82
74
|
```
|
|
83
75
|
|
|
84
|
-
`
|
|
76
|
+
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.
|
|
85
77
|
|
|
86
78
|
## Configuration
|
|
87
79
|
|
|
@@ -144,7 +136,7 @@ interface VersionPatterns {
|
|
|
144
136
|
}
|
|
145
137
|
```
|
|
146
138
|
|
|
147
|
-
Default: `{ major: ['!'], minor: ['feat'
|
|
139
|
+
Default: `{ major: ['!'], minor: ['feat'] }`
|
|
148
140
|
|
|
149
141
|
### Default work types
|
|
150
142
|
|
|
@@ -163,151 +155,70 @@ Default: `{ major: ['!'], minor: ['feat', 'feature'] }`
|
|
|
163
155
|
|
|
164
156
|
Work types from your config are merged with these defaults by key — your entries override or extend, they don't replace the full set.
|
|
165
157
|
|
|
166
|
-
##
|
|
158
|
+
## CLI reference
|
|
167
159
|
|
|
168
|
-
|
|
160
|
+
### Global options
|
|
169
161
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
type!: description # breaking change (triggers major bump)
|
|
175
|
-
scope|type!: description # scoped breaking change
|
|
176
|
-
type(scope)!: description # conventional scoped breaking change
|
|
177
|
-
```
|
|
162
|
+
| Flag | Description |
|
|
163
|
+
| ----------------- | ------------------- |
|
|
164
|
+
| `--help`, `-h` | Show help message |
|
|
165
|
+
| `--version`, `-V` | Show version number |
|
|
178
166
|
|
|
179
|
-
|
|
167
|
+
### `release-kit prepare`
|
|
180
168
|
|
|
181
|
-
|
|
169
|
+
Run release preparation with automatic workspace discovery.
|
|
182
170
|
|
|
183
|
-
|
|
171
|
+
| Flag | Description |
|
|
172
|
+
| ---------------------------- | ---------------------------------------------------------------- |
|
|
173
|
+
| `--dry-run` | Preview changes without writing files |
|
|
174
|
+
| `--bump=major\|minor\|patch` | Override the bump type for all components |
|
|
175
|
+
| `--force` | Bypass the "no commits since last tag" check (requires `--bump`) |
|
|
176
|
+
| `--only=name1,name2` | Only process the named components (monorepo only) |
|
|
177
|
+
| `--help`, `-h` | Show help |
|
|
184
178
|
|
|
185
|
-
|
|
186
|
-
import { component } from '@williamthorsen/release-kit';
|
|
179
|
+
Component names for `--only` match the package directory name (e.g., `arrays`, `release-kit`).
|
|
187
180
|
|
|
188
|
-
|
|
189
|
-
component('packages/arrays');
|
|
190
|
-
// => {
|
|
191
|
-
// dir: 'arrays',
|
|
192
|
-
// tagPrefix: 'arrays-v',
|
|
193
|
-
// packageFiles: ['packages/arrays/package.json'],
|
|
194
|
-
// changelogPaths: ['packages/arrays'],
|
|
195
|
-
// paths: ['packages/arrays/**'],
|
|
196
|
-
// }
|
|
197
|
-
```
|
|
181
|
+
### `release-kit init`
|
|
198
182
|
|
|
199
|
-
|
|
183
|
+
Initialize release-kit in the current repository. By default, scaffolds only the GitHub Actions workflow file. Use `--with-config` to also scaffold configuration files.
|
|
200
184
|
|
|
201
|
-
|
|
185
|
+
| Flag | Description |
|
|
186
|
+
| --------------- | -------------------------------------------------------------------------- |
|
|
187
|
+
| `--with-config` | Also scaffold `.config/release-kit.config.ts` and `.config/git-cliff.toml` |
|
|
188
|
+
| `--force` | Overwrite existing files instead of skipping them |
|
|
189
|
+
| `--dry-run` | Preview changes without writing files |
|
|
190
|
+
| `--help`, `-h` | Show help |
|
|
202
191
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
### Monorepo
|
|
206
|
-
|
|
207
|
-
```yaml
|
|
208
|
-
name: Release
|
|
209
|
-
|
|
210
|
-
on:
|
|
211
|
-
workflow_dispatch:
|
|
212
|
-
inputs:
|
|
213
|
-
only:
|
|
214
|
-
description: 'Components to release (comma-separated, leave empty for all)'
|
|
215
|
-
required: false
|
|
216
|
-
type: string
|
|
217
|
-
bump:
|
|
218
|
-
description: 'Override bump type (leave empty to auto-detect)'
|
|
219
|
-
required: false
|
|
220
|
-
type: choice
|
|
221
|
-
options:
|
|
222
|
-
- ''
|
|
223
|
-
- patch
|
|
224
|
-
- minor
|
|
225
|
-
- major
|
|
226
|
-
|
|
227
|
-
permissions:
|
|
228
|
-
contents: write
|
|
229
|
-
|
|
230
|
-
jobs:
|
|
231
|
-
release:
|
|
232
|
-
runs-on: ubuntu-latest
|
|
233
|
-
steps:
|
|
234
|
-
- uses: actions/checkout@v4
|
|
235
|
-
with:
|
|
236
|
-
fetch-depth: 0
|
|
237
|
-
token: ${{ secrets.GITHUB_TOKEN }}
|
|
238
|
-
|
|
239
|
-
- uses: actions/setup-node@v4
|
|
240
|
-
with:
|
|
241
|
-
node-version: '24'
|
|
242
|
-
|
|
243
|
-
- name: Run release preparation
|
|
244
|
-
run: |
|
|
245
|
-
ARGS=""
|
|
246
|
-
if [ -n "${{ inputs.only }}" ]; then
|
|
247
|
-
ARGS="$ARGS --only=${{ inputs.only }}"
|
|
248
|
-
fi
|
|
249
|
-
if [ -n "${{ inputs.bump }}" ]; then
|
|
250
|
-
ARGS="$ARGS --bump=${{ inputs.bump }}"
|
|
251
|
-
fi
|
|
252
|
-
npx @williamthorsen/release-kit prepare $ARGS
|
|
253
|
-
|
|
254
|
-
- name: Check for changes
|
|
255
|
-
id: check
|
|
256
|
-
run: |
|
|
257
|
-
if git diff --quiet; then
|
|
258
|
-
echo "changed=false" >> "$GITHUB_OUTPUT"
|
|
259
|
-
echo "No release-worthy changes found."
|
|
260
|
-
else
|
|
261
|
-
echo "changed=true" >> "$GITHUB_OUTPUT"
|
|
262
|
-
fi
|
|
263
|
-
|
|
264
|
-
- name: Read release tags
|
|
265
|
-
if: steps.check.outputs.changed == 'true'
|
|
266
|
-
id: tags
|
|
267
|
-
run: |
|
|
268
|
-
TAGS=$(cat tmp/.release-tags | tr '\n' ' ')
|
|
269
|
-
echo "tags=$TAGS" >> "$GITHUB_OUTPUT"
|
|
270
|
-
echo "Releasing: $TAGS"
|
|
271
|
-
|
|
272
|
-
- name: Commit, tag, and push
|
|
273
|
-
if: steps.check.outputs.changed == 'true'
|
|
274
|
-
run: |
|
|
275
|
-
git config user.name "github-actions[bot]"
|
|
276
|
-
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
277
|
-
git add -A
|
|
278
|
-
git commit -m "release: ${{ steps.tags.outputs.tags }}"
|
|
279
|
-
for TAG in ${{ steps.tags.outputs.tags }}; do
|
|
280
|
-
git tag "$TAG"
|
|
281
|
-
done
|
|
282
|
-
git push origin main ${{ steps.tags.outputs.tags }}
|
|
283
|
-
```
|
|
192
|
+
Scaffolded files:
|
|
284
193
|
|
|
285
|
-
|
|
194
|
+
- `.github/workflows/release.yaml` — workflow that delegates to a reusable release workflow
|
|
195
|
+
- `.config/release-kit.config.ts` — starter config with commented-out customization examples (with `--with-config`)
|
|
196
|
+
- `.config/git-cliff.toml` — copied from the bundled template (with `--with-config`)
|
|
286
197
|
|
|
287
|
-
|
|
198
|
+
### `release-kit sync-labels`
|
|
288
199
|
|
|
289
|
-
|
|
290
|
-
- name: Run release preparation
|
|
291
|
-
run: |
|
|
292
|
-
ARGS=""
|
|
293
|
-
if [ -n "${{ inputs.bump }}" ]; then
|
|
294
|
-
ARGS="--bump=${{ inputs.bump }}"
|
|
295
|
-
fi
|
|
296
|
-
npx @williamthorsen/release-kit prepare $ARGS
|
|
297
|
-
```
|
|
200
|
+
Manage GitHub label definitions via config-driven YAML files.
|
|
298
201
|
|
|
299
|
-
|
|
202
|
+
| Subcommand | Description | Flags |
|
|
203
|
+
| ---------- | -------------------------------------------------------------- | ---------------------- |
|
|
204
|
+
| `init` | Scaffold config, caller workflow, and generate labels | `--dry-run`, `--force` |
|
|
205
|
+
| `generate` | Regenerate `.github/labels.yaml` from config | — |
|
|
206
|
+
| `sync` | Trigger the `sync-labels` GitHub Actions workflow via `gh` CLI | — |
|
|
300
207
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
208
|
+
`init` scaffolds `.config/sync-labels.config.ts` with auto-detected workspace scope labels and a `.github/workflows/sync-labels.yaml` caller workflow, then generates `.github/labels.yaml`. `generate` reads the config and writes `.github/labels.yaml`. `sync` triggers the workflow remotely — it requires the `gh` CLI and an existing workflow file.
|
|
209
|
+
|
|
210
|
+
## GitHub Actions workflow
|
|
211
|
+
|
|
212
|
+
The `init` command scaffolds a release workflow at `.github/workflows/release.yaml` that delegates to a reusable release workflow. The scaffolded workflow accepts these inputs:
|
|
213
|
+
|
|
214
|
+
| Input | Type | Description |
|
|
215
|
+
| ------ | ------ | ------------------------------------------------------------------- |
|
|
216
|
+
| `only` | string | Components to release (comma-separated, leave empty for all) |
|
|
217
|
+
| `bump` | choice | Override bump type: `patch`, `minor`, `major` (empty = auto-detect) |
|
|
218
|
+
|
|
219
|
+
For repos that need a self-contained workflow instead of the reusable one, the scaffolded file can be expanded. The key steps are: checkout with full history (`fetch-depth: 0`), run `release-kit prepare` with optional `--only` and `--bump` flags, check for changes, read tags from `tmp/.release-tags`, then commit, tag, and push.
|
|
309
220
|
|
|
310
|
-
|
|
221
|
+
### Triggering a release
|
|
311
222
|
|
|
312
223
|
```sh
|
|
313
224
|
# All components
|
|
@@ -322,12 +233,14 @@ Or use the GitHub UI: Actions > Release > Run workflow.
|
|
|
322
233
|
|
|
323
234
|
## cliff.toml setup
|
|
324
235
|
|
|
325
|
-
The package includes a bundled `cliff.toml.template` that is used automatically when no custom config is found. The resolution order
|
|
236
|
+
The package includes a bundled `cliff.toml.template` that is used automatically when no custom config is found. The resolution order:
|
|
326
237
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
238
|
+
| Priority | Path | Notes |
|
|
239
|
+
| -------- | ----------------------------- | ----------------------------------------------- |
|
|
240
|
+
| 1 | `cliffConfigPath` in config | Explicit path, returned without existence check |
|
|
241
|
+
| 2 | `.config/git-cliff.toml` | Project-level override |
|
|
242
|
+
| 3 | `cliff.toml` | Repo root fallback |
|
|
243
|
+
| 4 | Bundled `cliff.toml.template` | Automatic fallback |
|
|
331
244
|
|
|
332
245
|
The bundled template provides a generic git-cliff configuration that:
|
|
333
246
|
|
|
@@ -344,6 +257,26 @@ This package shells out to two external tools:
|
|
|
344
257
|
- **`git`** — must be available on `PATH`. Used to find tags and retrieve commit history.
|
|
345
258
|
- **`git-cliff`** — automatically downloaded and cached via `npx` on first invocation. No need to install it as a dev dependency.
|
|
346
259
|
|
|
260
|
+
## Using `component()` for manual configuration
|
|
261
|
+
|
|
262
|
+
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:
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
import { component } from '@williamthorsen/release-kit';
|
|
266
|
+
|
|
267
|
+
// Accepts the full workspace-relative path
|
|
268
|
+
component('packages/arrays');
|
|
269
|
+
// => {
|
|
270
|
+
// dir: 'arrays',
|
|
271
|
+
// tagPrefix: 'arrays-v',
|
|
272
|
+
// packageFiles: ['packages/arrays/package.json'],
|
|
273
|
+
// changelogPaths: ['packages/arrays'],
|
|
274
|
+
// paths: ['packages/arrays/**'],
|
|
275
|
+
// }
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
The `dir` field is derived from `path.basename()`, so `packages/arrays` and `libs/arrays` both produce `dir: 'arrays'`. The `tagPrefix` is always `${dir}-v` — it cannot be customized.
|
|
279
|
+
|
|
347
280
|
## Legacy script-based approach
|
|
348
281
|
|
|
349
282
|
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.
|
package/bin/release-kit.js
CHANGED
|
@@ -1,2 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
try {
|
|
3
|
+
await import('../dist/esm/bin/release-kit.js');
|
|
4
|
+
} catch (error) {
|
|
5
|
+
if (error.code === 'ERR_MODULE_NOT_FOUND') {
|
|
6
|
+
process.stderr.write('release-kit: build output not found — run `pnpm run build` first\n');
|
|
7
|
+
} else {
|
|
8
|
+
process.stderr.write(`release-kit: failed to load: ${error.message}\n`);
|
|
9
|
+
}
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
package/dist/esm/.cache
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
89fa3a396cfa2f4f2cf5ce8827dbe171e310519ab3faa7325268a87693116335
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { parseArgs, translateParseError } from "@williamthorsen/node-monorepo-core";
|
|
2
3
|
import { commitCommand } from "../commitCommand.js";
|
|
3
4
|
import { initCommand } from "../init/initCommand.js";
|
|
4
5
|
import { prepareCommand } from "../prepareCommand.js";
|
|
@@ -7,6 +8,7 @@ import { generateCommand } from "../sync-labels/generateCommand.js";
|
|
|
7
8
|
import { syncLabelsInitCommand } from "../sync-labels/initCommand.js";
|
|
8
9
|
import { syncLabelsCommand } from "../sync-labels/syncCommand.js";
|
|
9
10
|
import { tagCommand } from "../tagCommand.js";
|
|
11
|
+
import { VERSION } from "../version.js";
|
|
10
12
|
function showUsage() {
|
|
11
13
|
console.info(`
|
|
12
14
|
Usage: release-kit <command> [options]
|
|
@@ -140,6 +142,10 @@ Options:
|
|
|
140
142
|
const args = process.argv.slice(2);
|
|
141
143
|
const command = args[0];
|
|
142
144
|
const flags = args.slice(1);
|
|
145
|
+
if (command === "--version" || command === "-V") {
|
|
146
|
+
console.info(VERSION);
|
|
147
|
+
process.exit(0);
|
|
148
|
+
}
|
|
143
149
|
if (command === "--help" || command === "-h" || command === void 0) {
|
|
144
150
|
showUsage();
|
|
145
151
|
process.exit(0);
|
|
@@ -186,15 +192,19 @@ if (command === "init") {
|
|
|
186
192
|
showInitHelp();
|
|
187
193
|
process.exit(0);
|
|
188
194
|
}
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
195
|
+
const initFlagSchema = {
|
|
196
|
+
dryRun: { long: "--dry-run", type: "boolean" },
|
|
197
|
+
force: { long: "--force", type: "boolean" },
|
|
198
|
+
withConfig: { long: "--with-config", type: "boolean" }
|
|
199
|
+
};
|
|
200
|
+
let parsed;
|
|
201
|
+
try {
|
|
202
|
+
parsed = parseArgs(flags, initFlagSchema);
|
|
203
|
+
} catch (error) {
|
|
204
|
+
console.error(`Error: ${translateParseError(error)}`);
|
|
193
205
|
process.exit(1);
|
|
194
206
|
}
|
|
195
|
-
const dryRun = flags
|
|
196
|
-
const force = flags.includes("--force");
|
|
197
|
-
const withConfig = flags.includes("--with-config");
|
|
207
|
+
const { dryRun, force, withConfig } = parsed.flags;
|
|
198
208
|
const exitCode = initCommand({ dryRun, force, withConfig });
|
|
199
209
|
process.exit(exitCode);
|
|
200
210
|
}
|
|
@@ -210,14 +220,18 @@ if (command === "sync-labels") {
|
|
|
210
220
|
showSyncLabelsInitHelp();
|
|
211
221
|
process.exit(0);
|
|
212
222
|
}
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
223
|
+
const syncLabelsInitFlagSchema = {
|
|
224
|
+
dryRun: { long: "--dry-run", type: "boolean" },
|
|
225
|
+
force: { long: "--force", type: "boolean" }
|
|
226
|
+
};
|
|
227
|
+
let syncParsed;
|
|
228
|
+
try {
|
|
229
|
+
syncParsed = parseArgs(subflags, syncLabelsInitFlagSchema);
|
|
230
|
+
} catch (error) {
|
|
231
|
+
console.error(`Error: ${translateParseError(error)}`);
|
|
217
232
|
process.exit(1);
|
|
218
233
|
}
|
|
219
|
-
const dryRun =
|
|
220
|
-
const force = subflags.includes("--force");
|
|
234
|
+
const { dryRun, force } = syncParsed.flags;
|
|
221
235
|
const exitCode = await syncLabelsInitCommand({ dryRun, force });
|
|
222
236
|
process.exit(exitCode);
|
|
223
237
|
}
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
|
+
import { parseArgs, translateParseError } from "@williamthorsen/node-monorepo-core";
|
|
3
4
|
import { RELEASE_SUMMARY_FILE, RELEASE_TAGS_FILE } from "./prepareCommand.js";
|
|
5
|
+
const commitFlagSchema = {
|
|
6
|
+
dryRun: { long: "--dry-run", type: "boolean" }
|
|
7
|
+
};
|
|
4
8
|
function commitCommand(argv) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
let parsed;
|
|
10
|
+
try {
|
|
11
|
+
parsed = parseArgs(argv, commitFlagSchema);
|
|
12
|
+
} catch (error) {
|
|
13
|
+
console.error(`Error: ${translateParseError(error)}`);
|
|
9
14
|
process.exit(1);
|
|
10
15
|
}
|
|
11
|
-
const dryRun =
|
|
16
|
+
const dryRun = parsed.flags.dryRun;
|
|
12
17
|
let tagsContent;
|
|
13
18
|
try {
|
|
14
19
|
tagsContent = readFileSync(RELEASE_TAGS_FILE, "utf8");
|
package/dist/esm/createTags.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
|
-
import { readFileSync
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { deleteFileIfExists } from "./deleteFileIfExists.js";
|
|
3
4
|
import { RELEASE_SUMMARY_FILE, RELEASE_TAGS_FILE } from "./prepareCommand.js";
|
|
4
5
|
function createTags(options) {
|
|
5
6
|
const { dryRun, noGitChecks } = options;
|
|
@@ -42,30 +43,10 @@ function createTags(options) {
|
|
|
42
43
|
for (const tag of tags) {
|
|
43
44
|
console.info(`\u{1F3F7}\uFE0F ${tag}`);
|
|
44
45
|
}
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
deleteFileIfExists(RELEASE_TAGS_FILE);
|
|
47
|
+
deleteFileIfExists(RELEASE_SUMMARY_FILE);
|
|
47
48
|
return tags;
|
|
48
49
|
}
|
|
49
|
-
function deleteTagsFile() {
|
|
50
|
-
try {
|
|
51
|
-
unlinkSync(RELEASE_TAGS_FILE);
|
|
52
|
-
} catch (error) {
|
|
53
|
-
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
throw error;
|
|
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
|
-
}
|
|
69
50
|
function assertCleanWorkingTree() {
|
|
70
51
|
try {
|
|
71
52
|
execFileSync("git", ["diff", "--quiet"]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function deleteFileIfExists(filePath: string): void;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { unlinkSync } from "node:fs";
|
|
2
|
+
function deleteFileIfExists(filePath) {
|
|
3
|
+
try {
|
|
4
|
+
unlinkSync(filePath);
|
|
5
|
+
} catch (error) {
|
|
6
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
throw error;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export {
|
|
13
|
+
deleteFileIfExists
|
|
14
|
+
};
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export { bumpVersion } from './bumpVersion.ts';
|
|
|
13
13
|
export { commitCommand } from './commitCommand.ts';
|
|
14
14
|
export { component } from './component.ts';
|
|
15
15
|
export { createTags } from './createTags.ts';
|
|
16
|
+
export { deleteFileIfExists } from './deleteFileIfExists.ts';
|
|
16
17
|
export { detectPackageManager } from './detectPackageManager.ts';
|
|
17
18
|
export { determineBumpType } from './determineBumpType.ts';
|
|
18
19
|
export { discoverWorkspaces } from './discoverWorkspaces.ts';
|
package/dist/esm/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { bumpVersion } from "./bumpVersion.js";
|
|
|
5
5
|
import { commitCommand } from "./commitCommand.js";
|
|
6
6
|
import { component } from "./component.js";
|
|
7
7
|
import { createTags } from "./createTags.js";
|
|
8
|
+
import { deleteFileIfExists } from "./deleteFileIfExists.js";
|
|
8
9
|
import { detectPackageManager } from "./detectPackageManager.js";
|
|
9
10
|
import { determineBumpType } from "./determineBumpType.js";
|
|
10
11
|
import { discoverWorkspaces } from "./discoverWorkspaces.js";
|
|
@@ -30,6 +31,7 @@ export {
|
|
|
30
31
|
commitCommand,
|
|
31
32
|
component,
|
|
32
33
|
createTags,
|
|
34
|
+
deleteFileIfExists,
|
|
33
35
|
detectPackageManager,
|
|
34
36
|
determineBumpType,
|
|
35
37
|
discoverWorkspaces,
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
parseArgs as coreParseArgs,
|
|
3
|
+
translateParseError,
|
|
4
|
+
writeFileWithCheck
|
|
5
|
+
} from "@williamthorsen/node-monorepo-core";
|
|
2
6
|
import { buildReleaseSummary } from "./buildReleaseSummary.js";
|
|
3
7
|
import { discoverWorkspaces } from "./discoverWorkspaces.js";
|
|
4
8
|
import { dim } from "./format.js";
|
|
@@ -25,43 +29,40 @@ Options:
|
|
|
25
29
|
--help Show this help message
|
|
26
30
|
`);
|
|
27
31
|
}
|
|
32
|
+
const prepareFlagSchema = {
|
|
33
|
+
dryRun: { long: "--dry-run", type: "boolean" },
|
|
34
|
+
force: { long: "--force", type: "boolean" },
|
|
35
|
+
bump: { long: "--bump", type: "string" },
|
|
36
|
+
only: { long: "--only", type: "string" },
|
|
37
|
+
help: { long: "--help", type: "boolean", short: "-h" }
|
|
38
|
+
};
|
|
28
39
|
function parseArgs(argv) {
|
|
29
|
-
let
|
|
30
|
-
|
|
40
|
+
let parsed;
|
|
41
|
+
try {
|
|
42
|
+
parsed = coreParseArgs(argv, prepareFlagSchema);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
throw new Error(translateParseError(error));
|
|
45
|
+
}
|
|
46
|
+
const { flags } = parsed;
|
|
47
|
+
if (flags.help) {
|
|
48
|
+
showHelp();
|
|
49
|
+
process.exit(0);
|
|
50
|
+
}
|
|
31
51
|
let bumpOverride;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
dryRun = true;
|
|
36
|
-
} else if (arg === "--force") {
|
|
37
|
-
force = true;
|
|
38
|
-
} else if (arg.startsWith("--bump=")) {
|
|
39
|
-
const value = arg.slice("--bump=".length);
|
|
40
|
-
if (!isReleaseType(value)) {
|
|
41
|
-
console.error(`Error: Invalid bump type "${value}". Must be one of: ${VALID_BUMP_TYPES.join(", ")}`);
|
|
42
|
-
process.exit(1);
|
|
43
|
-
}
|
|
44
|
-
bumpOverride = value;
|
|
45
|
-
} else if (arg.startsWith("--only=")) {
|
|
46
|
-
const value = arg.slice("--only=".length);
|
|
47
|
-
if (!value) {
|
|
48
|
-
console.error("Error: --only requires a comma-separated list of component names");
|
|
49
|
-
process.exit(1);
|
|
50
|
-
}
|
|
51
|
-
only = value.split(",");
|
|
52
|
-
} else if (arg === "--help" || arg === "-h") {
|
|
53
|
-
showHelp();
|
|
54
|
-
process.exit(0);
|
|
55
|
-
} else {
|
|
56
|
-
console.error(`Error: Unknown argument: ${arg}`);
|
|
57
|
-
process.exit(1);
|
|
52
|
+
if (flags.bump !== void 0) {
|
|
53
|
+
if (!isReleaseType(flags.bump)) {
|
|
54
|
+
throw new Error(`Invalid bump type "${flags.bump}". Must be one of: ${VALID_BUMP_TYPES.join(", ")}`);
|
|
58
55
|
}
|
|
56
|
+
bumpOverride = flags.bump;
|
|
59
57
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
let only;
|
|
59
|
+
if (flags.only !== void 0) {
|
|
60
|
+
only = flags.only.split(",");
|
|
61
|
+
}
|
|
62
|
+
if (flags.force && bumpOverride === void 0) {
|
|
63
|
+
throw new Error("--force requires --bump to specify the version bump type");
|
|
63
64
|
}
|
|
64
|
-
return { dryRun, force, bumpOverride, only };
|
|
65
|
+
return { dryRun: flags.dryRun, force: flags.force, bumpOverride, only };
|
|
65
66
|
}
|
|
66
67
|
function writeReleaseTags(tags, dryRun) {
|
|
67
68
|
if (tags.length === 0) {
|
|
@@ -70,7 +71,16 @@ function writeReleaseTags(tags, dryRun) {
|
|
|
70
71
|
return writeFileWithCheck(RELEASE_TAGS_FILE, tags.join("\n"), { dryRun, overwrite: true });
|
|
71
72
|
}
|
|
72
73
|
async function prepareCommand(argv) {
|
|
73
|
-
|
|
74
|
+
let dryRun;
|
|
75
|
+
let force;
|
|
76
|
+
let bumpOverride;
|
|
77
|
+
let only;
|
|
78
|
+
try {
|
|
79
|
+
({ dryRun, force, bumpOverride, only } = parseArgs(argv));
|
|
80
|
+
} catch (error) {
|
|
81
|
+
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
74
84
|
const options = {
|
|
75
85
|
dryRun,
|
|
76
86
|
force,
|
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
import { basename } from "node:path";
|
|
2
|
+
import { parseArgs, translateParseError } from "@williamthorsen/node-monorepo-core";
|
|
2
3
|
import { detectPackageManager } from "./detectPackageManager.js";
|
|
3
4
|
import { discoverWorkspaces } from "./discoverWorkspaces.js";
|
|
4
5
|
import { publish } from "./publish.js";
|
|
5
6
|
import { resolveReleaseTags } from "./resolveReleaseTags.js";
|
|
7
|
+
const publishFlagSchema = {
|
|
8
|
+
dryRun: { long: "--dry-run", type: "boolean" },
|
|
9
|
+
noGitChecks: { long: "--no-git-checks", type: "boolean" },
|
|
10
|
+
provenance: { long: "--provenance", type: "boolean" },
|
|
11
|
+
only: { long: "--only", type: "string" }
|
|
12
|
+
};
|
|
6
13
|
async function publishCommand(argv) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
14
|
+
let parsed;
|
|
15
|
+
try {
|
|
16
|
+
parsed = parseArgs(argv, publishFlagSchema);
|
|
17
|
+
} catch (error) {
|
|
18
|
+
console.error(`Error: ${translateParseError(error)}`);
|
|
11
19
|
process.exit(1);
|
|
12
20
|
}
|
|
13
|
-
const dryRun =
|
|
14
|
-
const
|
|
15
|
-
const provenance = argv.includes("--provenance");
|
|
16
|
-
const onlyArg = argv.find((f) => f.startsWith("--only="));
|
|
17
|
-
const only = onlyArg?.slice("--only=".length).split(",");
|
|
21
|
+
const { dryRun, noGitChecks, provenance } = parsed.flags;
|
|
22
|
+
const only = parsed.flags.only?.split(",");
|
|
18
23
|
let discoveredPaths;
|
|
19
24
|
try {
|
|
20
25
|
discoveredPaths = await discoverWorkspaces();
|
package/dist/esm/tagCommand.js
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
+
import { parseArgs, translateParseError } from "@williamthorsen/node-monorepo-core";
|
|
1
2
|
import { createTags } from "./createTags.js";
|
|
3
|
+
const tagFlagSchema = {
|
|
4
|
+
dryRun: { long: "--dry-run", type: "boolean" },
|
|
5
|
+
noGitChecks: { long: "--no-git-checks", type: "boolean" }
|
|
6
|
+
};
|
|
2
7
|
function tagCommand(argv) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
8
|
+
let parsed;
|
|
9
|
+
try {
|
|
10
|
+
parsed = parseArgs(argv, tagFlagSchema);
|
|
11
|
+
} catch (error) {
|
|
12
|
+
console.error(`Error: ${translateParseError(error)}`);
|
|
7
13
|
process.exit(1);
|
|
8
14
|
}
|
|
9
|
-
const dryRun =
|
|
10
|
-
const noGitChecks = argv.includes("--no-git-checks");
|
|
15
|
+
const { dryRun, noGitChecks } = parsed.flags;
|
|
11
16
|
try {
|
|
12
17
|
createTags({ dryRun, noGitChecks });
|
|
13
18
|
} catch (error) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const VERSION = "4.4.0";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@williamthorsen/release-kit",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.4.0",
|
|
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.
|
|
37
|
+
"@williamthorsen/node-monorepo-core": "0.2.5"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/js-yaml": "4.0.9",
|