Skip to content

Commit 2d4fda9

Browse files
committed
refactor: ignore non-ticket MD files
1 parent 305c528 commit 2d4fda9

File tree

4 files changed

+55
-11
lines changed

4 files changed

+55
-11
lines changed

knip.config.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ const config: KnipConfig = {
99
ignoreBinaries: [
1010
// Used for GitHub operations
1111
'gh',
12-
// Self-reference used in E2E tests
13-
'branchpilot',
1412
],
1513
}
1614

src/utils/tickets.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,28 @@ This is a draft PR`)
101101
expect(tickets[1]?.error).toContain('Missing required fields: branch')
102102
})
103103

104+
it('skips non-ticket markdown files and unrelated frontmatter', async () => {
105+
mockReaddir(['note.md', 'ticket.md', 'just-frontmatter.md'])
106+
vi.mocked(fs.readFile)
107+
// note.md: no frontmatter -> should be ignored
108+
.mockResolvedValueOnce('This is a plain markdown note without frontmatter')
109+
// ticket.md: valid ticket -> should be included
110+
.mockResolvedValueOnce(`---\nbranch: feature/ok\nwhen: "2024-01-01T00:00:00"\n---\nBody`)
111+
// just-frontmatter.md: has frontmatter but neither branch nor when -> should be ignored
112+
.mockResolvedValueOnce(`---\ntitle: Just a document\nauthor: Someone\n---\nSome content`)
113+
114+
vi.mocked(git.getGitRoot).mockResolvedValue('/repo')
115+
vi.mocked(config.loadRepoConfig).mockResolvedValue({})
116+
vi.mocked(github.gh).mockResolvedValue('[]')
117+
vi.mocked(github.getDefaultBranch).mockResolvedValue('main')
118+
vi.mocked(git.hasUnmergedCommits).mockResolvedValue(true)
119+
120+
const tickets = await loadAllTickets(['/test/dir'], {}, logger)
121+
expect(tickets).toHaveLength(1)
122+
expect(tickets[0]?.branch).toBe('feature/ok')
123+
expect(tickets[0]?.status === 'ready' || tickets[0]?.status === 'pending').toBe(true)
124+
})
125+
104126
it('handles tickets without title and body', async () => {
105127
mockReaddir(['minimal.md'])
106128
vi.mocked(fs.readFile).mockResolvedValueOnce(`---

src/utils/tickets.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,13 +150,20 @@ async function loadTicketsFromDirectory(dir: string, baseDir: string, logger: Lo
150150

151151
try {
152152
const raw = await fs.readFile(file, 'utf8')
153+
const hasFrontmatter = raw.trimStart().startsWith('---')
154+
if (!hasFrontmatter) {
155+
continue
156+
}
153157
const parsed = matter(raw)
158+
const data = parsed.data || {}
159+
if (!data.branch && !data.when) {
160+
continue
161+
}
162+
154163
const fm = TicketFrontSchema.safeParse(parsed.data)
155164

156165
if (!fm.success) {
157166
const missingFields: string[] = []
158-
const data = parsed.data || {}
159-
160167
if (!data.branch) missingFields.push('branch')
161168
if (!data.when) missingFields.push('when')
162169

tests/e2e/cli.test.ts

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { spawn } from 'node:child_process'
2-
import { existsSync } from 'node:fs'
2+
import fsp from 'node:fs/promises'
33
import os from 'node:os'
44
import path from 'node:path'
55
import { fileURLToPath } from 'node:url'
@@ -8,10 +8,21 @@ import { beforeAll, describe, expect, it } from 'vitest'
88

99
const __dirname = path.dirname(fileURLToPath(import.meta.url))
1010
const cliPath = path.join(__dirname, '../../dist/cli.mjs')
11+
let tarballPath: string | null = null
12+
let packTmpDir: string | null = null
1113

1214
beforeAll(async () => {
13-
if (!existsSync(cliPath)) {
14-
await execa('npm', ['run', 'build'], { cwd: path.join(__dirname, '../..') })
15+
await execa('npm', ['run', 'build'], { cwd: path.join(__dirname, '../..') })
16+
packTmpDir = await fsp.mkdtemp(path.join(os.tmpdir(), 'branchpilot-pack-'))
17+
const { stdout: packOut } = await execa('npm', ['pack', '--silent', path.join(__dirname, '../..')], {
18+
cwd: packTmpDir,
19+
})
20+
tarballPath = path.join(packTmpDir, packOut.trim())
21+
})
22+
23+
afterAll(async () => {
24+
if (packTmpDir) {
25+
await fsp.rm(packTmpDir, { recursive: true, force: true })
1526
}
1627
})
1728

@@ -136,7 +147,8 @@ describe('CLI', () => {
136147

137148
describe('npx execution', () => {
138149
it('works with npx for version command', async () => {
139-
const { stdout, exitCode } = await execa('npx', ['--yes', 'branchpilot', '--version'], {
150+
const pkgArg = tarballPath ?? 'branchpilot'
151+
const { stdout, exitCode } = await execa('npx', ['--yes', '--package', pkgArg, 'branchpilot', '--version'], {
140152
env: { ...process.env, NODE_ENV: 'test' },
141153
})
142154
expect(exitCode).toBe(0)
@@ -145,9 +157,14 @@ describe('CLI', () => {
145157

146158
it('works with npx for list command', async () => {
147159
const tempDir = os.tmpdir()
148-
const { stdout, exitCode } = await execa('npx', ['--yes', 'branchpilot', 'list', '--dir', tempDir], {
149-
env: { ...process.env, NODE_ENV: 'test' },
150-
})
160+
const pkgArg = tarballPath ?? 'branchpilot'
161+
const { stdout, exitCode } = await execa(
162+
'npx',
163+
['--yes', '--package', pkgArg, 'branchpilot', 'list', '--dir', tempDir],
164+
{
165+
env: { ...process.env, NODE_ENV: 'test' },
166+
},
167+
)
151168
expect(exitCode).toBe(0)
152169
expect(stdout).toContain('No tickets found')
153170
}, 20000)

0 commit comments

Comments
 (0)