@varlock/bumpy 1.1.0 → 1.2.1

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 (25) hide show
  1. package/README.md +256 -0
  2. package/dist/{add-BmNL5VwL.mjs → add-DF6bawDT.mjs} +6 -6
  3. package/dist/{ai-sMYUf3lP.mjs → ai-STKnq09z.mjs} +1 -1
  4. package/dist/{apply-release-plan-0kH62jhu.mjs → apply-release-plan-B1Wwx3HG.mjs} +5 -5
  5. package/dist/{bump-file-DVqR3k67.mjs → bump-file-C3S_bzSf.mjs} +78 -24
  6. package/dist/{changelog-github-DkACMj0j.mjs → changelog-github-DZSHX3Tb.mjs} +20 -5
  7. package/dist/{check-BjWF6SJm.mjs → check-BJL-YDWz.mjs} +38 -11
  8. package/dist/{ci-DY58ugIi.mjs → ci-C88ecvIP.mjs} +115 -33
  9. package/dist/{ci-setup-BQwktQEe.mjs → ci-setup-CARJFhcE.mjs} +1 -1
  10. package/dist/cli.mjs +24 -17
  11. package/dist/{config-B-Qg3DZH.mjs → config-D7Umr-fT.mjs} +3 -3
  12. package/dist/{fs-DYR2XuFE.mjs → fs-DnDogVn-.mjs} +16 -1
  13. package/dist/{generate-DX46X-rW.mjs → generate-D93b3NAD.mjs} +5 -5
  14. package/dist/{git-YDedMddc.mjs → git-H9S9z6g-.mjs} +10 -1
  15. package/dist/index.d.mts +13 -3
  16. package/dist/index.mjs +6 -6
  17. package/dist/{init-DkTPs_WQ.mjs → init-DJhMaceS.mjs} +3 -3
  18. package/dist/{package-manager-Clsmr-9r.mjs → package-manager-ByJ0wKYh.mjs} +1 -1
  19. package/dist/{publish-CGB4TIKD.mjs → publish-DGSV607z.mjs} +6 -6
  20. package/dist/{publish-pipeline-CXuqce1N.mjs → publish-pipeline-DiwZZ5AF.mjs} +3 -3
  21. package/dist/{release-plan-JNir7bSM.mjs → release-plan-CNOuSI-d.mjs} +1 -1
  22. package/dist/{status-EGYqULJg.mjs → status-S2ztf_8E.mjs} +38 -17
  23. package/dist/{version-BcfidiVX.mjs → version-BXrP4TIO.mjs} +11 -7
  24. package/dist/{workspace-DWXlwcH4.mjs → workspace-BHsAPUmC.mjs} +3 -3
  25. package/package.json +2 -1
@@ -1,13 +1,14 @@
1
1
  import { n as log, t as colorize } from "./logger-C2dEe5Su.mjs";
2
- import { a as loadConfig } from "./config-B-Qg3DZH.mjs";
3
- import { t as detectPackageManager } from "./package-manager-Clsmr-9r.mjs";
4
- import { n as discoverWorkspace } from "./workspace-DWXlwcH4.mjs";
2
+ import { a as loadConfig } from "./config-D7Umr-fT.mjs";
3
+ import { t as detectPackageManager } from "./package-manager-ByJ0wKYh.mjs";
4
+ import { n as discoverWorkspace } from "./workspace-BHsAPUmC.mjs";
5
5
  import { t as DependencyGraph } from "./dep-graph-DiLeAhl9.mjs";
6
6
  import { n as runArgs, r as runArgsAsync, s as tryRunArgs } from "./shell-CY7OD48z.mjs";
7
- import { r as readBumpFiles, t as filterBranchBumpFiles } from "./bump-file-DVqR3k67.mjs";
8
- import { t as assembleReleasePlan } from "./release-plan-JNir7bSM.mjs";
9
- import { r as getChangedFiles } from "./git-YDedMddc.mjs";
7
+ import { r as readBumpFiles, t as filterBranchBumpFiles } from "./bump-file-C3S_bzSf.mjs";
8
+ import { t as assembleReleasePlan } from "./release-plan-CNOuSI-d.mjs";
9
+ import { r as getChangedFiles } from "./git-H9S9z6g-.mjs";
10
10
  import { t as randomName } from "./names-C-TuOPbd.mjs";
11
+ import { findChangedPackages } from "./check-BJL-YDWz.mjs";
11
12
  import { t as resolveCommitMessage } from "./commit-message-BwsowSds.mjs";
12
13
  import { createHash } from "node:crypto";
13
14
  //#region src/commands/ci.ts
@@ -78,7 +79,7 @@ async function ciCheckCommand(rootDir, opts) {
78
79
  const config = await loadConfig(rootDir);
79
80
  const { packages } = await discoverWorkspace(rootDir, config);
80
81
  const depGraph = new DependencyGraph(packages);
81
- const allBumpFiles = await readBumpFiles(rootDir);
82
+ const { bumpFiles: allBumpFiles, errors: parseErrors } = await readBumpFiles(rootDir);
82
83
  if (detectPrBranch(rootDir) === config.versionPr.branch) {
83
84
  log.dim(" Skipping — this is the version PR branch.");
84
85
  return;
@@ -87,16 +88,24 @@ async function ciCheckCommand(rootDir, opts) {
87
88
  const shouldComment = opts.comment ?? inCI;
88
89
  const prNumber = detectPrNumber();
89
90
  const pm = await detectPackageManager(rootDir);
90
- const { branchBumpFiles: prBumpFiles, branchBumpFileIds: prBumpFileIds } = filterBranchBumpFiles(allBumpFiles, getChangedFiles(rootDir, config.baseBranch));
91
- if (prBumpFileIds.size > prBumpFiles.length) {
92
- log.success("Empty bump file found no releases needed.");
93
- if (shouldComment && prNumber) await postOrUpdatePrComment(prNumber, formatNoBumpFilesComment(detectPrBranch(rootDir), pm), rootDir, opts.patComments);
94
- return;
95
- }
91
+ const changedFiles = getChangedFiles(rootDir, config.baseBranch);
92
+ const { branchBumpFiles: prBumpFiles, hasEmptyBumpFile } = filterBranchBumpFiles(allBumpFiles, changedFiles, rootDir, parseErrors);
93
+ if (parseErrors.length > 0) for (const err of parseErrors) log.error(err);
96
94
  if (prBumpFiles.length === 0) {
97
- log.info("No bump files found in this PR.");
98
- if (shouldComment && prNumber) await postOrUpdatePrComment(prNumber, formatNoBumpFilesComment(detectPrBranch(rootDir), pm), rootDir, opts.patComments);
99
- if (opts.failOnMissing) process.exit(1);
95
+ if (hasEmptyBumpFile && parseErrors.length === 0) {
96
+ log.success("Empty bump file found no releases needed.");
97
+ if (shouldComment && prNumber) await postOrUpdatePrComment(prNumber, formatEmptyBumpFileComment(), rootDir, opts.patComments);
98
+ return;
99
+ }
100
+ const willFail = !opts.noFail || parseErrors.length > 0;
101
+ const msg = parseErrors.length > 0 ? "Bump file(s) found but failed to parse — see errors above." : "No bump files found in this PR.";
102
+ if (willFail) log.error(msg);
103
+ else log.warn(msg);
104
+ if (shouldComment && prNumber) {
105
+ const prBranch = detectPrBranch(rootDir);
106
+ await postOrUpdatePrComment(prNumber, parseErrors.length > 0 ? formatBumpFileErrorsComment(parseErrors, prBranch, pm) : formatNoBumpFilesComment(prBranch, pm), rootDir, opts.patComments);
107
+ }
108
+ if (willFail) process.exit(1);
100
109
  return;
101
110
  }
102
111
  const plan = assembleReleasePlan(prBumpFiles, packages, depGraph, config);
@@ -106,7 +115,15 @@ async function ciCheckCommand(rootDir, opts) {
106
115
  console.log(` ${r.name}: ${r.oldVersion} → ${colorize(r.newVersion, "cyan")}${tag}`);
107
116
  }
108
117
  if (plan.warnings.length > 0) for (const w of plan.warnings) log.warn(w);
109
- if (shouldComment && prNumber) await postOrUpdatePrComment(prNumber, formatReleasePlanComment(plan, prBumpFiles, prNumber, detectPrBranch(rootDir), pm, plan.warnings), rootDir, opts.patComments);
118
+ if (shouldComment && prNumber) await postOrUpdatePrComment(prNumber, formatReleasePlanComment(plan, prBumpFiles, prNumber, detectPrBranch(rootDir), pm, plan.warnings, parseErrors), rootDir, opts.patComments);
119
+ if (parseErrors.length > 0 && !opts.noFail) process.exit(1);
120
+ const coveredPackages = new Set(plan.releases.map((r) => r.name));
121
+ const missing = (await findChangedPackages(changedFiles, packages, rootDir, config)).filter((name) => !coveredPackages.has(name));
122
+ if (missing.length > 0) {
123
+ const willFail = opts.strict && !opts.noFail;
124
+ (willFail ? log.error : log.warn)(`${missing.length} changed package(s) not covered by bump files: ${missing.join(", ")}`);
125
+ if (willFail) process.exit(1);
126
+ }
110
127
  }
111
128
  /**
112
129
  * CI release: either auto-publish or create a version PR.
@@ -117,10 +134,14 @@ async function ciReleaseCommand(rootDir, opts) {
117
134
  ensureGitIdentity(rootDir, config);
118
135
  const { packages } = await discoverWorkspace(rootDir, config);
119
136
  const depGraph = new DependencyGraph(packages);
120
- const bumpFiles = await readBumpFiles(rootDir);
137
+ const { bumpFiles, errors: releaseParseErrors } = await readBumpFiles(rootDir);
138
+ if (releaseParseErrors.length > 0) {
139
+ for (const err of releaseParseErrors) log.error(err);
140
+ throw new Error("Bump file parse errors must be fixed before releasing.");
141
+ }
121
142
  if (bumpFiles.length === 0) {
122
143
  log.info("No pending bump files — checking for unpublished packages...");
123
- const { publishCommand } = await import("./publish-CGB4TIKD.mjs");
144
+ const { publishCommand } = await import("./publish-DGSV607z.mjs");
124
145
  await publishCommand(rootDir, { tag: opts.tag });
125
146
  return;
126
147
  }
@@ -134,7 +155,7 @@ async function ciReleaseCommand(rootDir, opts) {
134
155
  }
135
156
  async function autoPublish(rootDir, config, plan, tag) {
136
157
  log.step("Running bumpy version...");
137
- const { versionCommand } = await import("./version-BcfidiVX.mjs");
158
+ const { versionCommand } = await import("./version-BXrP4TIO.mjs");
138
159
  await versionCommand(rootDir);
139
160
  log.step("Committing version changes...");
140
161
  runArgs([
@@ -163,7 +184,7 @@ async function autoPublish(rootDir, config, plan, tag) {
163
184
  ], { cwd: rootDir });
164
185
  }
165
186
  log.step("Running bumpy publish...");
166
- const { publishCommand } = await import("./publish-CGB4TIKD.mjs");
187
+ const { publishCommand } = await import("./publish-DGSV607z.mjs");
167
188
  await publishCommand(rootDir, { tag });
168
189
  }
169
190
  /**
@@ -329,7 +350,7 @@ async function createVersionPr(rootDir, plan, config, packageDirs, branchName, p
329
350
  branch
330
351
  ], { cwd: rootDir });
331
352
  log.step("Running bumpy version...");
332
- const { versionCommand } = await import("./version-BcfidiVX.mjs");
353
+ const { versionCommand } = await import("./version-BXrP4TIO.mjs");
333
354
  await versionCommand(rootDir);
334
355
  runArgs([
335
356
  "git",
@@ -359,9 +380,10 @@ async function createVersionPr(rootDir, plan, config, packageDirs, branchName, p
359
380
  input: await resolveCommitMessage(config.versionCommitMessage, plan, rootDir)
360
381
  });
361
382
  pushWithToken(rootDir, branch, config);
362
- const prBody = formatVersionPrBody(plan, config.versionPr.preamble, packageDirs);
383
+ const repo = process.env.GITHUB_REPOSITORY;
363
384
  if (existingPr) {
364
385
  const validPr = validatePrNumber(existingPr);
386
+ const prBody = formatVersionPrBody(plan, config.versionPr.preamble, packageDirs, repo, validPr);
365
387
  log.step(`Updating existing PR #${validPr}...`);
366
388
  await withPatToken(!!patPr, () => runArgsAsync([
367
389
  "gh",
@@ -380,6 +402,7 @@ async function createVersionPr(rootDir, plan, config, packageDirs, branchName, p
380
402
  } else {
381
403
  log.step("Creating version PR...");
382
404
  const prTitle = config.versionPr.title;
405
+ const prBody = formatVersionPrBody(plan, config.versionPr.preamble, packageDirs, repo, null);
383
406
  const result = await withPatToken(!!patPr, () => runArgsAsync([
384
407
  "gh",
385
408
  "pr",
@@ -397,6 +420,23 @@ async function createVersionPr(rootDir, plan, config, packageDirs, branchName, p
397
420
  input: prBody
398
421
  }));
399
422
  log.success(`🐸 Created PR: ${result}`);
423
+ if (repo) {
424
+ const newPrNumber = result?.match(/\/pull\/(\d+)/)?.[1];
425
+ if (newPrNumber) {
426
+ const updatedBody = formatVersionPrBody(plan, config.versionPr.preamble, packageDirs, repo, newPrNumber);
427
+ await withPatToken(!!patPr, () => runArgsAsync([
428
+ "gh",
429
+ "pr",
430
+ "edit",
431
+ newPrNumber,
432
+ "--body-file",
433
+ "-"
434
+ ], {
435
+ cwd: rootDir,
436
+ input: updatedBody
437
+ }));
438
+ }
439
+ }
400
440
  if (!patPr) pushWithToken(rootDir, branch, config);
401
441
  }
402
442
  runArgs([
@@ -427,7 +467,7 @@ function pmRunCommand(pm) {
427
467
  if (pm === "yarn") return "yarn bumpy";
428
468
  return "npx bumpy";
429
469
  }
430
- function formatReleasePlanComment(plan, bumpFiles, prNumber, prBranch, pm, warnings = []) {
470
+ function formatReleasePlanComment(plan, bumpFiles, prNumber, prBranch, pm, warnings = [], parseErrors = []) {
431
471
  const repo = process.env.GITHUB_REPOSITORY;
432
472
  const lines = [];
433
473
  const preamble = [
@@ -465,12 +505,18 @@ function formatReleasePlanComment(plan, bumpFiles, prNumber, prBranch, pm, warni
465
505
  const filename = `${bf.id}.md`;
466
506
  const parts = [`\`${filename}\``];
467
507
  if (repo) {
468
- parts.push(`([view diff](https://github.com/${repo}/pull/${prNumber}/files#diff-.bumpy/${filename}))`);
508
+ parts.push(`([view diff](https://github.com/${repo}/pull/${prNumber}/changes#diff-${sha256Hex(`.bumpy/${filename}`)}))`);
469
509
  if (prBranch) parts.push(`([edit](https://github.com/${repo}/edit/${prBranch}/.bumpy/${filename}))`);
470
510
  }
471
511
  lines.push(`- ${parts.join(" ")}`);
472
512
  }
473
513
  lines.push("");
514
+ if (parseErrors.length > 0) {
515
+ lines.push("#### Errors");
516
+ lines.push("");
517
+ for (const e of parseErrors) lines.push(`> :x: ${e}`);
518
+ lines.push("");
519
+ }
474
520
  if (warnings.length > 0) {
475
521
  lines.push("#### Warnings");
476
522
  lines.push("");
@@ -484,6 +530,41 @@ function formatReleasePlanComment(plan, bumpFiles, prNumber, prBranch, pm, warni
484
530
  lines.push(`_This comment is maintained by [bumpy](https://bumpy.varlock.dev)._`);
485
531
  return lines.join("\n");
486
532
  }
533
+ function formatBumpFileErrorsComment(errors, prBranch, pm) {
534
+ const runCmd = pmRunCommand(pm);
535
+ const lines = [
536
+ `<a href="https://bumpy.varlock.dev"><img src="${FROG_IMG_BASE}/frog-neutral.png" alt="bumpy-frog" width="60" align="left" style="image-rendering: pixelated;" title="Hi! I'm bumpy!" /></a>`,
537
+ "",
538
+ "**This PR has bump file(s) with errors that need to be fixed.**",
539
+ "<br clear=\"left\" />\n",
540
+ "#### Errors",
541
+ "",
542
+ ...errors.map((e) => `> :x: ${e}`),
543
+ "",
544
+ "Please fix the errors above or recreate the bump file:\n",
545
+ "```bash",
546
+ `${runCmd} add`,
547
+ "```"
548
+ ];
549
+ const addLink = buildAddBumpFileLink(prBranch);
550
+ if (addLink) {
551
+ lines.push("");
552
+ lines.push(`Or [click here to add a bump file](${addLink}) directly on GitHub.`);
553
+ }
554
+ lines.push("\n---");
555
+ lines.push(`_This comment is maintained by [bumpy](https://bumpy.varlock.dev)._`);
556
+ return lines.join("\n");
557
+ }
558
+ function formatEmptyBumpFileComment() {
559
+ return [
560
+ `<a href="https://bumpy.varlock.dev"><img src="${FROG_IMG_BASE}/frog-neutral.png" alt="bumpy-frog" width="60" align="left" style="image-rendering: pixelated;" title="Hi! I'm bumpy!" /></a>`,
561
+ "",
562
+ "**This PR includes an empty bump file — no version bump is needed.** :white_check_mark:",
563
+ "<br clear=\"left\" />",
564
+ "\n---",
565
+ `_This comment is maintained by [bumpy](https://bumpy.varlock.dev)._`
566
+ ].join("\n");
567
+ }
487
568
  function formatNoBumpFilesComment(prBranch, pm) {
488
569
  const runCmd = pmRunCommand(pm);
489
570
  const lines = [
@@ -506,18 +587,19 @@ function formatNoBumpFilesComment(prBranch, pm) {
506
587
  return lines.join("\n");
507
588
  }
508
589
  function bumpSectionHeader(type) {
509
- return `### ${`<img src="${FROG_IMG_BASE}/frog-${type}.png" alt="${type}" width="52" style="image-rendering: pixelated;" align="right" />`} ${type.charAt(0).toUpperCase() + type.slice(1)} releases`;
590
+ const label = `${type.charAt(0).toUpperCase() + type.slice(1)} releases`;
591
+ return `### ${`<a href="https://bumpy.varlock.dev" title="${label}"><img src="${FROG_IMG_BASE}/frog-${type}.png" alt="${type}" width="52" style="image-rendering: pixelated;" align="right" /></a>`} ${label}`;
510
592
  }
511
593
  /** Build inline diff links for a package's changed files in the PR */
512
- function buildDiffLinks(pkgDir) {
513
- const pkgJsonPath = `${pkgDir}/package.json`;
514
- const changelogPath = `${pkgDir}/CHANGELOG.md`;
515
- return ` <sub>${[`[package.json](#diff-${sha256Hex(pkgJsonPath)})`, `[CHANGELOG.md](#diff-${sha256Hex(changelogPath)})`].join(" · ")}</sub>`;
594
+ function buildDiffLinks(pkgDir, changesBaseUrl) {
595
+ if (!changesBaseUrl) return "";
596
+ return ` <sub>[CHANGELOG.md](${changesBaseUrl}#diff-${sha256Hex(`${pkgDir}/CHANGELOG.md`)})</sub>`;
516
597
  }
517
598
  function sha256Hex(input) {
518
599
  return createHash("sha256").update(input).digest("hex");
519
600
  }
520
- function formatVersionPrBody(plan, preamble, packageDirs) {
601
+ function formatVersionPrBody(plan, preamble, packageDirs, repo, prNumber) {
602
+ const changesBaseUrl = repo && prNumber ? `https://github.com/${repo}/pull/${prNumber}/changes` : null;
521
603
  const lines = [];
522
604
  lines.push(preamble);
523
605
  lines.push("");
@@ -539,13 +621,13 @@ function formatVersionPrBody(plan, preamble, packageDirs) {
539
621
  for (const r of releases) {
540
622
  const suffix = r.isDependencyBump ? " _(dep)_" : r.isCascadeBump ? " _(cascade)_" : "";
541
623
  const pkgDir = packageDirs.get(r.name);
542
- const diffLinks = pkgDir ? buildDiffLinks(pkgDir) : "";
624
+ const diffLinks = pkgDir ? buildDiffLinks(pkgDir, changesBaseUrl) : "";
543
625
  lines.push(`#### \`${r.name}\` ${r.oldVersion} → **${r.newVersion}**${suffix}${diffLinks}`);
544
626
  lines.push("");
545
627
  const relevantBumpFiles = plan.bumpFiles.filter((bf) => r.bumpFiles.includes(bf.id));
546
628
  if (relevantBumpFiles.length > 0) {
547
629
  for (const bf of relevantBumpFiles) if (bf.summary) {
548
- const bfLink = ` ([bump file](#diff-${sha256Hex(`.bumpy/${bf.id}.md`)}))`;
630
+ const bfLink = changesBaseUrl ? ` ([bump file](${changesBaseUrl}#diff-${sha256Hex(`.bumpy/${bf.id}.md`)}))` : "";
549
631
  const summaryLines = bf.summary.split("\n");
550
632
  lines.push(`- ${summaryLines[0]}${bfLink}`);
551
633
  for (let i = 1; i < summaryLines.length; i++) if (summaryLines[i].trim()) lines.push(` ${summaryLines[i]}`);
@@ -1,5 +1,5 @@
1
1
  import { n as log, o as __toESM, r as require_picocolors } from "./logger-C2dEe5Su.mjs";
2
- import { t as detectPackageManager } from "./package-manager-Clsmr-9r.mjs";
2
+ import { t as detectPackageManager } from "./package-manager-ByJ0wKYh.mjs";
3
3
  import { s as tryRunArgs } from "./shell-CY7OD48z.mjs";
4
4
  import { a as fe, c as ot, i as _t, n as O, o as gt, r as Ot, s as mt, t as unwrap, u as wt } from "./clack-C6bVkGxf.mjs";
5
5
  //#region src/commands/ci-setup.ts
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-B-Qg3DZH.mjs";
3
+ import { n as findRoot } from "./config-D7Umr-fT.mjs";
4
4
  //#region src/cli.ts
5
5
  const args = process.argv.slice(2);
6
6
  const command = args[0];
@@ -25,13 +25,13 @@ async function main() {
25
25
  switch (command) {
26
26
  case "init": {
27
27
  const rootDir = await findRoot();
28
- const { initCommand } = await import("./init-DkTPs_WQ.mjs");
28
+ const { initCommand } = await import("./init-DJhMaceS.mjs");
29
29
  await initCommand(rootDir, { force: flags.force === true });
30
30
  break;
31
31
  }
32
32
  case "add": {
33
33
  const rootDir = await findRoot();
34
- const { addCommand } = await import("./add-BmNL5VwL.mjs");
34
+ const { addCommand } = await import("./add-DF6bawDT.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-EGYqULJg.mjs");
45
+ const { statusCommand } = await import("./status-S2ztf_8E.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-BcfidiVX.mjs");
57
+ const { versionCommand } = await import("./version-BXrP4TIO.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-DX46X-rW.mjs");
63
+ const { generateCommand } = await import("./generate-D93b3NAD.mjs");
64
64
  await generateCommand(rootDir, {
65
65
  from: flags.from,
66
66
  dryRun: flags["dry-run"] === true,
@@ -70,8 +70,11 @@ async function main() {
70
70
  }
71
71
  case "check": {
72
72
  const rootDir = await findRoot();
73
- const { checkCommand } = await import("./check-BjWF6SJm.mjs");
74
- await checkCommand(rootDir);
73
+ const { checkCommand } = await import("./check-BJL-YDWz.mjs");
74
+ await checkCommand(rootDir, {
75
+ strict: flags.strict === true,
76
+ noFail: flags["no-fail"] === true
77
+ });
75
78
  break;
76
79
  }
77
80
  case "ci": {
@@ -79,14 +82,15 @@ async function main() {
79
82
  const subcommand = args[1];
80
83
  const ciFlags = parseFlags(args.slice(2));
81
84
  if (subcommand === "check") {
82
- const { ciCheckCommand } = await import("./ci-DY58ugIi.mjs");
85
+ const { ciCheckCommand } = await import("./ci-C88ecvIP.mjs");
83
86
  await ciCheckCommand(rootDir, {
84
87
  comment: ciFlags.comment !== void 0 ? ciFlags.comment === true : void 0,
85
- failOnMissing: ciFlags["fail-on-missing"] === true,
88
+ strict: ciFlags.strict === true,
89
+ noFail: ciFlags["no-fail"] === true,
86
90
  patComments: ciFlags["pat-comments"] === true
87
91
  });
88
92
  } else if (subcommand === "release") {
89
- const { ciReleaseCommand } = await import("./ci-DY58ugIi.mjs");
93
+ const { ciReleaseCommand } = await import("./ci-C88ecvIP.mjs");
90
94
  await ciReleaseCommand(rootDir, {
91
95
  mode: ciFlags["auto-publish"] === true ? "auto-publish" : "version-pr",
92
96
  tag: ciFlags.tag,
@@ -94,7 +98,7 @@ async function main() {
94
98
  patPr: ciFlags["pat-pr"] === true
95
99
  });
96
100
  } else if (subcommand === "setup") {
97
- const { ciSetupCommand } = await import("./ci-setup-BQwktQEe.mjs");
101
+ const { ciSetupCommand } = await import("./ci-setup-CARJFhcE.mjs");
98
102
  await ciSetupCommand(rootDir);
99
103
  } else {
100
104
  log.error(`Unknown ci subcommand: ${subcommand}. Use "ci check", "ci release", or "ci setup".`);
@@ -104,7 +108,7 @@ async function main() {
104
108
  }
105
109
  case "publish": {
106
110
  const rootDir = await findRoot();
107
- const { publishCommand } = await import("./publish-CGB4TIKD.mjs");
111
+ const { publishCommand } = await import("./publish-DGSV607z.mjs");
108
112
  await publishCommand(rootDir, {
109
113
  dryRun: flags["dry-run"] === true,
110
114
  tag: flags.tag,
@@ -118,7 +122,7 @@ async function main() {
118
122
  const subcommand = args[1];
119
123
  const aiFlags = parseFlags(args.slice(2));
120
124
  if (subcommand === "setup") {
121
- const { aiSetupCommand } = await import("./ai-sMYUf3lP.mjs");
125
+ const { aiSetupCommand } = await import("./ai-STKnq09z.mjs");
122
126
  await aiSetupCommand(rootDir, { target: aiFlags.target });
123
127
  } else {
124
128
  log.error(`Unknown ai subcommand: ${subcommand}. Use "ai setup".`);
@@ -128,7 +132,7 @@ async function main() {
128
132
  }
129
133
  case "--version":
130
134
  case "-v":
131
- console.log(`bumpy 1.1.0`);
135
+ console.log(`bumpy 1.2.1`);
132
136
  break;
133
137
  case "help":
134
138
  case "--help":
@@ -148,7 +152,7 @@ async function main() {
148
152
  }
149
153
  function printHelp() {
150
154
  console.log(`
151
- ${colorize(`🐸 bumpy v1.1.0`, "bold")} - Modern monorepo versioning
155
+ ${colorize(`🐸 bumpy v1.2.1`, "bold")} - Modern monorepo versioning
152
156
 
153
157
  Usage: bumpy <command> [options]
154
158
 
@@ -158,6 +162,8 @@ function printHelp() {
158
162
  generate Generate bump file from branch commits
159
163
  status Show pending releases
160
164
  check Verify changed packages have bump files (for pre-push hooks)
165
+ --strict Fail if any changed package is uncovered (default: only fail if no bump files at all)
166
+ --no-fail Warn only, never exit 1
161
167
  version [--commit] Apply bump files and bump versions
162
168
  publish Publish versioned packages
163
169
  ci check PR check — report pending releases, comment on PR
@@ -191,7 +197,8 @@ function printHelp() {
191
197
 
192
198
  CI check options:
193
199
  --comment Force PR comment on/off (auto-detected in CI)
194
- --fail-on-missing Exit 1 if no bump files found
200
+ --strict Fail if any changed package is uncovered (default: only fail if no bump files at all)
201
+ --no-fail Warn only, never exit 1
195
202
 
196
203
  CI release options:
197
204
  --auto-publish Version + publish directly (default: create version PR)
@@ -1,5 +1,5 @@
1
1
  import { a as __exportAll } from "./logger-C2dEe5Su.mjs";
2
- import { a as readJson, n as exists } from "./fs-DYR2XuFE.mjs";
2
+ import { a as readJson, n as exists, o as readJsonc } from "./fs-DnDogVn-.mjs";
3
3
  import { resolve } from "node:path";
4
4
  //#region src/types.ts
5
5
  const BUMP_LEVELS = {
@@ -71,7 +71,7 @@ const DEFAULT_CONFIG = {
71
71
  preamble: [
72
72
  `<a href="https://bumpy.varlock.dev"><img src="https://raw.githubusercontent.com/dmno-dev/bumpy/main/images/frog-talking.png" alt="bumpy-frog" width="60" align="left" style="image-rendering: pixelated;" title="Hi! I'm bumpy!" /></a>`,
73
73
  "",
74
- `This PR was created and will be kept in sync by [bumpy](https://bumpy.varlock.dev) based on your .bumpy bump files. Merge it when you are ready to release the packages listed below:`,
74
+ `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:`,
75
75
  "<br clear=\"left\" />"
76
76
  ].join("\n")
77
77
  }
@@ -109,7 +109,7 @@ async function findRoot(startDir = process.cwd()) {
109
109
  async function loadConfig(rootDir) {
110
110
  const configPath = resolve(rootDir, BUMPY_DIR, CONFIG_FILE);
111
111
  let userConfig = {};
112
- if (await exists(configPath)) userConfig = await readJson(configPath);
112
+ if (await exists(configPath)) userConfig = await readJsonc(configPath);
113
113
  return mergeConfig(DEFAULT_CONFIG, userConfig);
114
114
  }
115
115
  /** Load per-package bumpy config from package.json["bumpy"] or .bumpy.config.json */
@@ -1,11 +1,23 @@
1
1
  import { a as __exportAll } from "./logger-C2dEe5Su.mjs";
2
2
  import { access, mkdir, readFile, readdir, unlink, writeFile } from "node:fs/promises";
3
+ //#region src/utils/jsonc.ts
4
+ const stringOrCommentRe = /("(?:\\?[^])*?")|(\/\/.*)|(\/\*[^]*?\*\/)/g;
5
+ const stringOrTrailingCommaRe = /("(?:\\?[^])*?")|(,\s*)(?=]|})/g;
6
+ function parseJsonc(text) {
7
+ try {
8
+ return JSON.parse(text);
9
+ } catch {
10
+ return JSON.parse(text.replace(stringOrCommentRe, "$1").replace(stringOrTrailingCommaRe, "$1"));
11
+ }
12
+ }
13
+ //#endregion
3
14
  //#region src/utils/fs.ts
4
15
  var fs_exports = /* @__PURE__ */ __exportAll({
5
16
  ensureDir: () => ensureDir,
6
17
  exists: () => exists,
7
18
  listFiles: () => listFiles,
8
19
  readJson: () => readJson,
20
+ readJsonc: () => readJsonc,
9
21
  readText: () => readText,
10
22
  removeFile: () => removeFile,
11
23
  updateJsonFields: () => updateJsonFields,
@@ -17,6 +29,9 @@ async function readJson(filePath) {
17
29
  const content = await readFile(filePath, "utf-8");
18
30
  return JSON.parse(content);
19
31
  }
32
+ async function readJsonc(filePath) {
33
+ return parseJsonc(await readFile(filePath, "utf-8"));
34
+ }
20
35
  async function writeJson(filePath, data, indent = 2) {
21
36
  await writeFile(filePath, JSON.stringify(data, null, indent) + "\n", "utf-8");
22
37
  }
@@ -78,4 +93,4 @@ async function ensureDir(dir) {
78
93
  await mkdir(dir, { recursive: true });
79
94
  }
80
95
  //#endregion
81
- export { readJson as a, updateJsonFields as c, writeText as d, listFiles as i, updateJsonNestedField as l, exists as n, readText as o, fs_exports as r, removeFile as s, ensureDir as t, writeJson as u };
96
+ export { readJson as a, removeFile as c, writeJson as d, writeText as f, listFiles as i, updateJsonFields as l, exists as n, readJsonc as o, fs_exports as r, readText as s, ensureDir as t, updateJsonNestedField as u };
@@ -1,10 +1,10 @@
1
1
  import { n as log, t as colorize } from "./logger-C2dEe5Su.mjs";
2
- import { t as ensureDir } from "./fs-DYR2XuFE.mjs";
3
- import { a as loadConfig, r as getBumpyDir } from "./config-B-Qg3DZH.mjs";
4
- import { t as discoverPackages } from "./workspace-DWXlwcH4.mjs";
2
+ import { t as ensureDir } from "./fs-DnDogVn-.mjs";
3
+ import { a as loadConfig, r as getBumpyDir } from "./config-D7Umr-fT.mjs";
4
+ import { t as discoverPackages } from "./workspace-BHsAPUmC.mjs";
5
5
  import { s as tryRunArgs } from "./shell-CY7OD48z.mjs";
6
- import { i as writeBumpFile } from "./bump-file-DVqR3k67.mjs";
7
- import { i as getFilesChangedInCommit, n as getBranchCommits } from "./git-YDedMddc.mjs";
6
+ import { i as writeBumpFile } from "./bump-file-C3S_bzSf.mjs";
7
+ import { a as getFilesChangedInCommit, n as getBranchCommits } from "./git-H9S9z6g-.mjs";
8
8
  import { n as slugify, t as randomName } from "./names-C-TuOPbd.mjs";
9
9
  import { relative } from "node:path";
10
10
  //#region src/commands/generate.ts
@@ -26,6 +26,15 @@ function hasUncommittedChanges(opts) {
26
26
  ], opts);
27
27
  return result !== null && result.length > 0;
28
28
  }
29
+ /** Get the current branch name */
30
+ function getCurrentBranch(opts) {
31
+ return tryRunArgs([
32
+ "git",
33
+ "rev-parse",
34
+ "--abbrev-ref",
35
+ "HEAD"
36
+ ], opts);
37
+ }
29
38
  /** Check if a tag already exists */
30
39
  function tagExists(tag, opts) {
31
40
  return tryRunArgs([
@@ -127,4 +136,4 @@ function listTags(pattern, opts) {
127
136
  return result.split("\n").filter(Boolean);
128
137
  }
129
138
  //#endregion
130
- export { hasUncommittedChanges as a, tagExists as c, getFilesChangedInCommit as i, getBranchCommits as n, listTags as o, getChangedFiles as r, pushWithTags as s, createTag as t };
139
+ export { getFilesChangedInCommit as a, pushWithTags as c, getCurrentBranch as i, tagExists as l, getBranchCommits as n, hasUncommittedChanges as o, getChangedFiles as r, listTags as s, createTag as t };
package/dist/index.d.mts CHANGED
@@ -193,10 +193,18 @@ declare class DependencyGraph {
193
193
  }
194
194
  //#endregion
195
195
  //#region src/core/bump-file.d.ts
196
+ interface ReadBumpFilesResult {
197
+ bumpFiles: BumpFile[];
198
+ errors: string[];
199
+ }
196
200
  /** Read all bump files from .bumpy/ directory, sorted by git creation order */
197
- declare function readBumpFiles(rootDir: string): Promise<BumpFile[]>;
201
+ declare function readBumpFiles(rootDir: string): Promise<ReadBumpFilesResult>;
202
+ interface BumpFileParseResult {
203
+ bumpFile: BumpFile | null;
204
+ errors: string[];
205
+ }
198
206
  /** Parse bump file content (for testing) */
199
- declare function parseBumpFile(content: string, id: string): BumpFile | null;
207
+ declare function parseBumpFile(content: string, id: string): BumpFileParseResult;
200
208
  /** Write a bump file */
201
209
  declare function writeBumpFile(rootDir: string, filename: string, releases: BumpFileRelease[], summary: string): Promise<string>;
202
210
  //#endregion
@@ -245,6 +253,8 @@ declare function prependToChangelog(existingContent: string, newEntry: string):
245
253
  interface GithubChangelogOptions {
246
254
  /** "owner/repo" — auto-detected from gh CLI if not provided */
247
255
  repo?: string;
256
+ /** Whether to include commit hash links in changelog entries (default: false) */
257
+ includeCommitLink?: boolean;
248
258
  /** Whether to include "Thanks @user" messages for contributors (default: true) */
249
259
  thankContributors?: boolean;
250
260
  /** GitHub usernames (without @) to skip "Thanks" messages for (e.g. internal team members) */
@@ -288,4 +298,4 @@ interface PublishResult {
288
298
  */
289
299
  declare function publishPackages(releasePlan: ReleasePlan, packages: Map<string, WorkspacePackage>, depGraph: DependencyGraph, config: BumpyConfig, rootDir: string, opts?: PublishOptions, catalogs?: CatalogMap, detectedPm?: PackageManager): Promise<PublishResult>;
290
300
  //#endregion
291
- export { BUMP_LEVELS, BumpFile, BumpFileRelease, BumpFileReleaseCascade, BumpFileReleaseSimple, BumpType, BumpTypeWithNone, BumpyConfig, type ChangelogContext, type ChangelogFormatter, DEFAULT_BUMP_RULES, DEFAULT_CONFIG, DEFAULT_PUBLISH_CONFIG, DEP_TYPES, DepType, DependencyBumpRule, DependencyGraph, DependentInfo, type GithubChangelogOptions, PackageConfig, PackageManager, PlannedRelease, PublishConfig, ReleasePlan, WorkspacePackage, applyReleasePlan, assembleReleasePlan, bumpLevel, bumpVersion, defaultFormatter, discoverPackages, findRoot, generateChangelogEntry, getBumpyDir, hasCascade, loadConfig, loadFormatter, matchGlob, maxBump, parseBumpFile, prependToChangelog, publishPackages, readBumpFiles, satisfies, stripProtocol, writeBumpFile };
301
+ export { BUMP_LEVELS, BumpFile, type BumpFileParseResult, BumpFileRelease, BumpFileReleaseCascade, BumpFileReleaseSimple, BumpType, BumpTypeWithNone, BumpyConfig, type ChangelogContext, type ChangelogFormatter, DEFAULT_BUMP_RULES, DEFAULT_CONFIG, DEFAULT_PUBLISH_CONFIG, DEP_TYPES, DepType, DependencyBumpRule, DependencyGraph, DependentInfo, type GithubChangelogOptions, PackageConfig, PackageManager, PlannedRelease, PublishConfig, type ReadBumpFilesResult, ReleasePlan, WorkspacePackage, applyReleasePlan, assembleReleasePlan, bumpLevel, bumpVersion, defaultFormatter, discoverPackages, findRoot, generateChangelogEntry, getBumpyDir, hasCascade, loadConfig, loadFormatter, matchGlob, maxBump, parseBumpFile, prependToChangelog, publishPackages, readBumpFiles, satisfies, stripProtocol, writeBumpFile };
package/dist/index.mjs CHANGED
@@ -1,9 +1,9 @@
1
- import { a as loadConfig, c as BUMP_LEVELS, d as DEFAULT_PUBLISH_CONFIG, f as DEP_TYPES, h as maxBump, l as DEFAULT_BUMP_RULES, m as hasCascade, n as findRoot, p as bumpLevel, r as getBumpyDir, s as matchGlob, u as DEFAULT_CONFIG } from "./config-B-Qg3DZH.mjs";
2
- import { t as discoverPackages } from "./workspace-DWXlwcH4.mjs";
1
+ import { a as loadConfig, c as BUMP_LEVELS, d as DEFAULT_PUBLISH_CONFIG, f as DEP_TYPES, h as maxBump, l as DEFAULT_BUMP_RULES, m as hasCascade, n as findRoot, p as bumpLevel, r as getBumpyDir, s as matchGlob, u as DEFAULT_CONFIG } from "./config-D7Umr-fT.mjs";
2
+ import { t as discoverPackages } from "./workspace-BHsAPUmC.mjs";
3
3
  import { t as DependencyGraph } from "./dep-graph-DiLeAhl9.mjs";
4
- import { i as writeBumpFile, n as parseBumpFile, r as readBumpFiles } from "./bump-file-DVqR3k67.mjs";
4
+ import { i as writeBumpFile, n as parseBumpFile, r as readBumpFiles } from "./bump-file-C3S_bzSf.mjs";
5
5
  import { n as satisfies, r as stripProtocol, t as bumpVersion } from "./semver-BJzWIuRz.mjs";
6
- import { t as assembleReleasePlan } from "./release-plan-JNir7bSM.mjs";
7
- import { a as prependToChangelog, i as loadFormatter, n as defaultFormatter, r as generateChangelogEntry, t as applyReleasePlan } from "./apply-release-plan-0kH62jhu.mjs";
8
- import { t as publishPackages } from "./publish-pipeline-CXuqce1N.mjs";
6
+ import { t as assembleReleasePlan } from "./release-plan-CNOuSI-d.mjs";
7
+ import { a as prependToChangelog, i as loadFormatter, n as defaultFormatter, r as generateChangelogEntry, t as applyReleasePlan } from "./apply-release-plan-B1Wwx3HG.mjs";
8
+ import { t as publishPackages } from "./publish-pipeline-DiwZZ5AF.mjs";
9
9
  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,13 +1,13 @@
1
1
  import { n as log, o as __toESM, r as require_picocolors } from "./logger-C2dEe5Su.mjs";
2
- import { a as readJson, d as writeText, i as listFiles, n as exists, o as readText, t as ensureDir, u as writeJson } from "./fs-DYR2XuFE.mjs";
3
- import { t as detectPackageManager } from "./package-manager-Clsmr-9r.mjs";
2
+ import { a as readJson, d as writeJson, f as writeText, i as listFiles, n as exists, s as readText, t as ensureDir } from "./fs-DnDogVn-.mjs";
3
+ import { t as detectPackageManager } from "./package-manager-ByJ0wKYh.mjs";
4
4
  import { t as run } from "./shell-CY7OD48z.mjs";
5
5
  import { c as ot, o as gt, s as mt, t as unwrap } from "./clack-C6bVkGxf.mjs";
6
6
  import { resolve } from "node:path";
7
7
  import { readdir, rename, rm } from "node:fs/promises";
8
8
  //#region ../../.bumpy/README.md
9
9
  var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
10
- var README_default = "# 🐸 Bumpy\n\nThis directory is used by [bumpy](https://bumpy.varlock.dev) to manage versioning and changelogs.\n\nBumpy is a modern versioning tool for JavaScript/TypeScript projects (monorepos and single packages). It uses **bump files** — small markdown files in this directory — to declare pending version changes. These files are consumed during the release process to compute version bumps, update changelogs, and publish packages.\n\n## How it works\n\n1. When you make a change that should trigger a release, create a bump file (typically one per PR)\n2. Bump files accumulate on your main branch until you're ready to release\n3. At release time, bumpy merges all pending bumps into a release plan, updates versions and changelogs, and publishes packages\n\n## Creating bump files\n\n### Interactive\n\n```bash\nbunx bumpy add\n```\n\n### Non-interactive (useful for AI-assisted development)\n\n```bash\nbunx bumpy add --packages \"package-name:minor,other-package:patch\" --message \"Description of changes\" --name \"my-change\"\n```\n\n### By hand\n\nCreate a `.md` file in this directory with YAML frontmatter mapping package names to bump levels (`major`, `minor`, or `patch`), and a markdown body for the changelog entry:\n\n```markdown\n---\n'package-name': minor\n---\n\nAdded a new feature.\n```\n\n### From conventional commits\n\n```bash\nbunx bumpy generate\n```\n\n### Empty bump files\n\nFor PRs that intentionally don't need a release (docs, CI, etc.):\n\n```bash\nbunx bumpy add --empty --name \"docs-update\"\n```\n\n## Keeping bump files up to date\n\nAs a PR evolves, make sure its bump file stays in sync. If the scope of changes grows (e.g., a patch becomes a new feature), update the bump level and description to match. Reviewers and AI assistants should treat the bump file as part of the PR — just like tests and docs.\n\n## Files in this directory\n\n- `_config.json` — bumpy configuration\n- `README.md` — this file\n- `*.md` (other than README.md) — pending bump files\n\n📖 Full documentation: https://bumpy.varlock.dev\n";
10
+ var README_default = "# 🐸 Bumpy\n\nThis directory is used by [bumpy](https://bumpy.varlock.dev) to manage versioning and changelogs.\n\nBumpy is a modern versioning tool for JavaScript/TypeScript projects (monorepos and single packages). It uses **bump files** — small markdown files in this directory — to declare pending version changes. These files are consumed during the release process to compute version bumps, update changelogs, and publish packages.\n\n## How it works\n\n1. When you make a change that should trigger a release, create a bump file (typically one per PR)\n2. Bump files accumulate on your main branch until you're ready to release\n3. At release time, bumpy merges all pending bumps into a release plan, updates versions and changelogs, and publishes packages\n\n## Creating bump files\n\n### Interactive\n\n```bash\nbunx bumpy add\n```\n\n### Non-interactive (useful for AI-assisted development)\n\n```bash\nbunx bumpy add --packages \"package-name:minor,other-package:patch\" --message \"Description of changes\" --name \"my-change\"\n```\n\n### By hand\n\nCreate a `.md` file in this directory with YAML frontmatter mapping package names to bump levels (`major`, `minor`, `patch`, or `none`), and a markdown body for the changelog entry:\n\n```markdown\n---\n'package-name': minor\n---\n\nAdded a new feature.\n```\n\n### From conventional commits\n\n```bash\nbunx bumpy generate\n```\n\n### Empty bump files\n\nFor PRs that intentionally don't need a release (docs, CI, etc.):\n\n```bash\nbunx bumpy add --empty --name \"docs-update\"\n```\n\n## Keeping bump files up to date\n\nAs a PR evolves, make sure its bump file stays in sync. If the scope of changes grows (e.g., a patch becomes a new feature), update the bump level and description to match. Reviewers and AI assistants should treat the bump file as part of the PR — just like tests and docs.\n\n## Files in this directory\n\n- `_config.json` — bumpy configuration\n- `README.md` — this file\n- `*.md` (other than README.md) — pending bump files\n\n📖 Full documentation: https://bumpy.varlock.dev\n";
11
11
  //#endregion
12
12
  //#region src/commands/init.ts
13
13
  const PM_RUNNER = {
@@ -1,4 +1,4 @@
1
- import { a as readJson, n as exists, o as readText } from "./fs-DYR2XuFE.mjs";
1
+ import { a as readJson, n as exists, s as readText } from "./fs-DnDogVn-.mjs";
2
2
  import { resolve } from "node:path";
3
3
  //#region ../../node_modules/.bun/js-yaml@4.1.1/node_modules/js-yaml/dist/js-yaml.mjs
4
4
  /*! js-yaml 4.1.1 https://github.com/nodeca/js-yaml @license MIT */