bunset 0.0.2 → 0.0.3
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/README.md +26 -13
- package/package.json +1 -1
- package/src/cli.ts +24 -8
- package/src/config.ts +4 -0
- package/src/index.ts +73 -3
- package/src/types.ts +2 -1
package/README.md
CHANGED
|
@@ -33,11 +33,12 @@ bunset [options]
|
|
|
33
33
|
| `--all` | Update all workspace packages |
|
|
34
34
|
| `--changed` | Update only changed packages (default for workspaces) |
|
|
35
35
|
| `--no-commit` | Do not commit changes to git (commits by default) |
|
|
36
|
-
| `--tag` | Tag the commit with new version (default) |
|
|
36
|
+
| `--no-tag` | Tag the commit with new version (default) |
|
|
37
37
|
| `--per-package-tags` | Use `pkg@1.2.3` tags instead of prefixed tags |
|
|
38
|
-
| `--tag-prefix` | Tag prefix (
|
|
38
|
+
| `--tag-prefix` | Tag prefix (auto-detected from last tag, or `v` if no tags) |
|
|
39
39
|
| `--sections` | Comma-separated changelog sections (default: `feat,fix,perf`) |
|
|
40
40
|
| `--dry-run` | Preview changes without writing files, committing, or tagging |
|
|
41
|
+
| `--debug` | Show detailed inclusion/exclusion reasoning (implies `--dry-run`) |
|
|
41
42
|
| `--no-filter-by-package` | Include all commits in every package changelog (monorepo) |
|
|
42
43
|
|
|
43
44
|
When bump type or scope flags are omitted, interactive prompts will ask.
|
|
@@ -87,21 +88,33 @@ Only sections listed in `--sections` (or the `sections` config option) appear in
|
|
|
87
88
|
|
|
88
89
|
### Config File
|
|
89
90
|
|
|
90
|
-
Place a `.bunset.toml` in your project root to set persistent defaults so you don't have to pass the same flags every time
|
|
91
|
+
Place a `.bunset.toml` in your project root to set persistent defaults so you don't have to pass the same flags every time. All fields are optional. CLI flags always take priority over config values.
|
|
91
92
|
|
|
92
93
|
```toml
|
|
93
|
-
bump = "patch"
|
|
94
|
-
scope = "changed"
|
|
95
|
-
commit = true
|
|
96
|
-
tag = true
|
|
97
|
-
per-package-tags = false
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
94
|
+
bump = "patch" # "patch" | "minor" | "major"
|
|
95
|
+
scope = "changed" # "all" | "changed"
|
|
96
|
+
commit = true # auto-commit (default: true)
|
|
97
|
+
tag = true # create git tags (default: true)
|
|
98
|
+
per-package-tags = false # pkg@version tags (monorepo)
|
|
99
|
+
tag-prefix = "v" # tag prefix (default: auto-detect)
|
|
100
|
+
sections = ["feat", "fix", "perf"] # changelog sections and order
|
|
101
|
+
dry-run = false # preview without writing
|
|
102
|
+
debug = false # detailed reasoning (implies dry-run)
|
|
103
|
+
filter-by-package = true # per-package filtering (monorepo)
|
|
102
104
|
```
|
|
103
105
|
|
|
104
|
-
|
|
106
|
+
| Key | Type | Default | Description |
|
|
107
|
+
|-----|------|---------|-------------|
|
|
108
|
+
| `bump` | `string` | _(prompt)_ | Version bump type: `"patch"`, `"minor"`, or `"major"`. Skips the interactive prompt when set. |
|
|
109
|
+
| `scope` | `string` | _(prompt)_ | Package scope: `"all"` or `"changed"`. Skips the interactive prompt when set (monorepo only). |
|
|
110
|
+
| `commit` | `boolean` | `true` | Whether to auto-commit the version bump and changelog changes. |
|
|
111
|
+
| `tag` | `boolean` | `true` | Whether to create git tags for released versions. |
|
|
112
|
+
| `per-package-tags` | `boolean` | `false` | Use `pkg@1.2.3` tags instead of prefixed tags. In a monorepo, packages with no matching commits are skipped entirely. |
|
|
113
|
+
| `tag-prefix` | `string` | _(auto)_ | Prefix for version tags. Auto-detected from the last git tag when not set (falls back to `"v"` if no tags exist). Set to `""` for bare version numbers, or e.g. `"project-v"` for `project-v1.2.3`. |
|
|
114
|
+
| `sections` | `string[]` | `["feat", "fix", "perf"]` | Which commit types to include in the changelog and in what order. Accepts any recognized type keyword. |
|
|
115
|
+
| `dry-run` | `boolean` | `false` | Preview all changes without writing files, committing, or tagging. |
|
|
116
|
+
| `debug` | `boolean` | `false` | Show detailed inclusion/exclusion reasoning. Implies `dry-run`. |
|
|
117
|
+
| `filter-by-package` | `boolean` | `true` | In a monorepo, only include commits that touched files within each package. Disable with `false` to include all commits in every changelog. |
|
|
105
118
|
|
|
106
119
|
## Testing
|
|
107
120
|
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -16,9 +16,10 @@ Options:
|
|
|
16
16
|
--no-commit Do not commit the version bump and changelog
|
|
17
17
|
--no-tag Do not create git tags for released versions
|
|
18
18
|
--per-package-tags Use package-scoped tags (pkg@version) instead of prefixed
|
|
19
|
-
--tag-prefix <str> Tag prefix (
|
|
19
|
+
--tag-prefix <str> Tag prefix (auto-detected from last tag, or "v" if no tags)
|
|
20
20
|
--sections <list> Comma-separated changelog sections (default: feat,fix,perf)
|
|
21
21
|
--dry-run Preview changes without writing files, committing, or tagging
|
|
22
|
+
--debug Show detailed inclusion/exclusion reasoning (implies --dry-run)
|
|
22
23
|
--no-filter-by-package
|
|
23
24
|
Include all commits in every package changelog (monorepo)
|
|
24
25
|
--help, -h Show this help message
|
|
@@ -50,9 +51,21 @@ Commit format:
|
|
|
50
51
|
Only sections listed in --sections (or config) are included in the changelog.
|
|
51
52
|
Default sections: feat, fix, perf.
|
|
52
53
|
|
|
53
|
-
Config:
|
|
54
|
+
Config file (.bunset.toml):
|
|
54
55
|
Place a .bunset.toml in your project root to set persistent defaults.
|
|
55
|
-
CLI flags always override config values.
|
|
56
|
+
All fields are optional. CLI flags always override config values.
|
|
57
|
+
|
|
58
|
+
Example:
|
|
59
|
+
bump = "patch" # "patch" | "minor" | "major"
|
|
60
|
+
scope = "changed" # "all" | "changed"
|
|
61
|
+
commit = true # auto-commit (default: true)
|
|
62
|
+
tag = true # create git tags (default: true)
|
|
63
|
+
per-package-tags = false # pkg@version tags (monorepo)
|
|
64
|
+
tag-prefix = "v" # tag prefix (default: auto-detect)
|
|
65
|
+
sections = ["feat", "fix", "perf"] # changelog sections and order
|
|
66
|
+
dry-run = false # preview without writing
|
|
67
|
+
debug = false # detailed reasoning (implies dry-run)
|
|
68
|
+
filter-by-package = true # per-package filtering (monorepo)`);
|
|
56
69
|
}
|
|
57
70
|
|
|
58
71
|
export function resolveOptions(
|
|
@@ -74,6 +87,7 @@ export function resolveOptions(
|
|
|
74
87
|
"dry-run": { type: "boolean", default: false },
|
|
75
88
|
"filter-by-package": { type: "boolean", default: true },
|
|
76
89
|
"tag-prefix": { type: "string" },
|
|
90
|
+
debug: { type: "boolean", default: false },
|
|
77
91
|
help: { type: "boolean", short: "h", default: false },
|
|
78
92
|
},
|
|
79
93
|
strict: true,
|
|
@@ -100,7 +114,8 @@ export function resolveOptions(
|
|
|
100
114
|
?? config.sections
|
|
101
115
|
?? DEFAULT_SECTIONS;
|
|
102
116
|
|
|
103
|
-
const
|
|
117
|
+
const debug = values.debug ? true : (config.debug ?? false);
|
|
118
|
+
const dryRun = debug || values["dry-run"] ? true : (config.dryRun ?? false);
|
|
104
119
|
|
|
105
120
|
const filterByPackage = values["filter-by-package"] === false
|
|
106
121
|
? false
|
|
@@ -108,14 +123,14 @@ export function resolveOptions(
|
|
|
108
123
|
|
|
109
124
|
const tagPrefix = values["tag-prefix"] as string | undefined
|
|
110
125
|
?? config.tagPrefix
|
|
111
|
-
??
|
|
126
|
+
?? null;
|
|
112
127
|
|
|
113
128
|
if (bump && scope) {
|
|
114
|
-
return { scope, bump, commit, tag, perPackageTags, sections, dryRun, filterByPackage, tagPrefix };
|
|
129
|
+
return { scope, bump, commit, tag, perPackageTags, sections, dryRun, filterByPackage, tagPrefix, debug };
|
|
115
130
|
}
|
|
116
131
|
|
|
117
132
|
return promptForMissing(
|
|
118
|
-
{ commit, tag, perPackageTags, sections, dryRun, filterByPackage, tagPrefix },
|
|
133
|
+
{ commit, tag, perPackageTags, sections, dryRun, filterByPackage, tagPrefix, debug },
|
|
119
134
|
bump,
|
|
120
135
|
scope,
|
|
121
136
|
isWs,
|
|
@@ -156,7 +171,8 @@ interface MergedDefaults {
|
|
|
156
171
|
sections: CommitType[];
|
|
157
172
|
dryRun: boolean;
|
|
158
173
|
filterByPackage: boolean;
|
|
159
|
-
tagPrefix: string;
|
|
174
|
+
tagPrefix: string | null;
|
|
175
|
+
debug: boolean;
|
|
160
176
|
}
|
|
161
177
|
|
|
162
178
|
async function promptForMissing(
|
package/src/config.ts
CHANGED
|
@@ -49,6 +49,10 @@ export async function loadConfig(
|
|
|
49
49
|
config.tagPrefix = raw["tag-prefix"];
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
if (typeof raw.debug === "boolean") {
|
|
53
|
+
config.debug = raw.debug;
|
|
54
|
+
}
|
|
55
|
+
|
|
52
56
|
if (Array.isArray(raw.sections)) {
|
|
53
57
|
const sections: CommitType[] = [];
|
|
54
58
|
for (const s of raw.sections) {
|
package/src/index.ts
CHANGED
|
@@ -29,10 +29,41 @@ const isWs = await isWorkspace(cwd);
|
|
|
29
29
|
const config = await loadConfig(cwd);
|
|
30
30
|
const options = await resolveOptions(isWs, config);
|
|
31
31
|
|
|
32
|
+
const dbg = options.debug;
|
|
33
|
+
function debug(msg: string): void {
|
|
34
|
+
if (dbg) console.log(`[debug] ${msg}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (dbg) {
|
|
38
|
+
console.log("--- Debug Mode (dry-run implied) ---\n");
|
|
39
|
+
debug(`cwd: ${cwd}`);
|
|
40
|
+
debug(`workspace: ${isWs}`);
|
|
41
|
+
debug(`config loaded: ${JSON.stringify(config)}`);
|
|
42
|
+
debug(`resolved options: ${JSON.stringify(options)}`);
|
|
43
|
+
console.log("");
|
|
44
|
+
}
|
|
45
|
+
|
|
32
46
|
const allPackages = await getAllPackages(cwd);
|
|
33
47
|
const lastTag = await getLastTag(cwd);
|
|
34
48
|
const rawCommits = await getCommitsSince(cwd, lastTag);
|
|
35
49
|
|
|
50
|
+
// Resolve tagPrefix: explicit value wins, otherwise infer from last tag
|
|
51
|
+
let tagPrefix: string;
|
|
52
|
+
if (options.tagPrefix !== null) {
|
|
53
|
+
tagPrefix = options.tagPrefix;
|
|
54
|
+
debug(`tag prefix explicit: "${tagPrefix}"`);
|
|
55
|
+
} else if (lastTag) {
|
|
56
|
+
const semverMatch = lastTag.match(/\d+\.\d+\.\d+/);
|
|
57
|
+
tagPrefix = semverMatch ? lastTag.slice(0, semverMatch.index) : "v";
|
|
58
|
+
debug(`tag prefix auto-detected: "${tagPrefix}" (from tag: ${lastTag})`);
|
|
59
|
+
} else {
|
|
60
|
+
tagPrefix = "v";
|
|
61
|
+
debug(`tag prefix default: "${tagPrefix}" (no previous tags found)`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
debug(`last tag: ${lastTag ?? "(none)"}`);
|
|
65
|
+
debug(`raw commits since tag: ${rawCommits.length}`);
|
|
66
|
+
|
|
36
67
|
if (rawCommits.length === 0) {
|
|
37
68
|
console.log("No commits found since last tag. Nothing to do.");
|
|
38
69
|
process.exit(0);
|
|
@@ -40,14 +71,33 @@ if (rawCommits.length === 0) {
|
|
|
40
71
|
|
|
41
72
|
const parsed = rawCommits.map((c) => parseCommit(c.hash, c.message));
|
|
42
73
|
|
|
74
|
+
if (dbg) {
|
|
75
|
+
console.log("");
|
|
76
|
+
debug("--- Parsed commits ---");
|
|
77
|
+
for (const c of parsed) {
|
|
78
|
+
const typeStr = c.type ?? "UNRECOGNIZED";
|
|
79
|
+
const scopeStr = c.commitScope ? `(${c.commitScope})` : "";
|
|
80
|
+
const included = c.type && options.sections.includes(c.type) ? "INCLUDED" : "EXCLUDED";
|
|
81
|
+
debug(` ${c.hash.slice(0, 7)} ${typeStr}${scopeStr}: ${c.description} → ${included} (section: ${c.type ?? "none"})`);
|
|
82
|
+
}
|
|
83
|
+
console.log("");
|
|
84
|
+
}
|
|
85
|
+
|
|
43
86
|
// In a monorepo with filtering, fetch the file list for each commit
|
|
44
87
|
const shouldFilter = isWs && options.filterByPackage;
|
|
88
|
+
debug(`per-package filtering: ${shouldFilter ? "enabled" : "disabled"}`);
|
|
45
89
|
if (shouldFilter) {
|
|
46
90
|
await Promise.all(
|
|
47
91
|
parsed.map(async (commit) => {
|
|
48
92
|
commit.files = await getCommitFiles(cwd, commit.hash);
|
|
49
93
|
}),
|
|
50
94
|
);
|
|
95
|
+
if (dbg) {
|
|
96
|
+
for (const c of parsed) {
|
|
97
|
+
debug(` ${c.hash.slice(0, 7)} files: ${c.files.length > 0 ? c.files.join(", ") : "(none)"}`);
|
|
98
|
+
}
|
|
99
|
+
console.log("");
|
|
100
|
+
}
|
|
51
101
|
}
|
|
52
102
|
|
|
53
103
|
const globalGroups = groupCommits(parsed);
|
|
@@ -62,6 +112,8 @@ let packages =
|
|
|
62
112
|
? await getChangedPackages(cwd, allPackages, lastTag)
|
|
63
113
|
: allPackages;
|
|
64
114
|
|
|
115
|
+
debug(`scope: ${options.scope}, packages to process: ${packages.map((p) => p.name).join(", ") || "(none)"}`);
|
|
116
|
+
|
|
65
117
|
if (packages.length === 0) {
|
|
66
118
|
console.log("No changed packages found. Nothing to do.");
|
|
67
119
|
process.exit(0);
|
|
@@ -81,7 +133,7 @@ function packageHasChanges(groups: GroupedCommits): boolean {
|
|
|
81
133
|
}
|
|
82
134
|
|
|
83
135
|
if (options.dryRun) {
|
|
84
|
-
console.log("--- Dry Run ---\n");
|
|
136
|
+
if (!dbg) console.log("--- Dry Run ---\n");
|
|
85
137
|
|
|
86
138
|
const tags: string[] = [];
|
|
87
139
|
|
|
@@ -89,6 +141,24 @@ if (options.dryRun) {
|
|
|
89
141
|
const groups = getPackageGroups(pkg, parsed);
|
|
90
142
|
const hasChanges = packageHasChanges(groups);
|
|
91
143
|
|
|
144
|
+
if (dbg) {
|
|
145
|
+
debug(`--- Package: ${pkg.name} ---`);
|
|
146
|
+
debug(` path: ${pkg.path}`);
|
|
147
|
+
debug(` current version: ${pkg.version ?? "0.0.0"}`);
|
|
148
|
+
for (const section of options.sections) {
|
|
149
|
+
const commits = groups[section];
|
|
150
|
+
if (commits.length > 0) {
|
|
151
|
+
debug(` ${section}: ${commits.length} commit(s)`);
|
|
152
|
+
for (const c of commits) {
|
|
153
|
+
debug(` - ${c.hash.slice(0, 7)} ${c.description}${c.commitScope ? ` (scope: ${c.commitScope})` : ""}`);
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
debug(` ${section}: 0 commits`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
debug(` has matching commits: ${hasChanges}`);
|
|
160
|
+
}
|
|
161
|
+
|
|
92
162
|
if (!hasChanges && options.perPackageTags) {
|
|
93
163
|
console.log(`${pkg.name}: no matching commits, skipping.`);
|
|
94
164
|
continue;
|
|
@@ -117,7 +187,7 @@ if (options.dryRun) {
|
|
|
117
187
|
if (options.perPackageTags) {
|
|
118
188
|
tags.push(`${pkg.name}@${newVersion}`);
|
|
119
189
|
} else {
|
|
120
|
-
tags.push(`${
|
|
190
|
+
tags.push(`${tagPrefix}${newVersion}`);
|
|
121
191
|
}
|
|
122
192
|
}
|
|
123
193
|
}
|
|
@@ -177,7 +247,7 @@ for (const pkg of packages) {
|
|
|
177
247
|
if (options.perPackageTags) {
|
|
178
248
|
tags.push(`${pkg.name}@${newVersion}`);
|
|
179
249
|
} else {
|
|
180
|
-
tags.push(`${
|
|
250
|
+
tags.push(`${tagPrefix}${newVersion}`);
|
|
181
251
|
}
|
|
182
252
|
}
|
|
183
253
|
}
|