@varlock/bumpy 0.0.2 → 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.
- package/.claude-plugin/plugin.json +2 -2
- package/config-schema.json +327 -0
- package/dist/add-BmNL5VwL.mjs +323 -0
- package/dist/{ai-CQhUyHAG.mjs → ai-sMYUf3lP.mjs} +21 -4
- package/dist/{apply-release-plan-D6TSrcwX.mjs → apply-release-plan-0kH62jhu.mjs} +35 -26
- package/dist/bump-file-DVqR3k67.mjs +157 -0
- package/dist/{changelog-github-Du62krXi.mjs → changelog-github-DkACMj0j.mjs} +23 -21
- package/dist/check-BjWF6SJm.mjs +65 -0
- package/dist/{ci-D6LQbR38.mjs → ci-DY58ugIi.mjs} +138 -91
- package/dist/{ci-setup-C6FlOfW5.mjs → ci-setup-BQwktQEe.mjs} +3 -3
- package/dist/cli.mjs +36 -41
- package/dist/commit-message-BwsowSds.mjs +23 -0
- package/dist/{config-BkwIEaQg.mjs → config-B-Qg3DZH.mjs} +30 -24
- package/dist/fs-DYR2XuFE.mjs +81 -0
- package/dist/generate-DX46X-rW.mjs +186 -0
- package/dist/{git-CGHVXXKw.mjs → git-YDedMddc.mjs} +54 -2
- package/dist/index.d.mts +68 -39
- package/dist/index.mjs +9 -9
- package/dist/init-DkTPs_WQ.mjs +196 -0
- package/dist/{names-Ck8cun7B.mjs → names-C-TuOPbd.mjs} +1 -1
- package/dist/{js-yaml-DpZfOoD4.mjs → package-manager-Clsmr-9r.mjs} +79 -1
- package/dist/picomatch-DMmqYjgq.mjs +1870 -0
- package/dist/{publish-D_7RqEYL.mjs → publish-CGB4TIKD.mjs} +26 -25
- package/dist/{publish-pipeline-ChnqW8nR.mjs → publish-pipeline-CXuqce1N.mjs} +24 -19
- package/dist/release-plan-JNir7bSM.mjs +264 -0
- package/dist/{semver-BTzYh8vc.mjs → semver-BJzWIuRz.mjs} +13 -3
- package/dist/{shell-Dj7JRD_q.mjs → shell-CY7OD48z.mjs} +20 -2
- package/dist/{status--Q8yAxQ4.mjs → status-EGYqULJg.mjs} +26 -22
- package/dist/{version-cAUkfYPx.mjs → version-BcfidiVX.mjs} +23 -22
- package/dist/{workspace-CxEKakDm.mjs → workspace-DWXlwcH4.mjs} +3 -3
- package/package.json +16 -1
- package/skills/add-change/SKILL.md +18 -14
- package/dist/add-BjyVIUlr.mjs +0 -175
- package/dist/changeset-UCZdSRDv.mjs +0 -108
- package/dist/check-jIwike9F.mjs +0 -51
- package/dist/fs-0AtnPUUe.mjs +0 -51
- package/dist/generate-Btrsn1qi.mjs +0 -177
- package/dist/init-B0q3wEQW.mjs +0 -22
- package/dist/migrate-CfQNwD0T.mjs +0 -121
- package/dist/package-manager-DcI5TdDE.mjs +0 -80
- package/dist/release-plan-BEzwApuK.mjs +0 -173
- /package/dist/{clack-CDRCHrC-.mjs → clack-C6bVkGxf.mjs} +0 -0
- /package/dist/{dep-graph-E-9-eQ2J.mjs → dep-graph-DiLeAhl9.mjs} +0 -0
|
@@ -1,37 +1,42 @@
|
|
|
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-
|
|
5
|
-
import { t as DependencyGraph } from "./dep-graph-
|
|
6
|
-
import {
|
|
7
|
-
import { r as
|
|
8
|
-
import { t as assembleReleasePlan } from "./release-plan-
|
|
9
|
-
import { t as applyReleasePlan } from "./apply-release-plan-
|
|
2
|
+
import { a as loadConfig } from "./config-B-Qg3DZH.mjs";
|
|
3
|
+
import { n as detectWorkspaces } from "./package-manager-Clsmr-9r.mjs";
|
|
4
|
+
import { t as discoverPackages } from "./workspace-DWXlwcH4.mjs";
|
|
5
|
+
import { t as DependencyGraph } from "./dep-graph-DiLeAhl9.mjs";
|
|
6
|
+
import { n as runArgs, s as tryRunArgs } from "./shell-CY7OD48z.mjs";
|
|
7
|
+
import { r as readBumpFiles } from "./bump-file-DVqR3k67.mjs";
|
|
8
|
+
import { t as assembleReleasePlan } from "./release-plan-JNir7bSM.mjs";
|
|
9
|
+
import { t as applyReleasePlan } from "./apply-release-plan-0kH62jhu.mjs";
|
|
10
|
+
import { t as resolveCommitMessage } from "./commit-message-BwsowSds.mjs";
|
|
10
11
|
//#region src/commands/version.ts
|
|
11
|
-
async function versionCommand(rootDir) {
|
|
12
|
+
async function versionCommand(rootDir, opts = {}) {
|
|
12
13
|
const config = await loadConfig(rootDir);
|
|
13
14
|
const packages = await discoverPackages(rootDir, config);
|
|
14
15
|
const depGraph = new DependencyGraph(packages);
|
|
15
|
-
const
|
|
16
|
-
if (
|
|
17
|
-
log.info("No pending
|
|
16
|
+
const bumpFiles = await readBumpFiles(rootDir);
|
|
17
|
+
if (bumpFiles.length === 0) {
|
|
18
|
+
log.info("No pending bump files.");
|
|
18
19
|
return;
|
|
19
20
|
}
|
|
20
|
-
const plan = assembleReleasePlan(
|
|
21
|
+
const plan = assembleReleasePlan(bumpFiles, packages, depGraph, config);
|
|
21
22
|
if (plan.releases.length === 0) {
|
|
22
|
-
log.warn("
|
|
23
|
+
log.warn("Bump files found but no packages would be released.");
|
|
23
24
|
return;
|
|
24
25
|
}
|
|
26
|
+
if (plan.warnings.length > 0) {
|
|
27
|
+
for (const w of plan.warnings) log.warn(w);
|
|
28
|
+
console.log();
|
|
29
|
+
}
|
|
25
30
|
log.step("Applying version bumps:");
|
|
26
31
|
for (const r of plan.releases) {
|
|
27
32
|
const tag = r.isDependencyBump ? " (dep)" : r.isCascadeBump ? " (cascade)" : "";
|
|
28
33
|
console.log(` ${r.name}: ${r.oldVersion} → ${colorize(r.newVersion, "cyan")}${tag}`);
|
|
29
34
|
}
|
|
30
35
|
await applyReleasePlan(plan, packages, rootDir, config);
|
|
31
|
-
log.success(
|
|
32
|
-
log.dim(` Deleted ${
|
|
36
|
+
log.success(`🐸 Updated ${plan.releases.length} package(s)`);
|
|
37
|
+
log.dim(` Deleted ${bumpFiles.length} bump file(s)`);
|
|
33
38
|
await updateLockfile(rootDir);
|
|
34
|
-
if (
|
|
39
|
+
if (opts.commit) try {
|
|
35
40
|
runArgs([
|
|
36
41
|
"git",
|
|
37
42
|
"add",
|
|
@@ -72,11 +77,7 @@ async function versionCommand(rootDir) {
|
|
|
72
77
|
"-"
|
|
73
78
|
], {
|
|
74
79
|
cwd: rootDir,
|
|
75
|
-
input:
|
|
76
|
-
"Version packages",
|
|
77
|
-
"",
|
|
78
|
-
...plan.releases.map((r) => `${r.name}@${r.newVersion}`)
|
|
79
|
-
].join("\n")
|
|
80
|
+
input: await resolveCommitMessage(config.versionCommitMessage, plan, rootDir)
|
|
80
81
|
});
|
|
81
82
|
log.success("Created git commit");
|
|
82
83
|
} catch (e) {
|
|
@@ -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-B-Qg3DZH.mjs";
|
|
3
|
+
import { n as detectWorkspaces } from "./package-manager-Clsmr-9r.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": "
|
|
3
|
+
"version": "1.1.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",
|
|
@@ -12,6 +24,7 @@
|
|
|
12
24
|
},
|
|
13
25
|
"files": [
|
|
14
26
|
"dist",
|
|
27
|
+
"config-schema.json",
|
|
15
28
|
".claude-plugin",
|
|
16
29
|
"skills"
|
|
17
30
|
],
|
|
@@ -31,9 +44,11 @@
|
|
|
31
44
|
"@clack/prompts": "^1.2.0",
|
|
32
45
|
"@types/bun": "latest",
|
|
33
46
|
"@types/js-yaml": "^4.0.9",
|
|
47
|
+
"@types/picomatch": "^4.0.3",
|
|
34
48
|
"@types/semver": "^7.7.0",
|
|
35
49
|
"js-yaml": "^4.1.0",
|
|
36
50
|
"picocolors": "^1.1.1",
|
|
51
|
+
"picomatch": "^4.0.4",
|
|
37
52
|
"semver": "^7.7.2",
|
|
38
53
|
"tsdown": "^0.21.8"
|
|
39
54
|
}
|
|
@@ -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,23 +43,26 @@ 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
|
|
|
54
|
-
Write a concise summary
|
|
50
|
+
Write a concise summary for the CHANGELOG entry. Keep it short — ideally a single sentence, at most two. Good summaries:
|
|
55
51
|
|
|
56
52
|
- Start with a verb: "Added...", "Fixed...", "Refactored..."
|
|
57
53
|
- Focus on user-facing impact, not implementation details
|
|
58
54
|
- Are specific enough to be useful months later
|
|
55
|
+
- Avoid filler, jargon, or restating the bump level
|
|
56
|
+
- Don't list every file changed — describe the logical change
|
|
57
|
+
|
|
58
|
+
Bad: "Updated the authentication module to fix an issue where the token refresh mechanism was not properly handling expired refresh tokens, causing silent failures in the auth flow."
|
|
59
|
+
Good: "Fixed token refresh failing silently on expired refresh tokens."
|
|
60
|
+
|
|
61
|
+
### 5. Create or update the bump file
|
|
59
62
|
|
|
60
|
-
|
|
63
|
+
Check if there are already bump files on this branch (from step 1's `bumpy status`). If one exists that covers the same logical change, **update it in place** by editing the `.bumpy/<name>.md` file directly — adjust the package list, bump levels, and summary to reflect the current state of the branch. Don't create a new bump file for every incremental change on the same branch.
|
|
61
64
|
|
|
62
|
-
|
|
65
|
+
If no relevant bump file exists yet, create one with the non-interactive CLI:
|
|
63
66
|
|
|
64
67
|
```bash
|
|
65
68
|
bumpy add \
|
|
@@ -83,7 +86,7 @@ bumpy add \
|
|
|
83
86
|
|
|
84
87
|
## Advanced: cascading bumps
|
|
85
88
|
|
|
86
|
-
If a change in a core package should explicitly cascade to dependents with specific bump levels, write the
|
|
89
|
+
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
90
|
|
|
88
91
|
```bash
|
|
89
92
|
cat > .bumpy/<name>.md << 'EOF'
|
|
@@ -105,4 +108,5 @@ EOF
|
|
|
105
108
|
- Only include packages that have **actual code changes** — bumpy handles dependency propagation automatically
|
|
106
109
|
- If the user hasn't made any changes yet, ask what they're planning to change
|
|
107
110
|
- 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
|
|
111
|
+
- One bump file per logical change — don't combine unrelated changes
|
|
112
|
+
- **Keep bump files up to date** — as work continues on a branch, the bump file should reflect the final state of all changes, not just the first commit. If packages were added/removed or the bump level changed (e.g., a patch fix grew into a minor feature), update the existing bump file accordingly
|
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/check-jIwike9F.mjs
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { n as log, t as colorize } from "./logger-C2dEe5Su.mjs";
|
|
2
|
-
import { a as loadConfig } from "./config-BkwIEaQg.mjs";
|
|
3
|
-
import { n as discoverWorkspace } from "./workspace-CxEKakDm.mjs";
|
|
4
|
-
import { r as readChangesets } from "./changeset-UCZdSRDv.mjs";
|
|
5
|
-
import { n as getChangedFiles } from "./git-CGHVXXKw.mjs";
|
|
6
|
-
import { relative } from "node:path";
|
|
7
|
-
//#region src/commands/check.ts
|
|
8
|
-
/**
|
|
9
|
-
* Local check: detect which packages have changed on this branch
|
|
10
|
-
* and verify they have corresponding changesets.
|
|
11
|
-
* Designed for pre-push hooks — no GitHub API needed.
|
|
12
|
-
*/
|
|
13
|
-
async function checkCommand(rootDir) {
|
|
14
|
-
const config = await loadConfig(rootDir);
|
|
15
|
-
const { packages } = await discoverWorkspace(rootDir, config);
|
|
16
|
-
const changesets = await readChangesets(rootDir);
|
|
17
|
-
const coveredPackages = /* @__PURE__ */ new Set();
|
|
18
|
-
for (const cs of changesets) for (const release of cs.releases) coveredPackages.add(release.name);
|
|
19
|
-
const baseBranch = config.baseBranch;
|
|
20
|
-
const changedFiles = getChangedFiles(rootDir, baseBranch);
|
|
21
|
-
if (changedFiles.length === 0) {
|
|
22
|
-
log.info("No changed files detected.");
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
const changedPackages = findChangedPackages(changedFiles, packages, rootDir);
|
|
26
|
-
if (changedPackages.length === 0) {
|
|
27
|
-
log.info("No managed packages have changed.");
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
const missing = changedPackages.filter((name) => !coveredPackages.has(name));
|
|
31
|
-
if (missing.length === 0) {
|
|
32
|
-
log.success(`All ${changedPackages.length} changed package(s) have changesets.`);
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
log.warn(`${missing.length} changed package(s) missing changesets:\n`);
|
|
36
|
-
for (const name of missing) console.log(` ${colorize(name, "yellow")}`);
|
|
37
|
-
console.log();
|
|
38
|
-
log.dim("Run `bumpy add` to create a changeset, or `bumpy add --empty` if no release is needed.");
|
|
39
|
-
process.exit(1);
|
|
40
|
-
}
|
|
41
|
-
/** Map changed files to the packages they belong to */
|
|
42
|
-
function findChangedPackages(changedFiles, packages, rootDir) {
|
|
43
|
-
const changed = /* @__PURE__ */ new Set();
|
|
44
|
-
for (const file of changedFiles) for (const [name, pkg] of packages) {
|
|
45
|
-
const pkgRelDir = relative(rootDir, pkg.dir);
|
|
46
|
-
if (file.startsWith(pkgRelDir + "/")) changed.add(name);
|
|
47
|
-
}
|
|
48
|
-
return [...changed];
|
|
49
|
-
}
|
|
50
|
-
//#endregion
|
|
51
|
-
export { checkCommand };
|
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 };
|