Skip to content

Commit 2e88cbc

Browse files
authored
Merge pull request #50 from dmno-dev/docs/typos-and-fixes
Fix doc typos, add changelog formatter for GitHub releases
2 parents 7981ab9 + 3c0373f commit 2e88cbc

9 files changed

Lines changed: 103 additions & 13 deletions

File tree

.bumpy/docs-typos-and-fixes.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@varlock/bumpy': patch
3+
---
4+
5+
Fix typos in docs/README, add claude to CLI help AI setup targets, and use changelog formatter for GitHub release bodies.

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ Bumpy uses **bump files** (you may know them as "changesets" if coming from [tha
2828
- Shows what packages will be released and their changelogs
2929
- Including packages bumped automatically due to dependency relationships
3030
- When release PR is merged, publishing is triggered
31-
- Oending bump files are deleted and packages are published with updated versions and changelogs
31+
- Pending bump files are deleted and packages are published with updated versions and changelogs
3232

3333
All of this is automated via two simple GitHub Actions workflows (see [CI setup](#ci--github-actions) below). You can also run everything locally with `bumpy status`, `bumpy version`, and `bumpy publish`.
3434

@@ -135,7 +135,7 @@ jobs:
135135
- run: bunx @varlock/bumpy ci release
136136
env:
137137
GH_TOKEN: ${{ github.token }}
138-
BUMPY_GH_TOKEN: ${{ secrets.BUMPY_GH_TOKEN }} # additonal PAT, needed to trigger CI checks on release PR
138+
BUMPY_GH_TOKEN: ${{ secrets.BUMPY_GH_TOKEN }} # additional PAT, needed to trigger CI checks on release PR
139139
```
140140
141141
> **Trusted publishing setup:** Configure each package on [npmjs.com](https://docs.npmjs.com/trusted-publishers/) → Package Settings → Trusted Publishers → GitHub Actions. Specify your org/user, repo, and the workflow filename (`bumpy-release.yml`). No `NPM_TOKEN` secret needed. Requires npm >= 11.5.1 - bumpy will warn if your version is too old.
@@ -240,7 +240,7 @@ Bumpy is built as a successor to [@changesets/changesets](https://github.com/cha
240240

241241
```bash
242242
bun install # install deps
243-
bun test # run tests
243+
bun run test # run tests
244244
bun run build # build CLI
245245
bunx bumpy --help # invoke built cli
246246
```

docs/changelog-formatters.md

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ There are several built-in formatters, or you can provide a custom function.
88

99
### `default`
1010

11-
Simple markdown formatter. Produces a version heading, date, and bullet points from bump file summaries. _This is the default -- no settting required to enable_.
11+
Simple markdown formatter. Produces a version heading, date, and bullet points from bump file summaries. _This is the default -- no setting required to enable_.
1212

1313
**Example output:**
1414

@@ -98,23 +98,36 @@ interface ChangelogContext {
9898
bumpFiles: BumpFile[];
9999
/** ISO date string (YYYY-MM-DD) */
100100
date: string;
101+
/** Where this entry will be used (default: 'changelog') */
102+
target?: 'changelog' | 'github-release';
101103
}
102104

103105
type ChangelogFormatter = (ctx: ChangelogContext) => string | Promise<string>;
104106
```
105107

108+
The `target` field tells your formatter where the output will be used:
109+
110+
- `'changelog'` — writing to a `CHANGELOG.md` file (default)
111+
- `'github-release'` — creating a GitHub release body
112+
113+
This lets you customize the output per context. For example, you might include full PR links in GitHub releases but keep changelog entries shorter, or omit the date sub-heading for GitHub releases since the release already has a timestamp.
114+
106115
### Example: custom formatter
107116

108117
```typescript
109118
import type { ChangelogFormatter } from '@varlock/bumpy';
110119

111120
const formatter: ChangelogFormatter = (ctx) => {
112-
const { release, bumpFiles, date } = ctx;
121+
const { release, bumpFiles, date, target } = ctx;
113122
const lines: string[] = [];
114123
lines.push(`## ${release.newVersion}`);
115124
lines.push('');
116-
lines.push(`_${date}_`);
117-
lines.push('');
125+
126+
// Only include the date in changelog files — GitHub releases already show a date
127+
if (target !== 'github-release') {
128+
lines.push(`_${date}_`);
129+
lines.push('');
130+
}
118131

119132
const relevantBumpFiles = bumpFiles.filter((bf) => release.bumpFiles.includes(bf.id));
120133

docs/cli.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# CLI Reference
22

3-
All commands can be run via `bunx @varlock/bumpy <command>` or, (or just `bunx bumpy` if installed locally). Alternatively, you can invoke it via a package.json script (e.g., `bun run bumpy <command>`).
3+
All commands can be run via `bunx @varlock/bumpy <command>` (or just `bunx bumpy` if installed locally). Alternatively, you can invoke it via a package.json script (e.g., `bun run bumpy <command>`).
44

55
## `bumpy init`
66

packages/bumpy/src/cli.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ function printHelp() {
233233
--branch <name> Branch name for version PR (default: bumpy/version-packages)
234234
235235
AI setup options:
236-
--target <tool> Target AI tool: opencode, cursor, codex
236+
--target <tool> Target AI tool: claude, opencode, cursor, codex
237237
238238
${colorize('https://bumpy.varlock.dev', 'dim')}
239239
`);

packages/bumpy/src/commands/publish.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { DependencyGraph } from '../core/dep-graph.ts';
55
import { pushWithTags, hasUncommittedChanges } from '../core/git.ts';
66
import { publishPackages } from '../core/publish-pipeline.ts';
77
import { createIndividualReleases, createAggregateRelease } from '../core/github-release.ts';
8+
import { loadFormatter } from '../core/changelog.ts';
89
import { detectWorkspaces } from '../utils/package-manager.ts';
910
import type { BumpyConfig, PackageConfig, ReleasePlan, PlannedRelease, WorkspacePackage } from '../types.ts';
1011

@@ -108,14 +109,19 @@ export async function publishCommand(rootDir: string, opts: PublishCommandOption
108109
const isAggregate = aggConfig === true || (typeof aggConfig === 'object' && aggConfig.enabled);
109110
const aggTitle = typeof aggConfig === 'object' ? aggConfig.title : undefined;
110111

112+
// Load the changelog formatter so GitHub release bodies match CHANGELOG.md
113+
const formatter = config.changelog !== false ? await loadFormatter(config.changelog, rootDir) : undefined;
114+
111115
if (isAggregate) {
112116
await createAggregateRelease(publishedReleases, releasePlan.bumpFiles, rootDir, {
113117
dryRun: opts.dryRun,
114118
title: aggTitle,
119+
formatter,
115120
});
116121
} else {
117122
await createIndividualReleases(publishedReleases, releasePlan.bumpFiles, rootDir, {
118123
dryRun: opts.dryRun,
124+
formatter,
119125
});
120126
}
121127
}

packages/bumpy/src/core/changelog.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ export interface ChangelogContext {
1212
bumpFiles: BumpFile[];
1313
/** ISO date string (YYYY-MM-DD) */
1414
date: string;
15+
/** Where this entry will be used — formatters can customize output per target (default: 'changelog') */
16+
target?: 'changelog' | 'github-release';
1517
}
1618

1719
/**
@@ -159,8 +161,9 @@ export async function generateChangelogEntry(
159161
bumpFiles: BumpFile[],
160162
formatter: ChangelogFormatter = defaultFormatter,
161163
date: string = new Date().toISOString().split('T')[0]!,
164+
target: ChangelogContext['target'] = 'changelog',
162165
): Promise<string> {
163-
return formatter({ release, bumpFiles, date });
166+
return formatter({ release, bumpFiles, date, target });
164167
}
165168

166169
/** Prepend a new entry to an existing CHANGELOG.md content */

packages/bumpy/src/core/github-release.ts

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { tryRunArgs, runArgsAsync } from '../utils/shell.ts';
22
import { log } from '../utils/logger.ts';
33
import { listTags } from './git.ts';
4+
import { generateChangelogEntry } from './changelog.ts';
5+
import type { ChangelogFormatter } from './changelog.ts';
46
import type { PlannedRelease, BumpFile } from '../types.ts';
57

68
/** Get the current HEAD commit SHA */
@@ -11,6 +13,7 @@ function getHeadSha(rootDir: string): string | null {
1113
export interface GitHubReleaseOptions {
1214
dryRun?: boolean;
1315
title?: string;
16+
formatter?: ChangelogFormatter;
1417
}
1518

1619
/** Create individual GitHub releases for each published package */
@@ -29,7 +32,9 @@ export async function createIndividualReleases(
2932

3033
for (const release of releases) {
3134
const tag = `${release.name}@${release.newVersion}`;
32-
const body = buildReleaseBody(release, bumpFiles);
35+
const body = opts.formatter
36+
? await generateReleaseBody(release, bumpFiles, opts.formatter)
37+
: buildReleaseBody(release, bumpFiles);
3338
const title = `${release.name} v${release.newVersion}`;
3439

3540
if (opts.dryRun) {
@@ -68,7 +73,9 @@ export async function createAggregateRelease(
6873
const date = new Date().toISOString().split('T')[0];
6974
const existing = listTags(`release-${date}*`, { cwd: rootDir });
7075
const { tag, title } = resolveAggregateTagAndTitle(date!, existing, opts.title);
71-
const body = buildAggregateBody(releases, bumpFiles);
76+
const body = opts.formatter
77+
? await generateAggregateBody(releases, bumpFiles, opts.formatter)
78+
: buildAggregateBody(releases, bumpFiles);
7279

7380
if (opts.dryRun) {
7481
log.dim(` Would create aggregate GitHub release: ${title}`);
@@ -93,6 +100,62 @@ export async function createAggregateRelease(
93100
}
94101
}
95102

103+
/** Generate a release body for a single package using the changelog formatter */
104+
async function generateReleaseBody(
105+
release: PlannedRelease,
106+
bumpFiles: BumpFile[],
107+
formatter: ChangelogFormatter,
108+
): Promise<string> {
109+
const entry = await generateChangelogEntry(release, bumpFiles, formatter, undefined, 'github-release');
110+
// Strip the version heading — the GitHub release title already has the version
111+
return stripVersionHeading(entry).trim() || 'No changelog entries.';
112+
}
113+
114+
/** Generate an aggregate release body using the changelog formatter */
115+
async function generateAggregateBody(
116+
releases: PlannedRelease[],
117+
bumpFiles: BumpFile[],
118+
formatter: ChangelogFormatter,
119+
): Promise<string> {
120+
const lines: string[] = [];
121+
122+
// Group by bump type
123+
const groups: [string, PlannedRelease[]][] = [
124+
['Major Changes', releases.filter((r) => r.type === 'major')],
125+
['Minor Changes', releases.filter((r) => r.type === 'minor')],
126+
['Patch Changes', releases.filter((r) => r.type === 'patch')],
127+
];
128+
129+
for (const [heading, group] of groups) {
130+
if (group.length === 0) continue;
131+
lines.push(`## ${heading}\n`);
132+
133+
for (const release of group) {
134+
lines.push(`### ${release.name} v${release.newVersion}\n`);
135+
const entry = await generateChangelogEntry(release, bumpFiles, formatter, undefined, 'github-release');
136+
const body = stripVersionHeading(entry).trim();
137+
if (body) {
138+
lines.push(body);
139+
} else if (release.isDependencyBump) {
140+
lines.push('- Updated dependencies');
141+
} else if (release.isCascadeBump) {
142+
lines.push('- Version bump via cascade rule');
143+
}
144+
lines.push('');
145+
}
146+
}
147+
148+
return lines.join('\n').trim() || 'No changelog entries.';
149+
}
150+
151+
/** Strip the leading ## version heading and date sub-heading from a changelog entry */
152+
function stripVersionHeading(entry: string): string {
153+
return entry
154+
.replace(/^## .+\n/, '') // remove ## version heading
155+
.replace(/^<sub>.+<\/sub>\n/, '') // remove <sub>date</sub> line
156+
.replace(/^_.+_\n/, ''); // remove _date_ line
157+
}
158+
96159
function buildReleaseBody(release: PlannedRelease, bumpFiles: BumpFile[]): string {
97160
const lines: string[] = [];
98161
const relevant = bumpFiles.filter((bf) => release.bumpFiles.includes(bf.id));

packages/bumpy/src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ export const DEFAULT_CONFIG: BumpyConfig = {
156156
title: '🐸 Versioned release',
157157
branch: 'bumpy/version-packages',
158158
preamble: [
159-
`<a href="https://bumpy.varlock.dev"><img src="https://raw.githubusercontent.com/dmno-dev/bumpy/main/images/frog-party.png" alt="bumpy-frog" width="60" align="left" style="image-rendering: pixelated;" title="Hi! I'm bumpy!" /></a>`,
159+
`<a href="https://bumpy.varlock.dev"><img src="https://raw.githubusercontent.com/dmno-dev/bumpy/main/images/frog-clipboard.png" alt="bumpy-frog" width="60" align="left" style="image-rendering: pixelated;" title="Hi! I'm bumpy!" /></a>`,
160160
'',
161161
`This PR was created and will be kept in sync by [bumpy](https://bumpy.varlock.dev) based on your bump files (in \`.bumpy/\`). Merge it when you are ready to release the packages listed below:`,
162162
'<br clear="left" />',

0 commit comments

Comments
 (0)