@varlock/bumpy 1.2.2 → 1.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.
package/README.md CHANGED
@@ -28,7 +28,7 @@ Bumpy uses **bump files** (you may know them as "changesets" if coming from [tha
28
28
  - Shows what packages will be released and their changelogs
29
29
  - Including packages bumped automatically due to dependency relationships
30
30
  - When release PR is merged, publishing is triggered
31
- - Oending bump files are deleted and packages are published with updated versions and changelogs
31
+ - Pending bump files are deleted and packages are published with updated versions and changelogs
32
32
 
33
33
  All of this is automated via two simple GitHub Actions workflows (see [CI setup](#ci--github-actions) below). You can also run everything locally with `bumpy status`, `bumpy version`, and `bumpy publish`.
34
34
 
@@ -104,7 +104,6 @@ jobs:
104
104
  - run: bunx @varlock/bumpy ci check
105
105
  env:
106
106
  GH_TOKEN: ${{ github.token }}
107
- BUMPY_GH_TOKEN: ${{ secrets.BUMPY_GH_TOKEN }} # additional PAT (optional)
108
107
  ```
109
108
 
110
109
  ### Release workflow
@@ -135,7 +134,7 @@ jobs:
135
134
  - run: bunx @varlock/bumpy ci release
136
135
  env:
137
136
  GH_TOKEN: ${{ github.token }}
138
- BUMPY_GH_TOKEN: ${{ secrets.BUMPY_GH_TOKEN }} # additonal PAT, needed to trigger CI checks on release PR
137
+ BUMPY_GH_TOKEN: ${{ secrets.BUMPY_GH_TOKEN }} # PAT so that version PR triggers CI
139
138
  ```
140
139
 
141
140
  > **Trusted publishing setup:** Configure each package on [npmjs.com](https://docs.npmjs.com/trusted-publishers/) → Package Settings → Trusted Publishers → GitHub Actions. Specify your org/user, repo, and the workflow filename (`bumpy-release.yml`). No `NPM_TOKEN` secret needed. Requires npm >= 11.5.1 - bumpy will warn if your version is too old.
@@ -240,7 +239,7 @@ Bumpy is built as a successor to [@changesets/changesets](https://github.com/cha
240
239
 
241
240
  ```bash
242
241
  bun install # install deps
243
- bun test # run tests
242
+ bun run test # run tests
244
243
  bun run build # build CLI
245
244
  bunx bumpy --help # invoke built cli
246
245
  ```
@@ -1,9 +1,9 @@
1
1
  import { n as log, o as __toESM, r as require_picocolors } from "./logger-C2dEe5Su.mjs";
2
2
  import { n as exists, t as ensureDir } from "./fs-DnDogVn-.mjs";
3
- import { a as loadConfig, o as loadPackageConfig, r as getBumpyDir, s as matchGlob } from "./config-CJIj8xG3.mjs";
4
- import { t as discoverPackages } from "./workspace-c9-TqXed.mjs";
3
+ import { a as loadConfig, o as loadPackageConfig, r as getBumpyDir, s as matchGlob } from "./config-D13G4-R8.mjs";
4
+ import { t as discoverPackages } from "./workspace-BKOAMeki.mjs";
5
5
  import { t as DependencyGraph } from "./dep-graph-E-9-eQ2J.mjs";
6
- import { i as writeBumpFile } from "./bump-file-BTsntOO-.mjs";
6
+ import { i as writeBumpFile } from "./bump-file-CoaSxqne.mjs";
7
7
  import { r as getChangedFiles } from "./git-D0__HP86.mjs";
8
8
  import { c as ot, d as yt, i as _t, l as pt, o as gt, r as Ot, s as mt, t as unwrap, u as wt } from "./clack-CJT1JFFa.mjs";
9
9
  import { n as slugify, t as randomName } from "./names-CBy7d8K_.mjs";
@@ -1,6 +1,6 @@
1
1
  import { a as readJson, c as removeFile, f as writeText, i as listFiles, l as updateJsonFields, n as exists, s as readText, u as updateJsonNestedField } from "./fs-DnDogVn-.mjs";
2
- import { r as getBumpyDir } from "./config-CJIj8xG3.mjs";
3
- import { a as prependToChangelog, i as loadFormatter, n as generateChangelogEntry } from "./changelog-xKuL0IKx.mjs";
2
+ import { r as getBumpyDir } from "./config-D13G4-R8.mjs";
3
+ import { a as prependToChangelog, i as loadFormatter, n as generateChangelogEntry } from "./changelog-BIkNBJ15.mjs";
4
4
  import { resolve } from "node:path";
5
5
  //#region src/core/apply-release-plan.ts
6
6
  /** Apply the release plan: bump versions, update changelogs, delete bump files */
@@ -1,5 +1,5 @@
1
1
  import { f as writeText, i as listFiles, s as readText } from "./fs-DnDogVn-.mjs";
2
- import { r as getBumpyDir } from "./config-CJIj8xG3.mjs";
2
+ import { r as getBumpyDir } from "./config-D13G4-R8.mjs";
3
3
  import { i as jsYaml } from "./package-manager-CClZtIHP.mjs";
4
4
  import { s as tryRunArgs } from "./shell-u3bYGxNy.mjs";
5
5
  import { resolve } from "node:path";
@@ -1,5 +1,5 @@
1
1
  import { n as log } from "./logger-C2dEe5Su.mjs";
2
- import { t as BUMP_LEVELS } from "./types-CSM0c2-m.mjs";
2
+ import { t as BUMP_LEVELS } from "./types-BX4pfmKh.mjs";
3
3
  import { relative, resolve } from "node:path";
4
4
  import { realpathSync } from "node:fs";
5
5
  //#region src/core/changelog.ts
@@ -43,7 +43,7 @@ const defaultFormatter = (ctx) => {
43
43
  const BUILTIN_FORMATTERS = {
44
44
  default: defaultFormatter,
45
45
  github: async () => {
46
- const { createGithubFormatter } = await import("./changelog-github-CEaDCtTk.mjs");
46
+ const { createGithubFormatter } = await import("./changelog-github-BAtgxeXv.mjs");
47
47
  return createGithubFormatter();
48
48
  }
49
49
  };
@@ -54,7 +54,7 @@ const BUILTIN_FORMATTERS = {
54
54
  async function loadFormatter(changelog, rootDir) {
55
55
  const [name, options] = Array.isArray(changelog) ? changelog : [changelog, {}];
56
56
  if (name === "github") {
57
- const { createGithubFormatter } = await import("./changelog-github-CEaDCtTk.mjs");
57
+ const { createGithubFormatter } = await import("./changelog-github-BAtgxeXv.mjs");
58
58
  return createGithubFormatter(options);
59
59
  }
60
60
  if (typeof name === "string" && BUILTIN_FORMATTERS[name]) {
@@ -88,11 +88,12 @@ async function loadFormatter(changelog, rootDir) {
88
88
  return defaultFormatter;
89
89
  }
90
90
  /** Generate a changelog entry using the configured formatter */
91
- async function generateChangelogEntry(release, bumpFiles, formatter = defaultFormatter, date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0]) {
91
+ async function generateChangelogEntry(release, bumpFiles, formatter = defaultFormatter, date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0], target = "changelog") {
92
92
  return formatter({
93
93
  release,
94
94
  bumpFiles,
95
- date
95
+ date,
96
+ target
96
97
  });
97
98
  }
98
99
  /** Prepend a new entry to an existing CHANGELOG.md content */
@@ -1,5 +1,5 @@
1
1
  import { s as tryRunArgs } from "./shell-u3bYGxNy.mjs";
2
- import { o as sortBumpFilesByType, r as getBumpTypeForPackage } from "./changelog-xKuL0IKx.mjs";
2
+ import { o as sortBumpFilesByType, r as getBumpTypeForPackage } from "./changelog-BIkNBJ15.mjs";
3
3
  //#region src/core/changelog-github.ts
4
4
  /** Authors filtered from "Thanks" attribution by default (e.g. bots) */
5
5
  /** Authors filtered from "Thanks" attribution by default (e.g. AI/automation bots) */
@@ -1,7 +1,7 @@
1
1
  import { n as log, o as __toESM, t as colorize } from "./logger-C2dEe5Su.mjs";
2
- import { a as loadConfig, o as loadPackageConfig, r as getBumpyDir } from "./config-CJIj8xG3.mjs";
3
- import { n as discoverWorkspace } from "./workspace-c9-TqXed.mjs";
4
- import { r as readBumpFiles, t as filterBranchBumpFiles } from "./bump-file-BTsntOO-.mjs";
2
+ import { a as loadConfig, o as loadPackageConfig, r as getBumpyDir } from "./config-D13G4-R8.mjs";
3
+ import { n as discoverWorkspace } from "./workspace-BKOAMeki.mjs";
4
+ import { r as readBumpFiles, t as filterBranchBumpFiles } from "./bump-file-CoaSxqne.mjs";
5
5
  import { r as getChangedFiles } from "./git-D0__HP86.mjs";
6
6
  import { t as require_picomatch } from "./picomatch-TGJi--_I.mjs";
7
7
  import { relative } from "node:path";
@@ -1,34 +1,28 @@
1
1
  import { n as log, t as colorize } from "./logger-C2dEe5Su.mjs";
2
- import { a as loadConfig } from "./config-CJIj8xG3.mjs";
2
+ import { a as loadConfig } from "./config-D13G4-R8.mjs";
3
3
  import { t as detectPackageManager } from "./package-manager-CClZtIHP.mjs";
4
- import { n as discoverWorkspace } from "./workspace-c9-TqXed.mjs";
4
+ import { n as discoverWorkspace } from "./workspace-BKOAMeki.mjs";
5
5
  import { t as DependencyGraph } from "./dep-graph-E-9-eQ2J.mjs";
6
6
  import { n as runArgs, r as runArgsAsync, s as tryRunArgs } from "./shell-u3bYGxNy.mjs";
7
- import { r as readBumpFiles, t as filterBranchBumpFiles } from "./bump-file-BTsntOO-.mjs";
8
- import { t as assembleReleasePlan } from "./release-plan-21H89Cx1.mjs";
7
+ import { r as readBumpFiles, t as filterBranchBumpFiles } from "./bump-file-CoaSxqne.mjs";
8
+ import { t as assembleReleasePlan } from "./release-plan-RBjKmavL.mjs";
9
9
  import { r as getChangedFiles } from "./git-D0__HP86.mjs";
10
10
  import { t as randomName } from "./names-CBy7d8K_.mjs";
11
- import { findChangedPackages } from "./check-D3eXRyKJ.mjs";
11
+ import { findChangedPackages } from "./check-C80o_gJK.mjs";
12
12
  import { t as resolveCommitMessage } from "./commit-message-DOIfDxfj.mjs";
13
13
  import { createHash } from "node:crypto";
14
14
  //#region src/commands/ci.ts
15
15
  /**
16
16
  * Temporarily override GH_TOKEN with BUMPY_GH_TOKEN for a gh CLI call.
17
17
  *
18
- * Use `--pat-pr` / `--pat-comments` flags to opt in. This is useful when
19
- * BUMPY_GH_TOKEN belongs to a dedicated automation/bot account. If you're
20
- * using a developer's personal PAT, it's better to leave these flags off so
21
- * that PRs and comments appear from github-actions[bot] allowing the
22
- * developer to still review and approve the PR.
18
+ * When BUMPY_GH_TOKEN is set (e.g. a dedicated bot PAT or GitHub App token),
19
+ * it is used so that PRs created by bumpy can trigger CI workflows (the
20
+ * default GITHUB_TOKEN cannot do this). If BUMPY_GH_TOKEN is not available
21
+ * (e.g. fork PRs where secrets are hidden), falls back to the default token.
23
22
  */
24
- function requirePatToken() {
23
+ async function withPatToken(fn) {
25
24
  const token = process.env.BUMPY_GH_TOKEN;
26
- if (!token) throw new Error("BUMPY_GH_TOKEN must be set when using --pat-pr or --pat-comments");
27
- return token;
28
- }
29
- async function withPatToken(usePat, fn) {
30
- if (!usePat) return fn();
31
- const token = requirePatToken();
25
+ if (!token) return fn();
32
26
  const originalGhToken = process.env.GH_TOKEN;
33
27
  process.env.GH_TOKEN = token;
34
28
  try {
@@ -94,7 +88,7 @@ async function ciCheckCommand(rootDir, opts) {
94
88
  if (prBumpFiles.length === 0) {
95
89
  if (emptyBumpFileIds.length > 0 && parseErrors.length === 0) {
96
90
  log.success("Empty bump file found — no releases needed.");
97
- if (shouldComment && prNumber) await postOrUpdatePrComment(prNumber, formatEmptyBumpFileComment(emptyBumpFileIds, prNumber, detectPrBranch(rootDir)), rootDir, opts.patComments);
91
+ if (shouldComment && prNumber) await postOrUpdatePrComment(prNumber, formatEmptyBumpFileComment(emptyBumpFileIds, prNumber, detectPrBranch(rootDir)), rootDir);
98
92
  return;
99
93
  }
100
94
  const willFail = !opts.noFail || parseErrors.length > 0;
@@ -103,7 +97,7 @@ async function ciCheckCommand(rootDir, opts) {
103
97
  else log.warn(msg);
104
98
  if (shouldComment && prNumber) {
105
99
  const prBranch = detectPrBranch(rootDir);
106
- await postOrUpdatePrComment(prNumber, parseErrors.length > 0 ? formatBumpFileErrorsComment(parseErrors, prBranch, pm) : formatNoBumpFilesComment(prBranch, pm), rootDir, opts.patComments);
100
+ await postOrUpdatePrComment(prNumber, parseErrors.length > 0 ? formatBumpFileErrorsComment(parseErrors, prBranch, pm) : formatNoBumpFilesComment(prBranch, pm), rootDir);
107
101
  }
108
102
  if (willFail) process.exit(1);
109
103
  return;
@@ -115,7 +109,7 @@ async function ciCheckCommand(rootDir, opts) {
115
109
  console.log(` ${r.name}: ${r.oldVersion} → ${colorize(r.newVersion, "cyan")}${tag}`);
116
110
  }
117
111
  if (plan.warnings.length > 0) for (const w of plan.warnings) log.warn(w);
118
- if (shouldComment && prNumber) await postOrUpdatePrComment(prNumber, formatReleasePlanComment(plan, prBumpFiles, prNumber, detectPrBranch(rootDir), pm, plan.warnings, parseErrors, emptyBumpFileIds), rootDir, opts.patComments);
112
+ if (shouldComment && prNumber) await postOrUpdatePrComment(prNumber, formatReleasePlanComment(plan, prBumpFiles, prNumber, detectPrBranch(rootDir), pm, plan.warnings, parseErrors, emptyBumpFileIds), rootDir);
119
113
  if (parseErrors.length > 0 && !opts.noFail) process.exit(1);
120
114
  const coveredPackages = new Set(plan.releases.map((r) => r.name));
121
115
  const missing = (await findChangedPackages(changedFiles, packages, rootDir, config)).filter((name) => !coveredPackages.has(name));
@@ -141,7 +135,7 @@ async function ciReleaseCommand(rootDir, opts) {
141
135
  }
142
136
  if (bumpFiles.length === 0) {
143
137
  log.info("No pending bump files — checking for unpublished packages...");
144
- const { publishCommand } = await import("./publish-BwidFqbo.mjs");
138
+ const { publishCommand } = await import("./publish-CfZCAlPx.mjs");
145
139
  await publishCommand(rootDir, { tag: opts.tag });
146
140
  return;
147
141
  }
@@ -151,11 +145,11 @@ async function ciReleaseCommand(rootDir, opts) {
151
145
  return;
152
146
  }
153
147
  if (opts.mode === "auto-publish") await autoPublish(rootDir, config, plan, opts.tag);
154
- else await createVersionPr(rootDir, plan, config, new Map([...packages.values()].map((p) => [p.name, p.relativeDir])), opts.branch, opts.patPr);
148
+ else await createVersionPr(rootDir, plan, config, new Map([...packages.values()].map((p) => [p.name, p.relativeDir])), opts.branch);
155
149
  }
156
150
  async function autoPublish(rootDir, config, plan, tag) {
157
151
  log.step("Running bumpy version...");
158
- const { versionCommand } = await import("./version-ClkaCNTE.mjs");
152
+ const { versionCommand } = await import("./version-DLU0h1cq.mjs");
159
153
  await versionCommand(rootDir);
160
154
  log.step("Committing version changes...");
161
155
  runArgs([
@@ -184,7 +178,7 @@ async function autoPublish(rootDir, config, plan, tag) {
184
178
  ], { cwd: rootDir });
185
179
  }
186
180
  log.step("Running bumpy publish...");
187
- const { publishCommand } = await import("./publish-BwidFqbo.mjs");
181
+ const { publishCommand } = await import("./publish-CfZCAlPx.mjs");
188
182
  await publishCommand(rootDir, { tag });
189
183
  }
190
184
  /**
@@ -306,7 +300,7 @@ function pushWithToken(rootDir, branch, config) {
306
300
  if (!token && repo) log.warn("BUMPY_GH_TOKEN is not set — PR checks will not trigger automatically.\n Run `bumpy ci setup` for help.");
307
301
  }
308
302
  }
309
- async function createVersionPr(rootDir, plan, config, packageDirs, branchName, patPr) {
303
+ async function createVersionPr(rootDir, plan, config, packageDirs, branchName) {
310
304
  const branch = validateBranchName(branchName || config.versionPr.branch);
311
305
  const baseBranch = validateBranchName(tryRunArgs([
312
306
  "git",
@@ -350,7 +344,7 @@ async function createVersionPr(rootDir, plan, config, packageDirs, branchName, p
350
344
  branch
351
345
  ], { cwd: rootDir });
352
346
  log.step("Running bumpy version...");
353
- const { versionCommand } = await import("./version-ClkaCNTE.mjs");
347
+ const { versionCommand } = await import("./version-DLU0h1cq.mjs");
354
348
  await versionCommand(rootDir);
355
349
  runArgs([
356
350
  "git",
@@ -381,11 +375,12 @@ async function createVersionPr(rootDir, plan, config, packageDirs, branchName, p
381
375
  });
382
376
  pushWithToken(rootDir, branch, config);
383
377
  const repo = process.env.GITHUB_REPOSITORY;
378
+ const noPatWarning = !process.env.BUMPY_GH_TOKEN && !!repo;
384
379
  if (existingPr) {
385
380
  const validPr = validatePrNumber(existingPr);
386
- const prBody = formatVersionPrBody(plan, config.versionPr.preamble, packageDirs, repo, validPr);
381
+ const prBody = formatVersionPrBody(plan, config.versionPr.preamble, packageDirs, repo, validPr, noPatWarning);
387
382
  log.step(`Updating existing PR #${validPr}...`);
388
- await withPatToken(!!patPr, () => runArgsAsync([
383
+ await withPatToken(() => runArgsAsync([
389
384
  "gh",
390
385
  "pr",
391
386
  "edit",
@@ -402,8 +397,8 @@ async function createVersionPr(rootDir, plan, config, packageDirs, branchName, p
402
397
  } else {
403
398
  log.step("Creating version PR...");
404
399
  const prTitle = config.versionPr.title;
405
- const prBody = formatVersionPrBody(plan, config.versionPr.preamble, packageDirs, repo, null);
406
- const result = await withPatToken(!!patPr, () => runArgsAsync([
400
+ const prBody = formatVersionPrBody(plan, config.versionPr.preamble, packageDirs, repo, null, noPatWarning);
401
+ const result = await withPatToken(() => runArgsAsync([
407
402
  "gh",
408
403
  "pr",
409
404
  "create",
@@ -423,8 +418,8 @@ async function createVersionPr(rootDir, plan, config, packageDirs, branchName, p
423
418
  if (repo) {
424
419
  const newPrNumber = result?.match(/\/pull\/(\d+)/)?.[1];
425
420
  if (newPrNumber) {
426
- const updatedBody = formatVersionPrBody(plan, config.versionPr.preamble, packageDirs, repo, newPrNumber);
427
- await withPatToken(!!patPr, () => runArgsAsync([
421
+ const updatedBody = formatVersionPrBody(plan, config.versionPr.preamble, packageDirs, repo, newPrNumber, noPatWarning);
422
+ await withPatToken(() => runArgsAsync([
428
423
  "gh",
429
424
  "pr",
430
425
  "edit",
@@ -437,7 +432,7 @@ async function createVersionPr(rootDir, plan, config, packageDirs, branchName, p
437
432
  }));
438
433
  }
439
434
  }
440
- if (!patPr) pushWithToken(rootDir, branch, config);
435
+ if (!process.env.BUMPY_GH_TOKEN) pushWithToken(rootDir, branch, config);
441
436
  }
442
437
  runArgs([
443
438
  "git",
@@ -619,7 +614,7 @@ function buildDiffLinks(pkgDir, changesBaseUrl) {
619
614
  function sha256Hex(input) {
620
615
  return createHash("sha256").update(input).digest("hex");
621
616
  }
622
- function formatVersionPrBody(plan, preamble, packageDirs, repo, prNumber) {
617
+ function formatVersionPrBody(plan, preamble, packageDirs, repo, prNumber, showNoPatWarning = false) {
623
618
  const changesBaseUrl = repo && prNumber ? `https://github.com/${repo}/pull/${prNumber}/changes` : null;
624
619
  const lines = [];
625
620
  lines.push(preamble);
@@ -658,10 +653,14 @@ function formatVersionPrBody(plan, preamble, packageDirs, repo, prNumber) {
658
653
  lines.push("");
659
654
  }
660
655
  }
656
+ if (showNoPatWarning) {
657
+ lines.push("> ⚠️ `BUMPY_GH_TOKEN` is not set — CI checks will not run automatically on this PR. Run `bumpy ci setup` for help.");
658
+ lines.push("");
659
+ }
661
660
  return lines.join("\n");
662
661
  }
663
662
  const COMMENT_MARKER = "<!-- bumpy-release-plan -->";
664
- async function postOrUpdatePrComment(prNumber, body, rootDir, usePat = false) {
663
+ async function postOrUpdatePrComment(prNumber, body, rootDir) {
665
664
  const validPr = validatePrNumber(prNumber);
666
665
  const markedBody = `${COMMENT_MARKER}\n${body}`;
667
666
  try {
@@ -676,7 +675,7 @@ async function postOrUpdatePrComment(prNumber, body, rootDir, usePat = false) {
676
675
  `.comments[] | select(.body | startswith("${COMMENT_MARKER}")) | .url | capture("issuecomment-(?<id>[0-9]+)$") | .id`
677
676
  ], { cwd: rootDir })?.split("\n")[0]?.trim();
678
677
  if (commentId) {
679
- await withPatToken(usePat, () => runArgsAsync([
678
+ await runArgsAsync([
680
679
  "gh",
681
680
  "api",
682
681
  `repos/{owner}/{repo}/issues/comments/${commentId}`,
@@ -687,10 +686,10 @@ async function postOrUpdatePrComment(prNumber, body, rootDir, usePat = false) {
687
686
  ], {
688
687
  cwd: rootDir,
689
688
  input: markedBody
690
- }));
689
+ });
691
690
  log.dim(" Updated PR comment");
692
691
  } else {
693
- await withPatToken(usePat, () => runArgsAsync([
692
+ await runArgsAsync([
694
693
  "gh",
695
694
  "pr",
696
695
  "comment",
@@ -700,7 +699,7 @@ async function postOrUpdatePrComment(prNumber, body, rootDir, usePat = false) {
700
699
  ], {
701
700
  cwd: rootDir,
702
701
  input: markedBody
703
- }));
702
+ });
704
703
  log.dim(" Posted PR comment");
705
704
  }
706
705
  } catch (err) {
package/dist/cli.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { n as log, t as colorize } from "./logger-C2dEe5Su.mjs";
3
- import { n as findRoot } from "./config-CJIj8xG3.mjs";
3
+ import { n as findRoot } from "./config-D13G4-R8.mjs";
4
4
  //#region src/cli.ts
5
5
  const args = process.argv.slice(2);
6
6
  const command = args[0];
@@ -31,7 +31,7 @@ async function main() {
31
31
  }
32
32
  case "add": {
33
33
  const rootDir = await findRoot();
34
- const { addCommand } = await import("./add-DEqGa5gI.mjs");
34
+ const { addCommand } = await import("./add-ET1tcdQm.mjs");
35
35
  await addCommand(rootDir, {
36
36
  packages: flags.packages,
37
37
  message: flags.message,
@@ -42,7 +42,7 @@ async function main() {
42
42
  }
43
43
  case "status": {
44
44
  const rootDir = await findRoot();
45
- const { statusCommand } = await import("./status-CDGxgXWd.mjs");
45
+ const { statusCommand } = await import("./status-pMfPtt1p.mjs");
46
46
  await statusCommand(rootDir, {
47
47
  json: flags.json === true,
48
48
  packagesOnly: flags.packages === true,
@@ -54,13 +54,13 @@ async function main() {
54
54
  }
55
55
  case "version": {
56
56
  const rootDir = await findRoot();
57
- const { versionCommand } = await import("./version-ClkaCNTE.mjs");
57
+ const { versionCommand } = await import("./version-DLU0h1cq.mjs");
58
58
  await versionCommand(rootDir, { commit: flags.commit === true });
59
59
  break;
60
60
  }
61
61
  case "generate": {
62
62
  const rootDir = await findRoot();
63
- const { generateCommand } = await import("./generate-wHN6Ll6p.mjs");
63
+ const { generateCommand } = await import("./generate-BJq--oCu.mjs");
64
64
  await generateCommand(rootDir, {
65
65
  from: flags.from,
66
66
  dryRun: flags["dry-run"] === true,
@@ -70,7 +70,7 @@ async function main() {
70
70
  }
71
71
  case "check": {
72
72
  const rootDir = await findRoot();
73
- const { checkCommand } = await import("./check-D3eXRyKJ.mjs");
73
+ const { checkCommand } = await import("./check-C80o_gJK.mjs");
74
74
  await checkCommand(rootDir, {
75
75
  strict: flags.strict === true,
76
76
  noFail: flags["no-fail"] === true
@@ -82,20 +82,18 @@ async function main() {
82
82
  const subcommand = args[1];
83
83
  const ciFlags = parseFlags(args.slice(2));
84
84
  if (subcommand === "check") {
85
- const { ciCheckCommand } = await import("./ci-BVTwTUUK.mjs");
85
+ const { ciCheckCommand } = await import("./ci-WJ8kTjeN.mjs");
86
86
  await ciCheckCommand(rootDir, {
87
87
  comment: ciFlags.comment !== void 0 ? ciFlags.comment === true : void 0,
88
88
  strict: ciFlags.strict === true,
89
- noFail: ciFlags["no-fail"] === true,
90
- patComments: ciFlags["pat-comments"] === true
89
+ noFail: ciFlags["no-fail"] === true
91
90
  });
92
91
  } else if (subcommand === "release") {
93
- const { ciReleaseCommand } = await import("./ci-BVTwTUUK.mjs");
92
+ const { ciReleaseCommand } = await import("./ci-WJ8kTjeN.mjs");
94
93
  await ciReleaseCommand(rootDir, {
95
94
  mode: ciFlags["auto-publish"] === true ? "auto-publish" : "version-pr",
96
95
  tag: ciFlags.tag,
97
- branch: ciFlags.branch,
98
- patPr: ciFlags["pat-pr"] === true
96
+ branch: ciFlags.branch
99
97
  });
100
98
  } else if (subcommand === "setup") {
101
99
  const { ciSetupCommand } = await import("./ci-setup-D1NCzbNH.mjs");
@@ -108,7 +106,7 @@ async function main() {
108
106
  }
109
107
  case "publish": {
110
108
  const rootDir = await findRoot();
111
- const { publishCommand } = await import("./publish-BwidFqbo.mjs");
109
+ const { publishCommand } = await import("./publish-CfZCAlPx.mjs");
112
110
  await publishCommand(rootDir, {
113
111
  dryRun: flags["dry-run"] === true,
114
112
  tag: flags.tag,
@@ -132,7 +130,7 @@ async function main() {
132
130
  }
133
131
  case "--version":
134
132
  case "-v":
135
- console.log(`bumpy 1.2.2`);
133
+ console.log(`bumpy 1.3.0`);
136
134
  break;
137
135
  case "help":
138
136
  case "--help":
@@ -152,7 +150,7 @@ async function main() {
152
150
  }
153
151
  function printHelp() {
154
152
  console.log(`
155
- ${colorize(`🐸 bumpy v1.2.2`, "bold")} - Modern monorepo versioning
153
+ ${colorize(`🐸 bumpy v1.3.0`, "bold")} - Modern monorepo versioning
156
154
 
157
155
  Usage: bumpy <command> [options]
158
156
 
@@ -206,7 +204,7 @@ function printHelp() {
206
204
  --branch <name> Branch name for version PR (default: bumpy/version-packages)
207
205
 
208
206
  AI setup options:
209
- --target <tool> Target AI tool: opencode, cursor, codex
207
+ --target <tool> Target AI tool: claude, opencode, cursor, codex
210
208
 
211
209
  ${colorize("https://bumpy.varlock.dev", "dim")}
212
210
  `);
@@ -1,6 +1,6 @@
1
1
  import { a as __exportAll } from "./logger-C2dEe5Su.mjs";
2
2
  import { a as readJson, n as exists, o as readJsonc } from "./fs-DnDogVn-.mjs";
3
- import { r as DEFAULT_CONFIG } from "./types-CSM0c2-m.mjs";
3
+ import { r as DEFAULT_CONFIG } from "./types-BX4pfmKh.mjs";
4
4
  import { resolve } from "node:path";
5
5
  //#region src/core/config.ts
6
6
  var config_exports = /* @__PURE__ */ __exportAll({
@@ -1,9 +1,9 @@
1
1
  import { n as log, t as colorize } from "./logger-C2dEe5Su.mjs";
2
2
  import { t as ensureDir } from "./fs-DnDogVn-.mjs";
3
- import { a as loadConfig, r as getBumpyDir } from "./config-CJIj8xG3.mjs";
4
- import { t as discoverPackages } from "./workspace-c9-TqXed.mjs";
3
+ import { a as loadConfig, r as getBumpyDir } from "./config-D13G4-R8.mjs";
4
+ import { t as discoverPackages } from "./workspace-BKOAMeki.mjs";
5
5
  import { s as tryRunArgs } from "./shell-u3bYGxNy.mjs";
6
- import { i as writeBumpFile } from "./bump-file-BTsntOO-.mjs";
6
+ import { i as writeBumpFile } from "./bump-file-CoaSxqne.mjs";
7
7
  import { a as getFilesChangedInCommit, n as getBranchCommits } from "./git-D0__HP86.mjs";
8
8
  import { n as slugify, t as randomName } from "./names-CBy7d8K_.mjs";
9
9
  import { relative } from "node:path";
package/dist/index.d.mts CHANGED
@@ -231,6 +231,8 @@ interface ChangelogContext {
231
231
  bumpFiles: BumpFile[];
232
232
  /** ISO date string (YYYY-MM-DD) */
233
233
  date: string;
234
+ /** Where this entry will be used — formatters can customize output per target (default: 'changelog') */
235
+ target?: 'changelog' | 'github-release';
234
236
  }
235
237
  /**
236
238
  * A changelog formatter receives full context and returns the complete
@@ -245,7 +247,7 @@ declare const defaultFormatter: ChangelogFormatter;
245
247
  */
246
248
  declare function loadFormatter(changelog: BumpyConfig['changelog'], rootDir: string): Promise<ChangelogFormatter>;
247
249
  /** Generate a changelog entry using the configured formatter */
248
- declare function generateChangelogEntry(release: PlannedRelease, bumpFiles: BumpFile[], formatter?: ChangelogFormatter, date?: string): Promise<string>;
250
+ declare function generateChangelogEntry(release: PlannedRelease, bumpFiles: BumpFile[], formatter?: ChangelogFormatter, date?: string, target?: ChangelogContext['target']): Promise<string>;
249
251
  /** Prepend a new entry to an existing CHANGELOG.md content */
250
252
  declare function prependToChangelog(existingContent: string, newEntry: string): string;
251
253
  //#endregion
package/dist/index.mjs CHANGED
@@ -1,11 +1,11 @@
1
- import { a as DEP_TYPES, c as maxBump, i as DEFAULT_PUBLISH_CONFIG, n as DEFAULT_BUMP_RULES, o as bumpLevel, r as DEFAULT_CONFIG, s as hasCascade, t as BUMP_LEVELS } from "./types-CSM0c2-m.mjs";
2
- import { a as loadConfig, n as findRoot, r as getBumpyDir, s as matchGlob } from "./config-CJIj8xG3.mjs";
3
- import { t as discoverPackages } from "./workspace-c9-TqXed.mjs";
1
+ import { a as DEP_TYPES, c as maxBump, i as DEFAULT_PUBLISH_CONFIG, n as DEFAULT_BUMP_RULES, o as bumpLevel, r as DEFAULT_CONFIG, s as hasCascade, t as BUMP_LEVELS } from "./types-BX4pfmKh.mjs";
2
+ import { a as loadConfig, n as findRoot, r as getBumpyDir, s as matchGlob } from "./config-D13G4-R8.mjs";
3
+ import { t as discoverPackages } from "./workspace-BKOAMeki.mjs";
4
4
  import { t as DependencyGraph } from "./dep-graph-E-9-eQ2J.mjs";
5
- import { i as writeBumpFile, n as parseBumpFile, r as readBumpFiles } from "./bump-file-BTsntOO-.mjs";
5
+ import { i as writeBumpFile, n as parseBumpFile, r as readBumpFiles } from "./bump-file-CoaSxqne.mjs";
6
6
  import { n as satisfies, r as stripProtocol, t as bumpVersion } from "./semver-DfQyVLM_.mjs";
7
- import { t as assembleReleasePlan } from "./release-plan-21H89Cx1.mjs";
8
- import { a as prependToChangelog, i as loadFormatter, n as generateChangelogEntry, t as defaultFormatter } from "./changelog-xKuL0IKx.mjs";
9
- import { t as applyReleasePlan } from "./apply-release-plan-Bi9OSWks.mjs";
7
+ import { t as assembleReleasePlan } from "./release-plan-RBjKmavL.mjs";
8
+ import { a as prependToChangelog, i as loadFormatter, n as generateChangelogEntry, t as defaultFormatter } from "./changelog-BIkNBJ15.mjs";
9
+ import { t as applyReleasePlan } from "./apply-release-plan-BvZ32LAm.mjs";
10
10
  import { t as publishPackages } from "./publish-pipeline-BvLIu7WF.mjs";
11
11
  export { BUMP_LEVELS, DEFAULT_BUMP_RULES, DEFAULT_CONFIG, DEFAULT_PUBLISH_CONFIG, DEP_TYPES, DependencyGraph, applyReleasePlan, assembleReleasePlan, bumpLevel, bumpVersion, defaultFormatter, discoverPackages, findRoot, generateChangelogEntry, getBumpyDir, hasCascade, loadConfig, loadFormatter, matchGlob, maxBump, parseBumpFile, prependToChangelog, publishPackages, readBumpFiles, satisfies, stripProtocol, writeBumpFile };
@@ -1,9 +1,10 @@
1
1
  import { n as log, t as colorize } from "./logger-C2dEe5Su.mjs";
2
- import { a as loadConfig } from "./config-CJIj8xG3.mjs";
2
+ import { a as loadConfig } from "./config-D13G4-R8.mjs";
3
3
  import { n as detectWorkspaces } from "./package-manager-CClZtIHP.mjs";
4
- import { n as discoverWorkspace } from "./workspace-c9-TqXed.mjs";
4
+ import { n as discoverWorkspace } from "./workspace-BKOAMeki.mjs";
5
5
  import { t as DependencyGraph } from "./dep-graph-E-9-eQ2J.mjs";
6
6
  import { r as runArgsAsync, s as tryRunArgs } from "./shell-u3bYGxNy.mjs";
7
+ import { i as loadFormatter, n as generateChangelogEntry } from "./changelog-BIkNBJ15.mjs";
7
8
  import { c as pushWithTags, o as hasUncommittedChanges, s as listTags } from "./git-D0__HP86.mjs";
8
9
  import { t as publishPackages } from "./publish-pipeline-BvLIu7WF.mjs";
9
10
  //#region src/core/github-release.ts
@@ -24,7 +25,7 @@ async function createIndividualReleases(releases, bumpFiles, rootDir, opts = {})
24
25
  const headSha = getHeadSha(rootDir);
25
26
  for (const release of releases) {
26
27
  const tag = `${release.name}@${release.newVersion}`;
27
- const body = buildReleaseBody(release, bumpFiles);
28
+ const body = opts.formatter ? await generateReleaseBody(release, bumpFiles, opts.formatter) : buildReleaseBody(release, bumpFiles);
28
29
  const title = `${release.name} v${release.newVersion}`;
29
30
  if (opts.dryRun) {
30
31
  log.dim(` Would create GitHub release: ${title}`);
@@ -58,7 +59,7 @@ async function createAggregateRelease(releases, bumpFiles, rootDir, opts = {}) {
58
59
  if (releases.length === 0) return;
59
60
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
60
61
  const { tag, title } = resolveAggregateTagAndTitle(date, listTags(`release-${date}*`, { cwd: rootDir }), opts.title);
61
- const body = buildAggregateBody(releases, bumpFiles);
62
+ const body = opts.formatter ? await generateAggregateBody(releases, bumpFiles, opts.formatter) : buildAggregateBody(releases, bumpFiles);
62
63
  if (opts.dryRun) {
63
64
  log.dim(` Would create aggregate GitHub release: ${title}`);
64
65
  log.dim(` Tag: ${tag}`);
@@ -88,6 +89,36 @@ async function createAggregateRelease(releases, bumpFiles, rootDir, opts = {}) {
88
89
  log.warn(`Failed to create aggregate GitHub release: ${err instanceof Error ? err.message : err}`);
89
90
  }
90
91
  }
92
+ /** Generate a release body for a single package using the changelog formatter */
93
+ async function generateReleaseBody(release, bumpFiles, formatter) {
94
+ return stripVersionHeading(await generateChangelogEntry(release, bumpFiles, formatter, void 0, "github-release")).trim() || "No changelog entries.";
95
+ }
96
+ /** Generate an aggregate release body using the changelog formatter */
97
+ async function generateAggregateBody(releases, bumpFiles, formatter) {
98
+ const lines = [];
99
+ const groups = [
100
+ ["Major Changes", releases.filter((r) => r.type === "major")],
101
+ ["Minor Changes", releases.filter((r) => r.type === "minor")],
102
+ ["Patch Changes", releases.filter((r) => r.type === "patch")]
103
+ ];
104
+ for (const [heading, group] of groups) {
105
+ if (group.length === 0) continue;
106
+ lines.push(`## ${heading}\n`);
107
+ for (const release of group) {
108
+ lines.push(`### ${release.name} v${release.newVersion}\n`);
109
+ const body = stripVersionHeading(await generateChangelogEntry(release, bumpFiles, formatter, void 0, "github-release")).trim();
110
+ if (body) lines.push(body);
111
+ else if (release.isDependencyBump) lines.push("- Updated dependencies");
112
+ else if (release.isCascadeBump) lines.push("- Version bump via cascade rule");
113
+ lines.push("");
114
+ }
115
+ }
116
+ return lines.join("\n").trim() || "No changelog entries.";
117
+ }
118
+ /** Strip the leading ## version heading and date sub-heading from a changelog entry */
119
+ function stripVersionHeading(entry) {
120
+ return entry.replace(/^## .+\n/, "").replace(/^<sub>.+<\/sub>\n/, "").replace(/^_.+_\n/, "");
121
+ }
91
122
  function buildReleaseBody(release, bumpFiles) {
92
123
  const lines = [];
93
124
  const relevant = bumpFiles.filter((bf) => release.bumpFiles.includes(bf.id));
@@ -148,7 +179,7 @@ async function publishCommand(rootDir, opts) {
148
179
  }
149
180
  let toPublish = await findUnpublishedPackages(packages, config);
150
181
  if (opts.filter) {
151
- const { matchGlob } = await import("./config-CJIj8xG3.mjs").then((n) => n.t);
182
+ const { matchGlob } = await import("./config-D13G4-R8.mjs").then((n) => n.t);
152
183
  const patterns = opts.filter.split(",").map((p) => p.trim());
153
184
  toPublish = toPublish.filter((r) => patterns.some((p) => matchGlob(r.name, p)));
154
185
  }
@@ -187,11 +218,16 @@ async function publishCommand(rootDir, opts) {
187
218
  const aggConfig = config.aggregateRelease;
188
219
  const isAggregate = aggConfig === true || typeof aggConfig === "object" && aggConfig.enabled;
189
220
  const aggTitle = typeof aggConfig === "object" ? aggConfig.title : void 0;
221
+ const formatter = config.changelog !== false ? await loadFormatter(config.changelog, rootDir) : void 0;
190
222
  if (isAggregate) await createAggregateRelease(publishedReleases, releasePlan.bumpFiles, rootDir, {
191
223
  dryRun: opts.dryRun,
192
- title: aggTitle
224
+ title: aggTitle,
225
+ formatter
226
+ });
227
+ else await createIndividualReleases(publishedReleases, releasePlan.bumpFiles, rootDir, {
228
+ dryRun: opts.dryRun,
229
+ formatter
193
230
  });
194
- else await createIndividualReleases(publishedReleases, releasePlan.bumpFiles, rootDir, { dryRun: opts.dryRun });
195
231
  }
196
232
  }
197
233
  /**
@@ -1,5 +1,5 @@
1
- import { c as maxBump, n as DEFAULT_BUMP_RULES, o as bumpLevel, s as hasCascade } from "./types-CSM0c2-m.mjs";
2
- import { s as matchGlob } from "./config-CJIj8xG3.mjs";
1
+ import { c as maxBump, n as DEFAULT_BUMP_RULES, o as bumpLevel, s as hasCascade } from "./types-BX4pfmKh.mjs";
2
+ import { s as matchGlob } from "./config-D13G4-R8.mjs";
3
3
  import { n as satisfies, t as bumpVersion } from "./semver-DfQyVLM_.mjs";
4
4
  //#region src/core/release-plan.ts
5
5
  /**
@@ -1,9 +1,9 @@
1
1
  import { n as log, t as colorize } from "./logger-C2dEe5Su.mjs";
2
- import { a as loadConfig } from "./config-CJIj8xG3.mjs";
3
- import { t as discoverPackages } from "./workspace-c9-TqXed.mjs";
2
+ import { a as loadConfig } from "./config-D13G4-R8.mjs";
3
+ import { t as discoverPackages } from "./workspace-BKOAMeki.mjs";
4
4
  import { t as DependencyGraph } from "./dep-graph-E-9-eQ2J.mjs";
5
- import { r as readBumpFiles, t as filterBranchBumpFiles } from "./bump-file-BTsntOO-.mjs";
6
- import { t as assembleReleasePlan } from "./release-plan-21H89Cx1.mjs";
5
+ import { r as readBumpFiles, t as filterBranchBumpFiles } from "./bump-file-CoaSxqne.mjs";
6
+ import { t as assembleReleasePlan } from "./release-plan-RBjKmavL.mjs";
7
7
  import { i as getCurrentBranch, r as getChangedFiles } from "./git-D0__HP86.mjs";
8
8
  //#region src/commands/status.ts
9
9
  async function statusCommand(rootDir, opts) {
@@ -31,7 +31,7 @@ async function statusCommand(rootDir, opts) {
31
31
  releases = releases.filter((r) => types.includes(r.type));
32
32
  }
33
33
  if (opts.filter) {
34
- const { matchGlob } = await import("./config-CJIj8xG3.mjs").then((n) => n.t);
34
+ const { matchGlob } = await import("./config-D13G4-R8.mjs").then((n) => n.t);
35
35
  const patterns = opts.filter.split(",").map((p) => p.trim());
36
36
  releases = releases.filter((r) => patterns.some((p) => matchGlob(r.name, p)));
37
37
  }
@@ -66,7 +66,7 @@ const DEFAULT_CONFIG = {
66
66
  title: "🐸 Versioned release",
67
67
  branch: "bumpy/version-packages",
68
68
  preamble: [
69
- `<a href="https://bumpy.varlock.dev"><img src="https://raw.githubusercontent.com/dmno-dev/bumpy/main/images/frog-party.png" alt="bumpy-frog" width="60" align="left" style="image-rendering: pixelated;" title="Hi! I'm bumpy!" /></a>`,
69
+ `<a href="https://bumpy.varlock.dev"><img src="https://raw.githubusercontent.com/dmno-dev/bumpy/main/images/frog-clipboard.png" alt="bumpy-frog" width="60" align="left" style="image-rendering: pixelated;" title="Hi! I'm bumpy!" /></a>`,
70
70
  "",
71
71
  `This PR was created and will be kept in sync by [bumpy](https://bumpy.varlock.dev) based on your bump files (in \`.bumpy/\`). Merge it when you are ready to release the packages listed below:`,
72
72
  "<br clear=\"left\" />"
@@ -1,12 +1,12 @@
1
1
  import { n as log, t as colorize } from "./logger-C2dEe5Su.mjs";
2
- import { a as loadConfig } from "./config-CJIj8xG3.mjs";
2
+ import { a as loadConfig } from "./config-D13G4-R8.mjs";
3
3
  import { n as detectWorkspaces } from "./package-manager-CClZtIHP.mjs";
4
- import { t as discoverPackages } from "./workspace-c9-TqXed.mjs";
4
+ import { t as discoverPackages } from "./workspace-BKOAMeki.mjs";
5
5
  import { t as DependencyGraph } from "./dep-graph-E-9-eQ2J.mjs";
6
6
  import { n as runArgs, s as tryRunArgs } from "./shell-u3bYGxNy.mjs";
7
- import { r as readBumpFiles } from "./bump-file-BTsntOO-.mjs";
8
- import { t as assembleReleasePlan } from "./release-plan-21H89Cx1.mjs";
9
- import { t as applyReleasePlan } from "./apply-release-plan-Bi9OSWks.mjs";
7
+ import { r as readBumpFiles } from "./bump-file-CoaSxqne.mjs";
8
+ import { t as assembleReleasePlan } from "./release-plan-RBjKmavL.mjs";
9
+ import { t as applyReleasePlan } from "./apply-release-plan-BvZ32LAm.mjs";
10
10
  import { t as resolveCommitMessage } from "./commit-message-DOIfDxfj.mjs";
11
11
  //#region src/commands/version.ts
12
12
  async function versionCommand(rootDir, opts = {}) {
@@ -1,5 +1,5 @@
1
1
  import { a as readJson, n as exists } from "./fs-DnDogVn-.mjs";
2
- import { i as isPackageManaged, o as loadPackageConfig } from "./config-CJIj8xG3.mjs";
2
+ import { i as isPackageManaged, o as loadPackageConfig } from "./config-D13G4-R8.mjs";
3
3
  import { n as detectWorkspaces } from "./package-manager-CClZtIHP.mjs";
4
4
  import { relative, resolve } from "node:path";
5
5
  import { readdir, stat } from "node:fs/promises";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@varlock/bumpy",
3
- "version": "1.2.2",
3
+ "version": "1.3.0",
4
4
  "description": "Modern monorepo versioning and changelog tool",
5
5
  "keywords": [
6
6
  "bump",