Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .bumpy/ci-check-channel-bump-files.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@varlock/bumpy': patch
---

`ci check` now reads bump files in channel directories, so promotion PRs (channel → main) and graduation PRs (channel → channel) correctly report the cycle's pending releases instead of failing with "no bump files found". Channel-dir bump files render with their subdir path (`next/feature.md`) so view/edit links resolve.
11 changes: 9 additions & 2 deletions packages/bumpy/src/commands/ci.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,13 @@ export async function ciCheckCommand(rootDir: string, opts: CheckOptions): Promi
const config = await loadConfig(rootDir);
const { packages } = await discoverWorkspace(rootDir, config);
const depGraph = new DependencyGraph(packages);
const { bumpFiles: allBumpFiles, errors: parseErrors } = await readBumpFiles(rootDir);
// Read channel dirs too: on promotion (channel → main) and graduation (channel →
// channel) PRs, the pending bump files live in `.bumpy/<channel>/`. Feature PRs
// never have shipped channel files in their diff vs the PR base, so this only
// surfaces files where they're genuinely pending.
const { bumpFiles: allBumpFiles, errors: parseErrors } = await readBumpFiles(rootDir, {
channels: channelNames(config),
});

// Skip on the version PR branch (and channel release PR branches) — they move/consume
// bump files by design
Expand Down Expand Up @@ -1073,7 +1079,8 @@ export function formatReleasePlanComment(
lines.push(`#### Bump files in this PR`);
lines.push('');
for (const bf of bumpFiles) {
const filename = `${bf.id}.md`;
// Channel-dir files (pending on promotion/graduation PRs) live at `.bumpy/<channel>/`
const filename = bf.channel ? `${bf.channel}/${bf.id}.md` : `${bf.id}.md`;
const parts: string[] = [`\`${filename}\``];
if (repo) {
parts.push(
Expand Down
20 changes: 19 additions & 1 deletion packages/bumpy/test/core/bump-file-channels.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import { describe, test, expect } from 'bun:test';
import { mkdir, writeFile } from 'node:fs/promises';
import { existsSync } from 'node:fs';
import { resolve } from 'node:path';
import { readBumpFiles, moveBumpFilesToChannel, recoverDeletedBumpFiles } from '../../src/core/bump-file.ts';
import {
readBumpFiles,
moveBumpFilesToChannel,
recoverDeletedBumpFiles,
filterBranchBumpFiles,
} from '../../src/core/bump-file.ts';
import { createTempGitRepo, cleanupTempDir, gitInDir } from '../helpers.ts';

async function writeBumpFileAt(dir: string, relPath: string, pkgName = 'pkg-a', bump = 'minor'): Promise<void> {
Expand Down Expand Up @@ -41,6 +46,19 @@ describe('readBumpFiles with channels', () => {
}
});

test('channel-dir files match diff paths in filterBranchBumpFiles (promotion PR shape)', async () => {
const dir = await createTempGitRepo();
try {
await writeBumpFileAt(dir, '.bumpy/next/shipped-feature.md');
const { bumpFiles } = await readBumpFiles(dir, { channels: ['next'] });
// A promotion PR's diff vs main lists the channel-dir path
const { branchBumpFiles } = filterBranchBumpFiles(bumpFiles, ['.bumpy/next/shipped-feature.md'], dir);
expect(branchBumpFiles.map((bf) => bf.id)).toEqual(['shipped-feature']);
} finally {
await cleanupTempDir(dir);
}
});

test('flags duplicate IDs across root and channel dirs as an error', async () => {
const dir = await createTempGitRepo();
try {
Expand Down
18 changes: 18 additions & 0 deletions packages/bumpy/test/core/ci-channel-comment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,21 @@ describe('formatReleasePlanComment — prerelease channel', () => {
expect(comment).toContain('Promote to a stable release by merging `next`');
});
});

describe('formatReleasePlanComment — promotion PR (channel-dir bump files, stable target)', () => {
// On a promotion PR (next → main) the pending bump files live in `.bumpy/next/`
const promotionPlan = makeReleasePlan(
[makeRelease('@myorg/core', '1.2.0', { type: 'minor', oldVersion: '1.1.0', bumpFiles: ['feat'] })],
[{ ...makeBumpFile('feat', [{ name: '@myorg/core', type: 'minor' }], 'Add a feature'), channel: 'next' }],
);
const comment = formatReleasePlanComment(promotionPlan, promotionPlan.bumpFiles, '1', 'next', 'npm');

test('renders channel-dir bump files with their subdir path', () => {
expect(comment).toContain('`next/feat.md`');
});

test('shows the stable plan (no channel banner, no preid suffix)', () => {
expect(comment).toContain('1.1.0 → **1.2.0**');
expect(comment).not.toContain('prerelease channel');
});
});