@varlock/bumpy 0.0.0 → 0.0.2

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/dist/cli.d.mts ADDED
@@ -0,0 +1 @@
1
+ export { };
package/dist/cli.mjs ADDED
@@ -0,0 +1,214 @@
1
+ #!/usr/bin/env node
2
+ import { n as log, t as colorize } from "./logger-C2dEe5Su.mjs";
3
+ import { n as findRoot } from "./config-BkwIEaQg.mjs";
4
+ //#region src/cli.ts
5
+ const args = process.argv.slice(2);
6
+ const command = args[0];
7
+ function parseFlags(args) {
8
+ const flags = {};
9
+ for (let i = 0; i < args.length; i++) {
10
+ const arg = args[i];
11
+ if (arg.startsWith("--")) {
12
+ const key = arg.slice(2);
13
+ const next = args[i + 1];
14
+ if (next && !next.startsWith("--")) {
15
+ flags[key] = next;
16
+ i++;
17
+ } else flags[key] = true;
18
+ }
19
+ }
20
+ return flags;
21
+ }
22
+ async function main() {
23
+ const flags = parseFlags(args.slice(1));
24
+ try {
25
+ switch (command) {
26
+ case "init": {
27
+ const rootDir = await findRoot();
28
+ const { initCommand } = await import("./init-B0q3wEQW.mjs");
29
+ await initCommand(rootDir);
30
+ break;
31
+ }
32
+ case "add": {
33
+ const rootDir = await findRoot();
34
+ const { addCommand } = await import("./add-BjyVIUlr.mjs");
35
+ await addCommand(rootDir, {
36
+ packages: flags.packages,
37
+ message: flags.message,
38
+ name: flags.name,
39
+ empty: flags.empty === true
40
+ });
41
+ break;
42
+ }
43
+ case "status": {
44
+ const rootDir = await findRoot();
45
+ const { statusCommand } = await import("./status--Q8yAxQ4.mjs");
46
+ await statusCommand(rootDir, {
47
+ json: flags.json === true,
48
+ packagesOnly: flags.packages === true,
49
+ bumpType: flags.bump,
50
+ filter: flags.filter,
51
+ verbose: flags.verbose === true
52
+ });
53
+ break;
54
+ }
55
+ case "version": {
56
+ const rootDir = await findRoot();
57
+ const { versionCommand } = await import("./version-cAUkfYPx.mjs");
58
+ await versionCommand(rootDir);
59
+ break;
60
+ }
61
+ case "generate": {
62
+ const rootDir = await findRoot();
63
+ const { generateCommand } = await import("./generate-Btrsn1qi.mjs");
64
+ await generateCommand(rootDir, {
65
+ from: flags.from,
66
+ dryRun: flags["dry-run"] === true,
67
+ name: flags.name
68
+ });
69
+ break;
70
+ }
71
+ case "migrate": {
72
+ const rootDir = await findRoot();
73
+ const { migrateCommand } = await import("./migrate-CfQNwD0T.mjs");
74
+ await migrateCommand(rootDir, { force: flags.force === true });
75
+ break;
76
+ }
77
+ case "check": {
78
+ const rootDir = await findRoot();
79
+ const { checkCommand } = await import("./check-jIwike9F.mjs");
80
+ await checkCommand(rootDir);
81
+ break;
82
+ }
83
+ case "ci": {
84
+ const rootDir = await findRoot();
85
+ const subcommand = args[1];
86
+ const ciFlags = parseFlags(args.slice(2));
87
+ if (subcommand === "check") {
88
+ const { ciCheckCommand } = await import("./ci-D6LQbR38.mjs");
89
+ await ciCheckCommand(rootDir, {
90
+ comment: ciFlags.comment !== void 0 ? ciFlags.comment === true : void 0,
91
+ failOnMissing: ciFlags["fail-on-missing"] === true
92
+ });
93
+ } else if (subcommand === "release") {
94
+ const { ciReleaseCommand } = await import("./ci-D6LQbR38.mjs");
95
+ await ciReleaseCommand(rootDir, {
96
+ mode: ciFlags["auto-publish"] === true ? "auto-publish" : "version-pr",
97
+ tag: ciFlags.tag,
98
+ branch: ciFlags.branch
99
+ });
100
+ } else if (subcommand === "setup") {
101
+ const { ciSetupCommand } = await import("./ci-setup-C6FlOfW5.mjs");
102
+ await ciSetupCommand(rootDir);
103
+ } else {
104
+ log.error(`Unknown ci subcommand: ${subcommand}. Use "ci check", "ci release", or "ci setup".`);
105
+ process.exit(1);
106
+ }
107
+ break;
108
+ }
109
+ case "publish": {
110
+ const rootDir = await findRoot();
111
+ const { publishCommand } = await import("./publish-D_7RqEYL.mjs");
112
+ await publishCommand(rootDir, {
113
+ dryRun: flags["dry-run"] === true,
114
+ tag: flags.tag,
115
+ noPush: flags["no-push"] === true,
116
+ filter: flags.filter
117
+ });
118
+ break;
119
+ }
120
+ case "ai": {
121
+ const rootDir = await findRoot();
122
+ const subcommand = args[1];
123
+ const aiFlags = parseFlags(args.slice(2));
124
+ if (subcommand === "setup") {
125
+ const { aiSetupCommand } = await import("./ai-CQhUyHAG.mjs");
126
+ await aiSetupCommand(rootDir, { target: aiFlags.target });
127
+ } else {
128
+ log.error(`Unknown ai subcommand: ${subcommand}. Use "ai setup".`);
129
+ process.exit(1);
130
+ }
131
+ break;
132
+ }
133
+ case "--version":
134
+ case "-v":
135
+ console.log(`bumpy 0.0.2`);
136
+ break;
137
+ case "help":
138
+ case "--help":
139
+ case "-h":
140
+ case void 0:
141
+ printHelp();
142
+ break;
143
+ default:
144
+ log.error(`Unknown command: ${command}`);
145
+ printHelp();
146
+ process.exit(1);
147
+ }
148
+ } catch (err) {
149
+ log.error(err instanceof Error ? err.message : String(err));
150
+ process.exit(1);
151
+ }
152
+ }
153
+ function printHelp() {
154
+ console.log(`
155
+ ${colorize(`🐸 bumpy v0.0.2`, "bold")} - Modern monorepo versioning
156
+
157
+ Usage: bumpy <command> [options]
158
+
159
+ Commands:
160
+ init Initialize .bumpy/ directory
161
+ add Create a new changeset
162
+ generate Generate changeset from conventional commits
163
+ status Show pending releases
164
+ check Verify changed packages have changesets (for pre-push hooks)
165
+ version Apply changesets and bump versions
166
+ publish Publish versioned packages
167
+ ci check PR check — report pending releases, comment on PR
168
+ ci release Release — create version PR or auto-publish
169
+ ci setup Set up a token for triggering CI on version PRs
170
+ migrate Migrate from .changeset/ to .bumpy/
171
+ ai setup Install AI skill for creating changesets
172
+
173
+ Add options:
174
+ --packages <list> Package bumps (e.g., "pkg-a:minor,pkg-b:patch")
175
+ --message <text> Changeset summary
176
+ --name <name> Changeset filename
177
+ --empty Create an empty changeset
178
+
179
+ Generate options:
180
+ --from <ref> Git ref to scan from (default: last version tag)
181
+ --dry-run Preview without creating a changeset
182
+ --name <name> Changeset filename
183
+
184
+ Status options:
185
+ --json Output as JSON (includes dirs, changesets, packageNames)
186
+ --packages Output only package names, one per line
187
+ --bump <types> Filter by bump type (e.g., "major", "minor,patch")
188
+ --filter <names> Filter by package name/glob (e.g., "@myorg/*")
189
+ --verbose Show changeset details
190
+
191
+ Publish options:
192
+ --dry-run Preview without publishing
193
+ --tag <tag> npm dist-tag (e.g., "next", "beta")
194
+ --no-push Skip pushing git tags to remote
195
+ --filter <names> Publish only matching packages (e.g., "@myorg/*")
196
+
197
+ CI check options:
198
+ --comment Force PR comment on/off (auto-detected in CI)
199
+ --fail-on-missing Exit 1 if no changesets found
200
+
201
+ CI release options:
202
+ --auto-publish Version + publish directly (default: create version PR)
203
+ --tag <tag> npm dist-tag for auto-publish
204
+ --branch <name> Branch name for version PR (default: bumpy/version-packages)
205
+
206
+ AI setup options:
207
+ --target <tool> Target AI tool: opencode, cursor, codex
208
+
209
+ ${colorize("https://github.com/dmno-dev/bumpy", "dim")}
210
+ `);
211
+ }
212
+ main();
213
+ //#endregion
214
+ export {};
@@ -0,0 +1,215 @@
1
+ import { a as __exportAll } from "./logger-C2dEe5Su.mjs";
2
+ import { a as readJson, n as exists } from "./fs-0AtnPUUe.mjs";
3
+ import { resolve } from "node:path";
4
+ //#region src/types.ts
5
+ const BUMP_LEVELS = {
6
+ patch: 0,
7
+ minor: 1,
8
+ major: 2
9
+ };
10
+ function bumpLevel(type) {
11
+ return BUMP_LEVELS[type];
12
+ }
13
+ function parseIsolatedBump(type) {
14
+ if (type.endsWith("-isolated")) return {
15
+ bump: type.replace("-isolated", ""),
16
+ isolated: true
17
+ };
18
+ return {
19
+ bump: type,
20
+ isolated: false
21
+ };
22
+ }
23
+ function maxBump(a, b) {
24
+ if (!a) return b;
25
+ return bumpLevel(a) >= bumpLevel(b) ? a : b;
26
+ }
27
+ const DEFAULT_BUMP_RULES = {
28
+ dependencies: {
29
+ trigger: "patch",
30
+ bumpAs: "patch"
31
+ },
32
+ peerDependencies: {
33
+ trigger: "major",
34
+ bumpAs: "major"
35
+ },
36
+ devDependencies: {
37
+ trigger: "none",
38
+ bumpAs: "patch"
39
+ },
40
+ optionalDependencies: {
41
+ trigger: "minor",
42
+ bumpAs: "patch"
43
+ }
44
+ };
45
+ const DEP_TYPES = [
46
+ "dependencies",
47
+ "devDependencies",
48
+ "peerDependencies",
49
+ "optionalDependencies"
50
+ ];
51
+ const DEFAULT_PUBLISH_CONFIG = {
52
+ packManager: "auto",
53
+ publishManager: "npm",
54
+ publishArgs: [],
55
+ protocolResolution: "pack"
56
+ };
57
+ const DEFAULT_CONFIG = {
58
+ baseBranch: "main",
59
+ access: "public",
60
+ commit: false,
61
+ changelog: "default",
62
+ fixed: [],
63
+ linked: [],
64
+ ignore: [],
65
+ include: [],
66
+ updateInternalDependencies: "out-of-range",
67
+ dependencyBumpRules: {},
68
+ privatePackages: {
69
+ version: false,
70
+ tag: false
71
+ },
72
+ packages: {},
73
+ publish: { ...DEFAULT_PUBLISH_CONFIG },
74
+ aggregateRelease: false,
75
+ gitUser: {
76
+ name: "bumpy-bot",
77
+ email: "276066384+bumpy-bot@users.noreply.github.com"
78
+ },
79
+ versionPr: {
80
+ title: "🐸 Versioned release",
81
+ branch: "bumpy/version-packages",
82
+ preamble: [
83
+ `<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>`,
84
+ "",
85
+ `This PR was created and will be kept in sync by [bumpy](https://github.com/dmno-dev/bumpy) based on your .bumpy changeset files. Merge it when you are ready to release the packages listed below:`,
86
+ "<br clear=\"left\" />"
87
+ ].join("\n")
88
+ }
89
+ };
90
+ function hasCascade(r) {
91
+ return "cascade" in r && Object.keys(r.cascade).length > 0;
92
+ }
93
+ //#endregion
94
+ //#region src/core/config.ts
95
+ var config_exports = /* @__PURE__ */ __exportAll({
96
+ findRoot: () => findRoot,
97
+ getBumpyDir: () => getBumpyDir,
98
+ isPackageManaged: () => isPackageManaged,
99
+ loadConfig: () => loadConfig,
100
+ loadPackageConfig: () => loadPackageConfig,
101
+ matchGlob: () => matchGlob
102
+ });
103
+ const BUMPY_DIR = ".bumpy";
104
+ const CONFIG_FILE = "_config.json";
105
+ /** Find the monorepo root by walking up from cwd looking for .bumpy/ */
106
+ async function findRoot(startDir = process.cwd()) {
107
+ let dir = resolve(startDir);
108
+ while (true) {
109
+ if (await exists(resolve(dir, BUMPY_DIR))) return dir;
110
+ if (await exists(resolve(dir, "package.json"))) try {
111
+ if ((await readJson(resolve(dir, "package.json"))).workspaces) return dir;
112
+ } catch {}
113
+ const parent = resolve(dir, "..");
114
+ if (parent === dir) break;
115
+ dir = parent;
116
+ }
117
+ return resolve(startDir);
118
+ }
119
+ /** Load the root bumpy config, merging with defaults */
120
+ async function loadConfig(rootDir) {
121
+ const configPath = resolve(rootDir, BUMPY_DIR, CONFIG_FILE);
122
+ let userConfig = {};
123
+ if (await exists(configPath)) userConfig = await readJson(configPath);
124
+ return mergeConfig(DEFAULT_CONFIG, userConfig);
125
+ }
126
+ /** Load per-package bumpy config from package.json["bumpy"] or .bumpy.config.json */
127
+ async function loadPackageConfig(pkgDir, rootConfig, pkgName) {
128
+ const rootPkgConfig = findPackageConfig(rootConfig, pkgName);
129
+ let pkgJsonConfig = {};
130
+ try {
131
+ const pkg = await readJson(resolve(pkgDir, "package.json"));
132
+ if (pkg.bumpy && typeof pkg.bumpy === "object") pkgJsonConfig = pkg.bumpy;
133
+ } catch {}
134
+ return mergePackageConfig(rootPkgConfig, pkgJsonConfig);
135
+ }
136
+ /** Find a package config from the root config, supporting glob patterns */
137
+ function findPackageConfig(config, pkgName) {
138
+ if (config.packages[pkgName]) return config.packages[pkgName];
139
+ for (const [pattern, pkgConfig] of Object.entries(config.packages)) if (matchGlob(pkgName, pattern)) return pkgConfig;
140
+ return {};
141
+ }
142
+ /** Simple glob matching for package names (supports * and **) */
143
+ function matchGlob(name, pattern) {
144
+ if (name === pattern) return true;
145
+ const regexStr = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "{{DOUBLE}}").replace(/\*/g, "[^/]*").replace(/{{DOUBLE}}/g, ".*");
146
+ return new RegExp(`^${regexStr}$`).test(name);
147
+ }
148
+ function mergeConfig(defaults, user) {
149
+ return {
150
+ ...defaults,
151
+ ...user,
152
+ dependencyBumpRules: {
153
+ ...defaults.dependencyBumpRules,
154
+ ...user.dependencyBumpRules
155
+ },
156
+ privatePackages: {
157
+ ...defaults.privatePackages,
158
+ ...user.privatePackages
159
+ },
160
+ publish: {
161
+ ...defaults.publish,
162
+ ...user.publish
163
+ },
164
+ packages: {
165
+ ...defaults.packages,
166
+ ...user.packages
167
+ }
168
+ };
169
+ }
170
+ function mergePackageConfig(...configs) {
171
+ const result = {};
172
+ for (const cfg of configs) {
173
+ Object.assign(result, cfg);
174
+ if (cfg.dependencyBumpRules) result.dependencyBumpRules = {
175
+ ...result.dependencyBumpRules,
176
+ ...cfg.dependencyBumpRules
177
+ };
178
+ if (cfg.specificDependencyRules) result.specificDependencyRules = {
179
+ ...result.specificDependencyRules,
180
+ ...cfg.specificDependencyRules
181
+ };
182
+ if (cfg.cascadeTo) result.cascadeTo = {
183
+ ...result.cascadeTo,
184
+ ...cfg.cascadeTo
185
+ };
186
+ }
187
+ return result;
188
+ }
189
+ function getBumpyDir(rootDir) {
190
+ return resolve(rootDir, BUMPY_DIR);
191
+ }
192
+ /**
193
+ * Determine if a package should be managed by bumpy.
194
+ * Resolution order:
195
+ * 1. Per-package `managed: false` → skip (explicit opt-out)
196
+ * 2. `config.ignore` glob match → skip
197
+ * 3. Per-package `managed: true` → include (explicit opt-in, overrides private)
198
+ * 4. `config.include` glob match → include (overrides private)
199
+ * 5. Private package + `config.privatePackages.version` false → skip
200
+ * 6. Otherwise → include
201
+ */
202
+ function isPackageManaged(pkgName, isPrivate, config, pkgBumpy) {
203
+ if (pkgBumpy?.managed === false) return false;
204
+ if (config.ignore.some((pattern) => matchGlob(pkgName, pattern))) {
205
+ if (pkgBumpy?.managed === true) return true;
206
+ if (config.include.some((pattern) => matchGlob(pkgName, pattern))) return true;
207
+ return false;
208
+ }
209
+ if (pkgBumpy?.managed === true) return true;
210
+ if (config.include.some((pattern) => matchGlob(pkgName, pattern))) return true;
211
+ if (isPrivate && !config.privatePackages.version) return false;
212
+ return true;
213
+ }
214
+ //#endregion
215
+ export { loadConfig as a, BUMP_LEVELS as c, DEFAULT_PUBLISH_CONFIG as d, DEP_TYPES as f, parseIsolatedBump as g, maxBump as h, isPackageManaged as i, DEFAULT_BUMP_RULES as l, hasCascade as m, findRoot as n, loadPackageConfig as o, bumpLevel as p, getBumpyDir as r, matchGlob as s, config_exports as t, DEFAULT_CONFIG as u };
@@ -0,0 +1,64 @@
1
+ //#region src/core/dep-graph.ts
2
+ var DependencyGraph = class {
3
+ /** Map from package name → packages that depend on it */
4
+ dependents = /* @__PURE__ */ new Map();
5
+ /** Set of all internal package names */
6
+ internalPackages;
7
+ constructor(packages) {
8
+ this.internalPackages = new Set(packages.keys());
9
+ this.build(packages);
10
+ }
11
+ build(packages) {
12
+ for (const [name, pkg] of packages) {
13
+ const depTypes = [
14
+ ["dependencies", pkg.dependencies],
15
+ ["devDependencies", pkg.devDependencies],
16
+ ["peerDependencies", pkg.peerDependencies],
17
+ ["optionalDependencies", pkg.optionalDependencies]
18
+ ];
19
+ for (const [depType, deps] of depTypes) for (const [depName, versionRange] of Object.entries(deps)) {
20
+ if (!this.internalPackages.has(depName)) continue;
21
+ if (!this.dependents.has(depName)) this.dependents.set(depName, []);
22
+ this.dependents.get(depName).push({
23
+ name,
24
+ depType,
25
+ versionRange
26
+ });
27
+ }
28
+ }
29
+ }
30
+ /** Get all packages that depend on the given package */
31
+ getDependents(pkgName) {
32
+ return this.dependents.get(pkgName) || [];
33
+ }
34
+ /** Check if a package is an internal workspace package */
35
+ isInternal(pkgName) {
36
+ return this.internalPackages.has(pkgName);
37
+ }
38
+ /** Get all internal package names */
39
+ allPackages() {
40
+ return [...this.internalPackages];
41
+ }
42
+ /** Topological sort — returns packages in dependency order (deps first) */
43
+ topologicalSort(packages) {
44
+ const visited = /* @__PURE__ */ new Set();
45
+ const result = [];
46
+ const visit = (name) => {
47
+ if (visited.has(name)) return;
48
+ visited.add(name);
49
+ const pkg = packages.get(name);
50
+ if (!pkg) return;
51
+ for (const deps of [
52
+ pkg.dependencies,
53
+ pkg.devDependencies,
54
+ pkg.peerDependencies,
55
+ pkg.optionalDependencies
56
+ ]) for (const depName of Object.keys(deps)) if (this.internalPackages.has(depName)) visit(depName);
57
+ result.push(name);
58
+ };
59
+ for (const name of this.internalPackages) visit(name);
60
+ return result;
61
+ }
62
+ };
63
+ //#endregion
64
+ export { DependencyGraph as t };
@@ -0,0 +1,51 @@
1
+ import { a as __exportAll } from "./logger-C2dEe5Su.mjs";
2
+ import { access, mkdir, readFile, readdir, unlink, writeFile } from "node:fs/promises";
3
+ //#region src/utils/fs.ts
4
+ var fs_exports = /* @__PURE__ */ __exportAll({
5
+ ensureDir: () => ensureDir,
6
+ exists: () => exists,
7
+ listFiles: () => listFiles,
8
+ readJson: () => readJson,
9
+ readText: () => readText,
10
+ removeFile: () => removeFile,
11
+ writeJson: () => writeJson,
12
+ writeText: () => writeText
13
+ });
14
+ async function readJson(filePath) {
15
+ const content = await readFile(filePath, "utf-8");
16
+ return JSON.parse(content);
17
+ }
18
+ async function writeJson(filePath, data, indent = 2) {
19
+ await writeFile(filePath, JSON.stringify(data, null, indent) + "\n", "utf-8");
20
+ }
21
+ async function readText(filePath) {
22
+ return readFile(filePath, "utf-8");
23
+ }
24
+ async function writeText(filePath, content) {
25
+ await writeFile(filePath, content, "utf-8");
26
+ }
27
+ async function exists(filePath) {
28
+ try {
29
+ await access(filePath);
30
+ return true;
31
+ } catch {
32
+ return false;
33
+ }
34
+ }
35
+ async function listFiles(dir, ext) {
36
+ try {
37
+ const entries = await readdir(dir);
38
+ if (ext) return entries.filter((e) => e.endsWith(ext));
39
+ return entries;
40
+ } catch {
41
+ return [];
42
+ }
43
+ }
44
+ async function removeFile(filePath) {
45
+ await unlink(filePath);
46
+ }
47
+ async function ensureDir(dir) {
48
+ await mkdir(dir, { recursive: true });
49
+ }
50
+ //#endregion
51
+ export { readJson as a, writeJson as c, listFiles as i, writeText as l, exists as n, readText as o, fs_exports as r, removeFile as s, ensureDir as t };