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
9 changes: 8 additions & 1 deletion actions/setup/js/add_comment.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,14 @@ async function main(config = {}) {
/** @type {{ id: string | number, html_url: string }} */
let comment;
if (isDiscussion) {
comment = await commentOnDiscussion(githubClient, repoParts.owner, repoParts.repo, itemNumber, processedBody, null);
// When triggered by a discussion_comment event (without explicit item_number),
// reply as a threaded comment to the triggering comment instead of posting top-level.
const hasExplicitItemNumber = message.item_number !== undefined && message.item_number !== null;
const replyToId = context.eventName === "discussion_comment" && !hasExplicitItemNumber ? context.payload?.comment?.node_id : null;
if (replyToId) {
core.info(`Replying as threaded comment to discussion comment node ID: ${replyToId}`);
}
comment = await commentOnDiscussion(githubClient, repoParts.owner, repoParts.repo, itemNumber, processedBody, replyToId);
} else {
// Use REST API for issues/PRs
const { data } = await githubClient.rest.issues.createComment({
Expand Down
118 changes: 118 additions & 0 deletions actions/setup/js/add_comment.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,124 @@ describe("add_comment", () => {
expect(result.itemNumber).toBe(10);
expect(result.isDiscussion).toBe(true);
});

it("should post a threaded reply when triggered by discussion_comment event", async () => {
const addCommentScript = fs.readFileSync(path.join(__dirname, "add_comment.cjs"), "utf8");

// Change context to discussion_comment event
mockContext.eventName = "discussion_comment";
mockContext.payload = {
discussion: {
number: 10,
},
comment: {
node_id: "DC_kwDOTriggeringComment123",
},
};

let capturedReplyToId = undefined;
mockGithub.graphql = async (query, variables) => {
if (query.includes("addDiscussionComment")) {
capturedReplyToId = variables?.replyToId;
return {
addDiscussionComment: {
comment: {
id: "DC_kwDOTest456",
body: variables?.body,
createdAt: "2024-01-01",
url: "https://github.com/owner/repo/discussions/10#discussioncomment-456",
},
},
};
}
return {
repository: {
discussion: {
id: "D_kwDOTest123",
url: "https://github.com/owner/repo/discussions/10",
},
},
};
};

const handler = await eval(`(async () => { ${addCommentScript}; return await main({}); })()`);

const message = {
type: "add_comment",
body: "This is a threaded reply to a discussion comment",
};

const result = await handler(message, {});

expect(result.success).toBe(true);
expect(result.isDiscussion).toBe(true);
// The reply should be threaded - replyToId should be the triggering comment node_id
expect(capturedReplyToId).toBe("DC_kwDOTriggeringComment123");
});

it("should post a top-level comment (not threaded) when triggered by discussion_comment with explicit item_number", async () => {
const addCommentScript = fs.readFileSync(path.join(__dirname, "add_comment.cjs"), "utf8");

// Change context to discussion_comment event
mockContext.eventName = "discussion_comment";
mockContext.payload = {
discussion: {
number: 10,
},
comment: {
node_id: "DC_kwDOTriggeringComment123",
},
};

let capturedReplyToId = undefined;
mockGithub.graphql = async (query, variables) => {
if (query.includes("addDiscussionComment")) {
capturedReplyToId = variables?.replyToId;
return {
addDiscussionComment: {
comment: {
id: "DC_kwDOTest456",
body: variables?.body,
createdAt: "2024-01-01",
url: "https://github.com/owner/repo/discussions/20#discussioncomment-456",
},
},
};
}
return {
repository: {
discussion: {
id: "D_kwDOTest123",
url: "https://github.com/owner/repo/discussions/20",
},
},
};
};

// Make REST API return 404 so the code falls back to discussion
mockGithub.rest.issues.createComment = async () => {
const err = new Error("Not Found");
// @ts-expect-error - Simulating GitHub REST API error
err.status = 404;
throw err;
};

const handler = await eval(`(async () => { ${addCommentScript}; return await main({}); })()`);

// Explicit item_number targeting a different discussion (via 404 fallback)
const message = {
type: "add_comment",
item_number: 20,
body: "This should be a top-level comment on discussion #20",
};

const result = await handler(message, {});

expect(result.success).toBe(true);
expect(result.isDiscussion).toBe(true);
// Should NOT be threaded since item_number was explicitly provided
expect(capturedReplyToId).toBeUndefined();
});
});

describe("regression test for wrong PR bug", () => {
Expand Down
Loading