Skip to content

Commit e8531cf

Browse files
committed
fix(github): match sticky comments by authenticated bot author
1 parent 50cdf0e commit e8531cf

2 files changed

Lines changed: 105 additions & 7 deletions

File tree

packages/opencode/src/cli/cmd/github.ts

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,26 @@ export function appendCommentAnchor(body: string, digest: string): string {
201201
return `${body}\n<!-- opencode:comment-key:sha256:${digest} -->`
202202
}
203203

204+
/**
205+
* Returns matching sticky comment ids in original API order.
206+
* A comment matches when it contains `anchor` and is authored by one of
207+
* the provided `authorLogins`.
208+
*/
209+
export function findStickyCommentIds(
210+
comments: Array<{ id: number; body?: string | null; user?: { login?: string | null } | null }>,
211+
anchor: string,
212+
authorLogins: readonly string[],
213+
): number[] {
214+
const allowedAuthors = new Set(authorLogins)
215+
return comments
216+
.filter((comment) => {
217+
if (!comment.body?.includes(anchor)) return false
218+
if (!comment.user?.login) return false
219+
return allowedAuthors.has(comment.user.login)
220+
})
221+
.map((comment) => comment.id)
222+
}
223+
204224
export const GithubCommand = cmd({
205225
command: "github",
206226
describe: "manage GitHub agent",
@@ -507,6 +527,7 @@ export const GithubRunCommand = effectCmd({
507527
let octoRest: Octokit
508528
let octoGraph: typeof graphql
509529
let gitConfig: string
530+
let commentAuthorLogin = AGENT_USERNAME
510531
let session: { id: SessionID; title: string; version: string }
511532
let shareId: string | undefined
512533
let exitCode = 0
@@ -558,6 +579,13 @@ export const GithubRunCommand = effectCmd({
558579
octoGraph = graphql.defaults({
559580
headers: { authorization: `token ${appToken}` },
560581
})
582+
commentAuthorLogin = await octoRest.rest.users
583+
.getAuthenticated()
584+
.then((response) => response.data.login)
585+
.catch((error) => {
586+
console.warn(`Warning: failed to resolve authenticated user login: ${String(error)}`)
587+
return AGENT_USERNAME
588+
})
561589

562590
const { userPrompt, promptFiles } = await getUserPrompt()
563591
if (!useGithubToken) {
@@ -1274,14 +1302,12 @@ export const GithubRunCommand = effectCmd({
12741302
issue_number: issueId!,
12751303
per_page: 100,
12761304
})
1277-
const matches = comments.filter(
1278-
(c) => c.user?.login === AGENT_USERNAME && c.body?.includes(anchor),
1279-
)
1280-
if (matches.length === 0) return undefined
1281-
if (matches.length > 1) {
1282-
console.warn(`Warning: found ${matches.length} sticky comments with same key; updating the most recent one`)
1305+
const matchedIds = findStickyCommentIds(comments, anchor, [AGENT_USERNAME, commentAuthorLogin])
1306+
if (matchedIds.length === 0) return undefined
1307+
if (matchedIds.length > 1) {
1308+
console.warn(`Warning: found ${matchedIds.length} sticky comments with same key; updating the most recent one`)
12831309
}
1284-
return matches[matches.length - 1].id
1310+
return matchedIds[matchedIds.length - 1]
12851311
}
12861312

12871313
async function addReaction(commentType?: "issue" | "pr_review") {

packages/opencode/test/cli/github-action.test.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
formatPromptTooLargeError,
66
buildCommentKeyDigest,
77
appendCommentAnchor,
8+
findStickyCommentIds,
89
} from "../../src/cli/cmd/github"
910
import type { MessageV2 } from "../../src/session/message-v2"
1011
import { SessionID, MessageID, PartID } from "../../src/session/schema"
@@ -256,3 +257,74 @@ describe("appendCommentAnchor", () => {
256257
expect(parts[parts.length - 2]).toBe("content")
257258
})
258259
})
260+
261+
describe("findStickyCommentIds", () => {
262+
test("matches comments by anchor and opencode bot author", () => {
263+
const ids = findStickyCommentIds(
264+
[
265+
{
266+
id: 1,
267+
user: { login: "opencode-agent[bot]" },
268+
body: "body\n<!-- opencode:comment-key:sha256:abc -->",
269+
},
270+
],
271+
"<!-- opencode:comment-key:sha256:abc -->",
272+
["opencode-agent[bot]"],
273+
)
274+
275+
expect(ids).toEqual([1])
276+
})
277+
278+
test("matches comments by anchor and github-actions bot author", () => {
279+
const ids = findStickyCommentIds(
280+
[
281+
{
282+
id: 2,
283+
user: { login: "github-actions[bot]" },
284+
body: "body\n<!-- opencode:comment-key:sha256:abc -->",
285+
},
286+
],
287+
"<!-- opencode:comment-key:sha256:abc -->",
288+
["opencode-agent[bot]", "github-actions[bot]"],
289+
)
290+
291+
expect(ids).toEqual([2])
292+
})
293+
294+
test("does not match comments from other authors", () => {
295+
const ids = findStickyCommentIds(
296+
[
297+
{
298+
id: 3,
299+
user: { login: "someone-else" },
300+
body: "body\n<!-- opencode:comment-key:sha256:abc -->",
301+
},
302+
],
303+
"<!-- opencode:comment-key:sha256:abc -->",
304+
["opencode-agent[bot]", "github-actions[bot]"],
305+
)
306+
307+
expect(ids).toEqual([])
308+
})
309+
310+
test("returns matching ids in API order", () => {
311+
const ids = findStickyCommentIds(
312+
[
313+
{
314+
id: 4,
315+
user: { login: "github-actions[bot]" },
316+
body: "body\n<!-- opencode:comment-key:sha256:abc -->",
317+
},
318+
{
319+
id: 5,
320+
user: { login: "github-actions[bot]" },
321+
body: "body\n<!-- opencode:comment-key:sha256:abc -->",
322+
},
323+
],
324+
"<!-- opencode:comment-key:sha256:abc -->",
325+
["github-actions[bot]"],
326+
)
327+
328+
expect(ids).toEqual([4, 5])
329+
})
330+
})

0 commit comments

Comments
 (0)