bunset 1.0.9 → 1.0.11
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/CHANGELOG.md +13 -0
- package/README.md +12 -6
- package/package.json +3 -3
- package/src/changelog.test.ts +33 -2
- package/src/changelog.ts +11 -0
- package/src/cli.ts +15 -8
- package/src/commits.ts +5 -1
- package/src/config.ts +8 -2
- package/src/git.ts +17 -0
- package/src/index.ts +91 -15
- package/src/types.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.11
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- add --release option to create GitHub releases with changelog notes (#2)
|
|
8
|
+
- update workspace root package.json version when scope is all (#1)
|
|
9
|
+
|
|
10
|
+
## 1.0.10
|
|
11
|
+
|
|
12
|
+
### Features
|
|
13
|
+
|
|
14
|
+
- default --sections to all types, add "all" keyword and type aliases
|
|
15
|
+
|
|
3
16
|
## 1.0.9
|
|
4
17
|
|
|
5
18
|
### Bug Fixes
|
package/README.md
CHANGED
|
@@ -29,10 +29,12 @@ bunx bunset [options]
|
|
|
29
29
|
| `--no-tag` | Tag the commit with new version (default) |
|
|
30
30
|
| `--per-package-tags` | Use `pkg@1.2.3` tags instead of prefixed tags |
|
|
31
31
|
| `--tag-prefix` | Tag prefix (auto-detected from last tag, or `v` if no tags) |
|
|
32
|
-
| `--sections` | Comma-separated changelog sections (default: `
|
|
32
|
+
| `--sections` | Comma-separated changelog sections, or `all` (default: `all`) |
|
|
33
33
|
| `--dry-run` | Preview changes without writing files, committing, or tagging |
|
|
34
34
|
| `--debug` | Show detailed inclusion/exclusion reasoning (implies `--dry-run`) |
|
|
35
35
|
| `--no-filter-by-package` | Include all commits in every package changelog (monorepo) |
|
|
36
|
+
| `--push` | Push the release commit and tags to the remote |
|
|
37
|
+
| `--release` | Create a GitHub release per tag with the changelog entry as the release notes (requires `--push` and the `gh` CLI) |
|
|
36
38
|
|
|
37
39
|
When bump type or scope flags are omitted, interactive prompts will ask.
|
|
38
40
|
|
|
@@ -84,16 +86,16 @@ Breaking commits are collected into a **Breaking Changes** section at the top of
|
|
|
84
86
|
- `feat`, `feature` — listed under **Features**
|
|
85
87
|
- `fix`, `bug`, `bugfix` — listed under **Bug Fixes**
|
|
86
88
|
- `refactor` — listed under **Refactors**
|
|
87
|
-
- `perf` — listed under **Performance**
|
|
89
|
+
- `perf`, `performance` — listed under **Performance**
|
|
88
90
|
- `style` — listed under **Style**
|
|
89
91
|
- `test` — listed under **Tests**
|
|
90
|
-
- `docs` — listed under **Documentation**
|
|
92
|
+
- `docs`, `documentation` — listed under **Documentation**
|
|
91
93
|
- `build` — listed under **Build**
|
|
92
94
|
- `ops` — listed under **Ops**
|
|
93
95
|
- `chore` — listed under **Chores**
|
|
94
96
|
- `ci` — listed under **CI**
|
|
95
97
|
|
|
96
|
-
Only sections listed in `--sections` (or the `sections` config option) appear in the changelog. The default is `feat,fix,perf`.
|
|
98
|
+
Only sections listed in `--sections` (or the `sections` config option) appear in the changelog. The default is `all` (every recognized type). Pass `--sections all` explicitly or use a comma-separated subset like `--sections feat,fix,perf`.
|
|
97
99
|
|
|
98
100
|
### Config File
|
|
99
101
|
|
|
@@ -106,7 +108,9 @@ commit = true # auto-commit (default: true)
|
|
|
106
108
|
tag = true # create git tags (default: true)
|
|
107
109
|
per-package-tags = false # pkg@version tags (monorepo)
|
|
108
110
|
tag-prefix = "v" # tag prefix (default: auto-detect)
|
|
109
|
-
sections =
|
|
111
|
+
sections = "all" # changelog sections and order ("all" or array)
|
|
112
|
+
push = false # push after tagging (default: false)
|
|
113
|
+
release = false # create GitHub release per tag (default: false)
|
|
110
114
|
dry-run = false # preview without writing
|
|
111
115
|
debug = false # detailed reasoning (implies dry-run)
|
|
112
116
|
filter-by-package = true # per-package filtering (monorepo)
|
|
@@ -120,10 +124,12 @@ filter-by-package = true # per-package filtering (monorepo)
|
|
|
120
124
|
| `tag` | `boolean` | `true` | Whether to create git tags for released versions. |
|
|
121
125
|
| `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. |
|
|
122
126
|
| `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`. |
|
|
123
|
-
| `sections` | `string[]
|
|
127
|
+
| `sections` | `string[] \| "all"` | `"all"` | Which commit types to include in the changelog and in what order. Use `"all"` for every type, or an array of recognized type keywords. |
|
|
124
128
|
| `dry-run` | `boolean` | `false` | Preview all changes without writing files, committing, or tagging. |
|
|
125
129
|
| `debug` | `boolean` | `false` | Show detailed inclusion/exclusion reasoning. Implies `dry-run`. |
|
|
126
130
|
| `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. |
|
|
131
|
+
| `push` | `boolean` | `false` | Push the release commit and tags to the remote after tagging. |
|
|
132
|
+
| `release` | `boolean` | `false` | Create a GitHub release for each tag with the changelog entry as the release notes. Requires `push = true` and the [`gh` CLI](https://cli.github.com/). When multiple packages share a tag, their entries are combined under per-package headings. |
|
|
127
133
|
|
|
128
134
|
## Testing
|
|
129
135
|
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bunset",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
4
4
|
"module": "src/index.ts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"bunset": "src/index.ts"
|
|
8
8
|
},
|
|
9
9
|
"devDependencies": {
|
|
10
|
-
"@types/bun": "
|
|
10
|
+
"@types/bun": "1.3.13"
|
|
11
11
|
},
|
|
12
12
|
"peerDependencies": {
|
|
13
|
-
"typescript": "^
|
|
13
|
+
"typescript": "^6.0.3"
|
|
14
14
|
},
|
|
15
15
|
"engines": {
|
|
16
16
|
"bun": "^1.3"
|
package/src/changelog.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { test, expect, describe, beforeEach, afterEach } from "bun:test";
|
|
2
|
-
import { buildChangelogEntry, writeChangelog } from "./changelog.ts";
|
|
2
|
+
import { buildChangelogEntry, buildReleaseNotes, writeChangelog } from "./changelog.ts";
|
|
3
3
|
import { COMMIT_TYPES } from "./commits.ts";
|
|
4
4
|
import type { GroupedCommits } from "./types.ts";
|
|
5
5
|
import { mkdtemp, rm } from "node:fs/promises";
|
|
@@ -56,7 +56,7 @@ describe("buildChangelogEntry", () => {
|
|
|
56
56
|
{ hash: "b", message: "", type: "test", commitScope: null, breaking: false, files: [], description: "Add tests" },
|
|
57
57
|
],
|
|
58
58
|
};
|
|
59
|
-
const entry = buildChangelogEntry("1.0.0", groups);
|
|
59
|
+
const entry = buildChangelogEntry("1.0.0", groups, [], ["feature", "bugfix", "perf"]);
|
|
60
60
|
expect(entry).toContain("### Features");
|
|
61
61
|
expect(entry).not.toContain("### Tests");
|
|
62
62
|
});
|
|
@@ -202,3 +202,34 @@ describe("writeChangelog", () => {
|
|
|
202
202
|
expect(idx1).toBeLessThan(idx2);
|
|
203
203
|
});
|
|
204
204
|
});
|
|
205
|
+
|
|
206
|
+
describe("buildReleaseNotes", () => {
|
|
207
|
+
test("returns empty string for no entries", () => {
|
|
208
|
+
expect(buildReleaseNotes([])).toBe("");
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test("strips the version heading from a single entry", () => {
|
|
212
|
+
const entry = "## 1.2.3\n\n### Features\n\n- Add login\n";
|
|
213
|
+
expect(buildReleaseNotes([{ pkgName: "pkg-a", entry }])).toBe(
|
|
214
|
+
"### Features\n\n- Add login\n",
|
|
215
|
+
);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test("combines multiple entries under per-package headings", () => {
|
|
219
|
+
const result = buildReleaseNotes([
|
|
220
|
+
{ pkgName: "pkg-a", entry: "## 1.2.3\n\n### Features\n\n- A\n" },
|
|
221
|
+
{ pkgName: "pkg-b", entry: "## 1.2.3\n\n### Bug Fixes\n\n- B\n" },
|
|
222
|
+
]);
|
|
223
|
+
expect(result).toContain("## pkg-a");
|
|
224
|
+
expect(result).toContain("## pkg-b");
|
|
225
|
+
expect(result).toContain("### Features");
|
|
226
|
+
expect(result).toContain("### Bug Fixes");
|
|
227
|
+
expect(result).not.toMatch(/^## 1\.2\.3/m);
|
|
228
|
+
expect(result.indexOf("## pkg-a")).toBeLessThan(result.indexOf("## pkg-b"));
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
test("handles entries without a leading version heading", () => {
|
|
232
|
+
const entry = "### Features\n\n- Plain entry\n";
|
|
233
|
+
expect(buildReleaseNotes([{ pkgName: "pkg-a", entry }])).toBe(entry);
|
|
234
|
+
});
|
|
235
|
+
});
|
package/src/changelog.ts
CHANGED
|
@@ -84,6 +84,17 @@ export function buildChangelogEntry(
|
|
|
84
84
|
return lines.join("\n");
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
export function buildReleaseNotes(
|
|
88
|
+
entries: { pkgName: string; entry: string }[],
|
|
89
|
+
): string {
|
|
90
|
+
const stripVersion = (e: string) => e.replace(/^## [^\n]*\n+/, "");
|
|
91
|
+
if (entries.length === 0) return "";
|
|
92
|
+
if (entries.length === 1) return stripVersion(entries[0]!.entry);
|
|
93
|
+
return entries
|
|
94
|
+
.map(({ pkgName, entry }) => `## ${pkgName}\n\n${stripVersion(entry)}`)
|
|
95
|
+
.join("\n\n");
|
|
96
|
+
}
|
|
97
|
+
|
|
87
98
|
export async function writeChangelog(
|
|
88
99
|
dir: string,
|
|
89
100
|
entry: string,
|
package/src/cli.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { parseArgs } from "node:util";
|
|
2
2
|
import type { BumpType, CliOptions, CommitType, PackageScope } from "./types.ts";
|
|
3
|
-
import { normalizeType, DEFAULT_SECTIONS } from "./commits.ts";
|
|
3
|
+
import { normalizeType, DEFAULT_SECTIONS, ALL_SECTIONS } from "./commits.ts";
|
|
4
4
|
|
|
5
5
|
export function printHelp(): void {
|
|
6
6
|
console.log(`bunset - Version bumping and changelog generation for Bun projects
|
|
@@ -17,8 +17,10 @@ Options:
|
|
|
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
19
|
--tag-prefix <str> Tag prefix (auto-detected from last tag, or "v" if no tags)
|
|
20
|
-
--sections <list> Comma-separated changelog sections (default:
|
|
20
|
+
--sections <list> Comma-separated changelog sections, or "all" (default: all)
|
|
21
21
|
--push Push commit and tags to remote after tagging
|
|
22
|
+
--release Create a GitHub release per tag with the changelog entry
|
|
23
|
+
as the release notes (requires --push and the gh CLI)
|
|
22
24
|
--dry-run Preview changes without writing files, committing, or tagging
|
|
23
25
|
--debug Show detailed inclusion/exclusion reasoning (implies --dry-run)
|
|
24
26
|
--no-filter-by-package
|
|
@@ -51,16 +53,16 @@ Commit format:
|
|
|
51
53
|
feat, feature → Features
|
|
52
54
|
fix, bug, bugfix → Bug Fixes
|
|
53
55
|
refactor → Refactors
|
|
54
|
-
perf
|
|
56
|
+
perf, performance → Performance
|
|
55
57
|
style → Style
|
|
56
58
|
test → Tests
|
|
57
|
-
docs
|
|
59
|
+
docs, documentation → Documentation
|
|
58
60
|
build → Build
|
|
59
61
|
ops → Ops
|
|
60
62
|
chore → Chores
|
|
61
63
|
ci → CI
|
|
62
64
|
Only sections listed in --sections (or config) are included in the changelog.
|
|
63
|
-
Default sections: feat,
|
|
65
|
+
Default sections: all (feat,fix,refactor,perf,style,test,docs,build,ops,chore,ci).
|
|
64
66
|
|
|
65
67
|
Config file (.bunset.toml):
|
|
66
68
|
Place a .bunset.toml in your project root to set persistent defaults.
|
|
@@ -73,8 +75,9 @@ Config file (.bunset.toml):
|
|
|
73
75
|
tag = true # create git tags (default: true)
|
|
74
76
|
per-package-tags = false # pkg@version tags (monorepo)
|
|
75
77
|
tag-prefix = "v" # tag prefix (default: auto-detect)
|
|
76
|
-
sections =
|
|
78
|
+
sections = "all" # changelog sections and order ("all" or array)
|
|
77
79
|
push = false # push after tagging (default: false)
|
|
80
|
+
release = false # create GitHub release (default: false)
|
|
78
81
|
dry-run = false # preview without writing
|
|
79
82
|
debug = false # detailed reasoning (implies dry-run)
|
|
80
83
|
filter-by-package = true # per-package filtering (monorepo)`);
|
|
@@ -99,6 +102,7 @@ export function resolveOptions(
|
|
|
99
102
|
"per-package-tags": { type: "boolean", default: false },
|
|
100
103
|
sections: { type: "string" },
|
|
101
104
|
push: { type: "boolean", default: false },
|
|
105
|
+
release: { type: "boolean", default: false },
|
|
102
106
|
"dry-run": { type: "boolean", default: false },
|
|
103
107
|
"filter-by-package": { type: "boolean", default: true },
|
|
104
108
|
"tag-prefix": { type: "string" },
|
|
@@ -138,6 +142,7 @@ export function resolveOptions(
|
|
|
138
142
|
?? DEFAULT_SECTIONS;
|
|
139
143
|
|
|
140
144
|
const push = values.push ? true : (config.push ?? false);
|
|
145
|
+
const release = values.release ? true : (config.release ?? false);
|
|
141
146
|
const debug = values.debug ? true : (config.debug ?? false);
|
|
142
147
|
const dryRun = debug || values["dry-run"] ? true : (config.dryRun ?? false);
|
|
143
148
|
|
|
@@ -150,11 +155,11 @@ export function resolveOptions(
|
|
|
150
155
|
?? null;
|
|
151
156
|
|
|
152
157
|
if (bump && scope && commit !== null && tag !== null) {
|
|
153
|
-
return { scope, bump, commit, tag, perPackageTags, sections, dryRun, filterByPackage, tagPrefix, push, debug };
|
|
158
|
+
return { scope, bump, commit, tag, perPackageTags, sections, dryRun, filterByPackage, tagPrefix, push, release, debug };
|
|
154
159
|
}
|
|
155
160
|
|
|
156
161
|
return promptForMissing(
|
|
157
|
-
{ commit, tag, perPackageTags, sections, dryRun, filterByPackage, tagPrefix, push, debug },
|
|
162
|
+
{ commit, tag, perPackageTags, sections, dryRun, filterByPackage, tagPrefix, push, release, debug },
|
|
158
163
|
bump,
|
|
159
164
|
scope,
|
|
160
165
|
isWs,
|
|
@@ -180,6 +185,7 @@ function resolveScope(
|
|
|
180
185
|
|
|
181
186
|
function parseSections(raw: string | undefined): CommitType[] | null {
|
|
182
187
|
if (!raw) return null;
|
|
188
|
+
if (raw.trim().toLowerCase() === "all") return [...ALL_SECTIONS];
|
|
183
189
|
const result: CommitType[] = [];
|
|
184
190
|
for (const part of raw.split(",")) {
|
|
185
191
|
const type = normalizeType(part.trim());
|
|
@@ -197,6 +203,7 @@ interface MergedDefaults {
|
|
|
197
203
|
filterByPackage: boolean;
|
|
198
204
|
tagPrefix: string | null;
|
|
199
205
|
push: boolean;
|
|
206
|
+
release: boolean;
|
|
200
207
|
debug: boolean;
|
|
201
208
|
}
|
|
202
209
|
|
package/src/commits.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { CommitType, ParsedCommit, GroupedCommits } from "./types.ts";
|
|
2
2
|
|
|
3
|
-
export const
|
|
3
|
+
export const ALL_SECTIONS: CommitType[] = ["feature", "bugfix", "refactor", "perf", "style", "test", "docs", "build", "ops", "chore", "ci"];
|
|
4
|
+
|
|
5
|
+
export const DEFAULT_SECTIONS: CommitType[] = ALL_SECTIONS;
|
|
4
6
|
|
|
5
7
|
const TYPE_MAP: Record<string, CommitType> = {
|
|
6
8
|
feat: "feature",
|
|
@@ -10,9 +12,11 @@ const TYPE_MAP: Record<string, CommitType> = {
|
|
|
10
12
|
bugfix: "bugfix",
|
|
11
13
|
refactor: "refactor",
|
|
12
14
|
perf: "perf",
|
|
15
|
+
performance: "perf",
|
|
13
16
|
style: "style",
|
|
14
17
|
test: "test",
|
|
15
18
|
docs: "docs",
|
|
19
|
+
documentation: "docs",
|
|
16
20
|
build: "build",
|
|
17
21
|
ops: "ops",
|
|
18
22
|
chore: "chore",
|
package/src/config.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import type { CliOptions, CommitType } from "./types.ts";
|
|
3
|
-
import { normalizeType } from "./commits.ts";
|
|
3
|
+
import { normalizeType, ALL_SECTIONS } from "./commits.ts";
|
|
4
4
|
|
|
5
5
|
const CONFIG_FILE = ".bunset.toml";
|
|
6
6
|
|
|
@@ -53,11 +53,17 @@ export async function loadConfig(
|
|
|
53
53
|
config.push = raw.push;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
if (typeof raw.release === "boolean") {
|
|
57
|
+
config.release = raw.release;
|
|
58
|
+
}
|
|
59
|
+
|
|
56
60
|
if (typeof raw.debug === "boolean") {
|
|
57
61
|
config.debug = raw.debug;
|
|
58
62
|
}
|
|
59
63
|
|
|
60
|
-
if (
|
|
64
|
+
if (raw.sections === "all") {
|
|
65
|
+
config.sections = [...ALL_SECTIONS];
|
|
66
|
+
} else if (Array.isArray(raw.sections)) {
|
|
61
67
|
const sections: CommitType[] = [];
|
|
62
68
|
for (const s of raw.sections) {
|
|
63
69
|
if (typeof s === "string") {
|
package/src/git.ts
CHANGED
|
@@ -89,3 +89,20 @@ export async function gitPush(
|
|
|
89
89
|
await $`git -C ${cwd} push --tags`.quiet();
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
|
+
|
|
93
|
+
export async function createGithubRelease(
|
|
94
|
+
cwd: string,
|
|
95
|
+
tag: string,
|
|
96
|
+
notes: string,
|
|
97
|
+
): Promise<void> {
|
|
98
|
+
const proc = Bun.spawn(
|
|
99
|
+
["gh", "release", "create", tag, "--title", tag, "--notes-file", "-"],
|
|
100
|
+
{ cwd, stdin: "pipe", stdout: "inherit", stderr: "inherit" },
|
|
101
|
+
);
|
|
102
|
+
proc.stdin.write(notes);
|
|
103
|
+
await proc.stdin.end();
|
|
104
|
+
const exitCode = await proc.exited;
|
|
105
|
+
if (exitCode !== 0) {
|
|
106
|
+
throw new Error(`gh release create exited with code ${exitCode}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
3
|
import { $ } from "bun";
|
|
4
|
+
import { join } from "node:path";
|
|
4
5
|
import { resolveOptions } from "./cli.ts";
|
|
5
6
|
import { loadConfig } from "./config.ts";
|
|
6
7
|
import {
|
|
@@ -8,13 +9,14 @@ import {
|
|
|
8
9
|
groupCommits,
|
|
9
10
|
filterCommitsForPackage,
|
|
10
11
|
} from "./commits.ts";
|
|
11
|
-
import { buildChangelogEntry, writeChangelog } from "./changelog.ts";
|
|
12
|
+
import { buildChangelogEntry, buildReleaseNotes, writeChangelog } from "./changelog.ts";
|
|
12
13
|
import {
|
|
13
14
|
getLastTag,
|
|
14
15
|
getCommitsSince,
|
|
15
16
|
getCommitFiles,
|
|
16
17
|
commitAndTag,
|
|
17
18
|
gitPush,
|
|
19
|
+
createGithubRelease,
|
|
18
20
|
} from "./git.ts";
|
|
19
21
|
import { getUpdatedDependencies } from "./deps.ts";
|
|
20
22
|
import {
|
|
@@ -71,6 +73,21 @@ if (rawCommits.length === 0) {
|
|
|
71
73
|
process.exit(1);
|
|
72
74
|
}
|
|
73
75
|
|
|
76
|
+
if (options.release && !options.push) {
|
|
77
|
+
console.error("--release requires --push (the tag must be on the remote).");
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (options.release && !options.tag) {
|
|
82
|
+
console.error("--release requires tagging to be enabled.");
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (options.release && !options.commit) {
|
|
87
|
+
console.error("--release requires --commit (tag/push/release run inside the commit step).");
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
|
|
74
91
|
const parsed = rawCommits.map((c) => parseCommit(c.hash, c.message, c.body));
|
|
75
92
|
|
|
76
93
|
if (dbg) {
|
|
@@ -141,11 +158,23 @@ function packageHasChanges(groups: GroupedCommits): boolean {
|
|
|
141
158
|
return options.sections.some((type) => groups[type].length > 0);
|
|
142
159
|
}
|
|
143
160
|
|
|
144
|
-
// When
|
|
161
|
+
// When scope is "all" in a workspace, also update the workspace root's package.json
|
|
162
|
+
const rootPackageJsonPath = join(cwd, "package.json");
|
|
163
|
+
const updateRoot =
|
|
164
|
+
isWs &&
|
|
165
|
+
options.scope === "all" &&
|
|
166
|
+
!packages.some((p) => p.packageJsonPath === rootPackageJsonPath);
|
|
167
|
+
const rootCurrentVersion = updateRoot
|
|
168
|
+
? ((await Bun.file(rootPackageJsonPath).json()).version ?? "0.0.0")
|
|
169
|
+
: null;
|
|
170
|
+
debug(`update root package.json: ${updateRoot}${updateRoot ? ` (current: ${rootCurrentVersion})` : ""}`);
|
|
171
|
+
|
|
172
|
+
// When using shared tags, sync all packages (and root, if updating) to the same target version
|
|
145
173
|
let targetVersion: string | null = null;
|
|
146
|
-
if (!options.perPackageTags && packages.length > 1) {
|
|
147
|
-
const
|
|
148
|
-
|
|
174
|
+
if (!options.perPackageTags && (packages.length > 1 || updateRoot)) {
|
|
175
|
+
const candidateVersions = packages.map((p) => p.version ?? "0.0.0");
|
|
176
|
+
if (updateRoot && rootCurrentVersion) candidateVersions.push(rootCurrentVersion);
|
|
177
|
+
const maxVersion = candidateVersions.reduce((max, v) => {
|
|
149
178
|
const [mj1, mn1, p1] = parseSemver(max);
|
|
150
179
|
const [mj2, mn2, p2] = parseSemver(v);
|
|
151
180
|
if (mj2 > mj1) return v;
|
|
@@ -162,6 +191,8 @@ if (options.dryRun) {
|
|
|
162
191
|
|
|
163
192
|
const tags: string[] = [];
|
|
164
193
|
const filesToCommit: string[] = [];
|
|
194
|
+
const tagEntries = new Map<string, { pkgName: string; entry: string }[]>();
|
|
195
|
+
let anyPackageUpdated = false;
|
|
165
196
|
|
|
166
197
|
for (const pkg of packages) {
|
|
167
198
|
const groups = getPackageGroups(pkg, parsed);
|
|
@@ -194,6 +225,7 @@ if (options.dryRun) {
|
|
|
194
225
|
const newVersion = targetVersion ?? bumpVersion(oldVersion, options.bump);
|
|
195
226
|
console.log(`${pkg.name}: ${oldVersion} → ${newVersion}`);
|
|
196
227
|
filesToCommit.push(pkg.packageJsonPath, `${pkg.path}/CHANGELOG.md`);
|
|
228
|
+
anyPackageUpdated = true;
|
|
197
229
|
|
|
198
230
|
const updatedDeps = await getUpdatedDependencies(
|
|
199
231
|
cwd,
|
|
@@ -211,14 +243,22 @@ if (options.dryRun) {
|
|
|
211
243
|
console.log(entry);
|
|
212
244
|
|
|
213
245
|
if (options.tag) {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
246
|
+
const tag = options.perPackageTags
|
|
247
|
+
? `${pkg.name}@${newVersion}`
|
|
248
|
+
: `${tagPrefix}${newVersion}`;
|
|
249
|
+
tags.push(tag);
|
|
250
|
+
const list = tagEntries.get(tag) ?? [];
|
|
251
|
+
list.push({ pkgName: pkg.name, entry });
|
|
252
|
+
tagEntries.set(tag, list);
|
|
219
253
|
}
|
|
220
254
|
}
|
|
221
255
|
|
|
256
|
+
if (updateRoot && rootCurrentVersion && anyPackageUpdated) {
|
|
257
|
+
const newRootVersion = targetVersion ?? bumpVersion(rootCurrentVersion, options.bump);
|
|
258
|
+
console.log(`(workspace root): ${rootCurrentVersion} → ${newRootVersion}`);
|
|
259
|
+
filesToCommit.push(rootPackageJsonPath);
|
|
260
|
+
}
|
|
261
|
+
|
|
222
262
|
const uniqueTags = [...new Set(tags)];
|
|
223
263
|
|
|
224
264
|
filesToCommit.push(`${cwd}/bun.lock`);
|
|
@@ -244,6 +284,14 @@ if (options.dryRun) {
|
|
|
244
284
|
console.log("Would push commit and tags to remote.");
|
|
245
285
|
}
|
|
246
286
|
|
|
287
|
+
if (options.release) {
|
|
288
|
+
for (const tag of uniqueTags) {
|
|
289
|
+
const entries = tagEntries.get(tag) ?? [];
|
|
290
|
+
console.log(`\nWould create GitHub release ${tag} with notes:`);
|
|
291
|
+
console.log(buildReleaseNotes(entries));
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
247
295
|
console.log(`\nFiles that would be modified (${filesToCommit.length}):`);
|
|
248
296
|
for (const f of filesToCommit) {
|
|
249
297
|
console.log(` ${f}`);
|
|
@@ -253,6 +301,8 @@ if (options.dryRun) {
|
|
|
253
301
|
|
|
254
302
|
const tags: string[] = [];
|
|
255
303
|
const changedFiles: string[] = [];
|
|
304
|
+
const tagEntries = new Map<string, { pkgName: string; entry: string }[]>();
|
|
305
|
+
let anyPackageUpdated = false;
|
|
256
306
|
|
|
257
307
|
for (const pkg of packages) {
|
|
258
308
|
const groups = getPackageGroups(pkg, parsed);
|
|
@@ -269,6 +319,7 @@ for (const pkg of packages) {
|
|
|
269
319
|
: await updatePackageVersion(pkg.packageJsonPath, options.bump);
|
|
270
320
|
changedFiles.push(pkg.packageJsonPath);
|
|
271
321
|
console.log(`${pkg.name}: ${oldVersion} → ${newVersion}`);
|
|
322
|
+
anyPackageUpdated = true;
|
|
272
323
|
|
|
273
324
|
const updatedDeps = await getUpdatedDependencies(
|
|
274
325
|
cwd,
|
|
@@ -285,14 +336,24 @@ for (const pkg of packages) {
|
|
|
285
336
|
changedFiles.push(`${pkg.path}/CHANGELOG.md`);
|
|
286
337
|
|
|
287
338
|
if (options.tag) {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
339
|
+
const tag = options.perPackageTags
|
|
340
|
+
? `${pkg.name}@${newVersion}`
|
|
341
|
+
: `${tagPrefix}${newVersion}`;
|
|
342
|
+
tags.push(tag);
|
|
343
|
+
const list = tagEntries.get(tag) ?? [];
|
|
344
|
+
list.push({ pkgName: pkg.name, entry });
|
|
345
|
+
tagEntries.set(tag, list);
|
|
293
346
|
}
|
|
294
347
|
}
|
|
295
348
|
|
|
349
|
+
if (updateRoot && anyPackageUpdated) {
|
|
350
|
+
const { oldVersion, newVersion } = targetVersion
|
|
351
|
+
? await setPackageVersion(rootPackageJsonPath, targetVersion)
|
|
352
|
+
: await updatePackageVersion(rootPackageJsonPath, options.bump);
|
|
353
|
+
changedFiles.push(rootPackageJsonPath);
|
|
354
|
+
console.log(`(workspace root): ${oldVersion} → ${newVersion}`);
|
|
355
|
+
}
|
|
356
|
+
|
|
296
357
|
const uniqueTags = [...new Set(tags)];
|
|
297
358
|
|
|
298
359
|
if (changedFiles.length === 0) {
|
|
@@ -320,6 +381,21 @@ if (options.commit) {
|
|
|
320
381
|
await gitPush(cwd, uniqueTags);
|
|
321
382
|
console.log("Pushed to remote.");
|
|
322
383
|
}
|
|
384
|
+
|
|
385
|
+
if (options.release) {
|
|
386
|
+
for (const tag of uniqueTags) {
|
|
387
|
+
const entries = tagEntries.get(tag) ?? [];
|
|
388
|
+
const notes = buildReleaseNotes(entries);
|
|
389
|
+
try {
|
|
390
|
+
await createGithubRelease(cwd, tag, notes);
|
|
391
|
+
console.log(`Created GitHub release: ${tag}`);
|
|
392
|
+
} catch (err) {
|
|
393
|
+
console.warn(
|
|
394
|
+
`⚠ Failed to create GitHub release ${tag}: ${(err as Error).message}`,
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
323
399
|
}
|
|
324
400
|
|
|
325
401
|
console.log("Done.");
|