@varlock/bumpy 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -22,7 +22,7 @@ A modern package versioning, release, and changelog generation tool. Built for m
22
22
  Bumpy uses **bump files** (you may know them as "changesets" if coming from [that tool 🦋](https://github.com/changesets/changesets)) - small markdown files that declare an intent to release packages with a bump level (patch/minor/major), and a description that ends up in changelogs. Developers create these files as part of their PRs, and these files are then used to consolidate changes, generate changelogs, and trigger publishing. Specifically:
23
23
 
24
24
  - Devs/agents create bump files as part of their PRs (using `bumpy add` or manually)
25
- - A pre-push git hook can enforce bump files exist for changed packages
25
+ - A git hook (pre-commit or pre-push) can enforce bump files exist for changed packages
26
26
  - In CI, a workflow checks PRs for bump files, leaves a comment on the PR detailing changed packages
27
27
  - As PRs merge to the base branch, a "release PR" is kept up to date
28
28
  - Shows what packages will be released and their changelogs
@@ -1,13 +1,13 @@
1
1
  import { n as log, o as __toESM, r as require_picocolors } from "./logger-C2dEe5Su.mjs";
2
2
  import { n as exists, t as ensureDir } from "./fs-DnDogVn-.mjs";
3
3
  import { a as loadConfig, o as loadPackageConfig, r as getBumpyDir, s as matchGlob } from "./config-D13G4-R8.mjs";
4
- import { t as discoverPackages } from "./workspace-BKOAMeki.mjs";
4
+ import { n as discoverWorkspace, t as discoverPackages } from "./workspace-BKOAMeki.mjs";
5
5
  import { t as DependencyGraph } from "./dep-graph-E-9-eQ2J.mjs";
6
6
  import { i as writeBumpFile } from "./bump-file-CoaSxqne.mjs";
7
- import { r as getChangedFiles } from "./git-D0__HP86.mjs";
7
+ import { r as getChangedFiles } from "./git-ukq7VTuZ.mjs";
8
8
  import { c as ot, d as yt, i as _t, l as pt, o as gt, r as Ot, s as mt, t as unwrap, u as wt } from "./clack-CJT1JFFa.mjs";
9
9
  import { n as slugify, t as randomName } from "./names-CBy7d8K_.mjs";
10
- import { t as require_picomatch } from "./picomatch-TGJi--_I.mjs";
10
+ import { findChangedPackages, t as require_picomatch } from "./check-CS8WIGZA.mjs";
11
11
  import { relative, resolve } from "node:path";
12
12
  import * as readline from "node:readline";
13
13
  //#region src/prompts/bump-select.ts
@@ -191,6 +191,25 @@ async function addCommand(rootDir, opts) {
191
191
  log.success(`🐸 Created empty bump file: .bumpy/${filename}.md`);
192
192
  return;
193
193
  }
194
+ if (opts.none) {
195
+ const { packages } = await discoverWorkspace(rootDir, config);
196
+ const changedPackages = await findChangedPackages(getChangedFiles(rootDir, config.baseBranch), packages, rootDir, config);
197
+ if (changedPackages.length === 0) {
198
+ log.info("No changed packages detected.");
199
+ return;
200
+ }
201
+ const releases = changedPackages.map((name) => ({
202
+ name,
203
+ type: "none"
204
+ }));
205
+ const summary = opts.message || "";
206
+ const filename = opts.name ? slugify(opts.name) : randomName();
207
+ if (await exists(resolve(bumpyDir, `${filename}.md`))) await writeBumpFile(rootDir, `${filename}-${Date.now()}`, releases, summary);
208
+ else await writeBumpFile(rootDir, filename, releases, summary);
209
+ log.success(`🐸 Created bump file with ${changedPackages.length} package(s) set to none: .bumpy/${filename}.md`);
210
+ for (const name of changedPackages) log.dim(` ${name}: none`);
211
+ return;
212
+ }
194
213
  let releases;
195
214
  let summary;
196
215
  let filename;
@@ -1,6 +1,6 @@
1
1
  import { a as readJson, c as removeFile, f as writeText, i as listFiles, l as updateJsonFields, n as exists, s as readText, u as updateJsonNestedField } from "./fs-DnDogVn-.mjs";
2
2
  import { r as getBumpyDir } from "./config-D13G4-R8.mjs";
3
- import { a as prependToChangelog, i as loadFormatter, n as generateChangelogEntry } from "./changelog-BIkNBJ15.mjs";
3
+ import { a as prependToChangelog, i as loadFormatter, n as generateChangelogEntry } from "./changelog-DP3OrTqQ.mjs";
4
4
  import { resolve } from "node:path";
5
5
  //#region src/core/apply-release-plan.ts
6
6
  /** Apply the release plan: bump versions, update changelogs, delete bump files */
@@ -6,7 +6,7 @@ import { realpathSync } from "node:fs";
6
6
  /** Get the bump type a bump file applies to a specific package */
7
7
  function getBumpTypeForPackage(bf, packageName) {
8
8
  const rel = bf.releases.find((r) => r.name === packageName);
9
- return rel?.type === "none" ? "patch" : rel?.type ?? "patch";
9
+ return rel?.type === "none" || !rel?.type ? "patch" : rel.type;
10
10
  }
11
11
  /** Sort bump files by bump type for a specific package (major → minor → patch) */
12
12
  function sortBumpFilesByType(bumpFiles, packageName) {
@@ -43,7 +43,7 @@ const defaultFormatter = (ctx) => {
43
43
  const BUILTIN_FORMATTERS = {
44
44
  default: defaultFormatter,
45
45
  github: async () => {
46
- const { createGithubFormatter } = await import("./changelog-github-BAtgxeXv.mjs");
46
+ const { createGithubFormatter } = await import("./changelog-github-Da5KekQd.mjs");
47
47
  return createGithubFormatter();
48
48
  }
49
49
  };
@@ -54,7 +54,7 @@ const BUILTIN_FORMATTERS = {
54
54
  async function loadFormatter(changelog, rootDir) {
55
55
  const [name, options] = Array.isArray(changelog) ? changelog : [changelog, {}];
56
56
  if (name === "github") {
57
- const { createGithubFormatter } = await import("./changelog-github-BAtgxeXv.mjs");
57
+ const { createGithubFormatter } = await import("./changelog-github-Da5KekQd.mjs");
58
58
  return createGithubFormatter(options);
59
59
  }
60
60
  if (typeof name === "string" && BUILTIN_FORMATTERS[name]) {
@@ -1,5 +1,5 @@
1
1
  import { s as tryRunArgs } from "./shell-u3bYGxNy.mjs";
2
- import { o as sortBumpFilesByType, r as getBumpTypeForPackage } from "./changelog-BIkNBJ15.mjs";
2
+ import { o as sortBumpFilesByType, r as getBumpTypeForPackage } from "./changelog-DP3OrTqQ.mjs";
3
3
  //#region src/core/changelog-github.ts
4
4
  /** Authors filtered from "Thanks" attribution by default (e.g. bots) */
5
5
  /** Authors filtered from "Thanks" attribution by default (e.g. AI/automation bots) */
@@ -1,4 +1,9 @@
1
- import { i as __commonJSMin } from "./logger-C2dEe5Su.mjs";
1
+ import { i as __commonJSMin, 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-D13G4-R8.mjs";
3
+ import { n as discoverWorkspace } from "./workspace-BKOAMeki.mjs";
4
+ import { r as readBumpFiles, t as filterBranchBumpFiles } from "./bump-file-CoaSxqne.mjs";
5
+ import { a as getFileStatuses, r as getChangedFiles } from "./git-ukq7VTuZ.mjs";
6
+ import { relative } from "node:path";
2
7
  //#region ../../node_modules/.bun/picomatch@4.0.4/node_modules/picomatch/lib/constants.js
3
8
  var require_constants = /* @__PURE__ */ __commonJSMin(((exports, module) => {
4
9
  const WIN_SLASH = "\\\\/";
@@ -1867,4 +1872,128 @@ var require_picomatch = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1867
1872
  module.exports = picomatch;
1868
1873
  }));
1869
1874
  //#endregion
1870
- export { require_picomatch as t };
1875
+ //#region src/commands/check.ts
1876
+ var import_picomatch = /* @__PURE__ */ __toESM(require_picomatch(), 1);
1877
+ /**
1878
+ * Local check: detect which packages have changed on this branch
1879
+ * and verify they have corresponding bump files.
1880
+ * Designed for pre-push hooks — no GitHub API needed.
1881
+ *
1882
+ * Default: at least one bump file must exist, uncovered packages are warned.
1883
+ * --strict: every changed package must be covered.
1884
+ * --no-fail: warn only, never exit 1.
1885
+ * --hook pre-commit: only staged + committed bump files count.
1886
+ * --hook pre-push: only committed bump files count.
1887
+ */
1888
+ async function checkCommand(rootDir, opts = {}) {
1889
+ const config = await loadConfig(rootDir);
1890
+ const { packages } = await discoverWorkspace(rootDir, config);
1891
+ const baseBranch = config.baseBranch;
1892
+ const changedFiles = getChangedFiles(rootDir, baseBranch);
1893
+ if (changedFiles.length === 0) {
1894
+ log.info("No changed files detected.");
1895
+ return;
1896
+ }
1897
+ const { bumpFiles: allBumpFiles, errors: parseErrors } = await readBumpFiles(rootDir);
1898
+ if (parseErrors.length > 0) {
1899
+ for (const err of parseErrors) log.error(err);
1900
+ process.exit(1);
1901
+ }
1902
+ const bumpyRelDir = relative(rootDir, getBumpyDir(rootDir));
1903
+ const fileStatuses = getFileStatuses(bumpyRelDir, { cwd: rootDir });
1904
+ const augmentedChangedFiles = [...changedFiles];
1905
+ for (const [file] of fileStatuses) if (file.endsWith(".md") && !file.endsWith("README.md") && !augmentedChangedFiles.includes(file)) augmentedChangedFiles.push(file);
1906
+ const { branchBumpFiles, emptyBumpFileIds } = filterBranchBumpFiles(allBumpFiles, augmentedChangedFiles, rootDir);
1907
+ const bumpFileStatuses = /* @__PURE__ */ new Map();
1908
+ for (const bf of branchBumpFiles) {
1909
+ const filePath = `${bumpyRelDir}/${bf.id}.md`;
1910
+ const status = fileStatuses.get(filePath);
1911
+ bumpFileStatuses.set(bf.id, status ?? "committed");
1912
+ }
1913
+ for (const id of emptyBumpFileIds) {
1914
+ const filePath = `${bumpyRelDir}/${id}.md`;
1915
+ const status = fileStatuses.get(filePath);
1916
+ bumpFileStatuses.set(id, status ?? "committed");
1917
+ }
1918
+ const effectiveBumpFiles = opts.hook ? branchBumpFiles.filter((bf) => {
1919
+ const status = bumpFileStatuses.get(bf.id);
1920
+ if (opts.hook === "pre-push") return status === "committed";
1921
+ if (opts.hook === "pre-commit") return status !== "untracked";
1922
+ return true;
1923
+ }) : branchBumpFiles;
1924
+ const effectiveEmptyIds = opts.hook ? emptyBumpFileIds.filter((id) => {
1925
+ const status = bumpFileStatuses.get(id);
1926
+ if (opts.hook === "pre-push") return status === "committed";
1927
+ if (opts.hook === "pre-commit") return status !== "untracked";
1928
+ return true;
1929
+ }) : emptyBumpFileIds;
1930
+ if (opts.hook) {
1931
+ const excludedBumpFiles = branchBumpFiles.filter((bf) => !effectiveBumpFiles.includes(bf));
1932
+ const excludedEmptyIds = emptyBumpFileIds.filter((id) => !effectiveEmptyIds.includes(id));
1933
+ for (const bf of excludedBumpFiles) {
1934
+ const status = bumpFileStatuses.get(bf.id);
1935
+ if (opts.hook === "pre-push" && status === "staged") log.warn(`${bumpyRelDir}/${bf.id}.md is staged but not committed — it won't be included in the push`);
1936
+ else if (status === "untracked") log.warn(`${bumpyRelDir}/${bf.id}.md is untracked — run \`git add\` to include it`);
1937
+ }
1938
+ for (const id of excludedEmptyIds) {
1939
+ const status = bumpFileStatuses.get(id);
1940
+ if (opts.hook === "pre-push" && status === "staged") log.warn(`${bumpyRelDir}/${id}.md is staged but not committed — it won't be included in the push`);
1941
+ else if (status === "untracked") log.warn(`${bumpyRelDir}/${id}.md is untracked — run \`git add\` to include it`);
1942
+ }
1943
+ }
1944
+ const hasEmptyBumpFile = effectiveEmptyIds.length > 0;
1945
+ const coveredPackages = /* @__PURE__ */ new Set();
1946
+ for (const bf of effectiveBumpFiles) for (const release of bf.releases) coveredPackages.add(release.name);
1947
+ const changedPackages = await findChangedPackages(changedFiles, packages, rootDir, config);
1948
+ if (changedPackages.length === 0) {
1949
+ log.info("No managed packages have changed.");
1950
+ return;
1951
+ }
1952
+ const missing = changedPackages.filter((name) => !coveredPackages.has(name));
1953
+ const hasAnyCoverage = effectiveBumpFiles.length > 0 || hasEmptyBumpFile;
1954
+ if (missing.length === 0 || hasEmptyBumpFile && !opts.strict) {
1955
+ if (hasEmptyBumpFile && missing.length > 0) log.success("Empty bump file found — uncovered packages acknowledged.");
1956
+ else log.success(`🐸 All ${changedPackages.length} changed package(s) have bump files.`);
1957
+ if (effectiveBumpFiles.length > 0) printBumpFileList(effectiveBumpFiles.map((bf) => bf.id), bumpyRelDir, bumpFileStatuses);
1958
+ return;
1959
+ }
1960
+ const willFail = !opts.noFail && (opts.strict || !hasAnyCoverage);
1961
+ (willFail ? log.error : log.warn)(`${missing.length} changed package(s) missing bump files:\n`);
1962
+ for (const name of missing) console.log(` ${colorize(name, "yellow")}`);
1963
+ if (effectiveBumpFiles.length > 0) {
1964
+ console.log();
1965
+ printBumpFileList(effectiveBumpFiles.map((bf) => bf.id), bumpyRelDir, bumpFileStatuses);
1966
+ }
1967
+ console.log();
1968
+ 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 bump.");
1969
+ else log.dim("Run `bumpy add` to create a bump file, or `bumpy add --empty` if no release is needed.");
1970
+ log.dim("To adjust which files trigger change detection, set `changedFilePatterns` in .bumpy/_config.json.");
1971
+ if (willFail) process.exit(1);
1972
+ }
1973
+ function printBumpFileList(ids, bumpyRelDir, statuses) {
1974
+ log.dim("Bump files on this branch:");
1975
+ for (const id of ids) {
1976
+ const status = statuses.get(id);
1977
+ const statusLabel = status === "staged" ? colorize(" (staged)", "yellow") : status === "untracked" ? colorize(" (untracked)", "yellow") : "";
1978
+ log.dim(` ${bumpyRelDir}/${id}.md${statusLabel}`);
1979
+ }
1980
+ }
1981
+ /** Map changed files to the packages they belong to */
1982
+ async function findChangedPackages(changedFiles, packages, rootDir, config) {
1983
+ const changed = /* @__PURE__ */ new Set();
1984
+ const matchers = /* @__PURE__ */ new Map();
1985
+ for (const [name, pkg] of packages) {
1986
+ const patterns = (await loadPackageConfig(pkg.dir, config, name)).changedFilePatterns ?? config.changedFilePatterns;
1987
+ matchers.set(name, (0, import_picomatch.default)(patterns));
1988
+ }
1989
+ for (const file of changedFiles) for (const [name, pkg] of packages) {
1990
+ const pkgRelDir = relative(rootDir, pkg.dir);
1991
+ if (file.startsWith(pkgRelDir + "/")) {
1992
+ const relToPackage = file.slice(pkgRelDir.length + 1);
1993
+ if (matchers.get(name)(relToPackage)) changed.add(name);
1994
+ }
1995
+ }
1996
+ return [...changed];
1997
+ }
1998
+ //#endregion
1999
+ export { checkCommand, findChangedPackages, require_picomatch as t };
@@ -5,11 +5,11 @@ import { n as discoverWorkspace } from "./workspace-BKOAMeki.mjs";
5
5
  import { t as DependencyGraph } from "./dep-graph-E-9-eQ2J.mjs";
6
6
  import { n as runArgs, r as runArgsAsync, s as tryRunArgs } from "./shell-u3bYGxNy.mjs";
7
7
  import { r as readBumpFiles, t as filterBranchBumpFiles } from "./bump-file-CoaSxqne.mjs";
8
- import { t as assembleReleasePlan } from "./release-plan-RBjKmavL.mjs";
9
- import { r as getChangedFiles } from "./git-D0__HP86.mjs";
8
+ import { t as assembleReleasePlan } from "./release-plan-pyxf71dx.mjs";
9
+ import { r as getChangedFiles } from "./git-ukq7VTuZ.mjs";
10
10
  import { t as randomName } from "./names-CBy7d8K_.mjs";
11
- import { findChangedPackages } from "./check-C80o_gJK.mjs";
12
- import { t as resolveCommitMessage } from "./commit-message-DOIfDxfj.mjs";
11
+ import { findChangedPackages } from "./check-CS8WIGZA.mjs";
12
+ import { t as resolveCommitMessage } from "./commit-message-3e4KhzFV.mjs";
13
13
  import { createHash } from "node:crypto";
14
14
  //#region src/commands/ci.ts
15
15
  /**
@@ -135,7 +135,7 @@ async function ciReleaseCommand(rootDir, opts) {
135
135
  }
136
136
  if (bumpFiles.length === 0) {
137
137
  log.info("No pending bump files — checking for unpublished packages...");
138
- const { publishCommand } = await import("./publish-CfZCAlPx.mjs");
138
+ const { publishCommand } = await import("./publish-DWdN3o9u.mjs");
139
139
  await publishCommand(rootDir, { tag: opts.tag });
140
140
  return;
141
141
  }
@@ -149,7 +149,7 @@ async function ciReleaseCommand(rootDir, opts) {
149
149
  }
150
150
  async function autoPublish(rootDir, config, plan, tag) {
151
151
  log.step("Running bumpy version...");
152
- const { versionCommand } = await import("./version-DLU0h1cq.mjs");
152
+ const { versionCommand } = await import("./version-BxDhayli.mjs");
153
153
  await versionCommand(rootDir);
154
154
  log.step("Committing version changes...");
155
155
  runArgs([
@@ -178,7 +178,7 @@ async function autoPublish(rootDir, config, plan, tag) {
178
178
  ], { cwd: rootDir });
179
179
  }
180
180
  log.step("Running bumpy publish...");
181
- const { publishCommand } = await import("./publish-CfZCAlPx.mjs");
181
+ const { publishCommand } = await import("./publish-DWdN3o9u.mjs");
182
182
  await publishCommand(rootDir, { tag });
183
183
  }
184
184
  /**
@@ -344,7 +344,7 @@ async function createVersionPr(rootDir, plan, config, packageDirs, branchName) {
344
344
  branch
345
345
  ], { cwd: rootDir });
346
346
  log.step("Running bumpy version...");
347
- const { versionCommand } = await import("./version-DLU0h1cq.mjs");
347
+ const { versionCommand } = await import("./version-BxDhayli.mjs");
348
348
  await versionCommand(rootDir);
349
349
  runArgs([
350
350
  "git",
package/dist/cli.mjs CHANGED
@@ -25,24 +25,25 @@ async function main() {
25
25
  switch (command) {
26
26
  case "init": {
27
27
  const rootDir = await findRoot();
28
- const { initCommand } = await import("./init-DND7zRGD.mjs");
28
+ const { initCommand } = await import("./init-CUIw0jg8.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-ET1tcdQm.mjs");
34
+ const { addCommand } = await import("./add-D1uiW7VA.mjs");
35
35
  await addCommand(rootDir, {
36
36
  packages: flags.packages,
37
37
  message: flags.message,
38
38
  name: flags.name,
39
- empty: flags.empty === true
39
+ empty: flags.empty === true,
40
+ none: flags.none === true
40
41
  });
41
42
  break;
42
43
  }
43
44
  case "status": {
44
45
  const rootDir = await findRoot();
45
- const { statusCommand } = await import("./status-pMfPtt1p.mjs");
46
+ const { statusCommand } = await import("./status-DyzcQ6CD.mjs");
46
47
  await statusCommand(rootDir, {
47
48
  json: flags.json === true,
48
49
  packagesOnly: flags.packages === true,
@@ -54,13 +55,13 @@ async function main() {
54
55
  }
55
56
  case "version": {
56
57
  const rootDir = await findRoot();
57
- const { versionCommand } = await import("./version-DLU0h1cq.mjs");
58
+ const { versionCommand } = await import("./version-BxDhayli.mjs");
58
59
  await versionCommand(rootDir, { commit: flags.commit === true });
59
60
  break;
60
61
  }
61
62
  case "generate": {
62
63
  const rootDir = await findRoot();
63
- const { generateCommand } = await import("./generate-BJq--oCu.mjs");
64
+ const { generateCommand } = await import("./generate-D0KJsvpD.mjs");
64
65
  await generateCommand(rootDir, {
65
66
  from: flags.from,
66
67
  dryRun: flags["dry-run"] === true,
@@ -70,10 +71,16 @@ async function main() {
70
71
  }
71
72
  case "check": {
72
73
  const rootDir = await findRoot();
73
- const { checkCommand } = await import("./check-C80o_gJK.mjs");
74
+ const { checkCommand } = await import("./check-CS8WIGZA.mjs");
75
+ const hookValue = flags.hook;
76
+ if (hookValue && hookValue !== "pre-commit" && hookValue !== "pre-push") {
77
+ log.error(`Invalid --hook value "${hookValue}". Expected "pre-commit" or "pre-push".`);
78
+ process.exit(1);
79
+ }
74
80
  await checkCommand(rootDir, {
75
81
  strict: flags.strict === true,
76
- noFail: flags["no-fail"] === true
82
+ noFail: flags["no-fail"] === true,
83
+ hook: hookValue
77
84
  });
78
85
  break;
79
86
  }
@@ -82,21 +89,21 @@ async function main() {
82
89
  const subcommand = args[1];
83
90
  const ciFlags = parseFlags(args.slice(2));
84
91
  if (subcommand === "check") {
85
- const { ciCheckCommand } = await import("./ci-WJ8kTjeN.mjs");
92
+ const { ciCheckCommand } = await import("./ci-CnIkaf7Q.mjs");
86
93
  await ciCheckCommand(rootDir, {
87
94
  comment: ciFlags.comment !== void 0 ? ciFlags.comment === true : void 0,
88
95
  strict: ciFlags.strict === true,
89
96
  noFail: ciFlags["no-fail"] === true
90
97
  });
91
98
  } else if (subcommand === "release") {
92
- const { ciReleaseCommand } = await import("./ci-WJ8kTjeN.mjs");
99
+ const { ciReleaseCommand } = await import("./ci-CnIkaf7Q.mjs");
93
100
  await ciReleaseCommand(rootDir, {
94
101
  mode: ciFlags["auto-publish"] === true ? "auto-publish" : "version-pr",
95
102
  tag: ciFlags.tag,
96
103
  branch: ciFlags.branch
97
104
  });
98
105
  } else if (subcommand === "setup") {
99
- const { ciSetupCommand } = await import("./ci-setup-D1NCzbNH.mjs");
106
+ const { ciSetupCommand } = await import("./ci-setup-DWxrdSK6.mjs");
100
107
  await ciSetupCommand(rootDir);
101
108
  } else {
102
109
  log.error(`Unknown ci subcommand: ${subcommand}. Use "ci check", "ci release", or "ci setup".`);
@@ -106,7 +113,7 @@ async function main() {
106
113
  }
107
114
  case "publish": {
108
115
  const rootDir = await findRoot();
109
- const { publishCommand } = await import("./publish-CfZCAlPx.mjs");
116
+ const { publishCommand } = await import("./publish-DWdN3o9u.mjs");
110
117
  await publishCommand(rootDir, {
111
118
  dryRun: flags["dry-run"] === true,
112
119
  tag: flags.tag,
@@ -130,7 +137,7 @@ async function main() {
130
137
  }
131
138
  case "--version":
132
139
  case "-v":
133
- console.log(`bumpy 1.3.0`);
140
+ console.log(`bumpy 1.4.0`);
134
141
  break;
135
142
  case "help":
136
143
  case "--help":
@@ -150,18 +157,21 @@ async function main() {
150
157
  }
151
158
  function printHelp() {
152
159
  console.log(`
153
- ${colorize(`🐸 bumpy v1.3.0`, "bold")} - Modern monorepo versioning
160
+ ${colorize(`🐸 bumpy v1.4.0`, "bold")} - Modern monorepo versioning
154
161
 
155
162
  Usage: bumpy <command> [options]
156
163
 
157
164
  Commands:
158
165
  init [--force] Initialize .bumpy/ (migrates from .changeset/ if found)
159
166
  add Create a new bump file
167
+ --none Set all changed packages to "none" (acknowledge without bumping)
168
+ --empty Create an empty bump file (no releases needed)
160
169
  generate Generate bump file from branch commits
161
170
  status Show pending releases
162
- check Verify changed packages have bump files (for pre-push hooks)
171
+ check Verify changed packages have bump files (for git hooks)
163
172
  --strict Fail if any changed package is uncovered (default: only fail if no bump files at all)
164
173
  --no-fail Warn only, never exit 1
174
+ --hook <context> Hook context: "pre-commit" or "pre-push" (controls which bump files count)
165
175
  version [--commit] Apply bump files and bump versions
166
176
  publish Publish versioned packages
167
177
  ci check PR check — report pending releases, comment on PR
@@ -4,7 +4,7 @@ import { a as loadConfig, r as getBumpyDir } from "./config-D13G4-R8.mjs";
4
4
  import { t as discoverPackages } from "./workspace-BKOAMeki.mjs";
5
5
  import { s as tryRunArgs } from "./shell-u3bYGxNy.mjs";
6
6
  import { i as writeBumpFile } from "./bump-file-CoaSxqne.mjs";
7
- import { a as getFilesChangedInCommit, n as getBranchCommits } from "./git-D0__HP86.mjs";
7
+ import { n as getBranchCommits, o as getFilesChangedInCommit } from "./git-ukq7VTuZ.mjs";
8
8
  import { n as slugify, t as randomName } from "./names-CBy7d8K_.mjs";
9
9
  import { relative } from "node:path";
10
10
  //#region src/commands/generate.ts
@@ -124,6 +124,26 @@ function getFilesChangedInCommit(hash, opts) {
124
124
  if (!result) return [];
125
125
  return result.split("\n").filter(Boolean);
126
126
  }
127
+ /** Get the git status of files in a directory (staged, unstaged, untracked) */
128
+ function getFileStatuses(dir, opts) {
129
+ const statuses = /* @__PURE__ */ new Map();
130
+ const result = tryRunArgs([
131
+ "git",
132
+ "status",
133
+ "--porcelain",
134
+ "--",
135
+ dir
136
+ ], opts);
137
+ if (!result) return statuses;
138
+ for (const line of result.split("\n")) {
139
+ if (!line.trim()) continue;
140
+ const indexStatus = line[0];
141
+ const file = line.slice(3);
142
+ if (indexStatus === "?") statuses.set(file, "untracked");
143
+ else statuses.set(file, "staged");
144
+ }
145
+ return statuses;
146
+ }
127
147
  /** Get all tags matching a pattern */
128
148
  function listTags(pattern, opts) {
129
149
  const result = tryRunArgs([
@@ -136,4 +156,4 @@ function listTags(pattern, opts) {
136
156
  return result.split("\n").filter(Boolean);
137
157
  }
138
158
  //#endregion
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 };
159
+ export { getFileStatuses as a, listTags as c, getCurrentBranch as i, pushWithTags as l, getBranchCommits as n, getFilesChangedInCommit as o, getChangedFiles as r, hasUncommittedChanges as s, createTag as t, tagExists as u };
package/dist/index.mjs CHANGED
@@ -4,8 +4,8 @@ import { t as discoverPackages } from "./workspace-BKOAMeki.mjs";
4
4
  import { t as DependencyGraph } from "./dep-graph-E-9-eQ2J.mjs";
5
5
  import { i as writeBumpFile, n as parseBumpFile, r as readBumpFiles } from "./bump-file-CoaSxqne.mjs";
6
6
  import { n as satisfies, r as stripProtocol, t as bumpVersion } from "./semver-DfQyVLM_.mjs";
7
- import { t as assembleReleasePlan } from "./release-plan-RBjKmavL.mjs";
8
- import { a as prependToChangelog, i as loadFormatter, n as generateChangelogEntry, t as defaultFormatter } from "./changelog-BIkNBJ15.mjs";
9
- import { t as applyReleasePlan } from "./apply-release-plan-BvZ32LAm.mjs";
10
- import { t as publishPackages } from "./publish-pipeline-BvLIu7WF.mjs";
7
+ import { t as assembleReleasePlan } from "./release-plan-pyxf71dx.mjs";
8
+ import { a as prependToChangelog, i as loadFormatter, n as generateChangelogEntry, t as defaultFormatter } from "./changelog-DP3OrTqQ.mjs";
9
+ import { t as applyReleasePlan } from "./apply-release-plan-60VfCLF8.mjs";
10
+ import { t as publishPackages } from "./publish-pipeline-C1slMaJV.mjs";
11
11
  export { BUMP_LEVELS, DEFAULT_BUMP_RULES, DEFAULT_CONFIG, DEFAULT_PUBLISH_CONFIG, DEP_TYPES, DependencyGraph, applyReleasePlan, assembleReleasePlan, bumpLevel, bumpVersion, defaultFormatter, discoverPackages, findRoot, generateChangelogEntry, getBumpyDir, hasCascade, loadConfig, loadFormatter, matchGlob, maxBump, parseBumpFile, prependToChangelog, publishPackages, readBumpFiles, satisfies, stripProtocol, writeBumpFile };
@@ -4,9 +4,9 @@ import { n as detectWorkspaces } from "./package-manager-CClZtIHP.mjs";
4
4
  import { n as discoverWorkspace } from "./workspace-BKOAMeki.mjs";
5
5
  import { t as DependencyGraph } from "./dep-graph-E-9-eQ2J.mjs";
6
6
  import { r as runArgsAsync, s as tryRunArgs } from "./shell-u3bYGxNy.mjs";
7
- import { i as loadFormatter, n as generateChangelogEntry } from "./changelog-BIkNBJ15.mjs";
8
- import { c as pushWithTags, o as hasUncommittedChanges, s as listTags } from "./git-D0__HP86.mjs";
9
- import { t as publishPackages } from "./publish-pipeline-BvLIu7WF.mjs";
7
+ import { i as loadFormatter, n as generateChangelogEntry } from "./changelog-DP3OrTqQ.mjs";
8
+ import { c as listTags, l as pushWithTags, s as hasUncommittedChanges } from "./git-ukq7VTuZ.mjs";
9
+ import { t as publishPackages } from "./publish-pipeline-C1slMaJV.mjs";
10
10
  //#region src/core/github-release.ts
11
11
  /** Get the current HEAD commit SHA */
12
12
  function getHeadSha(rootDir) {
@@ -3,7 +3,7 @@ import { a as readJson, u as updateJsonNestedField } from "./fs-DnDogVn-.mjs";
3
3
  import { r as resolveCatalogDep } from "./package-manager-CClZtIHP.mjs";
4
4
  import { i as runAsync, o as sq, r as runArgsAsync, s as tryRunArgs } from "./shell-u3bYGxNy.mjs";
5
5
  import { r as stripProtocol } from "./semver-DfQyVLM_.mjs";
6
- import { l as tagExists, t as createTag } from "./git-D0__HP86.mjs";
6
+ import { t as createTag, u as tagExists } from "./git-ukq7VTuZ.mjs";
7
7
  import { resolve } from "node:path";
8
8
  import { unlink } from "node:fs/promises";
9
9
  import { appendFileSync, existsSync, readFileSync, writeFileSync } from "node:fs";
@@ -20,21 +20,16 @@ function assembleReleasePlan(bumpFiles, packages, depGraph, config) {
20
20
  const planned = /* @__PURE__ */ new Map();
21
21
  const warnings = [];
22
22
  const cascadeOverrides = /* @__PURE__ */ new Map();
23
- const suppressedPackages = /* @__PURE__ */ new Set();
24
23
  for (const bf of bumpFiles) for (const release of bf.releases) {
25
24
  if (!packages.has(release.name)) continue;
26
25
  const bump = release.type;
27
- if (bump === "none") {
28
- suppressedPackages.add(release.name);
29
- continue;
30
- }
26
+ if (bump === "none") continue;
31
27
  const existing = planned.get(release.name);
32
28
  if (existing) {
33
29
  existing.type = maxBump(existing.type, bump);
34
30
  existing.bumpFiles.add(bf.id);
35
31
  } else planned.set(release.name, {
36
32
  type: bump,
37
- suppressed: false,
38
33
  isDependencyBump: false,
39
34
  isCascadeBump: false,
40
35
  bumpFiles: new Set([bf.id])
@@ -48,13 +43,6 @@ function assembleReleasePlan(bumpFiles, packages, depGraph, config) {
48
43
  }
49
44
  }
50
45
  }
51
- for (const name of suppressedPackages) if (!planned.has(name)) planned.set(name, {
52
- type: "patch",
53
- suppressed: true,
54
- isDependencyBump: false,
55
- isCascadeBump: false,
56
- bumpFiles: /* @__PURE__ */ new Set()
57
- });
58
46
  let changed = true;
59
47
  let iterations = 0;
60
48
  const MAX_ITERATIONS = 100;
@@ -62,7 +50,6 @@ function assembleReleasePlan(bumpFiles, packages, depGraph, config) {
62
50
  changed = false;
63
51
  iterations++;
64
52
  for (const [pkgName, bump] of planned) {
65
- if (bump.suppressed) continue;
66
53
  const pkg = packages.get(pkgName);
67
54
  const newVersion = bumpVersion(pkg.version, bump.type);
68
55
  const dependents = depGraph.getDependents(pkgName);
@@ -73,7 +60,6 @@ function assembleReleasePlan(bumpFiles, packages, depGraph, config) {
73
60
  let depBump;
74
61
  if (dep.depType === "peerDependencies") depBump = bump.type;
75
62
  else depBump = "patch";
76
- if (planned.get(dep.name)?.suppressed) throw new Error(`Cannot suppress bump for '${dep.name}' (via 'none' in bump file) — '${pkgName}' is bumping to ${newVersion} which breaks the declared range '${dep.versionRange}'. Either widen the range or remove the 'none' entry.`);
77
63
  if (dep.depType === "peerDependencies" && depBump !== "patch") {
78
64
  let resolvedRange = dep.versionRange.replace(/^workspace:/, "");
79
65
  if (resolvedRange === "^" || resolvedRange === "~") resolvedRange = `${resolvedRange}${pkg.version}`;
@@ -84,15 +70,11 @@ function assembleReleasePlan(bumpFiles, packages, depGraph, config) {
84
70
  }
85
71
  for (const group of config.fixed) {
86
72
  let groupBump;
87
- for (const nameOrGlob of group) for (const [name, bump] of planned) {
88
- if (bump.suppressed) continue;
89
- if (matchGlob(name, nameOrGlob)) groupBump = maxBump(groupBump, bump.type);
90
- }
73
+ for (const nameOrGlob of group) for (const [name, bump] of planned) if (matchGlob(name, nameOrGlob)) groupBump = maxBump(groupBump, bump.type);
91
74
  if (!groupBump) continue;
92
75
  for (const nameOrGlob of group) for (const [name] of packages) {
93
76
  if (!matchGlob(name, nameOrGlob)) continue;
94
77
  const existing = planned.get(name);
95
- if (existing && existing.suppressed) continue;
96
78
  if (existing) {
97
79
  const newType = maxBump(existing.type, groupBump);
98
80
  if (newType !== existing.type) {
@@ -102,7 +84,6 @@ function assembleReleasePlan(bumpFiles, packages, depGraph, config) {
102
84
  } else {
103
85
  planned.set(name, {
104
86
  type: groupBump,
105
- suppressed: false,
106
87
  isDependencyBump: false,
107
88
  isCascadeBump: false,
108
89
  bumpFiles: /* @__PURE__ */ new Set()
@@ -113,15 +94,12 @@ function assembleReleasePlan(bumpFiles, packages, depGraph, config) {
113
94
  }
114
95
  for (const group of config.linked) {
115
96
  let groupBump;
116
- for (const nameOrGlob of group) for (const [name, bump] of planned) {
117
- if (bump.suppressed) continue;
118
- if (matchGlob(name, nameOrGlob)) groupBump = maxBump(groupBump, bump.type);
119
- }
97
+ for (const nameOrGlob of group) for (const [name, bump] of planned) if (matchGlob(name, nameOrGlob)) groupBump = maxBump(groupBump, bump.type);
120
98
  if (!groupBump) continue;
121
99
  for (const nameOrGlob of group) for (const [name] of packages) {
122
100
  if (!matchGlob(name, nameOrGlob)) continue;
123
101
  const existing = planned.get(name);
124
- if (!existing || existing.suppressed) continue;
102
+ if (!existing) continue;
125
103
  const newType = maxBump(existing.type, groupBump);
126
104
  if (newType !== existing.type) {
127
105
  existing.type = newType;
@@ -130,12 +108,10 @@ function assembleReleasePlan(bumpFiles, packages, depGraph, config) {
130
108
  }
131
109
  }
132
110
  if (config.updateInternalDependencies !== "out-of-range") for (const [pkgName, bump] of planned) {
133
- if (bump.suppressed) continue;
134
111
  if (config.updateInternalDependencies === "minor" && bumpLevel(bump.type) < bumpLevel("minor")) continue;
135
112
  const bfOverrides = cascadeOverrides.get(pkgName);
136
113
  if (bfOverrides) for (const [pattern, cascadeBumpType] of bfOverrides) for (const [targetName] of packages) {
137
114
  if (!matchGlob(targetName, pattern)) continue;
138
- if (planned.get(targetName)?.suppressed) continue;
139
115
  if (applyBump(planned, targetName, cascadeBumpType, false, true, bump.bumpFiles)) changed = true;
140
116
  }
141
117
  const cascadeTo = packages.get(pkgName)?.bumpy?.cascadeTo;
@@ -144,7 +120,6 @@ function assembleReleasePlan(bumpFiles, packages, depGraph, config) {
144
120
  const cascadeBump = rule.bumpAs === "match" ? bump.type : rule.bumpAs;
145
121
  for (const [targetName] of packages) {
146
122
  if (!matchGlob(targetName, pattern)) continue;
147
- if (planned.get(targetName)?.suppressed) continue;
148
123
  if (applyBump(planned, targetName, cascadeBump, false, true, bump.bumpFiles)) changed = true;
149
124
  }
150
125
  }
@@ -153,17 +128,14 @@ function assembleReleasePlan(bumpFiles, packages, depGraph, config) {
153
128
  const rule = resolveRule(dep.name, dep.depType, packages, config);
154
129
  if (!rule) continue;
155
130
  if (!shouldTrigger(bump.type, rule.trigger)) continue;
156
- if (planned.get(dep.name)?.suppressed) continue;
157
131
  const depBump = rule.bumpAs === "match" ? bump.type : rule.bumpAs;
158
132
  if (applyBump(planned, dep.name, depBump, true, false, bump.bumpFiles)) changed = true;
159
133
  }
160
134
  }
161
135
  else for (const [pkgName, bump] of planned) {
162
- if (bump.suppressed) continue;
163
136
  const bfOverrides = cascadeOverrides.get(pkgName);
164
137
  if (bfOverrides) for (const [pattern, cascadeBumpType] of bfOverrides) for (const [targetName] of packages) {
165
138
  if (!matchGlob(targetName, pattern)) continue;
166
- if (planned.get(targetName)?.suppressed) continue;
167
139
  if (applyBump(planned, targetName, cascadeBumpType, false, true, bump.bumpFiles)) changed = true;
168
140
  }
169
141
  const cascadeTo = packages.get(pkgName)?.bumpy?.cascadeTo;
@@ -172,33 +144,13 @@ function assembleReleasePlan(bumpFiles, packages, depGraph, config) {
172
144
  const cascadeBump = rule.bumpAs === "match" ? bump.type : rule.bumpAs;
173
145
  for (const [targetName] of packages) {
174
146
  if (!matchGlob(targetName, pattern)) continue;
175
- if (planned.get(targetName)?.suppressed) continue;
176
147
  if (applyBump(planned, targetName, cascadeBump, false, true, bump.bumpFiles)) changed = true;
177
148
  }
178
149
  }
179
150
  }
180
151
  }
181
- for (const [name, bump] of planned) {
182
- if (!bump.suppressed) continue;
183
- const pkg = packages.get(name);
184
- for (const [depName, depBump] of planned) {
185
- if (depBump.suppressed) continue;
186
- const depPkg = packages.get(depName);
187
- const newDepVersion = bumpVersion(depPkg.version, depBump.type);
188
- for (const depType of [
189
- "dependencies",
190
- "peerDependencies",
191
- "optionalDependencies"
192
- ]) {
193
- const range = pkg[depType]?.[depName];
194
- if (!range) continue;
195
- if (!satisfies(newDepVersion, range, depPkg.version)) throw new Error(`Cannot suppress bump for '${name}' (via 'none' in bump file) — '${depName}' is bumping to ${newDepVersion} which breaks the declared range '${range}'. Either widen the range or remove the 'none' entry.`);
196
- }
197
- }
198
- }
199
152
  const releases = [];
200
153
  for (const [name, bump] of planned) {
201
- if (bump.suppressed) continue;
202
154
  const pkg = packages.get(name);
203
155
  if (!pkg) continue;
204
156
  const newVersion = bumpVersion(pkg.version, bump.type);
@@ -224,7 +176,6 @@ function assembleReleasePlan(bumpFiles, packages, depGraph, config) {
224
176
  function applyBump(planned, name, type, isDependencyBump, isCascadeBump, sourceBumpFiles) {
225
177
  const existing = planned.get(name);
226
178
  if (existing) {
227
- if (existing.suppressed) return false;
228
179
  const newType = maxBump(existing.type, type);
229
180
  if (newType === existing.type) return false;
230
181
  existing.type = newType;
@@ -235,7 +186,6 @@ function applyBump(planned, name, type, isDependencyBump, isCascadeBump, sourceB
235
186
  }
236
187
  planned.set(name, {
237
188
  type,
238
- suppressed: false,
239
189
  isDependencyBump,
240
190
  isCascadeBump,
241
191
  bumpFiles: new Set(sourceBumpFiles)
@@ -3,8 +3,8 @@ import { a as loadConfig } from "./config-D13G4-R8.mjs";
3
3
  import { t as discoverPackages } from "./workspace-BKOAMeki.mjs";
4
4
  import { t as DependencyGraph } from "./dep-graph-E-9-eQ2J.mjs";
5
5
  import { r as readBumpFiles, t as filterBranchBumpFiles } from "./bump-file-CoaSxqne.mjs";
6
- import { t as assembleReleasePlan } from "./release-plan-RBjKmavL.mjs";
7
- import { i as getCurrentBranch, r as getChangedFiles } from "./git-D0__HP86.mjs";
6
+ import { t as assembleReleasePlan } from "./release-plan-pyxf71dx.mjs";
7
+ import { i as getCurrentBranch, r as getChangedFiles } from "./git-ukq7VTuZ.mjs";
8
8
  //#region src/commands/status.ts
9
9
  async function statusCommand(rootDir, opts) {
10
10
  const config = await loadConfig(rootDir);
@@ -5,9 +5,9 @@ import { t as discoverPackages } from "./workspace-BKOAMeki.mjs";
5
5
  import { t as DependencyGraph } from "./dep-graph-E-9-eQ2J.mjs";
6
6
  import { n as runArgs, s as tryRunArgs } from "./shell-u3bYGxNy.mjs";
7
7
  import { r as readBumpFiles } from "./bump-file-CoaSxqne.mjs";
8
- import { t as assembleReleasePlan } from "./release-plan-RBjKmavL.mjs";
9
- import { t as applyReleasePlan } from "./apply-release-plan-BvZ32LAm.mjs";
10
- import { t as resolveCommitMessage } from "./commit-message-DOIfDxfj.mjs";
8
+ import { t as assembleReleasePlan } from "./release-plan-pyxf71dx.mjs";
9
+ import { t as applyReleasePlan } from "./apply-release-plan-60VfCLF8.mjs";
10
+ import { t as resolveCommitMessage } from "./commit-message-3e4KhzFV.mjs";
11
11
  //#region src/commands/version.ts
12
12
  async function versionCommand(rootDir, opts = {}) {
13
13
  const config = await loadConfig(rootDir);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@varlock/bumpy",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Modern monorepo versioning and changelog tool",
5
5
  "keywords": [
6
6
  "bump",
@@ -43,7 +43,7 @@ For each affected package, choose the appropriate bump level:
43
43
  | **minor** | New features: added exports, new options, new functionality |
44
44
  | **patch** | Bug fixes, internal refactors, documentation, dependency updates |
45
45
 
46
- Use `none` in a bump file to suppress a bump on a package that would otherwise be included via propagation. If skipping would leave a broken range, bumpy throws an error.
46
+ Use `none` in a bump file to acknowledge a change without triggering a direct bump. Cascading bumps from other packages can still apply normally.
47
47
 
48
48
  ### 4. Write a clear summary
49
49
 
@@ -1,92 +0,0 @@
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-D13G4-R8.mjs";
3
- import { n as discoverWorkspace } from "./workspace-BKOAMeki.mjs";
4
- import { r as readBumpFiles, t as filterBranchBumpFiles } from "./bump-file-CoaSxqne.mjs";
5
- import { r as getChangedFiles } from "./git-D0__HP86.mjs";
6
- import { t as require_picomatch } from "./picomatch-TGJi--_I.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 { bumpFiles: allBumpFiles, errors: parseErrors } = await readBumpFiles(rootDir);
29
- if (parseErrors.length > 0) {
30
- for (const err of parseErrors) log.error(err);
31
- process.exit(1);
32
- }
33
- const { branchBumpFiles, emptyBumpFileIds } = filterBranchBumpFiles(allBumpFiles, changedFiles, rootDir);
34
- if (emptyBumpFileIds.length > 0) {
35
- log.success("Empty bump file found — no releases needed.");
36
- return;
37
- }
38
- const coveredPackages = /* @__PURE__ */ new Set();
39
- for (const bf of branchBumpFiles) for (const release of bf.releases) coveredPackages.add(release.name);
40
- const changedPackages = await findChangedPackages(changedFiles, packages, rootDir, config);
41
- if (changedPackages.length === 0) {
42
- log.info("No managed packages have changed.");
43
- return;
44
- }
45
- const willFailNoBump = !opts.noFail;
46
- if (branchBumpFiles.length === 0) {
47
- (willFailNoBump ? log.error : log.warn)(`${changedPackages.length} changed package(s) missing bump files:\n`);
48
- for (const name of changedPackages) console.log(` ${colorize(name, "yellow")}`);
49
- console.log();
50
- log.dim("Run `bumpy add` to create a bump file, or `bumpy add --empty` if no release is needed.");
51
- log.dim("To adjust which files trigger change detection, set `changedFilePatterns` in .bumpy/_config.json.");
52
- if (willFailNoBump) process.exit(1);
53
- return;
54
- }
55
- const missing = changedPackages.filter((name) => !coveredPackages.has(name));
56
- if (missing.length === 0) {
57
- log.success(`🐸 All ${changedPackages.length} changed package(s) have bump files.`);
58
- return;
59
- }
60
- const willFailUncovered = opts.strict && !opts.noFail;
61
- (willFailUncovered ? log.error : log.warn)(`${missing.length} changed package(s) missing bump files:\n`);
62
- for (const name of missing) console.log(` ${colorize(name, "yellow")}`);
63
- if (branchBumpFiles.length > 0) {
64
- console.log();
65
- log.dim(`Existing bump files on this branch:`);
66
- for (const bf of branchBumpFiles) log.dim(` ${getBumpyDir(rootDir)}/${bf.id}.md`);
67
- }
68
- console.log();
69
- 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.");
70
- else log.dim("Run `bumpy add` to create a bump file, or `bumpy add --empty` if no release is needed.");
71
- log.dim("To adjust which files trigger change detection, set `changedFilePatterns` in .bumpy/_config.json.");
72
- if (willFailUncovered) process.exit(1);
73
- }
74
- /** Map changed files to the packages they belong to */
75
- async function findChangedPackages(changedFiles, packages, rootDir, config) {
76
- const changed = /* @__PURE__ */ new Set();
77
- const matchers = /* @__PURE__ */ new Map();
78
- for (const [name, pkg] of packages) {
79
- const patterns = (await loadPackageConfig(pkg.dir, config, name)).changedFilePatterns ?? config.changedFilePatterns;
80
- matchers.set(name, (0, import_picomatch.default)(patterns));
81
- }
82
- for (const file of changedFiles) for (const [name, pkg] of packages) {
83
- const pkgRelDir = relative(rootDir, pkg.dir);
84
- if (file.startsWith(pkgRelDir + "/")) {
85
- const relToPackage = file.slice(pkgRelDir.length + 1);
86
- if (matchers.get(name)(relToPackage)) changed.add(name);
87
- }
88
- }
89
- return [...changed];
90
- }
91
- //#endregion
92
- export { checkCommand, findChangedPackages };
File without changes