From dcd398e8aa010bc621d495848ba46b9b5f7d1407 Mon Sep 17 00:00:00 2001 From: Theo Ephraim Date: Mon, 20 Apr 2026 22:49:26 -0700 Subject: [PATCH] Fix loadFormatter dropping github changelog options like internalAuthors The generic built-in formatter lookup matched "github" before the options-aware path, so options (internalAuthors, repo, etc.) were silently ignored. Reorder the branches so the github-specific path runs first. Add loadFormatter tests covering this case. Co-Authored-By: Claude Opus 4.6 (1M context) --- .bumpy/fix-internal-authors.md | 5 ++ packages/bumpy/src/core/changelog.ts | 14 ++--- packages/bumpy/test/core/changelog.test.ts | 59 +++++++++++++++++++++- 3 files changed, 69 insertions(+), 9 deletions(-) create mode 100644 .bumpy/fix-internal-authors.md diff --git a/.bumpy/fix-internal-authors.md b/.bumpy/fix-internal-authors.md new file mode 100644 index 0000000..35e69f4 --- /dev/null +++ b/.bumpy/fix-internal-authors.md @@ -0,0 +1,5 @@ +--- +'@varlock/bumpy': patch +--- + +Fix `internalAuthors` option being ignored by the GitHub changelog formatter. The `loadFormatter` function matched the built-in "github" entry before reaching the options-aware path, so options like `internalAuthors` were silently dropped. diff --git a/packages/bumpy/src/core/changelog.ts b/packages/bumpy/src/core/changelog.ts index b14ee77..669e8df 100644 --- a/packages/bumpy/src/core/changelog.ts +++ b/packages/bumpy/src/core/changelog.ts @@ -74,7 +74,13 @@ const BUILTIN_FORMATTERS: Record Promise { const [name, options] = Array.isArray(changelog) ? changelog : [changelog, {}]; - // Built-in formatter + // Built-in with options (e.g., ["github", { repo: "..." }]) + if (name === 'github') { + const { createGithubFormatter } = await import('./changelog-github.ts'); + return createGithubFormatter(options as import('./changelog-github.ts').GithubChangelogOptions); + } + + // Built-in formatter (no options) if (typeof name === 'string' && BUILTIN_FORMATTERS[name]) { const builtin = BUILTIN_FORMATTERS[name]; if (typeof builtin === 'function' && builtin.length === 0) { @@ -84,12 +90,6 @@ export async function loadFormatter(changelog: BumpyConfig['changelog'], rootDir return builtin as ChangelogFormatter; } - // Built-in with options (e.g., ["github", { repo: "..." }]) - if (name === 'github') { - const { createGithubFormatter } = await import('./changelog-github.ts'); - return createGithubFormatter(options as import('./changelog-github.ts').GithubChangelogOptions); - } - // Custom module if (typeof name === 'string') { try { diff --git a/packages/bumpy/test/core/changelog.test.ts b/packages/bumpy/test/core/changelog.test.ts index 990830b..25123ad 100644 --- a/packages/bumpy/test/core/changelog.test.ts +++ b/packages/bumpy/test/core/changelog.test.ts @@ -1,6 +1,12 @@ -import { test, expect, describe } from 'bun:test'; +import { test, expect, describe, beforeEach, afterEach } from 'bun:test'; import { makeRelease, makeBumpFile } from '../helpers.ts'; -import { defaultFormatter, generateChangelogEntry, prependToChangelog } from '../../src/core/changelog.ts'; +import { installShellMock, uninstallShellMock, addMockRule } from '../helpers-shell-mock.ts'; +import { + defaultFormatter, + generateChangelogEntry, + loadFormatter, + prependToChangelog, +} from '../../src/core/changelog.ts'; describe('defaultFormatter', () => { test('formats basic release with bump files', async () => { @@ -154,3 +160,52 @@ describe('prependToChangelog', () => { expect(result).toContain('- New entry'); }); }); + +describe('loadFormatter', () => { + beforeEach(() => { + installShellMock(); + addMockRule({ match: 'gh repo view', response: 'dmno-dev/bumpy' }); + }); + + afterEach(() => { + uninstallShellMock(); + }); + + test('passes options through to github formatter', async () => { + addMockRule({ match: /^git log/, response: '' }); + + const formatter = await loadFormatter( + ['github', { internalAuthors: ['theoephraim'], repo: 'dmno-dev/bumpy' }], + '/tmp', + ); + const release = makeRelease('pkg-a', '1.0.1', { bumpFiles: ['cs1'] }); + const bumpFiles = [makeBumpFile('cs1', [{ name: 'pkg-a', type: 'patch' }], 'author: @theoephraim\nFixed it')]; + + const result = await formatter({ release, bumpFiles, date: '2026-04-14' }); + + expect(result).not.toContain('Thanks'); + }); + + test('github formatter without options still works', async () => { + addMockRule({ match: /^git log/, response: '' }); + + const formatter = await loadFormatter('github', '/tmp'); + const release = makeRelease('pkg-a', '1.0.1', { bumpFiles: ['cs1'] }); + const bumpFiles = [makeBumpFile('cs1', [{ name: 'pkg-a', type: 'patch' }], 'author: @someone\nFixed it')]; + + const result = await formatter({ release, bumpFiles, date: '2026-04-14' }); + + expect(result).toContain('Thanks [@someone]'); + }); + + test('loads default formatter by name', async () => { + const formatter = await loadFormatter('default', '/tmp'); + const release = makeRelease('pkg-a', '1.0.0', { bumpFiles: ['cs1'] }); + const bumpFiles = [makeBumpFile('cs1', [{ name: 'pkg-a', type: 'patch' }], 'A fix')]; + + const result = await formatter({ release, bumpFiles, date: '2026-04-14' }); + + expect(result).toContain('## 1.0.0'); + expect(result).toContain('- A fix'); + }); +});