@varlock/bumpy 1.0.0 → 1.2.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 (38) hide show
  1. package/README.md +256 -0
  2. package/config-schema.json +327 -0
  3. package/dist/{add-CgCjs4d-.mjs → add-yP81c9_q.mjs} +23 -13
  4. package/dist/{ai-sMYUf3lP.mjs → ai-STKnq09z.mjs} +1 -1
  5. package/dist/{apply-release-plan-CczGWJTk.mjs → apply-release-plan-CPzu6JcF.mjs} +12 -7
  6. package/dist/{bump-file-CCLXMLA8.mjs → bump-file-Br2bTaWp.mjs} +36 -10
  7. package/dist/{changelog-github-Cd8uJHZI.mjs → changelog-github-DkACMj0j.mjs} +1 -1
  8. package/dist/check-D_0exKi6.mjs +87 -0
  9. package/dist/{ci-Bhx--Tj6.mjs → ci-CvaikKX1.mjs} +48 -33
  10. package/dist/{ci-setup-qz4Y3v7T.mjs → ci-setup-CARJFhcE.mjs} +3 -3
  11. package/dist/cli.mjs +31 -31
  12. package/dist/commit-message-BwsowSds.mjs +23 -0
  13. package/dist/{config-XZWUL3ma.mjs → config-D7Umr-fT.mjs} +6 -5
  14. package/dist/{fs-DYR2XuFE.mjs → fs-DnDogVn-.mjs} +16 -1
  15. package/dist/{generate-gYKTpvex.mjs → generate-BOLrTYWR.mjs} +74 -65
  16. package/dist/{git-CGHVXXKw.mjs → git-YDedMddc.mjs} +54 -2
  17. package/dist/index.d.mts +13 -2
  18. package/dist/index.mjs +8 -8
  19. package/dist/init-DJhMaceS.mjs +196 -0
  20. package/dist/{js-yaml-DpZfOoD4.mjs → package-manager-ByJ0wKYh.mjs} +79 -1
  21. package/dist/picomatch-DMmqYjgq.mjs +1870 -0
  22. package/dist/{publish-Cun-zQ1b.mjs → publish-CPZwqyWh.mjs} +10 -10
  23. package/dist/{publish-pipeline-BwBuKCIk.mjs → publish-pipeline-BFt96o_h.mjs} +5 -5
  24. package/dist/{release-plan-Bi5QNSEo.mjs → release-plan-CNOuSI-d.mjs} +2 -2
  25. package/dist/{shell-Dj7JRD_q.mjs → shell-CY7OD48z.mjs} +20 -2
  26. package/dist/{status-CfE63ti5.mjs → status-skGX8uU7.mjs} +6 -6
  27. package/dist/{version-19vVt9dv.mjs → version-CnXcbqi1.mjs} +13 -16
  28. package/dist/{workspace-C5ULTyUN.mjs → workspace-BHsAPUmC.mjs} +3 -3
  29. package/package.json +5 -1
  30. package/skills/add-change/SKILL.md +11 -3
  31. package/dist/check-BOoxpWqk.mjs +0 -51
  32. package/dist/init-lA9E5pEc.mjs +0 -22
  33. package/dist/migrate-DmOYgmfD.mjs +0 -121
  34. package/dist/package-manager-VCe10bjc.mjs +0 -80
  35. /package/dist/{clack-CDRCHrC-.mjs → clack-C6bVkGxf.mjs} +0 -0
  36. /package/dist/{dep-graph-E-9-eQ2J.mjs → dep-graph-DiLeAhl9.mjs} +0 -0
  37. /package/dist/{names-9VubBmL0.mjs → names-C-TuOPbd.mjs} +0 -0
  38. /package/dist/{semver-DfQyVLM_.mjs → semver-BJzWIuRz.mjs} +0 -0
@@ -1,6 +1,6 @@
1
1
  import { n as log } from "./logger-C2dEe5Su.mjs";
2
- import { a as readJson, c as updateJsonFields, d as writeText, l as updateJsonNestedField, n as exists, o as readText } from "./fs-DYR2XuFE.mjs";
3
- import { t as deleteBumpFiles } from "./bump-file-CCLXMLA8.mjs";
2
+ 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";
3
+ import { r as getBumpyDir } from "./config-D7Umr-fT.mjs";
4
4
  import { relative, resolve } from "node:path";
5
5
  import { realpathSync } from "node:fs";
6
6
  //#region src/core/changelog.ts
@@ -28,7 +28,7 @@ const defaultFormatter = (ctx) => {
28
28
  const BUILTIN_FORMATTERS = {
29
29
  default: defaultFormatter,
30
30
  github: async () => {
31
- const { createGithubFormatter } = await import("./changelog-github-Cd8uJHZI.mjs");
31
+ const { createGithubFormatter } = await import("./changelog-github-DkACMj0j.mjs");
32
32
  return createGithubFormatter();
33
33
  }
34
34
  };
@@ -39,7 +39,7 @@ const BUILTIN_FORMATTERS = {
39
39
  async function loadFormatter(changelog, rootDir) {
40
40
  const [name, options] = Array.isArray(changelog) ? changelog : [changelog, {}];
41
41
  if (name === "github") {
42
- const { createGithubFormatter } = await import("./changelog-github-Cd8uJHZI.mjs");
42
+ const { createGithubFormatter } = await import("./changelog-github-DkACMj0j.mjs");
43
43
  return createGithubFormatter(options);
44
44
  }
45
45
  if (typeof name === "string" && BUILTIN_FORMATTERS[name]) {
@@ -95,7 +95,7 @@ function prependToChangelog(existingContent, newEntry) {
95
95
  /** Apply the release plan: bump versions, update changelogs, delete bump files */
96
96
  async function applyReleasePlan(releasePlan, packages, rootDir, config) {
97
97
  const releaseMap = new Map(releasePlan.releases.map((r) => [r.name, r]));
98
- const formatter = await loadFormatter(config.changelog, rootDir);
98
+ const formatter = config.changelog !== false ? await loadFormatter(config.changelog, rootDir) : null;
99
99
  for (const release of releasePlan.releases) {
100
100
  const pkgJsonPath = resolve(packages.get(release.name).dir, "package.json");
101
101
  const pkgJson = await readJson(pkgJsonPath);
@@ -115,14 +115,19 @@ async function applyReleasePlan(releasePlan, packages, rootDir, config) {
115
115
  }
116
116
  }
117
117
  }
118
- for (const release of releasePlan.releases) {
118
+ if (formatter) for (const release of releasePlan.releases) {
119
119
  const changelogPath = resolve(packages.get(release.name).dir, "CHANGELOG.md");
120
120
  const entry = await generateChangelogEntry(release, releasePlan.bumpFiles, formatter);
121
121
  let existingContent = "";
122
122
  if (await exists(changelogPath)) existingContent = await readText(changelogPath);
123
123
  await writeText(changelogPath, prependToChangelog(existingContent, entry));
124
124
  }
125
- await deleteBumpFiles(rootDir, releasePlan.bumpFiles.map((bf) => bf.id));
125
+ const bumpyDir = getBumpyDir(rootDir);
126
+ const allBumpFiles = await listFiles(bumpyDir, ".md");
127
+ for (const file of allBumpFiles) {
128
+ if (file === "README.md") continue;
129
+ await removeFile(resolve(bumpyDir, file));
130
+ }
126
131
  }
127
132
  /** Update a version range to include a new version, preserving the range prefix */
128
133
  function updateRange(range, newVersion) {
@@ -1,9 +1,10 @@
1
1
  import { n as log } from "./logger-C2dEe5Su.mjs";
2
- import { d as writeText, i as listFiles, o as readText, s as removeFile } from "./fs-DYR2XuFE.mjs";
3
- import { r as getBumpyDir } from "./config-XZWUL3ma.mjs";
4
- import { t as jsYaml } from "./js-yaml-DpZfOoD4.mjs";
5
- import { o as tryRunArgs } from "./shell-Dj7JRD_q.mjs";
2
+ import { f as writeText, i as listFiles, s as readText } from "./fs-DnDogVn-.mjs";
3
+ import { r as getBumpyDir } from "./config-D7Umr-fT.mjs";
4
+ import { i as jsYaml } from "./package-manager-ByJ0wKYh.mjs";
5
+ import { s as tryRunArgs } from "./shell-CY7OD48z.mjs";
6
6
  import { resolve } from "node:path";
7
+ import { existsSync } from "node:fs";
7
8
  //#region src/core/bump-file.ts
8
9
  const VALID_BUMP_TYPES = new Set([
9
10
  "major",
@@ -131,13 +132,38 @@ async function writeBumpFile(rootDir, filename, releases, summary) {
131
132
  }).trim()}\n---\n\n${summary}\n`);
132
133
  return filePath;
133
134
  }
134
- /** Delete consumed bump files */
135
- async function deleteBumpFiles(rootDir, ids) {
136
- const dir = getBumpyDir(rootDir);
137
- for (const id of ids) await removeFile(resolve(dir, `${id}.md`));
138
- }
139
135
  function fileToId(filePath) {
140
136
  return filePath.split("/").pop().replace(/\.md$/, "");
141
137
  }
138
+ /**
139
+ * Given a list of changed file paths (relative to root), extract the IDs
140
+ * of bump files that were added/modified. Shared by `check` and `ci check`.
141
+ */
142
+ function extractBumpFileIdsFromChangedFiles(changedFiles) {
143
+ return new Set(changedFiles.filter((f) => /^\.bumpy\/.*\.md$/.test(f) && !f.endsWith("README.md")).map((f) => f.replace(/^\.bumpy\//, "").replace(/\.md$/, "")));
144
+ }
145
+ /**
146
+ * Filter bump files to only those added/modified on the current branch.
147
+ * Also detects empty bump files (no releases) that still exist on disk,
148
+ * which signal intentionally no releases needed.
149
+ */
150
+ function filterBranchBumpFiles(allBumpFiles, changedFiles, rootDir) {
151
+ const branchBumpFileIds = extractBumpFileIdsFromChangedFiles(changedFiles);
152
+ const branchBumpFiles = allBumpFiles.filter((bf) => branchBumpFileIds.has(bf.id));
153
+ let hasEmptyBumpFile = false;
154
+ if (rootDir) {
155
+ const parsedIds = new Set(branchBumpFiles.map((bf) => bf.id));
156
+ const bumpyDir = getBumpyDir(rootDir);
157
+ for (const id of branchBumpFileIds) if (!parsedIds.has(id) && existsSync(resolve(bumpyDir, `${id}.md`))) {
158
+ hasEmptyBumpFile = true;
159
+ break;
160
+ }
161
+ }
162
+ return {
163
+ branchBumpFiles,
164
+ branchBumpFileIds,
165
+ hasEmptyBumpFile
166
+ };
167
+ }
142
168
  //#endregion
143
- export { writeBumpFile as i, parseBumpFile as n, readBumpFiles as r, deleteBumpFiles as t };
169
+ export { writeBumpFile as i, parseBumpFile as n, readBumpFiles as r, filterBranchBumpFiles as t };
@@ -1,4 +1,4 @@
1
- import { o as tryRunArgs } from "./shell-Dj7JRD_q.mjs";
1
+ import { s as tryRunArgs } from "./shell-CY7OD48z.mjs";
2
2
  //#region src/core/changelog-github.ts
3
3
  /**
4
4
  * GitHub-enhanced changelog formatter.
@@ -0,0 +1,87 @@
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-D7Umr-fT.mjs";
3
+ import { n as discoverWorkspace } from "./workspace-BHsAPUmC.mjs";
4
+ import { r as readBumpFiles, t as filterBranchBumpFiles } from "./bump-file-Br2bTaWp.mjs";
5
+ import { r as getChangedFiles } from "./git-YDedMddc.mjs";
6
+ import { t as require_picomatch } from "./picomatch-DMmqYjgq.mjs";
7
+ import { relative } from "node:path";
8
+ //#region src/commands/check.ts
9
+ var import_picomatch = /* @__PURE__ */ __toESM(require_picomatch(), 1);
10
+ /**
11
+ * Local check: detect which packages have changed on this branch
12
+ * and verify they have corresponding bump files.
13
+ * Designed for pre-push hooks — no GitHub API needed.
14
+ *
15
+ * Default: at least one bump file must exist, uncovered packages are warned.
16
+ * --strict: every changed package must be covered.
17
+ * --no-fail: warn only, never exit 1.
18
+ */
19
+ async function checkCommand(rootDir, opts = {}) {
20
+ const config = await loadConfig(rootDir);
21
+ const { packages } = await discoverWorkspace(rootDir, config);
22
+ const baseBranch = config.baseBranch;
23
+ const changedFiles = getChangedFiles(rootDir, baseBranch);
24
+ if (changedFiles.length === 0) {
25
+ log.info("No changed files detected.");
26
+ return;
27
+ }
28
+ const { branchBumpFiles, hasEmptyBumpFile } = filterBranchBumpFiles(await readBumpFiles(rootDir), changedFiles, rootDir);
29
+ if (hasEmptyBumpFile) {
30
+ log.success("Empty bump file found — no releases needed.");
31
+ return;
32
+ }
33
+ const coveredPackages = /* @__PURE__ */ new Set();
34
+ for (const bf of branchBumpFiles) for (const release of bf.releases) coveredPackages.add(release.name);
35
+ const changedPackages = await findChangedPackages(changedFiles, packages, rootDir, config);
36
+ if (changedPackages.length === 0) {
37
+ log.info("No managed packages have changed.");
38
+ return;
39
+ }
40
+ const willFailNoBump = !opts.noFail;
41
+ if (branchBumpFiles.length === 0) {
42
+ (willFailNoBump ? log.error : log.warn)(`${changedPackages.length} changed package(s) missing bump files:\n`);
43
+ for (const name of changedPackages) console.log(` ${colorize(name, "yellow")}`);
44
+ console.log();
45
+ log.dim("Run `bumpy add` to create a bump file, or `bumpy add --empty` if no release is needed.");
46
+ log.dim("To adjust which files trigger change detection, set `changedFilePatterns` in .bumpy/_config.json.");
47
+ if (willFailNoBump) process.exit(1);
48
+ return;
49
+ }
50
+ const missing = changedPackages.filter((name) => !coveredPackages.has(name));
51
+ if (missing.length === 0) {
52
+ log.success(`🐸 All ${changedPackages.length} changed package(s) have bump files.`);
53
+ return;
54
+ }
55
+ const willFailUncovered = opts.strict && !opts.noFail;
56
+ (willFailUncovered ? log.error : log.warn)(`${missing.length} changed package(s) missing bump files:\n`);
57
+ for (const name of missing) console.log(` ${colorize(name, "yellow")}`);
58
+ if (branchBumpFiles.length > 0) {
59
+ console.log();
60
+ log.dim(`Existing bump files on this branch:`);
61
+ for (const bf of branchBumpFiles) log.dim(` ${getBumpyDir(rootDir)}/${bf.id}.md`);
62
+ }
63
+ console.log();
64
+ if (opts.strict) log.dim("Run `bumpy add` to create a bump file. Use bump type `none` for packages that changed but don't need a release.");
65
+ else log.dim("Run `bumpy add` to create a bump file, or `bumpy add --empty` if no release is needed.");
66
+ log.dim("To adjust which files trigger change detection, set `changedFilePatterns` in .bumpy/_config.json.");
67
+ if (willFailUncovered) process.exit(1);
68
+ }
69
+ /** Map changed files to the packages they belong to */
70
+ async function findChangedPackages(changedFiles, packages, rootDir, config) {
71
+ const changed = /* @__PURE__ */ new Set();
72
+ const matchers = /* @__PURE__ */ new Map();
73
+ for (const [name, pkg] of packages) {
74
+ const patterns = (await loadPackageConfig(pkg.dir, config, name)).changedFilePatterns ?? config.changedFilePatterns;
75
+ matchers.set(name, (0, import_picomatch.default)(patterns));
76
+ }
77
+ for (const file of changedFiles) for (const [name, pkg] of packages) {
78
+ const pkgRelDir = relative(rootDir, pkg.dir);
79
+ if (file.startsWith(pkgRelDir + "/")) {
80
+ const relToPackage = file.slice(pkgRelDir.length + 1);
81
+ if (matchers.get(name)(relToPackage)) changed.add(name);
82
+ }
83
+ }
84
+ return [...changed];
85
+ }
86
+ //#endregion
87
+ export { checkCommand, findChangedPackages };
@@ -1,13 +1,15 @@
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-D7Umr-fT.mjs";
3
+ import { t as detectPackageManager } from "./package-manager-ByJ0wKYh.mjs";
4
+ import { n as discoverWorkspace } from "./workspace-BHsAPUmC.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-Br2bTaWp.mjs";
8
+ import { t as assembleReleasePlan } from "./release-plan-CNOuSI-d.mjs";
9
+ import { r as getChangedFiles } from "./git-YDedMddc.mjs";
10
+ import { t as randomName } from "./names-C-TuOPbd.mjs";
11
+ import { findChangedPackages } from "./check-D_0exKi6.mjs";
12
+ import { t as resolveCommitMessage } from "./commit-message-BwsowSds.mjs";
11
13
  import { createHash } from "node:crypto";
12
14
  //#region src/commands/ci.ts
13
15
  /**
@@ -87,12 +89,19 @@ async function ciCheckCommand(rootDir, opts) {
87
89
  const prNumber = detectPrNumber();
88
90
  const pm = await detectPackageManager(rootDir);
89
91
  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));
92
+ const { branchBumpFiles: prBumpFiles, hasEmptyBumpFile } = filterBranchBumpFiles(allBumpFiles, changedFiles, rootDir);
93
+ if (hasEmptyBumpFile) {
94
+ log.success("Empty bump file found — no releases needed.");
95
+ if (shouldComment && prNumber) await postOrUpdatePrComment(prNumber, formatNoBumpFilesComment(detectPrBranch(rootDir), pm), rootDir, opts.patComments);
96
+ return;
97
+ }
92
98
  if (prBumpFiles.length === 0) {
93
- log.info("No bump files found in this PR.");
99
+ const willFail = !opts.noFail;
100
+ const msg = "No bump files found in this PR.";
101
+ if (willFail) log.error(msg);
102
+ else log.warn(msg);
94
103
  if (shouldComment && prNumber) await postOrUpdatePrComment(prNumber, formatNoBumpFilesComment(detectPrBranch(rootDir), pm), rootDir, opts.patComments);
95
- if (opts.failOnMissing) process.exit(1);
104
+ if (willFail) process.exit(1);
96
105
  return;
97
106
  }
98
107
  const plan = assembleReleasePlan(prBumpFiles, packages, depGraph, config);
@@ -103,6 +112,13 @@ async function ciCheckCommand(rootDir, opts) {
103
112
  }
104
113
  if (plan.warnings.length > 0) for (const w of plan.warnings) log.warn(w);
105
114
  if (shouldComment && prNumber) await postOrUpdatePrComment(prNumber, formatReleasePlanComment(plan, prBumpFiles, prNumber, detectPrBranch(rootDir), pm, plan.warnings), rootDir, opts.patComments);
115
+ const coveredPackages = new Set(plan.releases.map((r) => r.name));
116
+ const missing = (await findChangedPackages(changedFiles, packages, rootDir, config)).filter((name) => !coveredPackages.has(name));
117
+ if (missing.length > 0) {
118
+ const willFail = opts.strict && !opts.noFail;
119
+ (willFail ? log.error : log.warn)(`${missing.length} changed package(s) not covered by bump files: ${missing.join(", ")}`);
120
+ if (willFail) process.exit(1);
121
+ }
106
122
  }
107
123
  /**
108
124
  * CI release: either auto-publish or create a version PR.
@@ -116,7 +132,7 @@ async function ciReleaseCommand(rootDir, opts) {
116
132
  const bumpFiles = await readBumpFiles(rootDir);
117
133
  if (bumpFiles.length === 0) {
118
134
  log.info("No pending bump files — checking for unpublished packages...");
119
- const { publishCommand } = await import("./publish-Cun-zQ1b.mjs");
135
+ const { publishCommand } = await import("./publish-CPZwqyWh.mjs");
120
136
  await publishCommand(rootDir, { tag: opts.tag });
121
137
  return;
122
138
  }
@@ -125,12 +141,12 @@ async function ciReleaseCommand(rootDir, opts) {
125
141
  log.info("Bump files found but no packages would be released.");
126
142
  return;
127
143
  }
128
- if (opts.mode === "auto-publish") await autoPublish(rootDir, config, opts.tag);
144
+ if (opts.mode === "auto-publish") await autoPublish(rootDir, config, plan, opts.tag);
129
145
  else await createVersionPr(rootDir, plan, config, new Map([...packages.values()].map((p) => [p.name, p.relativeDir])), opts.branch, opts.patPr);
130
146
  }
131
- async function autoPublish(rootDir, config, tag) {
147
+ async function autoPublish(rootDir, config, plan, tag) {
132
148
  log.step("Running bumpy version...");
133
- const { versionCommand } = await import("./version-19vVt9dv.mjs");
149
+ const { versionCommand } = await import("./version-CnXcbqi1.mjs");
134
150
  await versionCommand(rootDir);
135
151
  log.step("Committing version changes...");
136
152
  runArgs([
@@ -146,9 +162,12 @@ async function autoPublish(rootDir, config, tag) {
146
162
  runArgs([
147
163
  "git",
148
164
  "commit",
149
- "-m",
150
- "Version packages"
151
- ], { cwd: rootDir });
165
+ "-F",
166
+ "-"
167
+ ], {
168
+ cwd: rootDir,
169
+ input: await resolveCommitMessage(config.versionCommitMessage, plan, rootDir)
170
+ });
152
171
  runArgs([
153
172
  "git",
154
173
  "push",
@@ -156,7 +175,7 @@ async function autoPublish(rootDir, config, tag) {
156
175
  ], { cwd: rootDir });
157
176
  }
158
177
  log.step("Running bumpy publish...");
159
- const { publishCommand } = await import("./publish-Cun-zQ1b.mjs");
178
+ const { publishCommand } = await import("./publish-CPZwqyWh.mjs");
160
179
  await publishCommand(rootDir, { tag });
161
180
  }
162
181
  /**
@@ -322,7 +341,7 @@ async function createVersionPr(rootDir, plan, config, packageDirs, branchName, p
322
341
  branch
323
342
  ], { cwd: rootDir });
324
343
  log.step("Running bumpy version...");
325
- const { versionCommand } = await import("./version-19vVt9dv.mjs");
344
+ const { versionCommand } = await import("./version-CnXcbqi1.mjs");
326
345
  await versionCommand(rootDir);
327
346
  runArgs([
328
347
  "git",
@@ -349,11 +368,7 @@ async function createVersionPr(rootDir, plan, config, packageDirs, branchName, p
349
368
  "-"
350
369
  ], {
351
370
  cwd: rootDir,
352
- input: [
353
- "Version packages",
354
- "",
355
- ...plan.releases.map((r) => `${r.name}@${r.newVersion}`)
356
- ].join("\n")
371
+ input: await resolveCommitMessage(config.versionCommitMessage, plan, rootDir)
357
372
  });
358
373
  pushWithToken(rootDir, branch, config);
359
374
  const prBody = formatVersionPrBody(plan, config.versionPr.preamble, packageDirs);
@@ -373,7 +388,7 @@ async function createVersionPr(rootDir, plan, config, packageDirs, branchName, p
373
388
  cwd: rootDir,
374
389
  input: prBody
375
390
  }));
376
- log.success(`Updated PR #${validPr}`);
391
+ log.success(`🐸 Updated PR #${validPr}`);
377
392
  } else {
378
393
  log.step("Creating version PR...");
379
394
  const prTitle = config.versionPr.title;
@@ -393,7 +408,7 @@ async function createVersionPr(rootDir, plan, config, packageDirs, branchName, p
393
408
  cwd: rootDir,
394
409
  input: prBody
395
410
  }));
396
- log.success(`Created PR: ${result}`);
411
+ log.success(`🐸 Created PR: ${result}`);
397
412
  if (!patPr) pushWithToken(rootDir, branch, config);
398
413
  }
399
414
  runArgs([
@@ -428,7 +443,7 @@ function formatReleasePlanComment(plan, bumpFiles, prNumber, prBranch, pm, warni
428
443
  const repo = process.env.GITHUB_REPOSITORY;
429
444
  const lines = [];
430
445
  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>`,
446
+ `<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
447
  "",
433
448
  "**The changes in this PR will be included in the next version bump.**",
434
449
  "<br clear=\"left\" />"
@@ -478,13 +493,13 @@ function formatReleasePlanComment(plan, bumpFiles, prNumber, prBranch, pm, warni
478
493
  if (addLink) lines.push(`[Click here if you want to add another bump file to this PR](${addLink})\n`);
479
494
  else lines.push(`To add another bump file, run \`${pmRunCommand(pm)} add\`\n`);
480
495
  lines.push("---");
481
- lines.push(`_This comment is maintained by [bumpy](https://github.com/dmno-dev/bumpy)._`);
496
+ lines.push(`_This comment is maintained by [bumpy](https://bumpy.varlock.dev)._`);
482
497
  return lines.join("\n");
483
498
  }
484
499
  function formatNoBumpFilesComment(prBranch, pm) {
485
500
  const runCmd = pmRunCommand(pm);
486
501
  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>`,
502
+ `<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
503
  "",
489
504
  "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
505
  "<br clear=\"left\" />\n",
@@ -499,7 +514,7 @@ function formatNoBumpFilesComment(prBranch, pm) {
499
514
  lines.push(`Or [click here to add a bump file](${addLink}) directly on GitHub.`);
500
515
  }
501
516
  lines.push("\n---");
502
- lines.push(`_This comment is maintained by [bumpy](https://github.com/dmno-dev/bumpy)._`);
517
+ lines.push(`_This comment is maintained by [bumpy](https://bumpy.varlock.dev)._`);
503
518
  return lines.join("\n");
504
519
  }
505
520
  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-ByJ0wKYh.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-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-lA9E5pEc.mjs");
29
- await initCommand(rootDir);
28
+ const { initCommand } = await import("./init-DJhMaceS.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-yP81c9_q.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-skGX8uU7.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-CnXcbqi1.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-BOLrTYWR.mjs");
64
64
  await generateCommand(rootDir, {
65
65
  from: flags.from,
66
66
  dryRun: flags["dry-run"] === true,
@@ -68,16 +68,13 @@ 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");
80
- await checkCommand(rootDir);
73
+ const { checkCommand } = await import("./check-D_0exKi6.mjs");
74
+ await checkCommand(rootDir, {
75
+ strict: flags.strict === true,
76
+ noFail: flags["no-fail"] === true
77
+ });
81
78
  break;
82
79
  }
83
80
  case "ci": {
@@ -85,14 +82,15 @@ async function main() {
85
82
  const subcommand = args[1];
86
83
  const ciFlags = parseFlags(args.slice(2));
87
84
  if (subcommand === "check") {
88
- const { ciCheckCommand } = await import("./ci-Bhx--Tj6.mjs");
85
+ const { ciCheckCommand } = await import("./ci-CvaikKX1.mjs");
89
86
  await ciCheckCommand(rootDir, {
90
87
  comment: ciFlags.comment !== void 0 ? ciFlags.comment === true : void 0,
91
- failOnMissing: ciFlags["fail-on-missing"] === true,
88
+ strict: ciFlags.strict === true,
89
+ noFail: ciFlags["no-fail"] === true,
92
90
  patComments: ciFlags["pat-comments"] === true
93
91
  });
94
92
  } else if (subcommand === "release") {
95
- const { ciReleaseCommand } = await import("./ci-Bhx--Tj6.mjs");
93
+ const { ciReleaseCommand } = await import("./ci-CvaikKX1.mjs");
96
94
  await ciReleaseCommand(rootDir, {
97
95
  mode: ciFlags["auto-publish"] === true ? "auto-publish" : "version-pr",
98
96
  tag: ciFlags.tag,
@@ -100,7 +98,7 @@ async function main() {
100
98
  patPr: ciFlags["pat-pr"] === true
101
99
  });
102
100
  } else if (subcommand === "setup") {
103
- const { ciSetupCommand } = await import("./ci-setup-qz4Y3v7T.mjs");
101
+ const { ciSetupCommand } = await import("./ci-setup-CARJFhcE.mjs");
104
102
  await ciSetupCommand(rootDir);
105
103
  } else {
106
104
  log.error(`Unknown ci subcommand: ${subcommand}. Use "ci check", "ci release", or "ci setup".`);
@@ -110,7 +108,7 @@ async function main() {
110
108
  }
111
109
  case "publish": {
112
110
  const rootDir = await findRoot();
113
- const { publishCommand } = await import("./publish-Cun-zQ1b.mjs");
111
+ const { publishCommand } = await import("./publish-CPZwqyWh.mjs");
114
112
  await publishCommand(rootDir, {
115
113
  dryRun: flags["dry-run"] === true,
116
114
  tag: flags.tag,
@@ -124,7 +122,7 @@ async function main() {
124
122
  const subcommand = args[1];
125
123
  const aiFlags = parseFlags(args.slice(2));
126
124
  if (subcommand === "setup") {
127
- const { aiSetupCommand } = await import("./ai-sMYUf3lP.mjs");
125
+ const { aiSetupCommand } = await import("./ai-STKnq09z.mjs");
128
126
  await aiSetupCommand(rootDir, { target: aiFlags.target });
129
127
  } else {
130
128
  log.error(`Unknown ai subcommand: ${subcommand}. Use "ai setup".`);
@@ -134,7 +132,7 @@ async function main() {
134
132
  }
135
133
  case "--version":
136
134
  case "-v":
137
- console.log(`bumpy 1.0.0`);
135
+ console.log(`bumpy 1.2.0`);
138
136
  break;
139
137
  case "help":
140
138
  case "--help":
@@ -154,22 +152,23 @@ async function main() {
154
152
  }
155
153
  function printHelp() {
156
154
  console.log(`
157
- ${colorize(`🐸 bumpy v1.0.0`, "bold")} - Modern monorepo versioning
155
+ ${colorize(`🐸 bumpy v1.2.0`, "bold")} - Modern monorepo versioning
158
156
 
159
157
  Usage: bumpy <command> [options]
160
158
 
161
159
  Commands:
162
- init Initialize .bumpy/ directory
160
+ init [--force] Initialize .bumpy/ (migrates from .changeset/ if found)
163
161
  add Create a new bump file
164
- generate Generate bump file from conventional commits
162
+ generate Generate bump file from branch commits
165
163
  status Show pending releases
166
164
  check Verify changed packages have bump files (for pre-push hooks)
167
- version Apply bump files and bump versions
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
167
+ version [--commit] Apply bump files and bump versions
168
168
  publish Publish versioned packages
169
169
  ci check PR check — report pending releases, comment on PR
170
170
  ci release Release — create version PR or auto-publish
171
171
  ci setup Set up a token for triggering CI on version PRs
172
- migrate Migrate from .changeset/ to .bumpy/
173
172
  ai setup Install AI skill for creating bump files
174
173
 
175
174
  Add options:
@@ -179,7 +178,7 @@ function printHelp() {
179
178
  --empty Create an empty bump file
180
179
 
181
180
  Generate options:
182
- --from <ref> Git ref to scan from (default: last version tag)
181
+ --from <ref> Git ref to scan from (default: branch point from baseBranch)
183
182
  --dry-run Preview without creating a bump file
184
183
  --name <name> Bump file filename
185
184
 
@@ -198,7 +197,8 @@ function printHelp() {
198
197
 
199
198
  CI check options:
200
199
  --comment Force PR comment on/off (auto-detected in CI)
201
- --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
202
202
 
203
203
  CI release options:
204
204
  --auto-publish Version + publish directly (default: create version PR)
@@ -208,7 +208,7 @@ function printHelp() {
208
208
  AI setup options:
209
209
  --target <tool> Target AI tool: opencode, cursor, codex
210
210
 
211
- ${colorize("https://github.com/dmno-dev/bumpy", "dim")}
211
+ ${colorize("https://bumpy.varlock.dev", "dim")}
212
212
  `);
213
213
  }
214
214
  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 };
@@ -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 = {
@@ -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 bump files (in \`.bumpy/\`). Merge it when you are ready to release the packages listed below:`,
74
75
  "<br clear=\"left\" />"
75
76
  ].join("\n")
76
77
  }
@@ -108,7 +109,7 @@ async function findRoot(startDir = process.cwd()) {
108
109
  async function loadConfig(rootDir) {
109
110
  const configPath = resolve(rootDir, BUMPY_DIR, CONFIG_FILE);
110
111
  let userConfig = {};
111
- if (await exists(configPath)) userConfig = await readJson(configPath);
112
+ if (await exists(configPath)) userConfig = await readJsonc(configPath);
112
113
  return mergeConfig(DEFAULT_CONFIG, userConfig);
113
114
  }
114
115
  /** Load per-package bumpy config from package.json["bumpy"] or .bumpy.config.json */