@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
@@ -0,0 +1,327 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://raw.githubusercontent.com/dmno-dev/bumpy/main/packages/bumpy/config-schema.json",
4
+ "title": "Bumpy Configuration",
5
+ "description": "Configuration for @varlock/bumpy — monorepo versioning and changelog tool",
6
+ "type": "object",
7
+ "properties": {
8
+ "$schema": {
9
+ "type": "string",
10
+ "description": "Path or URL to the JSON schema"
11
+ },
12
+ "baseBranch": {
13
+ "type": "string",
14
+ "description": "Branch used for release comparisons",
15
+ "default": "main"
16
+ },
17
+ "access": {
18
+ "type": "string",
19
+ "enum": ["public", "restricted"],
20
+ "description": "Default npm publish access level",
21
+ "default": "public"
22
+ },
23
+ "versionCommitMessage": {
24
+ "type": "string",
25
+ "description": "Customize the commit message used when versioning. A plain string is used as-is. A path starting with \"./\" or \"../\" is loaded as a module that exports a function receiving the release plan and returning a message string."
26
+ },
27
+ "changelog": {
28
+ "description": "Changelog formatter — \"default\", \"github\", or path to a custom formatter. Can also be a tuple of [formatter, options]. Set to false to disable changelog generation.",
29
+ "default": "default",
30
+ "oneOf": [
31
+ { "type": "boolean", "const": false },
32
+ { "type": "string" },
33
+ {
34
+ "type": "array",
35
+ "items": [{ "type": "string" }, { "type": "object" }],
36
+ "minItems": 2,
37
+ "maxItems": 2
38
+ }
39
+ ]
40
+ },
41
+ "changedFilePatterns": {
42
+ "type": "array",
43
+ "description": "Glob patterns to filter which changed files count toward marking a package as changed. Can be overridden per-package.",
44
+ "items": { "type": "string" },
45
+ "default": ["**"]
46
+ },
47
+ "fixed": {
48
+ "type": "array",
49
+ "description": "Package groups that always bump together to the same version. Each element is an array of package name globs.",
50
+ "items": {
51
+ "type": "array",
52
+ "items": { "type": "string" }
53
+ },
54
+ "default": []
55
+ },
56
+ "linked": {
57
+ "type": "array",
58
+ "description": "Package groups that share the highest bump level. Each element is an array of package name globs.",
59
+ "items": {
60
+ "type": "array",
61
+ "items": { "type": "string" }
62
+ },
63
+ "default": []
64
+ },
65
+ "ignore": {
66
+ "type": "array",
67
+ "description": "Package name globs to exclude from versioning",
68
+ "items": { "type": "string" },
69
+ "default": []
70
+ },
71
+ "include": {
72
+ "type": "array",
73
+ "description": "Package name globs to explicitly include (overrides ignore and privatePackages)",
74
+ "items": { "type": "string" },
75
+ "default": []
76
+ },
77
+ "updateInternalDependencies": {
78
+ "type": "string",
79
+ "enum": ["patch", "minor", "out-of-range"],
80
+ "description": "When to update internal dependency version ranges",
81
+ "default": "out-of-range"
82
+ },
83
+ "dependencyBumpRules": {
84
+ "type": "object",
85
+ "description": "Controls how bumps propagate through dependency types",
86
+ "properties": {
87
+ "dependencies": { "$ref": "#/$defs/dependencyBumpRule" },
88
+ "devDependencies": { "$ref": "#/$defs/dependencyBumpRule" },
89
+ "peerDependencies": { "$ref": "#/$defs/dependencyBumpRule" },
90
+ "optionalDependencies": { "$ref": "#/$defs/dependencyBumpRule" }
91
+ },
92
+ "additionalProperties": false
93
+ },
94
+ "privatePackages": {
95
+ "type": "object",
96
+ "description": "Whether to version and/or create git tags for private packages",
97
+ "properties": {
98
+ "version": {
99
+ "type": "boolean",
100
+ "description": "Whether to version private packages",
101
+ "default": false
102
+ },
103
+ "tag": {
104
+ "type": "boolean",
105
+ "description": "Whether to create git tags for private packages",
106
+ "default": false
107
+ }
108
+ },
109
+ "additionalProperties": false
110
+ },
111
+ "allowCustomCommands": {
112
+ "description": "Allow per-package custom commands (buildCommand, publishCommand, checkPublished) defined in package.json \"bumpy\" fields. true = allow all, string[] = allow matching package name globs, false = only root-config commands allowed.",
113
+ "default": false,
114
+ "oneOf": [
115
+ { "type": "boolean" },
116
+ {
117
+ "type": "array",
118
+ "items": { "type": "string" }
119
+ }
120
+ ]
121
+ },
122
+ "packages": {
123
+ "type": "object",
124
+ "description": "Per-package config overrides, keyed by package name or glob pattern",
125
+ "additionalProperties": { "$ref": "#/$defs/packageConfig" }
126
+ },
127
+ "publish": {
128
+ "type": "object",
129
+ "description": "Publishing pipeline configuration",
130
+ "properties": {
131
+ "packManager": {
132
+ "type": "string",
133
+ "enum": ["auto", "npm", "pnpm", "bun", "yarn"],
134
+ "description": "Package manager to use for packing. \"auto\" detects from lockfile.",
135
+ "default": "auto"
136
+ },
137
+ "publishManager": {
138
+ "type": "string",
139
+ "enum": ["npm", "pnpm", "bun", "yarn"],
140
+ "description": "Command to use for publishing. npm supports OIDC/provenance.",
141
+ "default": "npm"
142
+ },
143
+ "publishArgs": {
144
+ "type": "array",
145
+ "items": { "type": "string" },
146
+ "description": "Extra args appended to the publish command (e.g., \"--provenance\")",
147
+ "default": []
148
+ },
149
+ "protocolResolution": {
150
+ "type": "string",
151
+ "enum": ["pack", "in-place", "none"],
152
+ "description": "How to handle workspace:/catalog: protocol resolution. \"pack\" = use PM's pack to build a clean tarball, \"in-place\" = rewrite package.json before publish, \"none\" = don't resolve.",
153
+ "default": "pack"
154
+ }
155
+ },
156
+ "additionalProperties": false
157
+ },
158
+ "aggregateRelease": {
159
+ "description": "GitHub release creation (requires gh CLI). false = individual release per package, true = single aggregated release, or an object with enabled and optional title (supports {{date}}).",
160
+ "default": false,
161
+ "oneOf": [
162
+ { "type": "boolean" },
163
+ {
164
+ "type": "object",
165
+ "properties": {
166
+ "enabled": {
167
+ "type": "boolean",
168
+ "description": "Whether to create an aggregated release"
169
+ },
170
+ "title": {
171
+ "type": "string",
172
+ "description": "Custom title for the aggregated release (supports {{date}})"
173
+ }
174
+ },
175
+ "required": ["enabled"],
176
+ "additionalProperties": false
177
+ }
178
+ ]
179
+ },
180
+ "gitUser": {
181
+ "type": "object",
182
+ "description": "Git identity used for CI commits",
183
+ "properties": {
184
+ "name": {
185
+ "type": "string",
186
+ "description": "Git user name",
187
+ "default": "bumpy-bot"
188
+ },
189
+ "email": {
190
+ "type": "string",
191
+ "description": "Git user email",
192
+ "default": "276066384+bumpy-bot@users.noreply.github.com"
193
+ }
194
+ },
195
+ "additionalProperties": false
196
+ },
197
+ "versionPr": {
198
+ "type": "object",
199
+ "description": "Customize the version PR created by bumpy ci release",
200
+ "properties": {
201
+ "title": {
202
+ "type": "string",
203
+ "description": "PR title",
204
+ "default": "🐸 Versioned release"
205
+ },
206
+ "branch": {
207
+ "type": "string",
208
+ "description": "Branch name for the version PR",
209
+ "default": "bumpy/version-packages"
210
+ },
211
+ "preamble": {
212
+ "type": "string",
213
+ "description": "HTML/markdown content prepended to the PR body"
214
+ }
215
+ },
216
+ "additionalProperties": false
217
+ }
218
+ },
219
+ "additionalProperties": false,
220
+ "$defs": {
221
+ "bumpType": {
222
+ "type": "string",
223
+ "enum": ["major", "minor", "patch"]
224
+ },
225
+ "dependencyBumpRule": {
226
+ "oneOf": [
227
+ {
228
+ "type": "object",
229
+ "properties": {
230
+ "trigger": {
231
+ "$ref": "#/$defs/bumpType",
232
+ "description": "Minimum bump level that triggers propagation"
233
+ },
234
+ "bumpAs": {
235
+ "description": "What level to bump the dependent (\"match\" mirrors the triggering level)",
236
+ "oneOf": [{ "$ref": "#/$defs/bumpType" }, { "type": "string", "const": "match" }]
237
+ }
238
+ },
239
+ "required": ["trigger", "bumpAs"],
240
+ "additionalProperties": false
241
+ },
242
+ {
243
+ "type": "boolean",
244
+ "const": false,
245
+ "description": "Set to false to disable propagation for this dependency type"
246
+ }
247
+ ]
248
+ },
249
+ "packageConfig": {
250
+ "type": "object",
251
+ "description": "Per-package configuration",
252
+ "properties": {
253
+ "managed": {
254
+ "type": "boolean",
255
+ "description": "Explicitly opt this package in or out of version management"
256
+ },
257
+ "access": {
258
+ "type": "string",
259
+ "enum": ["public", "restricted"],
260
+ "description": "Override the global access level"
261
+ },
262
+ "publishCommand": {
263
+ "description": "Custom command(s) to publish this package (replaces npm publish)",
264
+ "oneOf": [
265
+ { "type": "string" },
266
+ {
267
+ "type": "array",
268
+ "items": { "type": "string" }
269
+ }
270
+ ]
271
+ },
272
+ "buildCommand": {
273
+ "type": "string",
274
+ "description": "Command to run before publishing"
275
+ },
276
+ "registry": {
277
+ "type": "string",
278
+ "description": "Custom npm registry URL"
279
+ },
280
+ "skipNpmPublish": {
281
+ "type": "boolean",
282
+ "description": "Don't publish to npm (still creates git tags)"
283
+ },
284
+ "checkPublished": {
285
+ "type": "string",
286
+ "description": "Custom command that outputs the currently published version"
287
+ },
288
+ "changedFilePatterns": {
289
+ "type": "array",
290
+ "description": "Glob patterns to filter which changed files count toward marking this package as changed (overrides root setting)",
291
+ "items": { "type": "string" }
292
+ },
293
+ "dependencyBumpRules": {
294
+ "type": "object",
295
+ "description": "Per-package override for dependency propagation rules",
296
+ "properties": {
297
+ "dependencies": { "$ref": "#/$defs/dependencyBumpRule" },
298
+ "devDependencies": { "$ref": "#/$defs/dependencyBumpRule" },
299
+ "peerDependencies": { "$ref": "#/$defs/dependencyBumpRule" },
300
+ "optionalDependencies": { "$ref": "#/$defs/dependencyBumpRule" }
301
+ },
302
+ "additionalProperties": false
303
+ },
304
+ "cascadeTo": {
305
+ "type": "object",
306
+ "description": "Explicit cascade targets — glob pattern mapped to { trigger, bumpAs }",
307
+ "additionalProperties": {
308
+ "type": "object",
309
+ "properties": {
310
+ "trigger": {
311
+ "$ref": "#/$defs/bumpType",
312
+ "description": "Minimum bump level that triggers the cascade"
313
+ },
314
+ "bumpAs": {
315
+ "description": "What level to bump the target",
316
+ "oneOf": [{ "$ref": "#/$defs/bumpType" }, { "type": "string", "const": "match" }]
317
+ }
318
+ },
319
+ "required": ["trigger", "bumpAs"],
320
+ "additionalProperties": false
321
+ }
322
+ }
323
+ },
324
+ "additionalProperties": false
325
+ }
326
+ }
327
+ }
@@ -1,15 +1,17 @@
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-DYR2XuFE.mjs";
3
- import { a as loadConfig, r as getBumpyDir, s as matchGlob } from "./config-XZWUL3ma.mjs";
4
- import { t as discoverPackages } from "./workspace-C5ULTyUN.mjs";
5
- import { t as DependencyGraph } from "./dep-graph-E-9-eQ2J.mjs";
6
- import { i as writeBumpFile } from "./bump-file-CCLXMLA8.mjs";
7
- import { n as getChangedFiles } from "./git-CGHVXXKw.mjs";
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-CDRCHrC-.mjs";
9
- import { n as slugify, t as randomName } from "./names-9VubBmL0.mjs";
3
+ import { a as loadConfig, o as loadPackageConfig, r as getBumpyDir, s as matchGlob } from "./config-B-Qg3DZH.mjs";
4
+ import { t as discoverPackages } from "./workspace-DWXlwcH4.mjs";
5
+ import { t as DependencyGraph } from "./dep-graph-DiLeAhl9.mjs";
6
+ import { i as writeBumpFile } from "./bump-file-DVqR3k67.mjs";
7
+ import { r as getChangedFiles } from "./git-YDedMddc.mjs";
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-C6bVkGxf.mjs";
9
+ import { n as slugify, t as randomName } from "./names-C-TuOPbd.mjs";
10
+ import { t as require_picomatch } from "./picomatch-DMmqYjgq.mjs";
10
11
  import { relative, resolve } from "node:path";
11
12
  import * as readline from "node:readline";
12
13
  //#region src/prompts/bump-select.ts
14
+ var import_picomatch = /* @__PURE__ */ __toESM(require_picomatch(), 1);
13
15
  var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
14
16
  const LEVELS = [
15
17
  "none",
@@ -186,7 +188,7 @@ async function addCommand(rootDir, opts) {
186
188
  const filePath = resolve(bumpyDir, `${filename}.md`);
187
189
  const { writeText } = await import("./fs-DYR2XuFE.mjs").then((n) => n.r);
188
190
  await writeText(filePath, "---\n---\n");
189
- log.success(`Created empty bump file: .bumpy/${filename}.md`);
191
+ log.success(`🐸 Created empty bump file: .bumpy/${filename}.md`);
190
192
  return;
191
193
  }
192
194
  let releases;
@@ -206,10 +208,18 @@ async function addCommand(rootDir, opts) {
206
208
  }
207
209
  const baseBranch = config.baseBranch;
208
210
  const changedFiles = getChangedFiles(rootDir, baseBranch);
211
+ const matchers = /* @__PURE__ */ new Map();
212
+ for (const [name, pkg] of pkgs) {
213
+ const patterns = (await loadPackageConfig(pkg.dir, config, name)).changedFilePatterns ?? config.changedFilePatterns;
214
+ matchers.set(name, (0, import_picomatch.default)(patterns));
215
+ }
209
216
  const changedPackageNames = /* @__PURE__ */ new Set();
210
217
  for (const file of changedFiles) for (const [name, pkg] of pkgs) {
211
218
  const pkgRelDir = relative(rootDir, pkg.dir);
212
- if (file.startsWith(pkgRelDir + "/")) changedPackageNames.add(name);
219
+ if (file.startsWith(pkgRelDir + "/")) {
220
+ const relToPackage = file.slice(pkgRelDir.length + 1);
221
+ if (matchers.get(name)(relToPackage)) changedPackageNames.add(name);
222
+ }
213
223
  }
214
224
  const bumpSelectResult = await bumpSelectPrompt([...pkgs.values()].map((pkg) => ({
215
225
  name: pkg.name,
@@ -287,11 +297,11 @@ async function addCommand(rootDir, opts) {
287
297
  if (await exists(resolve(bumpyDir, `${filename}.md`))) filename = `${filename}-${Date.now()}`;
288
298
  await writeBumpFile(rootDir, filename, releases, summary);
289
299
  if (opts.packages) {
290
- log.success(`Created bump file: .bumpy/${filename}.md`);
300
+ log.success(`🐸 Created bump file: .bumpy/${filename}.md`);
291
301
  for (const r of releases) log.dim(` ${r.name}: ${r.type}${formatCascade(r)}`);
292
302
  } else {
293
303
  wt(releases.map((r) => `${import_picocolors.default.cyan(r.name)} ${import_picocolors.default.dim("→")} ${import_picocolors.default.bold(r.type)}${formatCascade(r)}`).join("\n"), "Bump file");
294
- gt(import_picocolors.default.green(`Created .bumpy/${filename}.md`));
304
+ gt(import_picocolors.default.green(`🐸 Created .bumpy/${filename}.md`));
295
305
  }
296
306
  }
297
307
  function formatCascade(r) {
@@ -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 updateJsonFields, d as writeText, i as listFiles, l as updateJsonNestedField, n as exists, o as readText, s as removeFile } from "./fs-DYR2XuFE.mjs";
3
+ import { r as getBumpyDir } from "./config-B-Qg3DZH.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,8 +1,8 @@
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 { d as writeText, i as listFiles, o as readText } from "./fs-DYR2XuFE.mjs";
3
+ import { r as getBumpyDir } from "./config-B-Qg3DZH.mjs";
4
+ import { i as jsYaml } from "./package-manager-Clsmr-9r.mjs";
5
+ import { s as tryRunArgs } from "./shell-CY7OD48z.mjs";
6
6
  import { resolve } from "node:path";
7
7
  //#region src/core/bump-file.ts
8
8
  const VALID_BUMP_TYPES = new Set([
@@ -131,13 +131,27 @@ async function writeBumpFile(rootDir, filename, releases, summary) {
131
131
  }).trim()}\n---\n\n${summary}\n`);
132
132
  return filePath;
133
133
  }
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
134
  function fileToId(filePath) {
140
135
  return filePath.split("/").pop().replace(/\.md$/, "");
141
136
  }
137
+ /**
138
+ * Given a list of changed file paths (relative to root), extract the IDs
139
+ * of bump files that were added/modified. Shared by `check` and `ci check`.
140
+ */
141
+ function extractBumpFileIdsFromChangedFiles(changedFiles) {
142
+ return new Set(changedFiles.filter((f) => /^\.bumpy\/.*\.md$/.test(f) && !f.endsWith("README.md")).map((f) => f.replace(/^\.bumpy\//, "").replace(/\.md$/, "")));
143
+ }
144
+ /**
145
+ * Filter bump files to only those added/modified on the current branch.
146
+ * Returns the filtered bump files and whether any changed bump file was
147
+ * empty (has no releases — signals intentionally no releases needed).
148
+ */
149
+ function filterBranchBumpFiles(allBumpFiles, changedFiles) {
150
+ const branchBumpFileIds = extractBumpFileIdsFromChangedFiles(changedFiles);
151
+ return {
152
+ branchBumpFiles: allBumpFiles.filter((bf) => branchBumpFileIds.has(bf.id)),
153
+ branchBumpFileIds
154
+ };
155
+ }
142
156
  //#endregion
143
- export { writeBumpFile as i, parseBumpFile as n, readBumpFiles as r, deleteBumpFiles as t };
157
+ 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,65 @@
1
+ import { n as log, o as __toESM, t as colorize } from "./logger-C2dEe5Su.mjs";
2
+ import { a as loadConfig, o as loadPackageConfig } from "./config-B-Qg3DZH.mjs";
3
+ import { n as discoverWorkspace } from "./workspace-DWXlwcH4.mjs";
4
+ import { r as readBumpFiles, t as filterBranchBumpFiles } from "./bump-file-DVqR3k67.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
+ async function checkCommand(rootDir) {
16
+ const config = await loadConfig(rootDir);
17
+ const { packages } = await discoverWorkspace(rootDir, config);
18
+ const baseBranch = config.baseBranch;
19
+ const changedFiles = getChangedFiles(rootDir, baseBranch);
20
+ if (changedFiles.length === 0) {
21
+ log.info("No changed files detected.");
22
+ return;
23
+ }
24
+ const { branchBumpFiles, branchBumpFileIds } = filterBranchBumpFiles(await readBumpFiles(rootDir), changedFiles);
25
+ if (branchBumpFileIds.size > branchBumpFiles.length) {
26
+ log.success("Empty bump file found — no releases needed.");
27
+ return;
28
+ }
29
+ const coveredPackages = /* @__PURE__ */ new Set();
30
+ for (const bf of branchBumpFiles) for (const release of bf.releases) coveredPackages.add(release.name);
31
+ const changedPackages = await findChangedPackages(changedFiles, packages, rootDir, config);
32
+ if (changedPackages.length === 0) {
33
+ log.info("No managed packages have changed.");
34
+ return;
35
+ }
36
+ const missing = changedPackages.filter((name) => !coveredPackages.has(name));
37
+ if (missing.length === 0) {
38
+ log.success(`🐸 All ${changedPackages.length} changed package(s) have bump files.`);
39
+ return;
40
+ }
41
+ log.warn(`${missing.length} changed package(s) missing bump files:\n`);
42
+ for (const name of missing) console.log(` ${colorize(name, "yellow")}`);
43
+ console.log();
44
+ log.dim("Run `bumpy add` to create a bump file, or `bumpy add --empty` if no release is needed.");
45
+ process.exit(1);
46
+ }
47
+ /** Map changed files to the packages they belong to */
48
+ async function findChangedPackages(changedFiles, packages, rootDir, config) {
49
+ const changed = /* @__PURE__ */ new Set();
50
+ const matchers = /* @__PURE__ */ new Map();
51
+ for (const [name, pkg] of packages) {
52
+ const patterns = (await loadPackageConfig(pkg.dir, config, name)).changedFilePatterns ?? config.changedFilePatterns;
53
+ matchers.set(name, (0, import_picomatch.default)(patterns));
54
+ }
55
+ for (const file of changedFiles) for (const [name, pkg] of packages) {
56
+ const pkgRelDir = relative(rootDir, pkg.dir);
57
+ if (file.startsWith(pkgRelDir + "/")) {
58
+ const relToPackage = file.slice(pkgRelDir.length + 1);
59
+ if (matchers.get(name)(relToPackage)) changed.add(name);
60
+ }
61
+ }
62
+ return [...changed];
63
+ }
64
+ //#endregion
65
+ export { checkCommand };