@williamthorsen/release-kit 2.0.0 → 2.1.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 CHANGED
@@ -2,129 +2,38 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
- ## [release-kit-v2.0.0] - 2026-03-16
5
+ ## [release-kit-v2.1.0] - 2026-03-17
6
6
 
7
7
  ### Features
8
8
 
9
- - #20 release-kit|feat!: Move reusable release workflow into repo (#26)
10
-
11
- Move the reusable release workflow from `williamthorsen/.github` into this repository as `.github/workflows/release-workflow.yaml`, versioned independently via `release-workflow-v{major}` tags. Update `release-kit init` templates to reference the new workflow location. The workflow no longer requires pnpm: it installs release-kit globally and runs it directly.
12
-
13
- - #22 release-kit|feat: Add --force flag to release-kit prepare (#25)
14
-
15
- Add a `--force` flag that allows release preparation to proceed even when no release-worthy commits are found.
16
-
17
9
  - #7 release-kit|feat!: Slim down release workflow by removing unnecessary pnpm install (#21)
18
10
 
19
- Remove pnpm setup steps from the reusable release workflow. Release-kit is self-contained and invokes git-cliff and prettier via `npx`, so no local dependency installation is needed.
20
-
21
- ### Refactoring
22
-
23
- - #6 release-kit|refactor: Clean up release-kit post-migration issues (#19)
24
-
25
- ## [release-kit-v1.0.1] - 2026-03-12
11
+ Make release-kit self-contained by invoking git-cliff via `npx --yes` instead of requiring it on PATH, and by appending modified file paths to the format command so lightweight formatters like `npx prettier --write` work without a full `pnpm install`. Update init templates, README, and consuming repo config/workflow to reference workflow v3.
26
12
 
27
- ### Bug fixes
28
-
29
- - #34 release-kit|fix: Fix failure to find consumer's config (#35)
13
+ - #22 release-kit|feat: Add --force flag to release-kit prepare (#25)
30
14
 
31
- Resolves the config file path to an absolute path using `process.cwd()` before passing it to both `existsSync` and `jiti.import()`. Previously, `jiti.import()` received a bare relative path which it resolved against `import.meta.url` (the package's install location), making it impossible for consumers to load their config files. Also fixes root tsconfig includes to cover `.config/` and removes a stale duplicate comment.
15
+ Add a `--force` flag to `release-kit prepare` that bypasses the "no commits since last tag" check in monorepo mode, allowing version bumping and changelog generation to proceed even when no new commits are found since the last release tag. The flag requires `--bump` since there are no commits to infer bump type from. The local release workflow gains a `force` boolean input for future use.
32
16
 
33
- ## [release-kit-v1.0.0] - 2026-03-12
17
+ - #20 release-kit|feat!: Move reusable release workflow into repo (#26)
34
18
 
35
- ### Features
19
+ Moves the reusable release workflow from `williamthorsen/.github` into this repo as `release-workflow.yaml`, stripping all pnpm-related steps since release-kit now runs git-cliff and prettier via `npx` internally. Updates this repo's caller workflow to use a relative path and update init templates to reference the new location. Establishes a naming convention (`{name}-workflow.yaml` for reusable, `{name}.yaml` for callers) and independent versioning strategy (`{name}-workflow-v{major}` tags), documented in `.github/workflows/README.md`.
36
20
 
37
- - #28 release-kit|feat!: Migrate to CLI-driven release preparation with auto-discovery (#31)
21
+ - #30 release-kit|feat: Allow git-cliff to be used without config (#31)
38
22
 
39
- 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.
23
+ Adds a `resolveCliffConfigPath()` function that searches for a git-cliff config in a 4-step cascade (explicit path → `.config/git-cliff.toml` `cliff.toml` bundled `cliff.toml.template`), eliminating the requirement for consuming repos to maintain a cliff config copy. Restructures the `init` command to scaffold only the workflow file by default, with new `--with-config` and `--force` flags. Moves `.release-tags` from `/tmp/release-kit/` to project-local `tmp/` for predictable behavior in local runs.
40
24
 
41
25
  ### Refactoring
42
26
 
43
- - #28 release-kit|refactor: Adjust location of config & tags file
44
-
45
- ### Tooling
46
-
47
- - #28 release-kit|tooling: Remove legacy release-kit scripts
48
-
49
- ## [release-kit-v0.3.0] - 2026-03-11
50
-
51
- ### Features
52
-
53
- - Release-kit|feat: Extract CLI runner into release-kit and co-locate scripts with workflow
54
-
55
- 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)`.
56
-
57
- Relocate release-prepare.ts and release.config.ts from scripts/ to .github/scripts/ so they live alongside the workflow they serve.
58
-
59
- - #20 release-kit|feat: Add release-kit init CLI command for automated repo setup (#22)
60
-
61
- 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.
62
-
63
- Also expand `runReleasePrepare` to polymorphically handle both `MonorepoReleaseConfig` and `ReleaseConfig`, and update the esbuild plugin to preserve shebangs during compilation.
64
-
65
- - #24 release-kit|feat: Return computed tags from release prepare and write .release-tags (#27)
66
-
67
- `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.
68
-
69
- ## [strings-v3.1.1] - 2026-03-10
70
-
71
- ### Tooling
72
-
73
- - \*|tooling: Change package registry from github to npmjs
74
-
75
- ## [tools-v3.0.1] - 2026-03-10
76
-
77
- ### Tooling
78
-
79
- - \*|tooling: Change package registry from github to npmjs
80
-
81
- ## [release-kit-v0.2.1] - 2026-03-09
82
-
83
- ### Tooling
84
-
85
- - Root|tooling: Make release-kit public
86
-
87
- ## [release-kit-v0.2.0] - 2026-03-09
88
-
89
- ### Documentation
90
-
91
- - Release-kit|docs: Rewrite README as adoption guide
92
-
93
- Replace minimal API docs with end-to-end adoption guide covering single-package and monorepo configurations, release scripts, GitHub Actions workflows that commit directly to `main`.
94
-
95
- ### Tooling
96
-
97
- - #13 root|tooling: Migrate from changesets to release-kit (#16)
98
-
99
- Replaces the `@changesets/cli`-based release workflow with the in-house `release-kit` package, adding `git-cliff` for changelog generation, a monorepo release config for all 13 packages, and a CLI wrapper script. Removes all changeset infrastructure and creates per-package baseline version tags.
100
-
101
- - #10 root|tooling: Publish release-kit to GitHub Package Registry (#17)
102
-
103
- Adds release infrastructure for the toolbelt monorepo: a GitHub Actions `workflow_dispatch` workflow that automates the full release cycle (prepare, commit, tag, push) on `main`, convenience `release:prepare` scripts in the release-kit package, and a `RELEASING.md` documenting the workflow-based release process.
27
+ - #6 release-kit|refactor: Clean up release-kit post-migration issues (#19)
104
28
 
105
- - #10 root|tooling: Streamline release-kit adoption
29
+ Addresses five code quality issues and a test coverage gap identified during the release-kit migration (#5). Extracts a duplicated `isRecord` type guard into a shared module, eliminates a double-read in `bumpAllVersions`, improves error handling in `usesPnpm` by replacing a silent catch with a structured error boundary, removes an unreachable `'feature'` pattern from version defaults, and adds an integration test for scaffold template path resolution.
106
30
 
107
- ## [tools-v3.0.0] - 2026-03-08
31
+ ## [release-kit-v1.0.1] - 2026-03-14
108
32
 
109
33
  ### Features
110
34
 
111
- - #7 release|feat: Create release-kit package (#9)
112
-
113
- Creates the `@williamthorsen/release-kit` package in the `toolbelt` monorepo, extracting version-bumping and changelog-generation logic from `skypilot-site` and `devtools/afg` into a reusable library. The package provides functions for parsing conventional commits, determining semver bump types, updating `package.json` versions across workspaces, and generating changelogs via `git-cliff`.
114
-
115
- Add contextual error messages to all I/O operations: file reads/writes in bumpAllVersions, execSync calls in generateChangelogs and releasePrepare, and git commands in getCommitsSinceTarget.
116
-
117
- Differentiate expected "no tag" errors from real failures in git describe. Replace string-based commit separator with null-byte to prevent collisions with commit message content. Log git log failures before returning empty results.
118
-
119
- Add tests for uppercase/mixed-case type resolution, workspace+breaking combo parsing, breaking-on-first-commit early return, and empty workTypes. Strengthen alias tests to use toStrictEqual for full shape
120
- verification.
121
-
122
- Simplify determineBumpType by replacing the redundant isKeyOf guard on RELEASE_PRIORITY with a direct lookup, since bump is already typed as ReleaseType. Simplify parseCommitMessage by replacing mutable object construction with a conditional spread for the optional workspace field.
123
-
124
- Add workspaceAliases field to ReleaseConfig and integrate into parseCommitMessage for resolving workspace shorthand names to canonical names. Replace execSync with execFileSync using argument arrays in generateChangelogs and getCommitsSinceTarget to prevent shell injection from paths with special characters. Remove redundant length-check guard in bumpAllVersions, keeping the undefined guard that also narrows the type.
125
-
126
- ### Refactoring
35
+ - #5 release-kit|feat: Migrate release-kit from toolbelt (#18)
127
36
 
128
- - Release-kit|refactor: Inline isKeyOf and remove toolbelt.objects dependency
37
+ Migrates the complete `@williamthorsen/release-kit` package (v1.0.1) from `williamthorsen/toolbelt` into `packages/release-kit/`, adds shebang preservation to the shared esbuild plugin for CLI binaries, and sets up dogfooding infrastructure so this monorepo uses release-kit for its own releases.
129
38
 
130
39
  <!-- generated by git-cliff -->
package/README.md CHANGED
@@ -13,17 +13,14 @@ pnpm add -D @williamthorsen/release-kit
13
13
  ## Quick start
14
14
 
15
15
  ```bash
16
- # 1. Set up release-kit in your repo (scaffolds workflow + optional config)
16
+ # 1. Set up release-kit in your repo (scaffolds the release workflow)
17
17
  npx @williamthorsen/release-kit init
18
18
 
19
19
  # 2. Preview what a release would do
20
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
24
21
  ```
25
22
 
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`.
23
+ 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`.
27
24
 
28
25
  ## How it works
29
26
 
@@ -31,7 +28,7 @@ That's it for most repos. The CLI auto-discovers workspaces and applies sensible
31
28
  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
29
  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
30
  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.
31
+ 5. **Release tags file**: writes computed tags to `tmp/.release-tags` for the release workflow to read when tagging and pushing.
35
32
 
36
33
  ## CLI reference
37
34
 
@@ -59,21 +56,23 @@ node packages/release-kit/dist/esm/bin/release-kit.js prepare --dry-run
59
56
 
60
57
  ### `release-kit init`
61
58
 
62
- Initialize release-kit in the current repository. Scaffolds a GitHub Actions workflow and an optional config file.
59
+ Initialize release-kit in the current repository. By default, scaffolds only the GitHub Actions workflow file. Use `--with-config` to also scaffold configuration files.
63
60
 
64
61
  ```
65
62
  Usage: release-kit init [options]
66
63
 
67
64
  Options:
68
- --dry-run Preview changes without writing files
69
- --help, -h Show help
65
+ --with-config Also scaffold .config/release-kit.config.ts and .config/git-cliff.toml
66
+ --force Overwrite existing files instead of skipping them
67
+ --dry-run Preview changes without writing files
68
+ --help, -h Show help
70
69
  ```
71
70
 
72
71
  Scaffolded files:
73
72
 
74
- - `.config/release-kit.config.ts` — starter config with commented-out customization examples
75
73
  - `.github/workflows/release.yaml` — workflow that delegates to a reusable release workflow
76
- - `cliff.toml` — copied from the bundled template (prompted if missing)
74
+ - `.config/release-kit.config.ts` — starter config with commented-out customization examples (with `--with-config`)
75
+ - `.config/git-cliff.toml` — copied from the bundled template (with `--with-config`)
77
76
 
78
77
  ## Configuration
79
78
 
@@ -105,14 +104,14 @@ The config file supports both `export default config` and `export const config =
105
104
 
106
105
  ### `ReleaseKitConfig` reference
107
106
 
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; modified file paths are appended as arguments |
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 |
107
+ | Field | Type | Description |
108
+ | ------------------ | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
109
+ | `cliffConfigPath` | `string` | Explicit path to cliff config. If omitted, resolved automatically: `.config/git-cliff.toml` `cliff.toml` → bundled template |
110
+ | `components` | `ComponentOverride[]` | Override or exclude discovered components (matched by `dir`) |
111
+ | `formatCommand` | `string` | Shell command to run after changelog generation; modified file paths are appended as arguments |
112
+ | `versionPatterns` | `VersionPatterns` | Rules for which commit types trigger major/minor bumps |
113
+ | `workspaceAliases` | `Record<string, string>` | Maps shorthand workspace names to canonical names in commits |
114
+ | `workTypes` | `Record<string, WorkTypeConfig>` | Work type definitions, merged with defaults by key |
116
115
 
117
116
  All fields are optional.
118
117
 
@@ -259,7 +258,7 @@ jobs:
259
258
  if: steps.check.outputs.changed == 'true'
260
259
  id: tags
261
260
  run: |
262
- TAGS=$(cat /tmp/release-kit/.release-tags | tr '\n' ' ')
261
+ TAGS=$(cat tmp/.release-tags | tr '\n' ' ')
263
262
  echo "tags=$TAGS" >> "$GITHUB_OUTPUT"
264
263
  echo "Releasing: $TAGS"
265
264
 
@@ -297,7 +296,7 @@ And the tag step with:
297
296
  if: steps.check.outputs.changed == 'true'
298
297
  id: tags
299
298
  run: |
300
- TAG=$(cat /tmp/release-kit/.release-tags)
299
+ TAG=$(cat tmp/.release-tags)
301
300
  echo "tag=$TAG" >> "$GITHUB_OUTPUT"
302
301
  ```
303
302
 
@@ -316,19 +315,20 @@ Or use the GitHub UI: Actions > Release > Run workflow.
316
315
 
317
316
  ## cliff.toml setup
318
317
 
319
- The package includes a `cliff.toml.template` with a generic git-cliff configuration that:
318
+ The package includes a bundled `cliff.toml.template` that is used automatically when no custom config is found. The resolution order is:
319
+
320
+ 1. Explicit `cliffConfigPath` in `.config/release-kit.config.ts`
321
+ 2. `.config/git-cliff.toml`
322
+ 3. `cliff.toml` (repo root)
323
+ 4. Bundled `cliff.toml.template` (automatic fallback)
324
+
325
+ The bundled template provides a generic git-cliff configuration that:
320
326
 
321
327
  - Strips issue-ticket prefixes matching `^[A-Z]+-\d+\s+` (e.g., `TOOL-123 `, `AFG-456 `)
322
328
  - Handles both `type: description` and `workspace|type: description` commit formats
323
329
  - Groups commits by work type into changelog sections
324
330
 
325
- Copy it to your repo root:
326
-
327
- ```bash
328
- cp node_modules/@williamthorsen/release-kit/cliff.toml.template cliff.toml
329
- ```
330
-
331
- Then customize as needed for your project.
331
+ To customize, scaffold a local copy with `release-kit init --with-config` and edit `.config/git-cliff.toml`.
332
332
 
333
333
  ## External dependencies
334
334
 
@@ -386,5 +386,6 @@ If your format command does not accept file arguments, update it to one that doe
386
386
  3. Delete the `.changeset/` directory.
387
387
  4. Run `npx @williamthorsen/release-kit init` to scaffold workflow and config files.
388
388
  5. Remove `changeset:*` scripts from `package.json` (no replacement needed — the CLI handles everything).
389
- 6. Copy `cliff.toml.template` to your repo root as `cliff.toml` (if `init` didn't create one).
390
- 7. Create an initial version tag for each package (e.g., `git tag v1.0.0` or `git tag arrays-v1.0.0`).
389
+ 6. Create an initial version tag for each package (e.g., `git tag v1.0.0` or `git tag arrays-v1.0.0`).
390
+
391
+ No cliff config copy is needed — the bundled template is used automatically. To customize, run `release-kit init --with-config`.
package/dist/esm/.cache CHANGED
@@ -1 +1 @@
1
- f3265b81405ed17fde593f32d16e47fc2fc43f6a87805009ff7d808d5c61a2af
1
+ c3020d624e72bb577d60cb16ff223c951d26227fa82aec1c15894f2b2429603f
@@ -19,11 +19,13 @@ function showInitHelp() {
19
19
  Usage: release-kit init [options]
20
20
 
21
21
  Initialize release-kit in the current repository.
22
- Scaffolds workflow and config files.
22
+ By default, scaffolds only the GitHub Actions workflow file.
23
23
 
24
24
  Options:
25
- --dry-run Preview changes without writing files
26
- --help, -h Show this help message
25
+ --with-config Also scaffold .config/release-kit.config.ts and .config/git-cliff.toml
26
+ --force Overwrite existing files instead of skipping them
27
+ --dry-run Preview changes without writing files
28
+ --help, -h Show this help message
27
29
  `);
28
30
  }
29
31
  function showPrepareHelp() {
@@ -59,13 +61,16 @@ if (command === "init") {
59
61
  showInitHelp();
60
62
  process.exit(0);
61
63
  }
62
- const unknownFlags = flags.filter((f) => f !== "--dry-run" && f !== "--help" && f !== "-h");
64
+ const knownInitFlags = /* @__PURE__ */ new Set(["--dry-run", "--force", "--with-config", "--help", "-h"]);
65
+ const unknownFlags = flags.filter((f) => !knownInitFlags.has(f));
63
66
  if (unknownFlags.length > 0) {
64
67
  console.error(`Error: Unknown option: ${unknownFlags[0]}`);
65
68
  process.exit(1);
66
69
  }
67
70
  const dryRun = flags.includes("--dry-run");
68
- const exitCode = await initCommand({ dryRun });
71
+ const force = flags.includes("--force");
72
+ const withConfig = flags.includes("--with-config");
73
+ const exitCode = initCommand({ dryRun, force, withConfig });
69
74
  process.exit(exitCode);
70
75
  }
71
76
  console.error(`Error: Unknown command: ${command}`);
@@ -1,6 +1,7 @@
1
1
  import { execFileSync } from "node:child_process";
2
+ import { resolveCliffConfigPath } from "./resolveCliffConfigPath.js";
2
3
  function generateChangelog(config, changelogPath, tag, dryRun, options) {
3
- const cliffConfigPath = config.cliffConfigPath ?? "cliff.toml";
4
+ const cliffConfigPath = resolveCliffConfigPath(config.cliffConfigPath, import.meta.url);
4
5
  const outputFile = `${changelogPath}/CHANGELOG.md`;
5
6
  const args = ["--config", cliffConfigPath, "--output", outputFile, "--tag", tag];
6
7
  for (const includePath of options?.includePaths ?? []) {
@@ -5,5 +5,3 @@ export interface CheckResult {
5
5
  export declare function isGitRepo(): CheckResult;
6
6
  export declare function hasPackageJson(): CheckResult;
7
7
  export declare function usesPnpm(): CheckResult;
8
- export declare function hasCliffToml(): CheckResult;
9
- export declare function notAlreadyInitialized(): CheckResult;
@@ -29,25 +29,8 @@ function usesPnpm() {
29
29
  message: "This project does not appear to use pnpm. A pnpm-lock.yaml or packageManager field is required."
30
30
  };
31
31
  }
32
- function hasCliffToml() {
33
- if (existsSync("cliff.toml")) {
34
- return { ok: true };
35
- }
36
- return { ok: false, message: "No cliff.toml found. This file is required for changelog generation." };
37
- }
38
- function notAlreadyInitialized() {
39
- if (!existsSync(".config/release-kit.config.ts") && !existsSync(".github/scripts/release.config.ts")) {
40
- return { ok: true };
41
- }
42
- return {
43
- ok: false,
44
- message: "release-kit appears to be already initialized."
45
- };
46
- }
47
32
  export {
48
- hasCliffToml,
49
33
  hasPackageJson,
50
34
  isGitRepo,
51
- notAlreadyInitialized,
52
35
  usesPnpm
53
36
  };
@@ -1,5 +1,7 @@
1
1
  interface InitOptions {
2
2
  dryRun: boolean;
3
+ force: boolean;
4
+ withConfig: boolean;
3
5
  }
4
- export declare function initCommand({ dryRun }: InitOptions): Promise<number>;
6
+ export declare function initCommand({ dryRun, force, withConfig }: InitOptions): number;
5
7
  export {};
@@ -1,7 +1,7 @@
1
- import { hasCliffToml, hasPackageJson, isGitRepo, notAlreadyInitialized, usesPnpm } from "./checks.js";
1
+ import { hasPackageJson, isGitRepo, usesPnpm } from "./checks.js";
2
2
  import { detectRepoType } from "./detectRepoType.js";
3
- import { confirm, printError, printStep, printSuccess } from "./prompt.js";
4
- import { copyCliffTemplate, scaffoldFiles } from "./scaffold.js";
3
+ import { printError, printStep, printSuccess } from "./prompt.js";
4
+ import { scaffoldFiles } from "./scaffold.js";
5
5
  function runRequiredCheck(label, result) {
6
6
  if (result.ok) {
7
7
  printSuccess(label);
@@ -10,59 +10,47 @@ function runRequiredCheck(label, result) {
10
10
  printError(result.message ?? `${label} failed`);
11
11
  return false;
12
12
  }
13
- async function checkEligibility(dryRun) {
13
+ function checkEligibility() {
14
14
  printStep("Checking eligibility");
15
- if (!runRequiredCheck("Git repository detected", isGitRepo())) return { status: "fail", overwrite: false };
16
- if (!runRequiredCheck("package.json found", hasPackageJson())) return { status: "fail", overwrite: false };
17
- if (!runRequiredCheck("pnpm detected", usesPnpm())) return { status: "fail", overwrite: false };
18
- const cliffCheck = hasCliffToml();
19
- if (cliffCheck.ok) {
20
- printSuccess("cliff.toml found");
21
- } else {
22
- console.info("");
23
- const shouldCreate = await confirm("No cliff.toml found. Create one from the bundled template?");
24
- if (shouldCreate) {
25
- copyCliffTemplate(dryRun);
26
- } else {
27
- printError("cliff.toml is required for changelog generation. Aborting.");
28
- return { status: "fail", overwrite: false };
29
- }
30
- }
31
- const initCheck = notAlreadyInitialized();
32
- if (!initCheck.ok) {
33
- console.info("");
34
- const shouldOverwrite = await confirm("release-kit appears to be already initialized. Overwrite existing files?");
35
- if (!shouldOverwrite) {
36
- console.info("Aborting.");
37
- return { status: "abort", overwrite: false };
38
- }
39
- return { status: "pass", overwrite: true };
40
- }
41
- return { status: "pass", overwrite: false };
15
+ if (!runRequiredCheck("Git repository detected", isGitRepo())) return false;
16
+ if (!runRequiredCheck("package.json found", hasPackageJson())) return false;
17
+ if (!runRequiredCheck("pnpm detected", usesPnpm())) return false;
18
+ return true;
42
19
  }
43
- async function initCommand({ dryRun }) {
20
+ function initCommand({ dryRun, force, withConfig }) {
44
21
  if (dryRun) {
45
22
  console.info("[dry-run mode]");
46
23
  }
47
- let eligibility;
24
+ let eligible;
48
25
  try {
49
- eligibility = await checkEligibility(dryRun);
26
+ eligible = checkEligibility();
50
27
  } catch (error) {
51
28
  printError(`Eligibility check failed: ${error instanceof Error ? error.message : String(error)}`);
52
29
  return 1;
53
30
  }
54
- if (eligibility.status === "fail") return 1;
55
- if (eligibility.status === "abort") return 0;
31
+ if (!eligible) return 1;
56
32
  printStep("Detecting repo type");
57
- const repoType = detectRepoType();
33
+ let repoType;
34
+ try {
35
+ repoType = detectRepoType();
36
+ } catch (error) {
37
+ printError(`Failed to detect repo type: ${error instanceof Error ? error.message : String(error)}`);
38
+ return 1;
39
+ }
58
40
  printSuccess(`Detected: ${repoType}`);
59
41
  printStep("Scaffolding files");
60
- scaffoldFiles({ repoType, dryRun, overwrite: eligibility.overwrite });
42
+ try {
43
+ scaffoldFiles({ repoType, dryRun, overwrite: force, withConfig });
44
+ } catch (error) {
45
+ printError(`Failed to scaffold files: ${error instanceof Error ? error.message : String(error)}`);
46
+ return 1;
47
+ }
61
48
  printStep("Next steps");
49
+ 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.";
62
50
  console.info(`
63
- 1. (Optional) Customize .config/release-kit.config.ts to exclude components, override version patterns, add custom work types, etc.
51
+ ${configHint}
64
52
  2. Test by running: npx @williamthorsen/release-kit prepare --dry-run
65
- 3. Commit the generated workflow file (and config file if created).
53
+ 3. Commit the generated files.
66
54
  `);
67
55
  return 0;
68
56
  }
@@ -1,4 +1,3 @@
1
- export declare function confirm(question: string): Promise<boolean>;
2
1
  export declare function printStep(message: string): void;
3
2
  export declare function printSuccess(message: string): void;
4
3
  export declare function printSkip(message: string): void;
@@ -1,28 +1,17 @@
1
- import { createInterface } from "node:readline/promises";
2
- async function confirm(question) {
3
- const rl = createInterface({ input: process.stdin, output: process.stdout });
4
- try {
5
- const answer = await rl.question(`${question} (y/n) `);
6
- return answer.trim().toLowerCase().startsWith("y");
7
- } finally {
8
- rl.close();
9
- }
10
- }
11
1
  function printStep(message) {
12
2
  console.info(`
13
3
  > ${message}`);
14
4
  }
15
5
  function printSuccess(message) {
16
- console.info(` [ok] ${message}`);
6
+ console.info(` \u2705 ${message}`);
17
7
  }
18
8
  function printSkip(message) {
19
- console.info(` [skip] ${message}`);
9
+ console.info(` \u26A0\uFE0F ${message}`);
20
10
  }
21
11
  function printError(message) {
22
- console.error(` [error] ${message}`);
12
+ console.error(` \u274C ${message}`);
23
13
  }
24
14
  export {
25
- confirm,
26
15
  printError,
27
16
  printSkip,
28
17
  printStep,
@@ -3,7 +3,8 @@ interface ScaffoldOptions {
3
3
  repoType: RepoType;
4
4
  dryRun: boolean;
5
5
  overwrite: boolean;
6
+ withConfig: boolean;
6
7
  }
7
- export declare function copyCliffTemplate(dryRun: boolean): void;
8
- export declare function scaffoldFiles({ repoType, dryRun, overwrite }: ScaffoldOptions): void;
8
+ export declare function copyCliffTemplate(dryRun: boolean, overwrite: boolean): void;
9
+ export declare function scaffoldFiles({ repoType, dryRun, overwrite, withConfig }: ScaffoldOptions): void;
9
10
  export {};
@@ -33,7 +33,7 @@ function writeIfAbsent(filePath, content, dryRun, overwrite) {
33
33
  printSuccess(`Created ${filePath}`);
34
34
  }
35
35
  }
36
- function copyCliffTemplate(dryRun) {
36
+ function copyCliffTemplate(dryRun, overwrite) {
37
37
  const thisDir = dirname(fileURLToPath(import.meta.url));
38
38
  const templatePath = resolve(thisDir, "..", "..", "..", "cliff.toml.template");
39
39
  if (!existsSync(templatePath)) {
@@ -48,15 +48,13 @@ function copyCliffTemplate(dryRun) {
48
48
  printError(`Failed to read cliff.toml.template: ${message}`);
49
49
  return;
50
50
  }
51
- writeIfAbsent("cliff.toml", content, dryRun, false);
51
+ writeIfAbsent(".config/git-cliff.toml", content, dryRun, overwrite);
52
52
  }
53
- function scaffoldFiles({ repoType, dryRun, overwrite }) {
54
- const files = [
55
- { path: ".config/release-kit.config.ts", content: releaseConfigScript(repoType) },
56
- { path: ".github/workflows/release.yaml", content: releaseWorkflow(repoType) }
57
- ];
58
- for (const file of files) {
59
- writeIfAbsent(file.path, file.content, dryRun, overwrite);
53
+ function scaffoldFiles({ repoType, dryRun, overwrite, withConfig }) {
54
+ writeIfAbsent(".github/workflows/release.yaml", releaseWorkflow(repoType), dryRun, overwrite);
55
+ if (withConfig) {
56
+ writeIfAbsent(".config/release-kit.config.ts", releaseConfigScript(repoType), dryRun, overwrite);
57
+ copyCliffTemplate(dryRun, overwrite);
60
58
  }
61
59
  }
62
60
  export {
@@ -24,7 +24,7 @@ async function prepareCommand(argv) {
24
24
  if (errors.length > 0) {
25
25
  console.error("Invalid config:");
26
26
  for (const err of errors) {
27
- console.error(` - ${err}`);
27
+ console.error(` \u274C ${err}`);
28
28
  }
29
29
  process.exit(1);
30
30
  }
@@ -0,0 +1 @@
1
+ export declare function resolveCliffConfigPath(cliffConfigPath: string | undefined, metaUrl: string): string;
@@ -0,0 +1,25 @@
1
+ import { existsSync } from "node:fs";
2
+ import { dirname, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ function resolveCliffConfigPath(cliffConfigPath, metaUrl) {
5
+ if (cliffConfigPath !== void 0) {
6
+ return cliffConfigPath;
7
+ }
8
+ const candidates = [".config/git-cliff.toml", "cliff.toml"];
9
+ for (const candidate of candidates) {
10
+ if (existsSync(candidate)) {
11
+ return candidate;
12
+ }
13
+ }
14
+ const thisDir = dirname(fileURLToPath(metaUrl));
15
+ const bundledPath = resolve(thisDir, "..", "..", "cliff.toml.template");
16
+ if (existsSync(bundledPath)) {
17
+ return bundledPath;
18
+ }
19
+ throw new Error(
20
+ `Could not resolve a git-cliff config file. Searched: ${candidates.join(", ")}, and bundled template at ${bundledPath}`
21
+ );
22
+ }
23
+ export {
24
+ resolveCliffConfigPath
25
+ };
@@ -1,5 +1,5 @@
1
1
  import type { MonorepoReleaseConfig, ReleaseConfig, ReleaseType } from './types.ts';
2
- export declare const RELEASE_TAGS_FILE = "/tmp/release-kit/.release-tags";
2
+ export declare const RELEASE_TAGS_FILE = "tmp/.release-tags";
3
3
  export declare function parseArgs(argv: string[]): {
4
4
  dryRun: boolean;
5
5
  force: boolean;
@@ -2,7 +2,7 @@ import { mkdirSync, writeFileSync } from "node:fs";
2
2
  import { dirname } from "node:path";
3
3
  import { releasePrepare } from "./releasePrepare.js";
4
4
  import { releasePrepareMono } from "./releasePrepareMono.js";
5
- const RELEASE_TAGS_FILE = "/tmp/release-kit/.release-tags";
5
+ const RELEASE_TAGS_FILE = "tmp/.release-tags";
6
6
  const VALID_BUMP_TYPES = ["major", "minor", "patch"];
7
7
  function isReleaseType(value) {
8
8
  return VALID_BUMP_TYPES.includes(value);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@williamthorsen/release-kit",
3
- "version": "2.0.0",
3
+ "version": "2.1.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",