@varlock/bumpy 0.0.1 → 1.0.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/.claude-plugin/plugin.json +2 -2
- package/dist/add-CgCjs4d-.mjs +313 -0
- package/dist/{ai-B8ZL2x8z.mjs → ai-sMYUf3lP.mjs} +22 -5
- package/dist/{apply-release-plan-DtU3rVyL.mjs → apply-release-plan-CczGWJTk.mjs} +34 -25
- package/dist/bump-file-CCLXMLA8.mjs +143 -0
- package/dist/changelog-github-Cd8uJHZI.mjs +195 -0
- package/dist/{check-CkRubvuk.mjs → check-BOoxpWqk.mjs} +11 -17
- package/dist/ci-Bhx--Tj6.mjs +629 -0
- package/dist/ci-setup-qz4Y3v7T.mjs +211 -0
- package/dist/clack-CDRCHrC-.mjs +1216 -0
- package/dist/cli.mjs +37 -31
- package/dist/{config-CJ2orhTL.mjs → config-XZWUL3ma.mjs} +28 -23
- package/dist/fs-DYR2XuFE.mjs +81 -0
- package/dist/{generate-oOFD9ABC.mjs → generate-gYKTpvex.mjs} +31 -12
- package/dist/git-CGHVXXKw.mjs +78 -0
- package/dist/index.d.mts +63 -37
- package/dist/index.mjs +9 -9
- package/dist/{init-Blw2GfC_.mjs → init-lA9E5pEc.mjs} +3 -3
- package/dist/logger-C2dEe5Su.mjs +135 -0
- package/dist/{migrate-DvOrXSw0.mjs → migrate-DmOYgmfD.mjs} +23 -16
- package/dist/{names-C-u50ofE.mjs → names-9VubBmL0.mjs} +3 -2
- package/dist/package-manager-VCe10bjc.mjs +80 -0
- package/dist/{publish-DZ3m7qkX.mjs → publish-Cun-zQ1b.mjs} +90 -35
- package/dist/{publish-pipeline-1M5GmbdP.mjs → publish-pipeline-BwBuKCIk.mjs} +56 -65
- package/dist/release-plan-Bi5QNSEo.mjs +264 -0
- package/dist/{semver-DWO6NFKN.mjs → semver-DfQyVLM_.mjs} +14 -4
- package/dist/shell-Dj7JRD_q.mjs +92 -0
- package/dist/{status-DRpq_Mha.mjs → status-CfE63ti5.mjs} +27 -23
- package/dist/version-19vVt9dv.mjs +124 -0
- package/dist/workspace-C5ULTyUN.mjs +107 -0
- package/package.json +16 -2
- package/skills/add-change/SKILL.md +8 -12
- package/dist/add-u5V9V3L7.mjs +0 -131
- package/dist/changelog-github-n-3zV1p9.mjs +0 -59
- package/dist/changeset-ClCYsChu.mjs +0 -75
- package/dist/ci-8KWWhjXl.mjs +0 -224
- package/dist/fs-DbNNEyzq.mjs +0 -51
- package/dist/logger-ZqggsyGZ.mjs +0 -176
- package/dist/prompt-BP8toAOI.mjs +0 -46
- package/dist/release-plan-CFnutSHD.mjs +0 -173
- package/dist/shell-DPlltpzb.mjs +0 -44
- package/dist/version-CJwf8XIA.mjs +0 -81
- package/dist/workspace-mVjawG8g.mjs +0 -183
- /package/dist/{dep-graph-DiLeAhl9.mjs → dep-graph-E-9-eQ2J.mjs} +0 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { h as maxBump, l as DEFAULT_BUMP_RULES, m as hasCascade, p as bumpLevel, s as matchGlob } from "./config-XZWUL3ma.mjs";
|
|
2
|
+
import { n as satisfies, t as bumpVersion } from "./semver-DfQyVLM_.mjs";
|
|
3
|
+
//#region src/core/release-plan.ts
|
|
4
|
+
/**
|
|
5
|
+
* Build a release plan from pending bump files, the dependency graph, and config.
|
|
6
|
+
* This is the core algorithm of bumpy.
|
|
7
|
+
*
|
|
8
|
+
* The propagation loop runs three phases until stable:
|
|
9
|
+
* Phase A — fix out-of-range dependencies (always runs)
|
|
10
|
+
* Phase B — enforce fixed/linked group constraints
|
|
11
|
+
* Phase C — apply cascades and proactive propagation rules
|
|
12
|
+
*/
|
|
13
|
+
function assembleReleasePlan(bumpFiles, packages, depGraph, config) {
|
|
14
|
+
if (bumpFiles.length === 0) return {
|
|
15
|
+
bumpFiles: [],
|
|
16
|
+
releases: [],
|
|
17
|
+
warnings: []
|
|
18
|
+
};
|
|
19
|
+
const planned = /* @__PURE__ */ new Map();
|
|
20
|
+
const warnings = [];
|
|
21
|
+
const cascadeOverrides = /* @__PURE__ */ new Map();
|
|
22
|
+
const suppressedPackages = /* @__PURE__ */ new Set();
|
|
23
|
+
for (const bf of bumpFiles) for (const release of bf.releases) {
|
|
24
|
+
if (!packages.has(release.name)) continue;
|
|
25
|
+
const bump = release.type;
|
|
26
|
+
if (bump === "none") {
|
|
27
|
+
suppressedPackages.add(release.name);
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const existing = planned.get(release.name);
|
|
31
|
+
if (existing) {
|
|
32
|
+
existing.type = maxBump(existing.type, bump);
|
|
33
|
+
existing.bumpFiles.add(bf.id);
|
|
34
|
+
} else planned.set(release.name, {
|
|
35
|
+
type: bump,
|
|
36
|
+
suppressed: false,
|
|
37
|
+
isDependencyBump: false,
|
|
38
|
+
isCascadeBump: false,
|
|
39
|
+
bumpFiles: new Set([bf.id])
|
|
40
|
+
});
|
|
41
|
+
if (hasCascade(release)) {
|
|
42
|
+
if (!cascadeOverrides.has(release.name)) cascadeOverrides.set(release.name, /* @__PURE__ */ new Map());
|
|
43
|
+
const overrides = cascadeOverrides.get(release.name);
|
|
44
|
+
for (const [pattern, bumpType] of Object.entries(release.cascade)) {
|
|
45
|
+
const existing = overrides.get(pattern);
|
|
46
|
+
overrides.set(pattern, maxBump(existing, bumpType));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
for (const name of suppressedPackages) if (!planned.has(name)) planned.set(name, {
|
|
51
|
+
type: "patch",
|
|
52
|
+
suppressed: true,
|
|
53
|
+
isDependencyBump: false,
|
|
54
|
+
isCascadeBump: false,
|
|
55
|
+
bumpFiles: /* @__PURE__ */ new Set()
|
|
56
|
+
});
|
|
57
|
+
let changed = true;
|
|
58
|
+
let iterations = 0;
|
|
59
|
+
const MAX_ITERATIONS = 100;
|
|
60
|
+
while (changed && iterations < MAX_ITERATIONS) {
|
|
61
|
+
changed = false;
|
|
62
|
+
iterations++;
|
|
63
|
+
for (const [pkgName, bump] of planned) {
|
|
64
|
+
if (bump.suppressed) continue;
|
|
65
|
+
const pkg = packages.get(pkgName);
|
|
66
|
+
const newVersion = bumpVersion(pkg.version, bump.type);
|
|
67
|
+
const dependents = depGraph.getDependents(pkgName);
|
|
68
|
+
for (const dep of dependents) {
|
|
69
|
+
if (dep.depType === "devDependencies") continue;
|
|
70
|
+
const currentVersion = pkg.version;
|
|
71
|
+
if (satisfies(newVersion, dep.versionRange, currentVersion)) continue;
|
|
72
|
+
let depBump;
|
|
73
|
+
if (dep.depType === "peerDependencies") depBump = bump.type;
|
|
74
|
+
else depBump = "patch";
|
|
75
|
+
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.`);
|
|
76
|
+
if (dep.depType === "peerDependencies" && depBump !== "patch") {
|
|
77
|
+
let resolvedRange = dep.versionRange.replace(/^workspace:/, "");
|
|
78
|
+
if (resolvedRange === "^" || resolvedRange === "~") resolvedRange = `${resolvedRange}${pkg.version}`;
|
|
79
|
+
if (/^\^0(\.|$)/.test(resolvedRange)) warnings.push(`${dep.name} gets a ${depBump} bump because ${pkgName}@${newVersion} is out of range for its peer dep "${dep.versionRange}" (resolves to ${resolvedRange}). npm treats ^ on 0.x as minor-breaking. Consider using >=0.x ranges for pre-1.0 peer deps.`);
|
|
80
|
+
}
|
|
81
|
+
if (applyBump(planned, dep.name, depBump, true, false, bump.bumpFiles)) changed = true;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
for (const group of config.fixed) {
|
|
85
|
+
let groupBump;
|
|
86
|
+
for (const nameOrGlob of group) for (const [name, bump] of planned) {
|
|
87
|
+
if (bump.suppressed) continue;
|
|
88
|
+
if (matchGlob(name, nameOrGlob)) groupBump = maxBump(groupBump, bump.type);
|
|
89
|
+
}
|
|
90
|
+
if (!groupBump) continue;
|
|
91
|
+
for (const nameOrGlob of group) for (const [name] of packages) {
|
|
92
|
+
if (!matchGlob(name, nameOrGlob)) continue;
|
|
93
|
+
const existing = planned.get(name);
|
|
94
|
+
if (existing && existing.suppressed) continue;
|
|
95
|
+
if (existing) {
|
|
96
|
+
const newType = maxBump(existing.type, groupBump);
|
|
97
|
+
if (newType !== existing.type) {
|
|
98
|
+
existing.type = newType;
|
|
99
|
+
changed = true;
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
planned.set(name, {
|
|
103
|
+
type: groupBump,
|
|
104
|
+
suppressed: false,
|
|
105
|
+
isDependencyBump: false,
|
|
106
|
+
isCascadeBump: false,
|
|
107
|
+
bumpFiles: /* @__PURE__ */ new Set()
|
|
108
|
+
});
|
|
109
|
+
changed = true;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
for (const group of config.linked) {
|
|
114
|
+
let groupBump;
|
|
115
|
+
for (const nameOrGlob of group) for (const [name, bump] of planned) {
|
|
116
|
+
if (bump.suppressed) continue;
|
|
117
|
+
if (matchGlob(name, nameOrGlob)) groupBump = maxBump(groupBump, bump.type);
|
|
118
|
+
}
|
|
119
|
+
if (!groupBump) continue;
|
|
120
|
+
for (const nameOrGlob of group) for (const [name] of packages) {
|
|
121
|
+
if (!matchGlob(name, nameOrGlob)) continue;
|
|
122
|
+
const existing = planned.get(name);
|
|
123
|
+
if (!existing || existing.suppressed) continue;
|
|
124
|
+
const newType = maxBump(existing.type, groupBump);
|
|
125
|
+
if (newType !== existing.type) {
|
|
126
|
+
existing.type = newType;
|
|
127
|
+
changed = true;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (config.updateInternalDependencies !== "out-of-range") for (const [pkgName, bump] of planned) {
|
|
132
|
+
if (bump.suppressed) continue;
|
|
133
|
+
if (config.updateInternalDependencies === "minor" && bumpLevel(bump.type) < bumpLevel("minor")) continue;
|
|
134
|
+
const bfOverrides = cascadeOverrides.get(pkgName);
|
|
135
|
+
if (bfOverrides) for (const [pattern, cascadeBumpType] of bfOverrides) for (const [targetName] of packages) {
|
|
136
|
+
if (!matchGlob(targetName, pattern)) continue;
|
|
137
|
+
if (planned.get(targetName)?.suppressed) continue;
|
|
138
|
+
if (applyBump(planned, targetName, cascadeBumpType, false, true, bump.bumpFiles)) changed = true;
|
|
139
|
+
}
|
|
140
|
+
const cascadeTo = packages.get(pkgName)?.bumpy?.cascadeTo;
|
|
141
|
+
if (cascadeTo) for (const [pattern, rule] of Object.entries(cascadeTo)) {
|
|
142
|
+
if (!shouldTrigger(bump.type, rule.trigger)) continue;
|
|
143
|
+
const cascadeBump = rule.bumpAs === "match" ? bump.type : rule.bumpAs;
|
|
144
|
+
for (const [targetName] of packages) {
|
|
145
|
+
if (!matchGlob(targetName, pattern)) continue;
|
|
146
|
+
if (planned.get(targetName)?.suppressed) continue;
|
|
147
|
+
if (applyBump(planned, targetName, cascadeBump, false, true, bump.bumpFiles)) changed = true;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
const dependents = depGraph.getDependents(pkgName);
|
|
151
|
+
for (const dep of dependents) {
|
|
152
|
+
const rule = resolveRule(dep.name, dep.depType, packages, config);
|
|
153
|
+
if (!rule) continue;
|
|
154
|
+
if (!shouldTrigger(bump.type, rule.trigger)) continue;
|
|
155
|
+
if (planned.get(dep.name)?.suppressed) continue;
|
|
156
|
+
const depBump = rule.bumpAs === "match" ? bump.type : rule.bumpAs;
|
|
157
|
+
if (applyBump(planned, dep.name, depBump, true, false, bump.bumpFiles)) changed = true;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
else for (const [pkgName, bump] of planned) {
|
|
161
|
+
if (bump.suppressed) continue;
|
|
162
|
+
const bfOverrides = cascadeOverrides.get(pkgName);
|
|
163
|
+
if (bfOverrides) for (const [pattern, cascadeBumpType] of bfOverrides) for (const [targetName] of packages) {
|
|
164
|
+
if (!matchGlob(targetName, pattern)) continue;
|
|
165
|
+
if (planned.get(targetName)?.suppressed) continue;
|
|
166
|
+
if (applyBump(planned, targetName, cascadeBumpType, false, true, bump.bumpFiles)) changed = true;
|
|
167
|
+
}
|
|
168
|
+
const cascadeTo = packages.get(pkgName)?.bumpy?.cascadeTo;
|
|
169
|
+
if (cascadeTo) for (const [pattern, rule] of Object.entries(cascadeTo)) {
|
|
170
|
+
if (!shouldTrigger(bump.type, rule.trigger)) continue;
|
|
171
|
+
const cascadeBump = rule.bumpAs === "match" ? bump.type : rule.bumpAs;
|
|
172
|
+
for (const [targetName] of packages) {
|
|
173
|
+
if (!matchGlob(targetName, pattern)) continue;
|
|
174
|
+
if (planned.get(targetName)?.suppressed) continue;
|
|
175
|
+
if (applyBump(planned, targetName, cascadeBump, false, true, bump.bumpFiles)) changed = true;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
for (const [name, bump] of planned) {
|
|
181
|
+
if (!bump.suppressed) continue;
|
|
182
|
+
const pkg = packages.get(name);
|
|
183
|
+
for (const [depName, depBump] of planned) {
|
|
184
|
+
if (depBump.suppressed) continue;
|
|
185
|
+
const depPkg = packages.get(depName);
|
|
186
|
+
const newDepVersion = bumpVersion(depPkg.version, depBump.type);
|
|
187
|
+
for (const depType of [
|
|
188
|
+
"dependencies",
|
|
189
|
+
"peerDependencies",
|
|
190
|
+
"optionalDependencies"
|
|
191
|
+
]) {
|
|
192
|
+
const range = pkg[depType]?.[depName];
|
|
193
|
+
if (!range) continue;
|
|
194
|
+
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.`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const releases = [];
|
|
199
|
+
for (const [name, bump] of planned) {
|
|
200
|
+
if (bump.suppressed) continue;
|
|
201
|
+
const pkg = packages.get(name);
|
|
202
|
+
if (!pkg) continue;
|
|
203
|
+
const newVersion = bumpVersion(pkg.version, bump.type);
|
|
204
|
+
releases.push({
|
|
205
|
+
name,
|
|
206
|
+
type: bump.type,
|
|
207
|
+
oldVersion: pkg.version,
|
|
208
|
+
newVersion,
|
|
209
|
+
bumpFiles: [...bump.bumpFiles],
|
|
210
|
+
isDependencyBump: bump.isDependencyBump,
|
|
211
|
+
isCascadeBump: bump.isCascadeBump
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
releases.sort((a, b) => a.name.localeCompare(b.name));
|
|
215
|
+
for (const [name, pkg] of packages) for (const [depName, range] of Object.entries(pkg.peerDependencies)) if (range === "workspace:*" && packages.has(depName)) warnings.push(`${name} has peer dep "${depName}": "workspace:*" — this will be published as a fixed range which may not match your intent. Consider using "workspace:^" instead.`);
|
|
216
|
+
return {
|
|
217
|
+
bumpFiles,
|
|
218
|
+
releases,
|
|
219
|
+
warnings
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
/** Apply a bump to a package, upgrading if already planned. Returns true if anything changed. */
|
|
223
|
+
function applyBump(planned, name, type, isDependencyBump, isCascadeBump, sourceBumpFiles) {
|
|
224
|
+
const existing = planned.get(name);
|
|
225
|
+
if (existing) {
|
|
226
|
+
if (existing.suppressed) return false;
|
|
227
|
+
const newType = maxBump(existing.type, type);
|
|
228
|
+
if (newType === existing.type) return false;
|
|
229
|
+
existing.type = newType;
|
|
230
|
+
if (isDependencyBump) existing.isDependencyBump = true;
|
|
231
|
+
if (isCascadeBump) existing.isCascadeBump = true;
|
|
232
|
+
for (const bf of sourceBumpFiles) existing.bumpFiles.add(bf);
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
planned.set(name, {
|
|
236
|
+
type,
|
|
237
|
+
suppressed: false,
|
|
238
|
+
isDependencyBump,
|
|
239
|
+
isCascadeBump,
|
|
240
|
+
bumpFiles: new Set(sourceBumpFiles)
|
|
241
|
+
});
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
/** Check if a bump level meets the trigger threshold */
|
|
245
|
+
function shouldTrigger(bumpType, trigger) {
|
|
246
|
+
return bumpLevel(bumpType) >= bumpLevel(trigger);
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Resolve the dependency bump rule for a specific dependent + dep type.
|
|
250
|
+
* Priority: per-package depType rules > global depType rules > defaults
|
|
251
|
+
* Returns false if the rule is disabled.
|
|
252
|
+
*/
|
|
253
|
+
function resolveRule(dependentName, depType, packages, config) {
|
|
254
|
+
const dependent = packages.get(dependentName);
|
|
255
|
+
if (dependent?.bumpy?.dependencyBumpRules && depType in dependent.bumpy.dependencyBumpRules) return dependent.bumpy.dependencyBumpRules[depType];
|
|
256
|
+
if (depType in config.dependencyBumpRules) return config.dependencyBumpRules[depType];
|
|
257
|
+
const defaultRule = DEFAULT_BUMP_RULES[depType];
|
|
258
|
+
return defaultRule !== void 0 ? defaultRule : {
|
|
259
|
+
trigger: "patch",
|
|
260
|
+
bumpAs: "patch"
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
//#endregion
|
|
264
|
+
export { assembleReleasePlan as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as __commonJSMin, o as __toESM } from "./logger-C2dEe5Su.mjs";
|
|
2
2
|
//#region ../../node_modules/.bun/semver@7.7.4/node_modules/semver/internal/constants.js
|
|
3
3
|
var require_constants = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
4
4
|
const SEMVER_SPEC_VERSION = "2.0.0";
|
|
@@ -1341,11 +1341,21 @@ function bumpVersion(version, type) {
|
|
|
1341
1341
|
if (!result) throw new Error(`Failed to bump ${version} by ${type}`);
|
|
1342
1342
|
return result;
|
|
1343
1343
|
}
|
|
1344
|
-
/**
|
|
1345
|
-
|
|
1344
|
+
/**
|
|
1345
|
+
* Check if a version satisfies a range.
|
|
1346
|
+
* @param version - The version to check
|
|
1347
|
+
* @param range - The version range (may include workspace: or catalog: protocol)
|
|
1348
|
+
* @param currentVersion - The dependency's current version, used to resolve workspace:^ and workspace:~
|
|
1349
|
+
*/
|
|
1350
|
+
function satisfies(version, range, currentVersion) {
|
|
1346
1351
|
if (range.startsWith("workspace:")) {
|
|
1347
1352
|
const cleanRange = range.slice(10);
|
|
1348
|
-
if (!cleanRange || cleanRange === "*"
|
|
1353
|
+
if (!cleanRange || cleanRange === "*") return true;
|
|
1354
|
+
if (cleanRange === "^" || cleanRange === "~") {
|
|
1355
|
+
if (!currentVersion) return true;
|
|
1356
|
+
const resolved = `${cleanRange}${currentVersion}`;
|
|
1357
|
+
return import_semver.default.satisfies(version, resolved);
|
|
1358
|
+
}
|
|
1349
1359
|
return import_semver.default.satisfies(version, cleanRange);
|
|
1350
1360
|
}
|
|
1351
1361
|
if (range.startsWith("catalog:")) return true;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { a as __exportAll } from "./logger-C2dEe5Su.mjs";
|
|
2
|
+
import { exec, execFile, execFileSync } from "node:child_process";
|
|
3
|
+
//#region src/utils/shell.ts
|
|
4
|
+
var shell_exports = /* @__PURE__ */ __exportAll({
|
|
5
|
+
runArgs: () => runArgs,
|
|
6
|
+
runArgsAsync: () => runArgsAsync,
|
|
7
|
+
runAsync: () => runAsync,
|
|
8
|
+
sq: () => sq,
|
|
9
|
+
tryRunArgs: () => tryRunArgs
|
|
10
|
+
});
|
|
11
|
+
/**
|
|
12
|
+
* Escape a value for safe interpolation inside a single-quoted shell string.
|
|
13
|
+
* Works by ending the current single-quote, inserting an escaped single-quote,
|
|
14
|
+
* and re-opening the single-quote: "it's" → 'it'\''s'
|
|
15
|
+
*/
|
|
16
|
+
function sq(value) {
|
|
17
|
+
return "'" + value.replace(/'/g, "'\\''") + "'";
|
|
18
|
+
}
|
|
19
|
+
function checkIntercept(args, opts) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
function runAsync(cmd, opts) {
|
|
23
|
+
const result = checkIntercept(cmd.split(/\s+/), opts);
|
|
24
|
+
if (result?.intercepted) {
|
|
25
|
+
if ("error" in result) return Promise.reject(new Error(result.error));
|
|
26
|
+
return Promise.resolve(result.result);
|
|
27
|
+
}
|
|
28
|
+
return new Promise((resolve, reject) => {
|
|
29
|
+
const child = exec(cmd, {
|
|
30
|
+
cwd: opts?.cwd,
|
|
31
|
+
encoding: "utf-8"
|
|
32
|
+
}, (err, stdout, stderr) => {
|
|
33
|
+
if (err) reject(/* @__PURE__ */ new Error(`Command failed: ${cmd}\n${stderr}`));
|
|
34
|
+
else resolve(stdout.trim());
|
|
35
|
+
});
|
|
36
|
+
if (opts?.input) {
|
|
37
|
+
child.stdin?.write(opts.input);
|
|
38
|
+
child.stdin?.end();
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/** Run a command with an argument array — bypasses the shell entirely */
|
|
43
|
+
function runArgs(args, opts) {
|
|
44
|
+
const result = checkIntercept(args, opts);
|
|
45
|
+
if (result?.intercepted) {
|
|
46
|
+
if ("error" in result) throw new Error(result.error);
|
|
47
|
+
return result.result;
|
|
48
|
+
}
|
|
49
|
+
const [cmd, ...rest] = args;
|
|
50
|
+
return execFileSync(cmd, rest, {
|
|
51
|
+
cwd: opts?.cwd,
|
|
52
|
+
input: opts?.input,
|
|
53
|
+
encoding: "utf-8",
|
|
54
|
+
stdio: [
|
|
55
|
+
opts?.input ? "pipe" : "pipe",
|
|
56
|
+
"pipe",
|
|
57
|
+
"pipe"
|
|
58
|
+
]
|
|
59
|
+
}).trim();
|
|
60
|
+
}
|
|
61
|
+
/** Async version of runArgs */
|
|
62
|
+
function runArgsAsync(args, opts) {
|
|
63
|
+
const result = checkIntercept(args, opts);
|
|
64
|
+
if (result?.intercepted) {
|
|
65
|
+
if ("error" in result) return Promise.reject(new Error(result.error));
|
|
66
|
+
return Promise.resolve(result.result);
|
|
67
|
+
}
|
|
68
|
+
const [cmd, ...rest] = args;
|
|
69
|
+
return new Promise((resolve, reject) => {
|
|
70
|
+
const child = execFile(cmd, rest, {
|
|
71
|
+
cwd: opts?.cwd,
|
|
72
|
+
encoding: "utf-8"
|
|
73
|
+
}, (err, stdout, stderr) => {
|
|
74
|
+
if (err) reject(/* @__PURE__ */ new Error(`Command failed: ${args.join(" ")}\n${stderr}`));
|
|
75
|
+
else resolve(stdout.trim());
|
|
76
|
+
});
|
|
77
|
+
if (opts?.input) {
|
|
78
|
+
child.stdin?.write(opts.input);
|
|
79
|
+
child.stdin?.end();
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/** tryRun equivalent for argument arrays */
|
|
84
|
+
function tryRunArgs(args, opts) {
|
|
85
|
+
try {
|
|
86
|
+
return runArgs(args, opts);
|
|
87
|
+
} catch {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//#endregion
|
|
92
|
+
export { sq as a, shell_exports as i, runArgsAsync as n, tryRunArgs as o, runAsync as r, runArgs as t };
|
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
import { n as log, t as colorize } from "./logger-
|
|
2
|
-
import { a as loadConfig } from "./config-
|
|
3
|
-
import { t as discoverPackages } from "./workspace-
|
|
4
|
-
import { t as DependencyGraph } from "./dep-graph-
|
|
5
|
-
import { r as
|
|
6
|
-
import { t as assembleReleasePlan } from "./release-plan-
|
|
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 discoverPackages } from "./workspace-C5ULTyUN.mjs";
|
|
4
|
+
import { t as DependencyGraph } from "./dep-graph-E-9-eQ2J.mjs";
|
|
5
|
+
import { r as readBumpFiles } from "./bump-file-CCLXMLA8.mjs";
|
|
6
|
+
import { t as assembleReleasePlan } from "./release-plan-Bi5QNSEo.mjs";
|
|
7
7
|
//#region src/commands/status.ts
|
|
8
8
|
async function statusCommand(rootDir, opts) {
|
|
9
9
|
const config = await loadConfig(rootDir);
|
|
10
10
|
const packages = await discoverPackages(rootDir, config);
|
|
11
11
|
const depGraph = new DependencyGraph(packages);
|
|
12
|
-
const
|
|
13
|
-
if (
|
|
12
|
+
const bumpFiles = await readBumpFiles(rootDir);
|
|
13
|
+
if (bumpFiles.length === 0) {
|
|
14
14
|
if (opts.json) console.log(JSON.stringify({
|
|
15
|
-
|
|
15
|
+
bumpFiles: [],
|
|
16
16
|
releases: [],
|
|
17
17
|
packageNames: []
|
|
18
18
|
}, null, 2));
|
|
19
|
-
else if (!opts.packagesOnly) log.info("No pending
|
|
19
|
+
else if (!opts.packagesOnly) log.info("No pending bump files.");
|
|
20
20
|
process.exit(1);
|
|
21
21
|
}
|
|
22
|
-
const plan = assembleReleasePlan(
|
|
22
|
+
const plan = assembleReleasePlan(bumpFiles, packages, depGraph, config);
|
|
23
23
|
let releases = plan.releases;
|
|
24
24
|
if (opts.bumpType) {
|
|
25
25
|
const types = opts.bumpType.split(",").map((t) => t.trim());
|
|
26
26
|
releases = releases.filter((r) => types.includes(r.type));
|
|
27
27
|
}
|
|
28
28
|
if (opts.filter) {
|
|
29
|
-
const { matchGlob } = await import("./config-
|
|
29
|
+
const { matchGlob } = await import("./config-XZWUL3ma.mjs").then((n) => n.t);
|
|
30
30
|
const patterns = opts.filter.split(",").map((p) => p.trim());
|
|
31
31
|
releases = releases.filter((r) => patterns.some((p) => matchGlob(r.name, p)));
|
|
32
32
|
}
|
|
33
33
|
if (opts.json) {
|
|
34
34
|
const jsonOutput = {
|
|
35
|
-
|
|
36
|
-
id:
|
|
37
|
-
summary:
|
|
38
|
-
releases:
|
|
35
|
+
bumpFiles: plan.bumpFiles.map((bf) => ({
|
|
36
|
+
id: bf.id,
|
|
37
|
+
summary: bf.summary,
|
|
38
|
+
releases: bf.releases.map((r) => ({
|
|
39
39
|
name: r.name,
|
|
40
40
|
type: r.type
|
|
41
41
|
}))
|
|
@@ -46,7 +46,7 @@ async function statusCommand(rootDir, opts) {
|
|
|
46
46
|
oldVersion: r.oldVersion,
|
|
47
47
|
newVersion: r.newVersion,
|
|
48
48
|
dir: packages.get(r.name)?.relativeDir,
|
|
49
|
-
|
|
49
|
+
bumpFiles: r.bumpFiles,
|
|
50
50
|
isDependencyBump: r.isDependencyBump,
|
|
51
51
|
isCascadeBump: r.isCascadeBump
|
|
52
52
|
})),
|
|
@@ -59,7 +59,7 @@ async function statusCommand(rootDir, opts) {
|
|
|
59
59
|
for (const r of releases) console.log(r.name);
|
|
60
60
|
return;
|
|
61
61
|
}
|
|
62
|
-
log.bold(`${
|
|
62
|
+
log.bold(`${bumpFiles.length} bump file(s) pending\n`);
|
|
63
63
|
if (releases.length === 0) {
|
|
64
64
|
log.warn("No packages match the current filters.");
|
|
65
65
|
return;
|
|
@@ -87,12 +87,16 @@ async function statusCommand(rootDir, opts) {
|
|
|
87
87
|
for (const r of group) printRelease(r, packages);
|
|
88
88
|
console.log();
|
|
89
89
|
}
|
|
90
|
+
if (plan.warnings.length > 0) {
|
|
91
|
+
for (const w of plan.warnings) log.warn(w);
|
|
92
|
+
console.log();
|
|
93
|
+
}
|
|
90
94
|
if (opts.verbose) {
|
|
91
|
-
log.bold("
|
|
92
|
-
for (const
|
|
93
|
-
console.log(` ${colorize(
|
|
94
|
-
for (const r of
|
|
95
|
-
if (
|
|
95
|
+
log.bold("Bump files:");
|
|
96
|
+
for (const bf of plan.bumpFiles) {
|
|
97
|
+
console.log(` ${colorize(bf.id, "cyan")}`);
|
|
98
|
+
for (const r of bf.releases) console.log(` ${r.name}: ${r.type}`);
|
|
99
|
+
if (bf.summary) console.log(` ${colorize(bf.summary.split("\n")[0], "dim")}`);
|
|
96
100
|
}
|
|
97
101
|
}
|
|
98
102
|
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { n as log, t as colorize } from "./logger-C2dEe5Su.mjs";
|
|
2
|
+
import { a as loadConfig } from "./config-XZWUL3ma.mjs";
|
|
3
|
+
import { n as detectWorkspaces } from "./package-manager-VCe10bjc.mjs";
|
|
4
|
+
import { t as discoverPackages } from "./workspace-C5ULTyUN.mjs";
|
|
5
|
+
import { t as DependencyGraph } from "./dep-graph-E-9-eQ2J.mjs";
|
|
6
|
+
import { 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 { t as applyReleasePlan } from "./apply-release-plan-CczGWJTk.mjs";
|
|
10
|
+
//#region src/commands/version.ts
|
|
11
|
+
async function versionCommand(rootDir) {
|
|
12
|
+
const config = await loadConfig(rootDir);
|
|
13
|
+
const packages = await discoverPackages(rootDir, config);
|
|
14
|
+
const depGraph = new DependencyGraph(packages);
|
|
15
|
+
const bumpFiles = await readBumpFiles(rootDir);
|
|
16
|
+
if (bumpFiles.length === 0) {
|
|
17
|
+
log.info("No pending bump files.");
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const plan = assembleReleasePlan(bumpFiles, packages, depGraph, config);
|
|
21
|
+
if (plan.releases.length === 0) {
|
|
22
|
+
log.warn("Bump files found but no packages would be released.");
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (plan.warnings.length > 0) {
|
|
26
|
+
for (const w of plan.warnings) log.warn(w);
|
|
27
|
+
console.log();
|
|
28
|
+
}
|
|
29
|
+
log.step("Applying version bumps:");
|
|
30
|
+
for (const r of plan.releases) {
|
|
31
|
+
const tag = r.isDependencyBump ? " (dep)" : r.isCascadeBump ? " (cascade)" : "";
|
|
32
|
+
console.log(` ${r.name}: ${r.oldVersion} → ${colorize(r.newVersion, "cyan")}${tag}`);
|
|
33
|
+
}
|
|
34
|
+
await applyReleasePlan(plan, packages, rootDir, config);
|
|
35
|
+
log.success(`Updated ${plan.releases.length} package(s)`);
|
|
36
|
+
log.dim(` Deleted ${bumpFiles.length} bump file(s)`);
|
|
37
|
+
await updateLockfile(rootDir);
|
|
38
|
+
if (config.commit) try {
|
|
39
|
+
runArgs([
|
|
40
|
+
"git",
|
|
41
|
+
"add",
|
|
42
|
+
"-A",
|
|
43
|
+
".bumpy/"
|
|
44
|
+
], { cwd: rootDir });
|
|
45
|
+
for (const r of plan.releases) {
|
|
46
|
+
const pkg = packages.get(r.name);
|
|
47
|
+
runArgs([
|
|
48
|
+
"git",
|
|
49
|
+
"add",
|
|
50
|
+
"--",
|
|
51
|
+
`${pkg.relativeDir}/package.json`
|
|
52
|
+
], { cwd: rootDir });
|
|
53
|
+
runArgs([
|
|
54
|
+
"git",
|
|
55
|
+
"add",
|
|
56
|
+
"--",
|
|
57
|
+
`${pkg.relativeDir}/CHANGELOG.md`
|
|
58
|
+
], { cwd: rootDir });
|
|
59
|
+
}
|
|
60
|
+
for (const lockfile of [
|
|
61
|
+
"bun.lock",
|
|
62
|
+
"bun.lockb",
|
|
63
|
+
"pnpm-lock.yaml",
|
|
64
|
+
"yarn.lock",
|
|
65
|
+
"package-lock.json"
|
|
66
|
+
]) tryRunArgs([
|
|
67
|
+
"git",
|
|
68
|
+
"add",
|
|
69
|
+
"--",
|
|
70
|
+
lockfile
|
|
71
|
+
], { cwd: rootDir });
|
|
72
|
+
runArgs([
|
|
73
|
+
"git",
|
|
74
|
+
"commit",
|
|
75
|
+
"-F",
|
|
76
|
+
"-"
|
|
77
|
+
], {
|
|
78
|
+
cwd: rootDir,
|
|
79
|
+
input: [
|
|
80
|
+
"Version packages",
|
|
81
|
+
"",
|
|
82
|
+
...plan.releases.map((r) => `${r.name}@${r.newVersion}`)
|
|
83
|
+
].join("\n")
|
|
84
|
+
});
|
|
85
|
+
log.success("Created git commit");
|
|
86
|
+
} catch (e) {
|
|
87
|
+
log.warn(`Git commit failed: ${e}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/** Run the package manager's install to update the lockfile */
|
|
91
|
+
async function updateLockfile(rootDir) {
|
|
92
|
+
const { packageManager } = await detectWorkspaces(rootDir);
|
|
93
|
+
const installArgs = getInstallArgs(packageManager);
|
|
94
|
+
log.step(`Updating lockfile (${installArgs.join(" ")})...`);
|
|
95
|
+
try {
|
|
96
|
+
runArgs(installArgs, { cwd: rootDir });
|
|
97
|
+
log.dim(" Lockfile updated");
|
|
98
|
+
} catch (err) {
|
|
99
|
+
log.warn(` Lockfile update failed: ${err instanceof Error ? err.message : err}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function getInstallArgs(pm) {
|
|
103
|
+
switch (pm) {
|
|
104
|
+
case "pnpm": return [
|
|
105
|
+
"pnpm",
|
|
106
|
+
"install",
|
|
107
|
+
"--lockfile-only"
|
|
108
|
+
];
|
|
109
|
+
case "bun": return ["bun", "install"];
|
|
110
|
+
case "yarn": return [
|
|
111
|
+
"yarn",
|
|
112
|
+
"install",
|
|
113
|
+
"--mode",
|
|
114
|
+
"update-lockfile"
|
|
115
|
+
];
|
|
116
|
+
default: return [
|
|
117
|
+
"npm",
|
|
118
|
+
"install",
|
|
119
|
+
"--package-lock-only"
|
|
120
|
+
];
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
//#endregion
|
|
124
|
+
export { versionCommand };
|