From 51df9e0d8c360fb5ceea8ada43ada891cfb2b382 Mon Sep 17 00:00:00 2001 From: Brian Lalor Date: Thu, 20 Aug 2026 13:28:50 -0400 Subject: [PATCH] fix: dedupe GitHub App reports --- .changeset/fresh-app-deduplication.md | 5 +++++ src/Github.test.ts | 18 ++++++++++++++++-- src/Github.ts | 10 +++++----- test/github.ts | 2 ++ 4 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 .changeset/fresh-app-deduplication.md diff --git a/.changeset/fresh-app-deduplication.md b/.changeset/fresh-app-deduplication.md new file mode 100644 index 0000000..0036f97 --- /dev/null +++ b/.changeset/fresh-app-deduplication.md @@ -0,0 +1,5 @@ +--- +'frog': patch +--- + +Matched labelled reports for GitHub App tokens and checked recent issues first during unlabelled fallback scans. diff --git a/src/Github.test.ts b/src/Github.test.ts index e4b060a..8a2c137 100644 --- a/src/Github.test.ts +++ b/src/Github.test.ts @@ -543,7 +543,7 @@ describe('find', () => { ).toBeUndefined() }) - test('behavior: caps pagination at 50 pages', async () => { + test('behavior: scans the newest issues before reaching the 50-page cap', async () => { const seeded = Array.from({ length: 5_001 }, (_, index) => ({ labels: [], title: index === 5_000 ? title : `Unrelated ${index}`, @@ -552,7 +552,7 @@ describe('find', () => { expect( await Github.find(client(instance.url), { hash: Github.hash(title), repo, title }), - ).toBeUndefined() + ).toMatchObject({ number: 5_001 }) expect( instance.requests.filter( (request) => request.method === 'GET' && request.path === '/repos/wevm/viem/issues', @@ -629,6 +629,20 @@ describe('index', () => { }) describe('matcher', () => { + test('behavior: indexes labelled issues without reported push access', async () => { + const instance = await github({ [repo]: [{ title }] }, { pushAccess: [] }) + + const matcher = await Github.matcher(client(instance.url), { label: 'friction', repo }) + + expect(matcher.labelled).toBe(false) + expect( + instance.requests.filter( + (request) => request.method === 'GET' && request.path === `/repos/${repo}/issues`, + ), + ).toHaveLength(1) + await expect(matcher.match(title)).resolves.toMatchObject({ number: 1 }) + }) + test('behavior: matches a report before a changed title', async () => { const report = 'acme/app:entry-a' const instance = await github( diff --git a/src/Github.ts b/src/Github.ts index c128c89..e7da797 100644 --- a/src/Github.ts +++ b/src/Github.ts @@ -1010,7 +1010,7 @@ async function listAll(client: Client, options: { repo: string }): Promise !options.exclude?.(issue) && (!options.expectedAuthor || issue.author === options.expectedAuthor) - const labelledIssues = push ? (await list(client, options)).filter(accepts) : [] + const labelledIssues = (await list(client, options)).filter(accepts) const titleOverrides = new Map() const updateIndex = (index: Map, issues: readonly Issue[]) => { index.clear() diff --git a/test/github.ts b/test/github.ts index cbac478..42a22ac 100644 --- a/test/github.ts +++ b/test/github.ts @@ -370,6 +370,7 @@ export async function github(seed: Seed = {}, options: Options = {}): Promise