@varlock/bumpy 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/config-schema.json +327 -0
  2. package/dist/{add-CgCjs4d-.mjs → add-BmNL5VwL.mjs} +21 -11
  3. package/dist/{apply-release-plan-CczGWJTk.mjs → apply-release-plan-0kH62jhu.mjs} +12 -7
  4. package/dist/{bump-file-CCLXMLA8.mjs → bump-file-DVqR3k67.mjs} +24 -10
  5. package/dist/{changelog-github-Cd8uJHZI.mjs → changelog-github-DkACMj0j.mjs} +1 -1
  6. package/dist/check-BjWF6SJm.mjs +65 -0
  7. package/dist/{ci-Bhx--Tj6.mjs → ci-DY58ugIi.mjs} +35 -32
  8. package/dist/{ci-setup-qz4Y3v7T.mjs → ci-setup-BQwktQEe.mjs} +3 -3
  9. package/dist/cli.mjs +20 -27
  10. package/dist/commit-message-BwsowSds.mjs +23 -0
  11. package/dist/{config-XZWUL3ma.mjs → config-B-Qg3DZH.mjs} +4 -3
  12. package/dist/{generate-gYKTpvex.mjs → generate-DX46X-rW.mjs} +73 -64
  13. package/dist/{git-CGHVXXKw.mjs → git-YDedMddc.mjs} +54 -2
  14. package/dist/index.d.mts +13 -2
  15. package/dist/index.mjs +8 -8
  16. package/dist/init-DkTPs_WQ.mjs +196 -0
  17. package/dist/{js-yaml-DpZfOoD4.mjs → package-manager-Clsmr-9r.mjs} +79 -1
  18. package/dist/picomatch-DMmqYjgq.mjs +1870 -0
  19. package/dist/{publish-Cun-zQ1b.mjs → publish-CGB4TIKD.mjs} +10 -10
  20. package/dist/{publish-pipeline-BwBuKCIk.mjs → publish-pipeline-CXuqce1N.mjs} +4 -4
  21. package/dist/{release-plan-Bi5QNSEo.mjs → release-plan-JNir7bSM.mjs} +2 -2
  22. package/dist/{shell-Dj7JRD_q.mjs → shell-CY7OD48z.mjs} +20 -2
  23. package/dist/{status-CfE63ti5.mjs → status-EGYqULJg.mjs} +6 -6
  24. package/dist/{version-19vVt9dv.mjs → version-BcfidiVX.mjs} +13 -16
  25. package/dist/{workspace-C5ULTyUN.mjs → workspace-DWXlwcH4.mjs} +2 -2
  26. package/package.json +4 -1
  27. package/skills/add-change/SKILL.md +11 -3
  28. package/dist/check-BOoxpWqk.mjs +0 -51
  29. package/dist/init-lA9E5pEc.mjs +0 -22
  30. package/dist/migrate-DmOYgmfD.mjs +0 -121
  31. package/dist/package-manager-VCe10bjc.mjs +0 -80
  32. /package/dist/{clack-CDRCHrC-.mjs → clack-C6bVkGxf.mjs} +0 -0
  33. /package/dist/{dep-graph-E-9-eQ2J.mjs → dep-graph-DiLeAhl9.mjs} +0 -0
  34. /package/dist/{names-9VubBmL0.mjs → names-C-TuOPbd.mjs} +0 -0
  35. /package/dist/{semver-DfQyVLM_.mjs → semver-BJzWIuRz.mjs} +0 -0
@@ -1,13 +1,14 @@
1
1
  import { n as log, t as colorize } from "./logger-C2dEe5Su.mjs";
2
- import { a as loadConfig } from "./config-XZWUL3ma.mjs";
3
- import { t as detectPackageManager } from "./package-manager-VCe10bjc.mjs";
4
- import { n as discoverWorkspace } from "./workspace-C5ULTyUN.mjs";
5
- import { t as DependencyGraph } from "./dep-graph-E-9-eQ2J.mjs";
6
- import { n as runArgsAsync, o as tryRunArgs, t as runArgs } from "./shell-Dj7JRD_q.mjs";
7
- import { r as readBumpFiles } from "./bump-file-CCLXMLA8.mjs";
8
- import { t as assembleReleasePlan } from "./release-plan-Bi5QNSEo.mjs";
9
- import { n as getChangedFiles } from "./git-CGHVXXKw.mjs";
10
- import { t as randomName } from "./names-9VubBmL0.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";
5
+ import { t as DependencyGraph } from "./dep-graph-DiLeAhl9.mjs";
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";
10
+ import { t as randomName } from "./names-C-TuOPbd.mjs";
11
+ import { t as resolveCommitMessage } from "./commit-message-BwsowSds.mjs";
11
12
  import { createHash } from "node:crypto";
12
13
  //#region src/commands/ci.ts
13
14
  /**
@@ -86,9 +87,12 @@ async function ciCheckCommand(rootDir, opts) {
86
87
  const shouldComment = opts.comment ?? inCI;
87
88
  const prNumber = detectPrNumber();
88
89
  const pm = await detectPackageManager(rootDir);
89
- const changedFiles = getChangedFiles(rootDir, config.baseBranch);
90
- const prBumpFileIds = new Set(changedFiles.filter((f) => /^\.bumpy\/.*\.md$/.test(f) && !f.endsWith("README.md")).map((f) => f.replace(/^\.bumpy\//, "").replace(/\.md$/, "")));
91
- const prBumpFiles = allBumpFiles.filter((bf) => prBumpFileIds.has(bf.id));
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
+ }
92
96
  if (prBumpFiles.length === 0) {
93
97
  log.info("No bump files found in this PR.");
94
98
  if (shouldComment && prNumber) await postOrUpdatePrComment(prNumber, formatNoBumpFilesComment(detectPrBranch(rootDir), pm), rootDir, opts.patComments);
@@ -116,7 +120,7 @@ async function ciReleaseCommand(rootDir, opts) {
116
120
  const bumpFiles = await readBumpFiles(rootDir);
117
121
  if (bumpFiles.length === 0) {
118
122
  log.info("No pending bump files — checking for unpublished packages...");
119
- const { publishCommand } = await import("./publish-Cun-zQ1b.mjs");
123
+ const { publishCommand } = await import("./publish-CGB4TIKD.mjs");
120
124
  await publishCommand(rootDir, { tag: opts.tag });
121
125
  return;
122
126
  }
@@ -125,12 +129,12 @@ async function ciReleaseCommand(rootDir, opts) {
125
129
  log.info("Bump files found but no packages would be released.");
126
130
  return;
127
131
  }
128
- if (opts.mode === "auto-publish") await autoPublish(rootDir, config, opts.tag);
132
+ if (opts.mode === "auto-publish") await autoPublish(rootDir, config, plan, opts.tag);
129
133
  else await createVersionPr(rootDir, plan, config, new Map([...packages.values()].map((p) => [p.name, p.relativeDir])), opts.branch, opts.patPr);
130
134
  }
131
- async function autoPublish(rootDir, config, tag) {
135
+ async function autoPublish(rootDir, config, plan, tag) {
132
136
  log.step("Running bumpy version...");
133
- const { versionCommand } = await import("./version-19vVt9dv.mjs");
137
+ const { versionCommand } = await import("./version-BcfidiVX.mjs");
134
138
  await versionCommand(rootDir);
135
139
  log.step("Committing version changes...");
136
140
  runArgs([
@@ -146,9 +150,12 @@ async function autoPublish(rootDir, config, tag) {
146
150
  runArgs([
147
151
  "git",
148
152
  "commit",
149
- "-m",
150
- "Version packages"
151
- ], { cwd: rootDir });
153
+ "-F",
154
+ "-"
155
+ ], {
156
+ cwd: rootDir,
157
+ input: await resolveCommitMessage(config.versionCommitMessage, plan, rootDir)
158
+ });
152
159
  runArgs([
153
160
  "git",
154
161
  "push",
@@ -156,7 +163,7 @@ async function autoPublish(rootDir, config, tag) {
156
163
  ], { cwd: rootDir });
157
164
  }
158
165
  log.step("Running bumpy publish...");
159
- const { publishCommand } = await import("./publish-Cun-zQ1b.mjs");
166
+ const { publishCommand } = await import("./publish-CGB4TIKD.mjs");
160
167
  await publishCommand(rootDir, { tag });
161
168
  }
162
169
  /**
@@ -322,7 +329,7 @@ async function createVersionPr(rootDir, plan, config, packageDirs, branchName, p
322
329
  branch
323
330
  ], { cwd: rootDir });
324
331
  log.step("Running bumpy version...");
325
- const { versionCommand } = await import("./version-19vVt9dv.mjs");
332
+ const { versionCommand } = await import("./version-BcfidiVX.mjs");
326
333
  await versionCommand(rootDir);
327
334
  runArgs([
328
335
  "git",
@@ -349,11 +356,7 @@ async function createVersionPr(rootDir, plan, config, packageDirs, branchName, p
349
356
  "-"
350
357
  ], {
351
358
  cwd: rootDir,
352
- input: [
353
- "Version packages",
354
- "",
355
- ...plan.releases.map((r) => `${r.name}@${r.newVersion}`)
356
- ].join("\n")
359
+ input: await resolveCommitMessage(config.versionCommitMessage, plan, rootDir)
357
360
  });
358
361
  pushWithToken(rootDir, branch, config);
359
362
  const prBody = formatVersionPrBody(plan, config.versionPr.preamble, packageDirs);
@@ -373,7 +376,7 @@ async function createVersionPr(rootDir, plan, config, packageDirs, branchName, p
373
376
  cwd: rootDir,
374
377
  input: prBody
375
378
  }));
376
- log.success(`Updated PR #${validPr}`);
379
+ log.success(`🐸 Updated PR #${validPr}`);
377
380
  } else {
378
381
  log.step("Creating version PR...");
379
382
  const prTitle = config.versionPr.title;
@@ -393,7 +396,7 @@ async function createVersionPr(rootDir, plan, config, packageDirs, branchName, p
393
396
  cwd: rootDir,
394
397
  input: prBody
395
398
  }));
396
- log.success(`Created PR: ${result}`);
399
+ log.success(`🐸 Created PR: ${result}`);
397
400
  if (!patPr) pushWithToken(rootDir, branch, config);
398
401
  }
399
402
  runArgs([
@@ -428,7 +431,7 @@ function formatReleasePlanComment(plan, bumpFiles, prNumber, prBranch, pm, warni
428
431
  const repo = process.env.GITHUB_REPOSITORY;
429
432
  const lines = [];
430
433
  const preamble = [
431
- `<a href="https://github.com/dmno-dev/bumpy"><img src="${FROG_IMG_BASE}/frog-talking.png" alt="bumpy-frog" width="60" align="left" style="image-rendering: pixelated;" title="Hi! I'm bumpy!" /></a>`,
434
+ `<a href="https://bumpy.varlock.dev"><img src="${FROG_IMG_BASE}/frog-talking.png" alt="bumpy-frog" width="60" align="left" style="image-rendering: pixelated;" title="Hi! I'm bumpy!" /></a>`,
432
435
  "",
433
436
  "**The changes in this PR will be included in the next version bump.**",
434
437
  "<br clear=\"left\" />"
@@ -478,13 +481,13 @@ function formatReleasePlanComment(plan, bumpFiles, prNumber, prBranch, pm, warni
478
481
  if (addLink) lines.push(`[Click here if you want to add another bump file to this PR](${addLink})\n`);
479
482
  else lines.push(`To add another bump file, run \`${pmRunCommand(pm)} add\`\n`);
480
483
  lines.push("---");
481
- lines.push(`_This comment is maintained by [bumpy](https://github.com/dmno-dev/bumpy)._`);
484
+ lines.push(`_This comment is maintained by [bumpy](https://bumpy.varlock.dev)._`);
482
485
  return lines.join("\n");
483
486
  }
484
487
  function formatNoBumpFilesComment(prBranch, pm) {
485
488
  const runCmd = pmRunCommand(pm);
486
489
  const lines = [
487
- `<a href="https://github.com/dmno-dev/bumpy"><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>`,
490
+ `<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>`,
488
491
  "",
489
492
  "Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. **If these changes should result in a version bump, you need to add a bump file.**",
490
493
  "<br clear=\"left\" />\n",
@@ -499,7 +502,7 @@ function formatNoBumpFilesComment(prBranch, pm) {
499
502
  lines.push(`Or [click here to add a bump file](${addLink}) directly on GitHub.`);
500
503
  }
501
504
  lines.push("\n---");
502
- lines.push(`_This comment is maintained by [bumpy](https://github.com/dmno-dev/bumpy)._`);
505
+ lines.push(`_This comment is maintained by [bumpy](https://bumpy.varlock.dev)._`);
503
506
  return lines.join("\n");
504
507
  }
505
508
  function bumpSectionHeader(type) {
@@ -1,7 +1,7 @@
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-VCe10bjc.mjs";
3
- import { o as tryRunArgs } from "./shell-Dj7JRD_q.mjs";
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-CDRCHrC-.mjs";
2
+ import { t as detectPackageManager } from "./package-manager-Clsmr-9r.mjs";
3
+ import { s as tryRunArgs } from "./shell-CY7OD48z.mjs";
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
6
6
  var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
7
7
  const PAT_PERMISSIONS = [
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-XZWUL3ma.mjs";
3
+ import { n as findRoot } from "./config-B-Qg3DZH.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-lA9E5pEc.mjs");
29
- await initCommand(rootDir);
28
+ const { initCommand } = await import("./init-DkTPs_WQ.mjs");
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-CgCjs4d-.mjs");
34
+ const { addCommand } = await import("./add-BmNL5VwL.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-CfE63ti5.mjs");
45
+ const { statusCommand } = await import("./status-EGYqULJg.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-19vVt9dv.mjs");
58
- await versionCommand(rootDir);
57
+ const { versionCommand } = await import("./version-BcfidiVX.mjs");
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-gYKTpvex.mjs");
63
+ const { generateCommand } = await import("./generate-DX46X-rW.mjs");
64
64
  await generateCommand(rootDir, {
65
65
  from: flags.from,
66
66
  dryRun: flags["dry-run"] === true,
@@ -68,15 +68,9 @@ async function main() {
68
68
  });
69
69
  break;
70
70
  }
71
- case "migrate": {
72
- const rootDir = await findRoot();
73
- const { migrateCommand } = await import("./migrate-DmOYgmfD.mjs");
74
- await migrateCommand(rootDir, { force: flags.force === true });
75
- break;
76
- }
77
71
  case "check": {
78
72
  const rootDir = await findRoot();
79
- const { checkCommand } = await import("./check-BOoxpWqk.mjs");
73
+ const { checkCommand } = await import("./check-BjWF6SJm.mjs");
80
74
  await checkCommand(rootDir);
81
75
  break;
82
76
  }
@@ -85,14 +79,14 @@ async function main() {
85
79
  const subcommand = args[1];
86
80
  const ciFlags = parseFlags(args.slice(2));
87
81
  if (subcommand === "check") {
88
- const { ciCheckCommand } = await import("./ci-Bhx--Tj6.mjs");
82
+ const { ciCheckCommand } = await import("./ci-DY58ugIi.mjs");
89
83
  await ciCheckCommand(rootDir, {
90
84
  comment: ciFlags.comment !== void 0 ? ciFlags.comment === true : void 0,
91
85
  failOnMissing: ciFlags["fail-on-missing"] === true,
92
86
  patComments: ciFlags["pat-comments"] === true
93
87
  });
94
88
  } else if (subcommand === "release") {
95
- const { ciReleaseCommand } = await import("./ci-Bhx--Tj6.mjs");
89
+ const { ciReleaseCommand } = await import("./ci-DY58ugIi.mjs");
96
90
  await ciReleaseCommand(rootDir, {
97
91
  mode: ciFlags["auto-publish"] === true ? "auto-publish" : "version-pr",
98
92
  tag: ciFlags.tag,
@@ -100,7 +94,7 @@ async function main() {
100
94
  patPr: ciFlags["pat-pr"] === true
101
95
  });
102
96
  } else if (subcommand === "setup") {
103
- const { ciSetupCommand } = await import("./ci-setup-qz4Y3v7T.mjs");
97
+ const { ciSetupCommand } = await import("./ci-setup-BQwktQEe.mjs");
104
98
  await ciSetupCommand(rootDir);
105
99
  } else {
106
100
  log.error(`Unknown ci subcommand: ${subcommand}. Use "ci check", "ci release", or "ci setup".`);
@@ -110,7 +104,7 @@ async function main() {
110
104
  }
111
105
  case "publish": {
112
106
  const rootDir = await findRoot();
113
- const { publishCommand } = await import("./publish-Cun-zQ1b.mjs");
107
+ const { publishCommand } = await import("./publish-CGB4TIKD.mjs");
114
108
  await publishCommand(rootDir, {
115
109
  dryRun: flags["dry-run"] === true,
116
110
  tag: flags.tag,
@@ -134,7 +128,7 @@ async function main() {
134
128
  }
135
129
  case "--version":
136
130
  case "-v":
137
- console.log(`bumpy 1.0.0`);
131
+ console.log(`bumpy 1.1.0`);
138
132
  break;
139
133
  case "help":
140
134
  case "--help":
@@ -154,22 +148,21 @@ async function main() {
154
148
  }
155
149
  function printHelp() {
156
150
  console.log(`
157
- ${colorize(`🐸 bumpy v1.0.0`, "bold")} - Modern monorepo versioning
151
+ ${colorize(`🐸 bumpy v1.1.0`, "bold")} - Modern monorepo versioning
158
152
 
159
153
  Usage: bumpy <command> [options]
160
154
 
161
155
  Commands:
162
- init Initialize .bumpy/ directory
156
+ init [--force] Initialize .bumpy/ (migrates from .changeset/ if found)
163
157
  add Create a new bump file
164
- generate Generate bump file from conventional commits
158
+ generate Generate bump file from branch commits
165
159
  status Show pending releases
166
160
  check Verify changed packages have bump files (for pre-push hooks)
167
- version Apply bump files and bump versions
161
+ version [--commit] Apply bump files and bump versions
168
162
  publish Publish versioned packages
169
163
  ci check PR check — report pending releases, comment on PR
170
164
  ci release Release — create version PR or auto-publish
171
165
  ci setup Set up a token for triggering CI on version PRs
172
- migrate Migrate from .changeset/ to .bumpy/
173
166
  ai setup Install AI skill for creating bump files
174
167
 
175
168
  Add options:
@@ -179,7 +172,7 @@ function printHelp() {
179
172
  --empty Create an empty bump file
180
173
 
181
174
  Generate options:
182
- --from <ref> Git ref to scan from (default: last version tag)
175
+ --from <ref> Git ref to scan from (default: branch point from baseBranch)
183
176
  --dry-run Preview without creating a bump file
184
177
  --name <name> Bump file filename
185
178
 
@@ -208,7 +201,7 @@ function printHelp() {
208
201
  AI setup options:
209
202
  --target <tool> Target AI tool: opencode, cursor, codex
210
203
 
211
- ${colorize("https://github.com/dmno-dev/bumpy", "dim")}
204
+ ${colorize("https://bumpy.varlock.dev", "dim")}
212
205
  `);
213
206
  }
214
207
  main();
@@ -0,0 +1,23 @@
1
+ import { resolve } from "node:path";
2
+ //#region src/core/commit-message.ts
3
+ /** Build the default version commit message */
4
+ function defaultCommitMessage(plan) {
5
+ return [
6
+ "Version packages",
7
+ "",
8
+ ...plan.releases.map((r) => `${r.name}@${r.newVersion}`)
9
+ ].join("\n");
10
+ }
11
+ /** Resolve the commit message from config, falling back to the default */
12
+ async function resolveCommitMessage(config, plan, rootDir) {
13
+ if (!config) return defaultCommitMessage(plan);
14
+ if (config.startsWith("./") || config.startsWith("../")) {
15
+ const mod = await import(resolve(rootDir, config));
16
+ const fn = mod.default ?? mod;
17
+ if (typeof fn !== "function") throw new Error(`versionCommitMessage module "${config}" must export a function`);
18
+ return fn(plan);
19
+ }
20
+ return config;
21
+ }
22
+ //#endregion
23
+ export { resolveCommitMessage as t };
@@ -44,7 +44,8 @@ const DEFAULT_PUBLISH_CONFIG = {
44
44
  const DEFAULT_CONFIG = {
45
45
  baseBranch: "main",
46
46
  access: "public",
47
- commit: false,
47
+ versionCommitMessage: void 0,
48
+ changedFilePatterns: ["**"],
48
49
  changelog: "default",
49
50
  fixed: [],
50
51
  linked: [],
@@ -68,9 +69,9 @@ const DEFAULT_CONFIG = {
68
69
  title: "🐸 Versioned release",
69
70
  branch: "bumpy/version-packages",
70
71
  preamble: [
71
- `<a href="https://github.com/dmno-dev/bumpy"><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>`,
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>`,
72
73
  "",
73
- `This PR was created and will be kept in sync by [bumpy](https://github.com/dmno-dev/bumpy) 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 .bumpy bump files. Merge it when you are ready to release the packages listed below:`,
74
75
  "<br clear=\"left\" />"
75
76
  ].join("\n")
76
77
  }
@@ -1,10 +1,12 @@
1
1
  import { n as log, t as colorize } from "./logger-C2dEe5Su.mjs";
2
2
  import { t as ensureDir } from "./fs-DYR2XuFE.mjs";
3
- import { a as loadConfig, r as getBumpyDir } from "./config-XZWUL3ma.mjs";
4
- import { t as discoverPackages } from "./workspace-C5ULTyUN.mjs";
5
- import { o as tryRunArgs } from "./shell-Dj7JRD_q.mjs";
6
- import { i as writeBumpFile } from "./bump-file-CCLXMLA8.mjs";
7
- import { n as slugify, t as randomName } from "./names-9VubBmL0.mjs";
3
+ import { a as loadConfig, r as getBumpyDir } from "./config-B-Qg3DZH.mjs";
4
+ import { t as discoverPackages } from "./workspace-DWXlwcH4.mjs";
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";
8
+ import { n as slugify, t as randomName } from "./names-C-TuOPbd.mjs";
9
+ import { relative } from "node:path";
8
10
  //#region src/commands/generate.ts
9
11
  const BUMP_MAP = {
10
12
  feat: "minor",
@@ -21,57 +23,62 @@ const BUMP_MAP = {
21
23
  async function generateCommand(rootDir, opts) {
22
24
  const config = await loadConfig(rootDir);
23
25
  const packages = await discoverPackages(rootDir, config);
24
- const from = opts.from || findLastVersionTag(rootDir);
25
- if (!from) {
26
- log.error("Could not detect last version tag. Use --from <ref> to specify.");
27
- process.exit(1);
28
- }
29
- log.step(`Scanning commits from ${colorize(from, "cyan")}...`);
30
- const rawLog = tryRunArgs([
31
- "git",
32
- "log",
33
- `${from}..HEAD`,
34
- "--format=%H%n%s%n%b%n---END---"
35
- ], { cwd: rootDir });
36
- if (!rawLog) {
37
- log.info("No commits found since " + from);
38
- return;
26
+ let commits;
27
+ if (opts.from) {
28
+ log.step(`Scanning commits from ${colorize(opts.from, "cyan")}...`);
29
+ const rawLog = tryRunArgs([
30
+ "git",
31
+ "log",
32
+ `${opts.from}..HEAD`,
33
+ "--format=%H%n%s%n%b%n---END---"
34
+ ], { cwd: rootDir });
35
+ if (!rawLog) {
36
+ log.info("No commits found since " + opts.from);
37
+ return;
38
+ }
39
+ commits = parseGitLog(rawLog);
40
+ } else {
41
+ log.step(`Scanning commits on this branch (vs ${colorize(config.baseBranch, "cyan")})...`);
42
+ commits = getBranchCommits(rootDir, config.baseBranch);
39
43
  }
40
- const conventional = parseGitLog(rawLog).map(parseConventionalCommit).filter((c) => c !== null);
41
- if (conventional.length === 0) {
42
- log.info("No conventional commits found. Commits must follow the format: type(scope): description");
44
+ if (commits.length === 0) {
45
+ log.info("No commits found on this branch.");
43
46
  return;
44
47
  }
45
- log.dim(` Found ${conventional.length} conventional commit(s)`);
48
+ log.dim(` Found ${commits.length} commit(s)`);
46
49
  const scopeMap = buildScopeMap(packages, config);
47
50
  const releaseMap = /* @__PURE__ */ new Map();
48
- for (const commit of conventional) {
49
- const bump = commit.breaking ? "major" : BUMP_MAP[commit.type] || "patch";
50
- let pkgNames = [];
51
- if (commit.scope) {
52
- const resolved = resolveScope(commit.scope, scopeMap, packages);
53
- if (resolved.length > 0) pkgNames = resolved;
54
- else {
55
- log.dim(` Skipping: unknown scope "${commit.scope}" in: ${commit.description}`);
51
+ let ccCount = 0;
52
+ let fileBasedCount = 0;
53
+ for (const commit of commits) {
54
+ const cc = parseConventionalCommit(commit);
55
+ if (cc) {
56
+ ccCount++;
57
+ const bump = cc.breaking ? "major" : BUMP_MAP[cc.type] || "patch";
58
+ let pkgNames = [];
59
+ if (cc.scope) {
60
+ const resolved = resolveScope(cc.scope, scopeMap, packages);
61
+ if (resolved.length > 0) pkgNames = resolved;
62
+ }
63
+ if (pkgNames.length > 0) {
64
+ for (const name of pkgNames) mergeRelease(releaseMap, name, bump, cc.description);
56
65
  continue;
57
66
  }
67
+ const touchedPkgs = mapFilesToPackages(getFilesChangedInCommit(commit.hash, { cwd: rootDir }), packages, rootDir);
68
+ if (touchedPkgs.length > 0) for (const name of touchedPkgs) mergeRelease(releaseMap, name, bump, cc.description);
69
+ else log.dim(` Skipping CC (no matching packages): ${cc.type}: ${cc.description}`);
58
70
  } else {
59
- log.dim(` Skipping (no scope): ${commit.type}: ${commit.description}`);
60
- continue;
61
- }
62
- for (const name of pkgNames) {
63
- const existing = releaseMap.get(name);
64
- if (existing) {
65
- if (bumpPriority(bump) > bumpPriority(existing.type)) existing.type = bump;
66
- existing.messages.push(commit.description);
67
- } else releaseMap.set(name, {
68
- type: bump,
69
- messages: [commit.description]
70
- });
71
+ const touchedPkgs = mapFilesToPackages(getFilesChangedInCommit(commit.hash, { cwd: rootDir }), packages, rootDir);
72
+ if (touchedPkgs.length > 0) {
73
+ fileBasedCount++;
74
+ for (const name of touchedPkgs) mergeRelease(releaseMap, name, "patch", commit.subject);
75
+ } else log.dim(` Skipping (no matching packages): ${commit.subject}`);
71
76
  }
72
77
  }
78
+ if (ccCount > 0) log.dim(` ${ccCount} conventional commit(s)`);
79
+ if (fileBasedCount > 0) log.dim(` ${fileBasedCount} commit(s) detected via changed files`);
73
80
  if (releaseMap.size === 0) {
74
- log.info("No package bumps detected from conventional commits.");
81
+ log.info("No package bumps detected from commits.");
75
82
  return;
76
83
  }
77
84
  const releases = [];
@@ -94,9 +101,29 @@ async function generateCommand(rootDir, opts) {
94
101
  await ensureDir(getBumpyDir(rootDir));
95
102
  const filename = opts.name ? slugify(opts.name) : randomName();
96
103
  await writeBumpFile(rootDir, filename, releases, summaryLines.join("\n"));
97
- log.success(`Created bump file: .bumpy/${filename}.md`);
104
+ log.success(`🐸 Created bump file: .bumpy/${filename}.md`);
98
105
  for (const r of releases) log.dim(` ${r.name}: ${r.type}`);
99
106
  }
107
+ /** Merge a bump into the release map, keeping the highest bump level */
108
+ function mergeRelease(releaseMap, name, bump, message) {
109
+ const existing = releaseMap.get(name);
110
+ if (existing) {
111
+ if (bumpPriority(bump) > bumpPriority(existing.type)) existing.type = bump;
112
+ existing.messages.push(message);
113
+ } else releaseMap.set(name, {
114
+ type: bump,
115
+ messages: [message]
116
+ });
117
+ }
118
+ /** Map file paths to package names based on directory containment */
119
+ function mapFilesToPackages(files, packages, rootDir) {
120
+ const matched = /* @__PURE__ */ new Set();
121
+ for (const file of files) for (const [name, pkg] of packages) {
122
+ const pkgRelDir = relative(rootDir, pkg.dir);
123
+ if (file.startsWith(pkgRelDir + "/")) matched.add(name);
124
+ }
125
+ return [...matched];
126
+ }
100
127
  /** Parse raw git log output into individual commits */
101
128
  function parseGitLog(raw) {
102
129
  const commits = [];
@@ -155,23 +182,5 @@ function resolveScope(scope, scopeMap, packages) {
155
182
  function bumpPriority(type) {
156
183
  return type === "major" ? 2 : type === "minor" ? 1 : 0;
157
184
  }
158
- /** Find the most recent version tag in the repo */
159
- function findLastVersionTag(rootDir) {
160
- return tryRunArgs([
161
- "git",
162
- "describe",
163
- "--tags",
164
- "--abbrev=0",
165
- "--match",
166
- "v*"
167
- ], { cwd: rootDir }) || tryRunArgs([
168
- "git",
169
- "describe",
170
- "--tags",
171
- "--abbrev=0",
172
- "--match",
173
- "*@*"
174
- ], { cwd: rootDir }) || null;
175
- }
176
185
  //#endregion
177
186
  export { generateCommand };
@@ -1,4 +1,4 @@
1
- import { o as tryRunArgs, t as runArgs } from "./shell-Dj7JRD_q.mjs";
1
+ import { n as runArgs, s as tryRunArgs } from "./shell-CY7OD48z.mjs";
2
2
  //#region src/core/git.ts
3
3
  /** Create a git tag */
4
4
  function createTag(tag, opts) {
@@ -63,6 +63,58 @@ function getChangedFiles(rootDir, baseBranch) {
63
63
  if (!diff) return [];
64
64
  return diff.split("\n").filter(Boolean);
65
65
  }
66
+ /** Get commits on the current branch since it diverged from baseBranch */
67
+ function getBranchCommits(rootDir, baseBranch) {
68
+ if (!tryRunArgs([
69
+ "git",
70
+ "rev-parse",
71
+ "--verify",
72
+ `origin/${baseBranch}`
73
+ ], { cwd: rootDir })) tryRunArgs([
74
+ "git",
75
+ "fetch",
76
+ "origin",
77
+ baseBranch,
78
+ "--depth=1"
79
+ ], { cwd: rootDir });
80
+ const rawLog = tryRunArgs([
81
+ "git",
82
+ "log",
83
+ `${tryRunArgs([
84
+ "git",
85
+ "merge-base",
86
+ "HEAD",
87
+ `origin/${baseBranch}`
88
+ ], { cwd: rootDir }) || `origin/${baseBranch}`}..HEAD`,
89
+ "--format=%H%n%s%n%b%n---END---"
90
+ ], { cwd: rootDir });
91
+ if (!rawLog) return [];
92
+ const commits = [];
93
+ const entries = rawLog.split("---END---").filter((e) => e.trim());
94
+ for (const entry of entries) {
95
+ const lines = entry.trim().split("\n");
96
+ if (lines.length < 2) continue;
97
+ commits.push({
98
+ hash: lines[0].trim(),
99
+ subject: lines[1].trim(),
100
+ body: lines.slice(2).join("\n").trim()
101
+ });
102
+ }
103
+ return commits;
104
+ }
105
+ /** Get files changed in a specific commit */
106
+ function getFilesChangedInCommit(hash, opts) {
107
+ const result = tryRunArgs([
108
+ "git",
109
+ "diff-tree",
110
+ "--no-commit-id",
111
+ "--name-only",
112
+ "-r",
113
+ hash
114
+ ], opts);
115
+ if (!result) return [];
116
+ return result.split("\n").filter(Boolean);
117
+ }
66
118
  /** Get all tags matching a pattern */
67
119
  function listTags(pattern, opts) {
68
120
  const result = tryRunArgs([
@@ -75,4 +127,4 @@ function listTags(pattern, opts) {
75
127
  return result.split("\n").filter(Boolean);
76
128
  }
77
129
  //#endregion
78
- export { pushWithTags as a, listTags as i, getChangedFiles as n, tagExists as o, hasUncommittedChanges as r, createTag as t };
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 };
package/dist/index.d.mts CHANGED
@@ -32,10 +32,19 @@ interface PublishConfig {
32
32
  interface BumpyConfig {
33
33
  baseBranch: string;
34
34
  access: 'public' | 'restricted';
35
- commit: boolean;
36
- changelog: string | [string, Record<string, unknown>];
35
+ /**
36
+ * Customize the commit message used when versioning.
37
+ * A string starting with "./" is treated as a path to a module that exports
38
+ * a function receiving the release plan and returning a message string.
39
+ * Any other string is used as a static commit message.
40
+ * Omit to use the default: "Version packages\n\npkg@version..."
41
+ */
42
+ versionCommitMessage?: string;
43
+ changelog: false | string | [string, Record<string, unknown>];
37
44
  fixed: string[][];
38
45
  linked: string[][];
46
+ /** Glob patterns to filter which changed files count toward marking a package as changed */
47
+ changedFilePatterns: string[];
39
48
  /** Package names/globs to exclude from version management */
40
49
  ignore: string[];
41
50
  /** Package names/globs to explicitly include (overrides private + ignore) */
@@ -90,6 +99,8 @@ interface PackageConfig {
90
99
  skipNpmPublish?: boolean;
91
100
  /** Command to check if a version is already published. Should output the published version string. */
92
101
  checkPublished?: string;
102
+ /** Glob patterns to filter which changed files count toward marking this package as changed */
103
+ changedFilePatterns?: string[];
93
104
  dependencyBumpRules?: Partial<Record<DepType, DependencyBumpRule | false>>;
94
105
  cascadeTo?: Record<string, DependencyBumpRule>;
95
106
  }