@williamthorsen/release-kit 2.0.0 → 2.3.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 (75) hide show
  1. package/CHANGELOG.md +77 -58
  2. package/README.md +33 -42
  3. package/bin/release-kit.js +2 -0
  4. package/dist/esm/.cache +1 -1
  5. package/dist/esm/bin/release-kit.js +158 -5
  6. package/dist/esm/bumpAllVersions.d.ts +2 -2
  7. package/dist/esm/bumpAllVersions.js +1 -4
  8. package/dist/esm/component.d.ts +1 -1
  9. package/dist/esm/component.js +2 -3
  10. package/dist/esm/createTags.d.ts +5 -0
  11. package/dist/esm/createTags.js +73 -0
  12. package/dist/esm/detectPackageManager.d.ts +2 -0
  13. package/dist/esm/detectPackageManager.js +44 -0
  14. package/dist/esm/findPackageRoot.d.ts +1 -0
  15. package/dist/esm/findPackageRoot.js +17 -0
  16. package/dist/esm/format.d.ts +3 -0
  17. package/dist/esm/format.js +14 -0
  18. package/dist/esm/generateChangelogs.d.ts +2 -2
  19. package/dist/esm/generateChangelogs.js +14 -13
  20. package/dist/esm/hasPrettierConfig.d.ts +1 -0
  21. package/dist/esm/hasPrettierConfig.js +42 -0
  22. package/dist/esm/index.d.ts +12 -2
  23. package/dist/esm/index.js +11 -2
  24. package/dist/esm/init/checks.d.ts +0 -2
  25. package/dist/esm/init/checks.js +0 -17
  26. package/dist/esm/init/initCommand.d.ts +3 -1
  27. package/dist/esm/init/initCommand.js +35 -40
  28. package/dist/esm/init/prompt.d.ts +0 -1
  29. package/dist/esm/init/prompt.js +3 -14
  30. package/dist/esm/init/scaffold.d.ts +4 -2
  31. package/dist/esm/init/scaffold.js +20 -48
  32. package/dist/esm/init/templates.js +14 -4
  33. package/dist/esm/loadConfig.js +0 -6
  34. package/dist/esm/prepareCommand.d.ts +10 -0
  35. package/dist/esm/prepareCommand.js +97 -21
  36. package/dist/esm/publish.d.ts +7 -0
  37. package/dist/esm/publish.js +49 -0
  38. package/dist/esm/publishCommand.d.ts +1 -0
  39. package/dist/esm/publishCommand.js +54 -0
  40. package/dist/esm/releasePrepare.d.ts +2 -2
  41. package/dist/esm/releasePrepare.js +46 -17
  42. package/dist/esm/releasePrepareMono.d.ts +2 -2
  43. package/dist/esm/releasePrepareMono.js +54 -24
  44. package/dist/esm/reportPrepare.d.ts +2 -0
  45. package/dist/esm/reportPrepare.js +120 -0
  46. package/dist/esm/resolveCliffConfigPath.d.ts +1 -0
  47. package/dist/esm/resolveCliffConfigPath.js +25 -0
  48. package/dist/esm/resolveReleaseTags.d.ts +6 -0
  49. package/dist/esm/resolveReleaseTags.js +42 -0
  50. package/dist/esm/runReleasePrepare.d.ts +1 -1
  51. package/dist/esm/runReleasePrepare.js +4 -3
  52. package/dist/esm/sync-labels/generateCommand.d.ts +4 -0
  53. package/dist/esm/sync-labels/generateCommand.js +51 -0
  54. package/dist/esm/sync-labels/initCommand.d.ts +6 -0
  55. package/dist/esm/sync-labels/initCommand.js +62 -0
  56. package/dist/esm/sync-labels/loadSyncLabelsConfig.d.ts +3 -0
  57. package/dist/esm/sync-labels/loadSyncLabelsConfig.js +59 -0
  58. package/dist/esm/sync-labels/presets.d.ts +2 -0
  59. package/dist/esm/sync-labels/presets.js +40 -0
  60. package/dist/esm/sync-labels/resolveLabels.d.ts +2 -0
  61. package/dist/esm/sync-labels/resolveLabels.js +43 -0
  62. package/dist/esm/sync-labels/scaffold.d.ts +6 -0
  63. package/dist/esm/sync-labels/scaffold.js +25 -0
  64. package/dist/esm/sync-labels/syncCommand.d.ts +1 -0
  65. package/dist/esm/sync-labels/syncCommand.js +33 -0
  66. package/dist/esm/sync-labels/templates.d.ts +4 -0
  67. package/dist/esm/sync-labels/templates.js +50 -0
  68. package/dist/esm/sync-labels/types.d.ts +9 -0
  69. package/dist/esm/sync-labels/types.js +0 -0
  70. package/dist/esm/tagCommand.d.ts +1 -0
  71. package/dist/esm/tagCommand.js +20 -0
  72. package/dist/esm/types.d.ts +29 -1
  73. package/dist/esm/validateConfig.js +11 -6
  74. package/package.json +12 -5
  75. package/presets/labels/common.yaml +52 -0
@@ -1,63 +1,35 @@
1
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
- import { dirname, resolve } from "node:path";
3
- import { fileURLToPath } from "node:url";
4
- import { printError, printSkip, printSuccess } from "./prompt.js";
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { writeFileWithCheck } from "@williamthorsen/node-monorepo-core";
4
+ import { findPackageRoot } from "../findPackageRoot.js";
5
5
  import { releaseConfigScript, releaseWorkflow } from "./templates.js";
6
- function tryWriteFile(filePath, content) {
7
- try {
8
- writeFileSync(filePath, content, "utf8");
9
- return true;
10
- } catch (error) {
11
- const message = error instanceof Error ? error.message : String(error);
12
- printError(`Failed to write ${filePath}: ${message}`);
13
- return false;
14
- }
15
- }
16
- function writeIfAbsent(filePath, content, dryRun, overwrite) {
17
- if (existsSync(filePath) && !overwrite) {
18
- printSkip(`${filePath} (already exists)`);
19
- return;
20
- }
21
- if (dryRun) {
22
- printSuccess(`[dry-run] Would create ${filePath}`);
23
- return;
24
- }
25
- try {
26
- mkdirSync(dirname(filePath), { recursive: true });
27
- } catch (error) {
28
- const message = error instanceof Error ? error.message : String(error);
29
- printError(`Failed to create directory for ${filePath}: ${message}`);
30
- return;
31
- }
32
- if (tryWriteFile(filePath, content)) {
33
- printSuccess(`Created ${filePath}`);
34
- }
35
- }
36
- function copyCliffTemplate(dryRun) {
37
- const thisDir = dirname(fileURLToPath(import.meta.url));
38
- const templatePath = resolve(thisDir, "..", "..", "..", "cliff.toml.template");
6
+ function copyCliffTemplate(dryRun, overwrite) {
7
+ const destPath = ".config/git-cliff.toml";
8
+ const root = findPackageRoot(import.meta.url);
9
+ const templatePath = resolve(root, "cliff.toml.template");
39
10
  if (!existsSync(templatePath)) {
40
- printError(`Could not find cliff.toml.template at ${templatePath}`);
41
- return;
11
+ return { filePath: destPath, outcome: "failed", error: `Could not find bundled template at ${templatePath}` };
42
12
  }
43
13
  let content;
44
14
  try {
45
15
  content = readFileSync(templatePath, "utf8");
46
16
  } catch (error) {
47
17
  const message = error instanceof Error ? error.message : String(error);
48
- printError(`Failed to read cliff.toml.template: ${message}`);
49
- return;
18
+ return { filePath: destPath, outcome: "failed", error: `Failed to read template ${templatePath}: ${message}` };
50
19
  }
51
- writeIfAbsent("cliff.toml", content, dryRun, false);
20
+ return writeFileWithCheck(destPath, content, { dryRun, overwrite });
52
21
  }
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) }
22
+ function scaffoldFiles({ repoType, dryRun, overwrite, withConfig }) {
23
+ const results = [
24
+ writeFileWithCheck(".github/workflows/release.yaml", releaseWorkflow(repoType), { dryRun, overwrite })
57
25
  ];
58
- for (const file of files) {
59
- writeIfAbsent(file.path, file.content, dryRun, overwrite);
26
+ if (withConfig) {
27
+ results.push(
28
+ writeFileWithCheck(".config/release-kit.config.ts", releaseConfigScript(repoType), { dryRun, overwrite }),
29
+ copyCliffTemplate(dryRun, overwrite)
30
+ );
60
31
  }
32
+ return results;
61
33
  }
62
34
  export {
63
35
  copyCliffTemplate,
@@ -8,8 +8,7 @@ const config: ReleaseKitConfig = {
8
8
  // { dir: 'my-package', shouldExclude: true },
9
9
  // ],
10
10
 
11
- // TODO: Uncomment and adjust if you have a format command
12
- // formatCommand: 'npx prettier --write',
11
+ // Formatting: prettier is auto-detected. Set formatCommand to override.
13
12
 
14
13
  // Uncomment to override the default version patterns:
15
14
  // versionPatterns: { major: ['!'], minor: ['feat', 'feature'] },
@@ -24,8 +23,7 @@ export default config;
24
23
  return `import type { ReleaseKitConfig } from '@williamthorsen/release-kit';
25
24
 
26
25
  const config: ReleaseKitConfig = {
27
- // TODO: Uncomment and adjust if you have a format command
28
- // formatCommand: 'npx prettier --write',
26
+ // Formatting: prettier is auto-detected. Set formatCommand to override.
29
27
 
30
28
  // Uncomment to override the default version patterns:
31
29
  // versionPatterns: { major: ['!'], minor: ['feat', 'feature'] },
@@ -58,6 +56,11 @@ on:
58
56
  - patch
59
57
  - minor
60
58
  - major
59
+ force:
60
+ description: 'Force a version bump even when there are no release-worthy changes'
61
+ required: false
62
+ type: boolean
63
+ default: false
61
64
 
62
65
  permissions:
63
66
  contents: write
@@ -69,6 +72,7 @@ jobs:
69
72
  with:
70
73
  only: \${{ inputs.only }}
71
74
  bump: \${{ inputs.bump }}
75
+ force: \${{ inputs.force }}
72
76
  `;
73
77
  }
74
78
  return `# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
@@ -86,6 +90,11 @@ on:
86
90
  - patch
87
91
  - minor
88
92
  - major
93
+ force:
94
+ description: 'Force a version bump even when there are no release-worthy changes'
95
+ required: false
96
+ type: boolean
97
+ default: false
89
98
 
90
99
  permissions:
91
100
  contents: write
@@ -96,6 +105,7 @@ jobs:
96
105
  uses: williamthorsen/node-monorepo-tools/.github/workflows/release-workflow.yaml@release-workflow-v1
97
106
  with:
98
107
  bump: \${{ inputs.bump }}
108
+ force: \${{ inputs.force }}
99
109
  `;
100
110
  }
101
111
  export {
@@ -30,12 +30,6 @@ function mergeMonorepoConfig(discoveredPaths, userConfig) {
30
30
  components = components.filter((c) => {
31
31
  const override = overrides.get(c.dir);
32
32
  return override?.shouldExclude !== true;
33
- }).map((c) => {
34
- const override = overrides.get(c.dir);
35
- if (override?.tagPrefix !== void 0) {
36
- return { ...c, tagPrefix: override.tagPrefix };
37
- }
38
- return c;
39
33
  });
40
34
  }
41
35
  const workTypes = userConfig?.workTypes === void 0 ? { ...DEFAULT_WORK_TYPES } : { ...DEFAULT_WORK_TYPES, ...userConfig.workTypes };
@@ -1 +1,11 @@
1
+ import type { WriteResult } from '@williamthorsen/node-monorepo-core';
2
+ import type { ReleaseType } from './types.ts';
3
+ export declare const RELEASE_TAGS_FILE = "tmp/.release-tags";
4
+ export declare function parseArgs(argv: string[]): {
5
+ dryRun: boolean;
6
+ force: boolean;
7
+ bumpOverride: ReleaseType | undefined;
8
+ only: string[] | undefined;
9
+ };
10
+ export declare function writeReleaseTags(tags: string[], dryRun: boolean): WriteResult | undefined;
1
11
  export declare function prepareCommand(argv: string[]): Promise<void>;
@@ -1,9 +1,72 @@
1
+ import { writeFileWithCheck } from "@williamthorsen/node-monorepo-core";
1
2
  import { discoverWorkspaces } from "./discoverWorkspaces.js";
3
+ import { dim } from "./format.js";
2
4
  import { loadConfig, mergeMonorepoConfig, mergeSinglePackageConfig } from "./loadConfig.js";
3
5
  import { releasePrepare } from "./releasePrepare.js";
4
6
  import { releasePrepareMono } from "./releasePrepareMono.js";
5
- import { parseArgs, RELEASE_TAGS_FILE, writeReleaseTags } from "./runReleasePrepare.js";
7
+ import { reportPrepare } from "./reportPrepare.js";
6
8
  import { validateConfig } from "./validateConfig.js";
9
+ const RELEASE_TAGS_FILE = "tmp/.release-tags";
10
+ const VALID_BUMP_TYPES = ["major", "minor", "patch"];
11
+ function isReleaseType(value) {
12
+ return VALID_BUMP_TYPES.includes(value);
13
+ }
14
+ function showHelp() {
15
+ console.info(`
16
+ Usage: npx @williamthorsen/release-kit prepare [options]
17
+
18
+ Options:
19
+ --dry-run Run without modifying any files
20
+ --bump=major|minor|patch Override the bump type for all components
21
+ --force Bypass the "no commits since last tag" check (monorepo only, requires --bump)
22
+ --only=name1,name2 Only process the named components (comma-separated, monorepo only)
23
+ --help Show this help message
24
+ `);
25
+ }
26
+ function parseArgs(argv) {
27
+ let dryRun = false;
28
+ let force = false;
29
+ let bumpOverride;
30
+ let only;
31
+ for (const arg of argv) {
32
+ if (arg === "--dry-run") {
33
+ dryRun = true;
34
+ } else if (arg === "--force") {
35
+ force = true;
36
+ } else if (arg.startsWith("--bump=")) {
37
+ const value = arg.slice("--bump=".length);
38
+ if (!isReleaseType(value)) {
39
+ console.error(`Error: Invalid bump type "${value}". Must be one of: ${VALID_BUMP_TYPES.join(", ")}`);
40
+ process.exit(1);
41
+ }
42
+ bumpOverride = value;
43
+ } else if (arg.startsWith("--only=")) {
44
+ const value = arg.slice("--only=".length);
45
+ if (!value) {
46
+ console.error("Error: --only requires a comma-separated list of component names");
47
+ process.exit(1);
48
+ }
49
+ only = value.split(",");
50
+ } else if (arg === "--help" || arg === "-h") {
51
+ showHelp();
52
+ process.exit(0);
53
+ } else {
54
+ console.error(`Error: Unknown argument: ${arg}`);
55
+ process.exit(1);
56
+ }
57
+ }
58
+ if (force && bumpOverride === void 0) {
59
+ console.error("Error: --force requires --bump to specify the version bump type");
60
+ process.exit(1);
61
+ }
62
+ return { dryRun, force, bumpOverride, only };
63
+ }
64
+ function writeReleaseTags(tags, dryRun) {
65
+ if (tags.length === 0) {
66
+ return void 0;
67
+ }
68
+ return writeFileWithCheck(RELEASE_TAGS_FILE, tags.join("\n"), { dryRun, overwrite: true });
69
+ }
7
70
  async function prepareCommand(argv) {
8
71
  const { dryRun, force, bumpOverride, only } = parseArgs(argv);
9
72
  const options = {
@@ -11,6 +74,9 @@ async function prepareCommand(argv) {
11
74
  force,
12
75
  ...bumpOverride === void 0 ? {} : { bumpOverride }
13
76
  };
77
+ if (dryRun) {
78
+ console.info("\n\u{1F50D} DRY RUN \u2014 no files will be modified\n");
79
+ }
14
80
  let rawConfig;
15
81
  try {
16
82
  rawConfig = await loadConfig();
@@ -24,7 +90,7 @@ async function prepareCommand(argv) {
24
90
  if (errors.length > 0) {
25
91
  console.error("Invalid config:");
26
92
  for (const err of errors) {
27
- console.error(` - ${err}`);
93
+ console.error(` \u274C ${err}`);
28
94
  }
29
95
  process.exit(1);
30
96
  }
@@ -37,20 +103,13 @@ async function prepareCommand(argv) {
37
103
  console.error(`Error discovering workspaces: ${error instanceof Error ? error.message : String(error)}`);
38
104
  process.exit(1);
39
105
  }
40
- let tags = [];
41
106
  if (discoveredPaths === void 0) {
42
107
  if (only !== void 0) {
43
108
  console.error("Error: --only is only supported for monorepo configurations");
44
109
  process.exit(1);
45
110
  }
46
111
  const config = mergeSinglePackageConfig(userConfig);
47
- try {
48
- tags = releasePrepare(config, options);
49
- writeReleaseTags(tags, dryRun);
50
- } catch (error) {
51
- console.error("Error preparing release:", error instanceof Error ? error.message : String(error));
52
- process.exit(1);
53
- }
112
+ runAndReport(() => releasePrepare(config, options), dryRun);
54
113
  } else {
55
114
  const config = mergeMonorepoConfig(discoveredPaths, userConfig);
56
115
  if (only !== void 0) {
@@ -63,19 +122,36 @@ async function prepareCommand(argv) {
63
122
  }
64
123
  config.components = config.components.filter((c) => only.includes(c.dir));
65
124
  }
66
- try {
67
- tags = releasePrepareMono(config, options);
68
- writeReleaseTags(tags, dryRun);
69
- } catch (error) {
70
- console.error("Error preparing release:", error instanceof Error ? error.message : String(error));
71
- process.exit(1);
72
- }
125
+ runAndReport(() => releasePrepareMono(config, options), dryRun);
126
+ }
127
+ }
128
+ function runAndReport(execute, dryRun) {
129
+ let result;
130
+ try {
131
+ result = execute();
132
+ } catch (error) {
133
+ console.error("Error preparing release:", error instanceof Error ? error.message : String(error));
134
+ process.exit(1);
73
135
  }
74
- if (tags.length > 0) {
75
- console.info(`
76
- Release tags file: ${RELEASE_TAGS_FILE}`);
136
+ process.stdout.write(reportPrepare(result) + "\n");
137
+ const writeResult = writeReleaseTags(result.tags, dryRun);
138
+ if (writeResult?.outcome === "failed") {
139
+ console.error(`Error writing release tags: ${writeResult.error ?? "unknown error"}`);
140
+ process.exit(1);
141
+ }
142
+ if (writeResult) {
143
+ if (dryRun) {
144
+ console.info(dim(` [dry-run] Would write ${RELEASE_TAGS_FILE}: ${result.tags.join(" ")}`));
145
+ } else {
146
+ console.info(dim(` Wrote ${RELEASE_TAGS_FILE}: ${result.tags.join(" ")}`));
147
+ console.info(dim(`
148
+ Release tags file: ${RELEASE_TAGS_FILE}`));
149
+ }
77
150
  }
78
151
  }
79
152
  export {
80
- prepareCommand
153
+ RELEASE_TAGS_FILE,
154
+ parseArgs,
155
+ prepareCommand,
156
+ writeReleaseTags
81
157
  };
@@ -0,0 +1,7 @@
1
+ import type { PackageManager } from './detectPackageManager.ts';
2
+ import type { ResolvedTag } from './resolveReleaseTags.ts';
3
+ export interface PublishOptions {
4
+ dryRun: boolean;
5
+ noGitChecks: boolean;
6
+ }
7
+ export declare function publish(resolvedTags: ResolvedTag[], packageManager: PackageManager, options: PublishOptions): void;
@@ -0,0 +1,49 @@
1
+ import { execFileSync } from "node:child_process";
2
+ function publish(resolvedTags, packageManager, options) {
3
+ const { dryRun, noGitChecks } = options;
4
+ if (resolvedTags.length === 0) {
5
+ return;
6
+ }
7
+ console.info(dryRun ? "[dry-run] Would publish:" : "Publishing:");
8
+ for (const { tag, workspacePath } of resolvedTags) {
9
+ console.info(` ${tag} (${workspacePath})`);
10
+ }
11
+ const published = [];
12
+ for (const { tag, workspacePath } of resolvedTags) {
13
+ const executable = resolveExecutable(packageManager);
14
+ const args = buildPublishArgs(packageManager, { dryRun, noGitChecks });
15
+ try {
16
+ console.info(`
17
+ ${dryRun ? "[dry-run] " : ""}Running: ${executable} ${args.join(" ")} (cwd: ${workspacePath})`);
18
+ execFileSync(executable, args, { cwd: workspacePath, stdio: "inherit" });
19
+ published.push(tag);
20
+ } catch (error) {
21
+ if (published.length > 0) {
22
+ console.warn("Packages published before failure:");
23
+ for (const t of published) {
24
+ console.warn(` ${t}`);
25
+ }
26
+ }
27
+ throw error;
28
+ }
29
+ }
30
+ }
31
+ function resolveExecutable(packageManager) {
32
+ if (packageManager === "yarn-berry") {
33
+ return "yarn";
34
+ }
35
+ return packageManager;
36
+ }
37
+ function buildPublishArgs(packageManager, options) {
38
+ const args = packageManager === "yarn-berry" ? ["npm", "publish"] : ["publish"];
39
+ if (options.dryRun) {
40
+ args.push("--dry-run");
41
+ }
42
+ if (options.noGitChecks && packageManager === "pnpm") {
43
+ args.push("--no-git-checks");
44
+ }
45
+ return args;
46
+ }
47
+ export {
48
+ publish
49
+ };
@@ -0,0 +1 @@
1
+ export declare function publishCommand(argv: string[]): Promise<void>;
@@ -0,0 +1,54 @@
1
+ import { basename } from "node:path";
2
+ import { detectPackageManager } from "./detectPackageManager.js";
3
+ import { discoverWorkspaces } from "./discoverWorkspaces.js";
4
+ import { publish } from "./publish.js";
5
+ import { resolveReleaseTags } from "./resolveReleaseTags.js";
6
+ async function publishCommand(argv) {
7
+ const knownFlags = /* @__PURE__ */ new Set(["--dry-run", "--no-git-checks"]);
8
+ const unknownFlags = argv.filter((f) => !f.startsWith("--only=") && !knownFlags.has(f));
9
+ if (unknownFlags.length > 0) {
10
+ console.error(`Error: Unknown option: ${unknownFlags[0]}`);
11
+ process.exit(1);
12
+ }
13
+ const dryRun = argv.includes("--dry-run");
14
+ const noGitChecks = argv.includes("--no-git-checks");
15
+ const onlyArg = argv.find((f) => f.startsWith("--only="));
16
+ const only = onlyArg?.slice("--only=".length).split(",");
17
+ let discoveredPaths;
18
+ try {
19
+ discoveredPaths = await discoverWorkspaces();
20
+ } catch (error) {
21
+ console.error(`Error discovering workspaces: ${error instanceof Error ? error.message : String(error)}`);
22
+ process.exit(1);
23
+ }
24
+ if (only !== void 0 && discoveredPaths === void 0) {
25
+ console.error("Error: --only is only supported for monorepo configurations");
26
+ process.exit(1);
27
+ }
28
+ const workspaceMap = discoveredPaths === void 0 ? void 0 : new Map(discoveredPaths.map((p) => [basename(p), p]));
29
+ let resolvedTags = resolveReleaseTags(workspaceMap);
30
+ if (resolvedTags.length === 0) {
31
+ console.error("Error: No release tags found on HEAD. Create tags with `release-kit tag` first.");
32
+ process.exit(1);
33
+ }
34
+ if (only !== void 0) {
35
+ const availableNames = resolvedTags.map((t) => t.dir);
36
+ for (const name of only) {
37
+ if (!availableNames.includes(name)) {
38
+ console.error(`Error: Unknown package "${name}" in --only. Available: ${availableNames.join(", ")}`);
39
+ process.exit(1);
40
+ }
41
+ }
42
+ resolvedTags = resolvedTags.filter((t) => only.includes(t.dir));
43
+ }
44
+ const packageManager = detectPackageManager();
45
+ try {
46
+ publish(resolvedTags, packageManager, { dryRun, noGitChecks });
47
+ } catch (error) {
48
+ console.error(error instanceof Error ? error.message : String(error));
49
+ process.exit(1);
50
+ }
51
+ }
52
+ export {
53
+ publishCommand
54
+ };
@@ -1,7 +1,7 @@
1
- import type { ReleaseConfig, ReleaseType } from './types.ts';
1
+ import type { PrepareResult, ReleaseConfig, ReleaseType } from './types.ts';
2
2
  export interface ReleasePrepareOptions {
3
3
  dryRun: boolean;
4
4
  force?: boolean;
5
5
  bumpOverride?: ReleaseType;
6
6
  }
7
- export declare function releasePrepare(config: ReleaseConfig, options: ReleasePrepareOptions): string[];
7
+ export declare function releasePrepare(config: ReleaseConfig, options: ReleasePrepareOptions): PrepareResult;
@@ -4,39 +4,50 @@ import { DEFAULT_VERSION_PATTERNS, DEFAULT_WORK_TYPES } from "./defaults.js";
4
4
  import { determineBumpType } from "./determineBumpType.js";
5
5
  import { generateChangelogs } from "./generateChangelogs.js";
6
6
  import { getCommitsSinceTarget } from "./getCommitsSinceTarget.js";
7
+ import { hasPrettierConfig } from "./hasPrettierConfig.js";
7
8
  import { parseCommitMessage } from "./parseCommitMessage.js";
8
9
  function releasePrepare(config, options) {
9
10
  const { dryRun, bumpOverride } = options;
10
11
  const workTypes = config.workTypes ?? { ...DEFAULT_WORK_TYPES };
11
12
  const versionPatterns = config.versionPatterns ?? { ...DEFAULT_VERSION_PATTERNS };
12
- console.info("Finding commits since last release...");
13
13
  const { tag, commits } = getCommitsSinceTarget(config.tagPrefix);
14
- console.info(` Found ${commits.length} commits since ${tag ?? "the beginning"}`);
15
14
  let releaseType;
15
+ let parsedCommitCount;
16
16
  if (bumpOverride === void 0) {
17
17
  const parsedCommits = commits.map((c) => parseCommitMessage(c.message, c.hash, workTypes, config.workspaceAliases)).filter((c) => c !== void 0);
18
- console.info(` Parsed ${parsedCommits.length} typed commits`);
18
+ parsedCommitCount = parsedCommits.length;
19
19
  releaseType = determineBumpType(parsedCommits, workTypes, versionPatterns);
20
20
  } else {
21
21
  releaseType = bumpOverride;
22
- console.info(` Using bump override: ${releaseType}`);
23
22
  }
24
23
  if (releaseType === void 0) {
25
- console.info("No release-worthy changes found. Skipping.");
26
- return [];
24
+ return {
25
+ components: [
26
+ {
27
+ status: "skipped",
28
+ previousTag: tag,
29
+ commitCount: commits.length,
30
+ parsedCommitCount,
31
+ bumpedFiles: [],
32
+ changelogFiles: [],
33
+ skipReason: "No release-worthy changes found. Skipping."
34
+ }
35
+ ],
36
+ tags: [],
37
+ dryRun
38
+ };
27
39
  }
28
- console.info(`Bumping versions (${releaseType})...`);
29
- const newVersion = bumpAllVersions(config.packageFiles, releaseType, dryRun);
30
- const newTag = `${config.tagPrefix}${newVersion}`;
31
- console.info("Generating changelogs...");
32
- generateChangelogs(config, newTag, dryRun);
33
- if (config.formatCommand !== void 0) {
40
+ const bump = bumpAllVersions(config.packageFiles, releaseType, dryRun);
41
+ const newTag = `${config.tagPrefix}${bump.newVersion}`;
42
+ const changelogFiles = generateChangelogs(config, newTag, dryRun);
43
+ const formatCommandStr = config.formatCommand ?? (hasPrettierConfig() ? "npx prettier --write" : void 0);
44
+ let formatCommand;
45
+ if (formatCommandStr !== void 0) {
34
46
  const modifiedFiles = [...config.packageFiles, ...config.changelogPaths.map((p) => `${p}/CHANGELOG.md`)];
35
- const fullCommand = `${config.formatCommand} ${modifiedFiles.join(" ")}`;
47
+ const fullCommand = `${formatCommandStr} ${modifiedFiles.join(" ")}`;
36
48
  if (dryRun) {
37
- console.info(` [dry-run] Would run format command: ${fullCommand}`);
49
+ formatCommand = { command: fullCommand, executed: false, files: modifiedFiles };
38
50
  } else {
39
- console.info(` Running format command: ${fullCommand}`);
40
51
  try {
41
52
  execSync(fullCommand, { stdio: "inherit" });
42
53
  } catch (error) {
@@ -44,10 +55,28 @@ function releasePrepare(config, options) {
44
55
  `Format command failed ('${fullCommand}'): ${error instanceof Error ? error.message : String(error)}`
45
56
  );
46
57
  }
58
+ formatCommand = { command: fullCommand, executed: true, files: modifiedFiles };
47
59
  }
48
60
  }
49
- console.info(`Release preparation complete: ${newTag}`);
50
- return [newTag];
61
+ return {
62
+ components: [
63
+ {
64
+ status: "released",
65
+ previousTag: tag,
66
+ commitCount: commits.length,
67
+ parsedCommitCount,
68
+ releaseType,
69
+ currentVersion: bump.currentVersion,
70
+ newVersion: bump.newVersion,
71
+ tag: newTag,
72
+ bumpedFiles: bump.files,
73
+ changelogFiles
74
+ }
75
+ ],
76
+ tags: [newTag],
77
+ formatCommand,
78
+ dryRun
79
+ };
51
80
  }
52
81
  export {
53
82
  releasePrepare
@@ -1,3 +1,3 @@
1
1
  import type { ReleasePrepareOptions } from './releasePrepare.ts';
2
- import type { MonorepoReleaseConfig } from './types.ts';
3
- export declare function releasePrepareMono(config: MonorepoReleaseConfig, options: ReleasePrepareOptions): string[];
2
+ import type { MonorepoReleaseConfig, PrepareResult } from './types.ts';
3
+ export declare function releasePrepareMono(config: MonorepoReleaseConfig, options: ReleasePrepareOptions): PrepareResult;