@varlock/bumpy 0.0.2 → 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-CQhUyHAG.mjs → ai-sMYUf3lP.mjs} +21 -4
- package/dist/{apply-release-plan-D6TSrcwX.mjs → apply-release-plan-CczGWJTk.mjs} +28 -24
- package/dist/bump-file-CCLXMLA8.mjs +143 -0
- package/dist/{changelog-github-Du62krXi.mjs → changelog-github-Cd8uJHZI.mjs} +22 -20
- package/dist/{check-jIwike9F.mjs → check-BOoxpWqk.mjs} +9 -9
- package/dist/{ci-D6LQbR38.mjs → ci-Bhx--Tj6.mjs} +116 -72
- package/dist/{ci-setup-C6FlOfW5.mjs → ci-setup-qz4Y3v7T.mjs} +1 -1
- package/dist/cli.mjs +32 -30
- package/dist/{config-BkwIEaQg.mjs → config-XZWUL3ma.mjs} +27 -22
- package/dist/fs-DYR2XuFE.mjs +81 -0
- package/dist/{generate-Btrsn1qi.mjs → generate-gYKTpvex.mjs} +8 -8
- package/dist/index.d.mts +55 -37
- package/dist/index.mjs +8 -8
- package/dist/{init-B0q3wEQW.mjs → init-lA9E5pEc.mjs} +2 -2
- package/dist/{migrate-CfQNwD0T.mjs → migrate-DmOYgmfD.mjs} +10 -10
- package/dist/{names-Ck8cun7B.mjs → names-9VubBmL0.mjs} +1 -1
- package/dist/{package-manager-DcI5TdDE.mjs → package-manager-VCe10bjc.mjs} +1 -1
- package/dist/{publish-D_7RqEYL.mjs → publish-Cun-zQ1b.mjs} +21 -20
- package/dist/{publish-pipeline-ChnqW8nR.mjs → publish-pipeline-BwBuKCIk.mjs} +22 -17
- package/dist/release-plan-Bi5QNSEo.mjs +264 -0
- package/dist/{semver-BTzYh8vc.mjs → semver-DfQyVLM_.mjs} +13 -3
- package/dist/{status--Q8yAxQ4.mjs → status-CfE63ti5.mjs} +25 -21
- package/dist/{version-cAUkfYPx.mjs → version-19vVt9dv.mjs} +16 -12
- package/dist/{workspace-CxEKakDm.mjs → workspace-C5ULTyUN.mjs} +3 -3
- package/package.json +13 -1
- package/skills/add-change/SKILL.md +8 -12
- package/dist/add-BjyVIUlr.mjs +0 -175
- package/dist/changeset-UCZdSRDv.mjs +0 -108
- package/dist/fs-0AtnPUUe.mjs +0 -51
- package/dist/release-plan-BEzwApuK.mjs +0 -173
|
@@ -1,27 +1,31 @@
|
|
|
1
1
|
import { n as log, t as colorize } from "./logger-C2dEe5Su.mjs";
|
|
2
|
-
import { a as loadConfig } from "./config-
|
|
3
|
-
import { n as detectWorkspaces } from "./package-manager-
|
|
4
|
-
import { t as discoverPackages } from "./workspace-
|
|
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
5
|
import { t as DependencyGraph } from "./dep-graph-E-9-eQ2J.mjs";
|
|
6
6
|
import { o as tryRunArgs, t as runArgs } from "./shell-Dj7JRD_q.mjs";
|
|
7
|
-
import { r as
|
|
8
|
-
import { t as assembleReleasePlan } from "./release-plan-
|
|
9
|
-
import { t as applyReleasePlan } from "./apply-release-plan-
|
|
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
10
|
//#region src/commands/version.ts
|
|
11
11
|
async function versionCommand(rootDir) {
|
|
12
12
|
const config = await loadConfig(rootDir);
|
|
13
13
|
const packages = await discoverPackages(rootDir, config);
|
|
14
14
|
const depGraph = new DependencyGraph(packages);
|
|
15
|
-
const
|
|
16
|
-
if (
|
|
17
|
-
log.info("No pending
|
|
15
|
+
const bumpFiles = await readBumpFiles(rootDir);
|
|
16
|
+
if (bumpFiles.length === 0) {
|
|
17
|
+
log.info("No pending bump files.");
|
|
18
18
|
return;
|
|
19
19
|
}
|
|
20
|
-
const plan = assembleReleasePlan(
|
|
20
|
+
const plan = assembleReleasePlan(bumpFiles, packages, depGraph, config);
|
|
21
21
|
if (plan.releases.length === 0) {
|
|
22
|
-
log.warn("
|
|
22
|
+
log.warn("Bump files found but no packages would be released.");
|
|
23
23
|
return;
|
|
24
24
|
}
|
|
25
|
+
if (plan.warnings.length > 0) {
|
|
26
|
+
for (const w of plan.warnings) log.warn(w);
|
|
27
|
+
console.log();
|
|
28
|
+
}
|
|
25
29
|
log.step("Applying version bumps:");
|
|
26
30
|
for (const r of plan.releases) {
|
|
27
31
|
const tag = r.isDependencyBump ? " (dep)" : r.isCascadeBump ? " (cascade)" : "";
|
|
@@ -29,7 +33,7 @@ async function versionCommand(rootDir) {
|
|
|
29
33
|
}
|
|
30
34
|
await applyReleasePlan(plan, packages, rootDir, config);
|
|
31
35
|
log.success(`Updated ${plan.releases.length} package(s)`);
|
|
32
|
-
log.dim(` Deleted ${
|
|
36
|
+
log.dim(` Deleted ${bumpFiles.length} bump file(s)`);
|
|
33
37
|
await updateLockfile(rootDir);
|
|
34
38
|
if (config.commit) try {
|
|
35
39
|
runArgs([
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as readJson, n as exists } from "./fs-
|
|
2
|
-
import { i as isPackageManaged, o as loadPackageConfig } from "./config-
|
|
3
|
-
import { n as detectWorkspaces } from "./package-manager-
|
|
1
|
+
import { a as readJson, n as exists } from "./fs-DYR2XuFE.mjs";
|
|
2
|
+
import { i as isPackageManaged, o as loadPackageConfig } from "./config-XZWUL3ma.mjs";
|
|
3
|
+
import { n as detectWorkspaces } from "./package-manager-VCe10bjc.mjs";
|
|
4
4
|
import { relative, resolve } from "node:path";
|
|
5
5
|
import { readdir, stat } from "node:fs/promises";
|
|
6
6
|
//#region src/core/workspace.ts
|
package/package.json
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@varlock/bumpy",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Modern monorepo versioning and changelog tool",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"bump",
|
|
7
|
+
"changelog",
|
|
8
|
+
"changesets",
|
|
9
|
+
"monorepo",
|
|
10
|
+
"version",
|
|
11
|
+
"version management"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://bumpy.varlock.dev",
|
|
14
|
+
"bugs": "https://github.com/dmno-dev/bumpy/issues",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"author": "dmno-dev",
|
|
5
17
|
"repository": {
|
|
6
18
|
"type": "git",
|
|
7
19
|
"url": "https://github.com/dmno-dev/bumpy",
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: add-change
|
|
3
|
-
description: Create a bumpy
|
|
3
|
+
description: Create a bumpy bump file describing which packages changed and how, for version bumping and changelog generation. Use when the user wants to record a change, add a bump file, or prepare packages for release.
|
|
4
4
|
argument-hint: '[description of changes]'
|
|
5
5
|
allowed-tools: Read Grep Glob Bash Edit Write
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
# Create a bumpy
|
|
8
|
+
# Create a bumpy bump file
|
|
9
9
|
|
|
10
|
-
You are helping the user create a **bumpy
|
|
10
|
+
You are helping the user create a **bumpy bump file** — a markdown file in `.bumpy/` that describes which packages changed and how. Bumpy uses these to bump versions and generate changelogs.
|
|
11
11
|
|
|
12
12
|
## Steps
|
|
13
13
|
|
|
@@ -17,7 +17,7 @@ First, understand what changed. Run these in parallel:
|
|
|
17
17
|
|
|
18
18
|
- `git diff --stat` — see which files changed
|
|
19
19
|
- `git diff --cached --stat` — see staged changes
|
|
20
|
-
- `bumpy status --json` — see if there are already pending
|
|
20
|
+
- `bumpy status --json` — see if there are already pending bump files
|
|
21
21
|
|
|
22
22
|
If the user provided a description via `$ARGUMENTS`, use that as additional context for understanding the change.
|
|
23
23
|
|
|
@@ -43,11 +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
|
-
|
|
47
|
-
|
|
48
|
-
- Internal refactors with no API changes
|
|
49
|
-
- Dev tooling / test changes
|
|
50
|
-
- Documentation-only changes
|
|
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.
|
|
51
47
|
|
|
52
48
|
### 4. Write a clear summary
|
|
53
49
|
|
|
@@ -57,7 +53,7 @@ Write a concise summary (1-3 sentences) describing **what** changed and **why**.
|
|
|
57
53
|
- Focus on user-facing impact, not implementation details
|
|
58
54
|
- Are specific enough to be useful months later
|
|
59
55
|
|
|
60
|
-
### 5. Create the
|
|
56
|
+
### 5. Create the bump file
|
|
61
57
|
|
|
62
58
|
Use the non-interactive CLI:
|
|
63
59
|
|
|
@@ -83,7 +79,7 @@ bumpy add \
|
|
|
83
79
|
|
|
84
80
|
## Advanced: cascading bumps
|
|
85
81
|
|
|
86
|
-
If a change in a core package should explicitly cascade to dependents with specific bump levels, write the
|
|
82
|
+
If a change in a core package should explicitly cascade to dependents with specific bump levels, write the bump file directly instead of using the CLI:
|
|
87
83
|
|
|
88
84
|
```bash
|
|
89
85
|
cat > .bumpy/<name>.md << 'EOF'
|
|
@@ -105,4 +101,4 @@ EOF
|
|
|
105
101
|
- Only include packages that have **actual code changes** — bumpy handles dependency propagation automatically
|
|
106
102
|
- If the user hasn't made any changes yet, ask what they're planning to change
|
|
107
103
|
- If the change doesn't affect any publishable packages (e.g., only root config files), suggest using `bumpy add --empty` to satisfy CI checks
|
|
108
|
-
- One
|
|
104
|
+
- One bump file per logical change — don't combine unrelated changes
|
package/dist/add-BjyVIUlr.mjs
DELETED
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
import { n as log, o as __toESM, r as require_picocolors } from "./logger-C2dEe5Su.mjs";
|
|
2
|
-
import { n as exists, t as ensureDir } from "./fs-0AtnPUUe.mjs";
|
|
3
|
-
import { a as loadConfig, r as getBumpyDir, s as matchGlob } from "./config-BkwIEaQg.mjs";
|
|
4
|
-
import { t as discoverPackages } from "./workspace-CxEKakDm.mjs";
|
|
5
|
-
import { t as DependencyGraph } from "./dep-graph-E-9-eQ2J.mjs";
|
|
6
|
-
import { i as writeChangeset } from "./changeset-UCZdSRDv.mjs";
|
|
7
|
-
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";
|
|
8
|
-
import { n as slugify, t as randomName } from "./names-Ck8cun7B.mjs";
|
|
9
|
-
import { resolve } from "node:path";
|
|
10
|
-
//#region src/commands/add.ts
|
|
11
|
-
var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
12
|
-
const BUMP_CHOICES = [
|
|
13
|
-
{
|
|
14
|
-
label: "patch",
|
|
15
|
-
value: "patch"
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
label: "minor",
|
|
19
|
-
value: "minor"
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
label: "major",
|
|
23
|
-
value: "major"
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
label: "patch (isolated)",
|
|
27
|
-
value: "patch-isolated",
|
|
28
|
-
hint: "no cascade"
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
label: "minor (isolated)",
|
|
32
|
-
value: "minor-isolated",
|
|
33
|
-
hint: "no cascade"
|
|
34
|
-
}
|
|
35
|
-
];
|
|
36
|
-
const CASCADE_CHOICES = [
|
|
37
|
-
{
|
|
38
|
-
label: "patch",
|
|
39
|
-
value: "patch"
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
label: "minor",
|
|
43
|
-
value: "minor"
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
label: "major",
|
|
47
|
-
value: "major"
|
|
48
|
-
}
|
|
49
|
-
];
|
|
50
|
-
async function addCommand(rootDir, opts) {
|
|
51
|
-
const config = await loadConfig(rootDir);
|
|
52
|
-
const bumpyDir = getBumpyDir(rootDir);
|
|
53
|
-
await ensureDir(bumpyDir);
|
|
54
|
-
if (opts.empty) {
|
|
55
|
-
const filename = opts.name ? slugify(opts.name) : randomName();
|
|
56
|
-
const filePath = resolve(bumpyDir, `${filename}.md`);
|
|
57
|
-
const { writeText } = await import("./fs-0AtnPUUe.mjs").then((n) => n.r);
|
|
58
|
-
await writeText(filePath, "---\n---\n");
|
|
59
|
-
log.success(`Created empty changeset: .bumpy/${filename}.md`);
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
let releases;
|
|
63
|
-
let summary;
|
|
64
|
-
let filename;
|
|
65
|
-
if (opts.packages) {
|
|
66
|
-
releases = parsePackagesFlag(opts.packages);
|
|
67
|
-
summary = opts.message || "";
|
|
68
|
-
filename = opts.name ? slugify(opts.name) : randomName();
|
|
69
|
-
} else {
|
|
70
|
-
mt(import_picocolors.default.bgCyan(import_picocolors.default.black(" bumpy add ")));
|
|
71
|
-
const pkgs = await discoverPackages(rootDir, config);
|
|
72
|
-
const depGraph = new DependencyGraph(pkgs);
|
|
73
|
-
if (pkgs.size === 0) {
|
|
74
|
-
pt("No managed packages found in this workspace.");
|
|
75
|
-
process.exit(1);
|
|
76
|
-
}
|
|
77
|
-
const selected = unwrap(await yt({
|
|
78
|
-
message: "Which packages should be included in this changeset?",
|
|
79
|
-
options: [...pkgs.values()].map((pkg) => ({
|
|
80
|
-
label: pkg.name,
|
|
81
|
-
value: pkg.name,
|
|
82
|
-
hint: pkg.version
|
|
83
|
-
})),
|
|
84
|
-
required: true
|
|
85
|
-
}));
|
|
86
|
-
releases = [];
|
|
87
|
-
for (const name of selected) {
|
|
88
|
-
const bumpType = unwrap(await _t({
|
|
89
|
-
message: `Bump type for ${import_picocolors.default.cyan(name)}`,
|
|
90
|
-
options: BUMP_CHOICES
|
|
91
|
-
}));
|
|
92
|
-
const release = {
|
|
93
|
-
name,
|
|
94
|
-
type: bumpType
|
|
95
|
-
};
|
|
96
|
-
if (!bumpType.endsWith("-isolated")) {
|
|
97
|
-
const dependents = depGraph.getDependents(name);
|
|
98
|
-
const cascadeTargets = pkgs.get(name).bumpy?.cascadeTo;
|
|
99
|
-
if (dependents.length > 0 || cascadeTargets) {
|
|
100
|
-
if (unwrap(await ot({
|
|
101
|
-
message: `${import_picocolors.default.cyan(name)} has ${import_picocolors.default.bold(String(dependents.length))} dependents. Specify explicit cascades?`,
|
|
102
|
-
initialValue: false
|
|
103
|
-
}))) {
|
|
104
|
-
const allTargets = /* @__PURE__ */ new Set();
|
|
105
|
-
for (const d of dependents) allTargets.add(d.name);
|
|
106
|
-
if (cascadeTargets) {
|
|
107
|
-
for (const pattern of Object.keys(cascadeTargets)) for (const [pName] of pkgs) if (matchGlob(pName, pattern)) allTargets.add(pName);
|
|
108
|
-
}
|
|
109
|
-
const cascadeSelected = unwrap(await yt({
|
|
110
|
-
message: "Which packages should cascade?",
|
|
111
|
-
options: [...allTargets].map((n) => ({
|
|
112
|
-
label: n,
|
|
113
|
-
value: n
|
|
114
|
-
})),
|
|
115
|
-
required: false
|
|
116
|
-
}));
|
|
117
|
-
if (cascadeSelected.length > 0) {
|
|
118
|
-
const cascadeBump = unwrap(await _t({
|
|
119
|
-
message: "Cascade bump type",
|
|
120
|
-
options: CASCADE_CHOICES
|
|
121
|
-
}));
|
|
122
|
-
const cascade = {};
|
|
123
|
-
for (const target of cascadeSelected) cascade[target] = cascadeBump;
|
|
124
|
-
release.cascade = cascade;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
releases.push(release);
|
|
130
|
-
}
|
|
131
|
-
summary = unwrap(await Ot({
|
|
132
|
-
message: "Summary (what changed and why)",
|
|
133
|
-
placeholder: "A short description of the change",
|
|
134
|
-
validate: (value) => {
|
|
135
|
-
if (!value || !value.trim()) return "Summary is required";
|
|
136
|
-
}
|
|
137
|
-
}));
|
|
138
|
-
const defaultName = randomName();
|
|
139
|
-
filename = slugify(unwrap(await Ot({
|
|
140
|
-
message: "Changeset name",
|
|
141
|
-
placeholder: defaultName,
|
|
142
|
-
defaultValue: defaultName,
|
|
143
|
-
validate: (value) => {
|
|
144
|
-
if (!value) return void 0;
|
|
145
|
-
if (!slugify(value)) return "Name must contain at least one alphanumeric character";
|
|
146
|
-
}
|
|
147
|
-
}))) || defaultName;
|
|
148
|
-
}
|
|
149
|
-
if (await exists(resolve(bumpyDir, `${filename}.md`))) filename = `${filename}-${Date.now()}`;
|
|
150
|
-
await writeChangeset(rootDir, filename, releases, summary);
|
|
151
|
-
if (opts.packages) {
|
|
152
|
-
log.success(`Created changeset: .bumpy/${filename}.md`);
|
|
153
|
-
for (const r of releases) log.dim(` ${r.name}: ${r.type}${formatCascade(r)}`);
|
|
154
|
-
} else {
|
|
155
|
-
wt(releases.map((r) => `${import_picocolors.default.cyan(r.name)} ${import_picocolors.default.dim("→")} ${import_picocolors.default.bold(r.type)}${formatCascade(r)}`).join("\n"), "Changeset");
|
|
156
|
-
gt(import_picocolors.default.green(`Created .bumpy/${filename}.md`));
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
function formatCascade(r) {
|
|
160
|
-
if (!("cascade" in r) || Object.keys(r.cascade).length === 0) return "";
|
|
161
|
-
const parts = Object.entries(r.cascade).map(([k, v]) => `${k}:${v}`);
|
|
162
|
-
return import_picocolors.default.dim(` (cascade: ${parts.join(", ")})`);
|
|
163
|
-
}
|
|
164
|
-
function parsePackagesFlag(input) {
|
|
165
|
-
return input.split(",").map((entry) => {
|
|
166
|
-
const [name, type] = entry.trim().split(":");
|
|
167
|
-
if (!name || !type) throw new Error(`Invalid package format: "${entry}". Expected "name:bumpType"`);
|
|
168
|
-
return {
|
|
169
|
-
name: name.trim(),
|
|
170
|
-
type: type.trim()
|
|
171
|
-
};
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
//#endregion
|
|
175
|
-
export { addCommand };
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import { i as listFiles, l as writeText, o as readText, s as removeFile } from "./fs-0AtnPUUe.mjs";
|
|
2
|
-
import { r as getBumpyDir } from "./config-BkwIEaQg.mjs";
|
|
3
|
-
import { t as jsYaml } from "./js-yaml-DpZfOoD4.mjs";
|
|
4
|
-
import { o as tryRunArgs } from "./shell-Dj7JRD_q.mjs";
|
|
5
|
-
import { resolve } from "node:path";
|
|
6
|
-
//#region src/core/changeset.ts
|
|
7
|
-
/** Read all changeset files from .bumpy/ directory, sorted by git creation order */
|
|
8
|
-
async function readChangesets(rootDir) {
|
|
9
|
-
const dir = getBumpyDir(rootDir);
|
|
10
|
-
const files = await listFiles(dir, ".md");
|
|
11
|
-
const changesets = [];
|
|
12
|
-
for (const file of files) {
|
|
13
|
-
if (file === "README.md") continue;
|
|
14
|
-
const cs = await parseChangesetFile(resolve(dir, file));
|
|
15
|
-
if (cs) changesets.push(cs);
|
|
16
|
-
}
|
|
17
|
-
const creationOrder = getChangesetCreationOrder(rootDir);
|
|
18
|
-
if (creationOrder.size > 0) changesets.sort((a, b) => {
|
|
19
|
-
return (creationOrder.get(a.id) ?? Infinity) - (creationOrder.get(b.id) ?? Infinity) || a.id.localeCompare(b.id);
|
|
20
|
-
});
|
|
21
|
-
return changesets;
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Use `git log` to get the commit timestamp when each changeset file was first added.
|
|
25
|
-
* Returns a map of changeset ID → unix timestamp (seconds).
|
|
26
|
-
*/
|
|
27
|
-
function getChangesetCreationOrder(rootDir) {
|
|
28
|
-
const order = /* @__PURE__ */ new Map();
|
|
29
|
-
const result = tryRunArgs([
|
|
30
|
-
"git",
|
|
31
|
-
"log",
|
|
32
|
-
"--diff-filter=A",
|
|
33
|
-
"--format=%at",
|
|
34
|
-
"--name-only",
|
|
35
|
-
"--",
|
|
36
|
-
".bumpy/*.md"
|
|
37
|
-
], { cwd: rootDir });
|
|
38
|
-
if (!result) return order;
|
|
39
|
-
let currentTimestamp = 0;
|
|
40
|
-
for (const line of result.split("\n")) {
|
|
41
|
-
const trimmed = line.trim();
|
|
42
|
-
if (!trimmed) continue;
|
|
43
|
-
if (/^\d+$/.test(trimmed)) currentTimestamp = parseInt(trimmed, 10);
|
|
44
|
-
else if (trimmed.startsWith(".bumpy/") && trimmed.endsWith(".md")) {
|
|
45
|
-
const id = trimmed.replace(/^\.bumpy\//, "").replace(/\.md$/, "");
|
|
46
|
-
order.set(id, currentTimestamp);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
return order;
|
|
50
|
-
}
|
|
51
|
-
/** Parse a single changeset markdown file */
|
|
52
|
-
async function parseChangesetFile(filePath) {
|
|
53
|
-
return parseChangeset(await readText(filePath), fileToId(filePath));
|
|
54
|
-
}
|
|
55
|
-
/** Parse changeset content (for testing) */
|
|
56
|
-
function parseChangeset(content, id) {
|
|
57
|
-
const match = content.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
|
|
58
|
-
if (!match) return null;
|
|
59
|
-
const frontmatter = match[1];
|
|
60
|
-
const summary = match[2].trim();
|
|
61
|
-
const parsed = jsYaml.load(frontmatter);
|
|
62
|
-
if (!parsed || typeof parsed !== "object") return null;
|
|
63
|
-
const releases = [];
|
|
64
|
-
for (const [name, value] of Object.entries(parsed)) if (typeof value === "string") releases.push({
|
|
65
|
-
name,
|
|
66
|
-
type: value
|
|
67
|
-
});
|
|
68
|
-
else if (value && typeof value === "object") {
|
|
69
|
-
const obj = value;
|
|
70
|
-
const release = {
|
|
71
|
-
name,
|
|
72
|
-
type: obj.bump,
|
|
73
|
-
cascade: obj.cascade || {}
|
|
74
|
-
};
|
|
75
|
-
releases.push(release);
|
|
76
|
-
}
|
|
77
|
-
if (releases.length === 0) return null;
|
|
78
|
-
return {
|
|
79
|
-
id,
|
|
80
|
-
releases,
|
|
81
|
-
summary
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
/** Write a changeset file */
|
|
85
|
-
async function writeChangeset(rootDir, filename, releases, summary) {
|
|
86
|
-
const filePath = resolve(getBumpyDir(rootDir), `${filename}.md`);
|
|
87
|
-
const frontmatter = {};
|
|
88
|
-
for (const release of releases) if ("cascade" in release && Object.keys(release.cascade).length > 0) frontmatter[release.name] = {
|
|
89
|
-
bump: release.type,
|
|
90
|
-
cascade: release.cascade
|
|
91
|
-
};
|
|
92
|
-
else frontmatter[release.name] = release.type;
|
|
93
|
-
await writeText(filePath, `---\n${jsYaml.dump(frontmatter, {
|
|
94
|
-
lineWidth: -1,
|
|
95
|
-
quotingType: "\""
|
|
96
|
-
}).trim()}\n---\n\n${summary}\n`);
|
|
97
|
-
return filePath;
|
|
98
|
-
}
|
|
99
|
-
/** Delete consumed changeset files */
|
|
100
|
-
async function deleteChangesets(rootDir, ids) {
|
|
101
|
-
const dir = getBumpyDir(rootDir);
|
|
102
|
-
for (const id of ids) await removeFile(resolve(dir, `${id}.md`));
|
|
103
|
-
}
|
|
104
|
-
function fileToId(filePath) {
|
|
105
|
-
return filePath.split("/").pop().replace(/\.md$/, "");
|
|
106
|
-
}
|
|
107
|
-
//#endregion
|
|
108
|
-
export { writeChangeset as i, parseChangeset as n, readChangesets as r, deleteChangesets as t };
|
package/dist/fs-0AtnPUUe.mjs
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
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 };
|
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
import { g as parseIsolatedBump, h as maxBump, l as DEFAULT_BUMP_RULES, m as hasCascade, p as bumpLevel, s as matchGlob } from "./config-BkwIEaQg.mjs";
|
|
2
|
-
import { n as satisfies, r as stripProtocol, t as bumpVersion } from "./semver-BTzYh8vc.mjs";
|
|
3
|
-
//#region src/core/release-plan.ts
|
|
4
|
-
/**
|
|
5
|
-
* Build a release plan from pending changesets, the dependency graph, and config.
|
|
6
|
-
* This is the core algorithm of bumpy.
|
|
7
|
-
*/
|
|
8
|
-
function assembleReleasePlan(changesets, packages, depGraph, config) {
|
|
9
|
-
if (changesets.length === 0) return {
|
|
10
|
-
changesets: [],
|
|
11
|
-
releases: []
|
|
12
|
-
};
|
|
13
|
-
const planned = /* @__PURE__ */ new Map();
|
|
14
|
-
const cascadeOverrides = /* @__PURE__ */ new Map();
|
|
15
|
-
for (const cs of changesets) for (const release of cs.releases) {
|
|
16
|
-
if (!packages.has(release.name)) continue;
|
|
17
|
-
const { bump, isolated } = parseIsolatedBump(release.type);
|
|
18
|
-
const existing = planned.get(release.name);
|
|
19
|
-
if (existing) {
|
|
20
|
-
existing.type = maxBump(existing.type, bump);
|
|
21
|
-
if (!isolated) existing.isolated = false;
|
|
22
|
-
existing.changesets.add(cs.id);
|
|
23
|
-
} else planned.set(release.name, {
|
|
24
|
-
type: bump,
|
|
25
|
-
isolated,
|
|
26
|
-
isDependencyBump: false,
|
|
27
|
-
isCascadeBump: false,
|
|
28
|
-
changesets: new Set([cs.id])
|
|
29
|
-
});
|
|
30
|
-
if (hasCascade(release)) {
|
|
31
|
-
if (!cascadeOverrides.has(release.name)) cascadeOverrides.set(release.name, /* @__PURE__ */ new Map());
|
|
32
|
-
const overrides = cascadeOverrides.get(release.name);
|
|
33
|
-
for (const [pattern, bumpType] of Object.entries(release.cascade)) {
|
|
34
|
-
const existing = overrides.get(pattern);
|
|
35
|
-
overrides.set(pattern, maxBump(existing, bumpType));
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
for (const group of config.fixed) {
|
|
40
|
-
let groupBump;
|
|
41
|
-
let groupIsolated = true;
|
|
42
|
-
for (const nameOrGlob of group) for (const [name, bump] of planned) if (matchGlob(name, nameOrGlob)) {
|
|
43
|
-
groupBump = maxBump(groupBump, bump.type);
|
|
44
|
-
if (!bump.isolated) groupIsolated = false;
|
|
45
|
-
}
|
|
46
|
-
if (!groupBump) continue;
|
|
47
|
-
for (const nameOrGlob of group) for (const [name] of packages) {
|
|
48
|
-
if (!matchGlob(name, nameOrGlob)) continue;
|
|
49
|
-
const existing = planned.get(name);
|
|
50
|
-
if (existing) {
|
|
51
|
-
existing.type = groupBump;
|
|
52
|
-
existing.isolated = groupIsolated;
|
|
53
|
-
} else planned.set(name, {
|
|
54
|
-
type: groupBump,
|
|
55
|
-
isolated: groupIsolated,
|
|
56
|
-
isDependencyBump: false,
|
|
57
|
-
isCascadeBump: false,
|
|
58
|
-
changesets: /* @__PURE__ */ new Set()
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
for (const group of config.linked) {
|
|
63
|
-
let groupBump;
|
|
64
|
-
for (const nameOrGlob of group) for (const [name, bump] of planned) if (matchGlob(name, nameOrGlob)) groupBump = maxBump(groupBump, bump.type);
|
|
65
|
-
if (!groupBump) continue;
|
|
66
|
-
for (const nameOrGlob of group) for (const [name] of packages) {
|
|
67
|
-
if (!matchGlob(name, nameOrGlob)) continue;
|
|
68
|
-
const existing = planned.get(name);
|
|
69
|
-
if (existing) existing.type = groupBump;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
let changed = true;
|
|
73
|
-
let iterations = 0;
|
|
74
|
-
const MAX_ITERATIONS = 100;
|
|
75
|
-
while (changed && iterations < MAX_ITERATIONS) {
|
|
76
|
-
changed = false;
|
|
77
|
-
iterations++;
|
|
78
|
-
for (const [pkgName, bump] of planned) {
|
|
79
|
-
if (bump.isolated) continue;
|
|
80
|
-
const csOverrides = cascadeOverrides.get(pkgName);
|
|
81
|
-
if (csOverrides) for (const [pattern, cascadeBumpType] of csOverrides) for (const [targetName] of packages) {
|
|
82
|
-
if (!matchGlob(targetName, pattern)) continue;
|
|
83
|
-
if (applyBump(planned, targetName, cascadeBumpType, false, true, bump.changesets)) changed = true;
|
|
84
|
-
}
|
|
85
|
-
const cascadeTo = packages.get(pkgName)?.bumpy?.cascadeTo;
|
|
86
|
-
if (cascadeTo) for (const [pattern, rule] of Object.entries(cascadeTo)) {
|
|
87
|
-
if (!shouldTrigger(bump.type, rule.trigger)) continue;
|
|
88
|
-
const cascadeBump = rule.bumpAs === "match" ? bump.type : rule.bumpAs;
|
|
89
|
-
for (const [targetName] of packages) {
|
|
90
|
-
if (!matchGlob(targetName, pattern)) continue;
|
|
91
|
-
if (applyBump(planned, targetName, cascadeBump, false, true, bump.changesets)) changed = true;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
const dependents = depGraph.getDependents(pkgName);
|
|
95
|
-
for (const dep of dependents) {
|
|
96
|
-
const rule = resolveRule(dep.name, pkgName, dep.depType, packages, config);
|
|
97
|
-
if (!shouldTrigger(bump.type, rule.trigger)) continue;
|
|
98
|
-
if (config.updateInternalDependencies === "out-of-range") {
|
|
99
|
-
if (satisfies(bumpVersion(packages.get(pkgName).version, bump.type), stripProtocol(dep.versionRange))) continue;
|
|
100
|
-
}
|
|
101
|
-
if (config.updateInternalDependencies === "none") continue;
|
|
102
|
-
if (config.updateInternalDependencies === "minor" && bumpLevel(bump.type) < bumpLevel("minor")) continue;
|
|
103
|
-
const depBump = rule.bumpAs === "match" ? bump.type : rule.bumpAs;
|
|
104
|
-
if (applyBump(planned, dep.name, depBump, true, false, bump.changesets)) changed = true;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
const releases = [];
|
|
109
|
-
for (const [name, bump] of planned) {
|
|
110
|
-
const pkg = packages.get(name);
|
|
111
|
-
if (!pkg) continue;
|
|
112
|
-
const newVersion = bumpVersion(pkg.version, bump.type);
|
|
113
|
-
releases.push({
|
|
114
|
-
name,
|
|
115
|
-
type: bump.type,
|
|
116
|
-
oldVersion: pkg.version,
|
|
117
|
-
newVersion,
|
|
118
|
-
changesets: [...bump.changesets],
|
|
119
|
-
isDependencyBump: bump.isDependencyBump,
|
|
120
|
-
isCascadeBump: bump.isCascadeBump
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
releases.sort((a, b) => a.name.localeCompare(b.name));
|
|
124
|
-
return {
|
|
125
|
-
changesets,
|
|
126
|
-
releases
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
/** Apply a bump to a package, upgrading if already planned. Returns true if anything changed. */
|
|
130
|
-
function applyBump(planned, name, type, isDependencyBump, isCascadeBump, sourceChangesets) {
|
|
131
|
-
const existing = planned.get(name);
|
|
132
|
-
if (existing) {
|
|
133
|
-
const newType = maxBump(existing.type, type);
|
|
134
|
-
if (newType === existing.type) return false;
|
|
135
|
-
existing.type = newType;
|
|
136
|
-
if (isDependencyBump) existing.isDependencyBump = true;
|
|
137
|
-
if (isCascadeBump) existing.isCascadeBump = true;
|
|
138
|
-
for (const cs of sourceChangesets) existing.changesets.add(cs);
|
|
139
|
-
return true;
|
|
140
|
-
}
|
|
141
|
-
planned.set(name, {
|
|
142
|
-
type,
|
|
143
|
-
isolated: false,
|
|
144
|
-
isDependencyBump,
|
|
145
|
-
isCascadeBump,
|
|
146
|
-
changesets: new Set(sourceChangesets)
|
|
147
|
-
});
|
|
148
|
-
return true;
|
|
149
|
-
}
|
|
150
|
-
/** Check if a bump level meets the trigger threshold */
|
|
151
|
-
function shouldTrigger(bumpType, trigger) {
|
|
152
|
-
if (trigger === "none") return false;
|
|
153
|
-
return bumpLevel(bumpType) >= bumpLevel(trigger);
|
|
154
|
-
}
|
|
155
|
-
/**
|
|
156
|
-
* Resolve the dependency bump rule for a specific dependent/dependency pair.
|
|
157
|
-
* Priority: specificDependencyRules > per-package depType rules > global depType rules > defaults
|
|
158
|
-
*/
|
|
159
|
-
function resolveRule(dependentName, dependencyName, depType, packages, config) {
|
|
160
|
-
const dependent = packages.get(dependentName);
|
|
161
|
-
if (dependent?.bumpy?.specificDependencyRules?.[dependencyName]) return dependent.bumpy.specificDependencyRules[dependencyName];
|
|
162
|
-
if (dependent?.bumpy?.specificDependencyRules) {
|
|
163
|
-
for (const [pattern, rule] of Object.entries(dependent.bumpy.specificDependencyRules)) if (matchGlob(dependencyName, pattern)) return rule;
|
|
164
|
-
}
|
|
165
|
-
if (dependent?.bumpy?.dependencyBumpRules?.[depType]) return dependent.bumpy.dependencyBumpRules[depType];
|
|
166
|
-
if (config.dependencyBumpRules[depType]) return config.dependencyBumpRules[depType];
|
|
167
|
-
return DEFAULT_BUMP_RULES[depType] || {
|
|
168
|
-
trigger: "patch",
|
|
169
|
-
bumpAs: "patch"
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
//#endregion
|
|
173
|
-
export { assembleReleasePlan as t };
|