diff --git a/actions/setup/js/safe_output_helpers.cjs b/actions/setup/js/safe_output_helpers.cjs index 997bcbeb62e..a62a9528111 100644 --- a/actions/setup/js/safe_output_helpers.cjs +++ b/actions/setup/js/safe_output_helpers.cjs @@ -72,8 +72,10 @@ function resolveTarget(params) { const { targetConfig, item, context, itemType, supportsPR = false, supportsIssue = false } = params; // Check context type - const isIssueContext = context.eventName === "issues" || context.eventName === "issue_comment"; - const isPRContext = context.eventName === "pull_request" || context.eventName === "pull_request_target" || context.eventName === "pull_request_review" || context.eventName === "pull_request_review_comment"; + const prEventNames = new Set(["pull_request", "pull_request_target", "pull_request_review", "pull_request_review_comment"]); + const isIssueCommentOnPR = context.eventName === "issue_comment" && Boolean(context.payload?.issue?.pull_request); + const isIssueContext = context.eventName === "issues" || (context.eventName === "issue_comment" && !isIssueCommentOnPR); + const isPRContext = prEventNames.has(context.eventName) || isIssueCommentOnPR; // Default target is "triggering" const target = targetConfig || "triggering"; @@ -206,6 +208,9 @@ function resolveTarget(params) { if (context.payload.pull_request) { itemNumber = context.payload.pull_request.number; contextType = "pull request"; + } else if (isIssueCommentOnPR) { + itemNumber = context.payload.issue.number; + contextType = "pull request"; } else { return { success: false, diff --git a/actions/setup/js/safe_output_helpers.test.cjs b/actions/setup/js/safe_output_helpers.test.cjs index 9e68fe46f9d..110c623aaef 100644 --- a/actions/setup/js/safe_output_helpers.test.cjs +++ b/actions/setup/js/safe_output_helpers.test.cjs @@ -272,6 +272,24 @@ describe("safe_output_helpers", () => { expect(result.contextType).toBe("pull request"); }); + it("should resolve issue_comment on PR context for PR-only handlers", () => { + const result = helpers.resolveTarget({ + ...baseParams, + context: { + eventName: "issue_comment", + payload: { + issue: { + number: 246, + pull_request: { url: "https://api.github.com/repos/o/r/pulls/246" }, + }, + }, + }, + }); + expect(result.success).toBe(true); + expect(result.number).toBe(246); + expect(result.contextType).toBe("pull request"); + }); + it("should fail when triggering and not in PR context", () => { const result = helpers.resolveTarget({ ...baseParams, diff --git a/actions/setup/js/submit_pr_review.test.cjs b/actions/setup/js/submit_pr_review.test.cjs index 572a7c91378..f591de328be 100644 --- a/actions/setup/js/submit_pr_review.test.cjs +++ b/actions/setup/js/submit_pr_review.test.cjs @@ -418,6 +418,61 @@ describe("submit_pr_review (Handler Factory Architecture)", () => { delete global.context; }); + it("should set review context from issue_comment on a PR for body-only reviews", async () => { + global.github = { + rest: { + pulls: { + get: vi.fn().mockResolvedValue({ + data: { + number: 42, + head: { sha: "issue-comment-pr-sha" }, + }, + }), + }, + }, + graphql: vi.fn().mockResolvedValue({}), + }; + + global.context = { + eventName: "issue_comment", + repo: { owner: "test-owner", repo: "test-repo" }, + payload: { + issue: { + number: 42, + pull_request: { url: "https://api.github.com/repos/test-owner/test-repo/pulls/42" }, + }, + }, + }; + + const localBuffer = createReviewBuffer(); + const { main } = require("./submit_pr_review.cjs"); + const localHandler = await main({ max: 1, _prReviewBuffer: localBuffer }); + + const result = await localHandler( + { + type: "submit_pull_request_review", + body: "Looks good overall.", + event: "COMMENT", + }, + {} + ); + + expect(result.success).toBe(true); + expect(localBuffer.hasReviewMetadata()).toBe(true); + const ctx = localBuffer.getReviewContext(); + expect(ctx).not.toBeNull(); + expect(ctx.repo).toBe("test-owner/test-repo"); + expect(ctx.pullRequestNumber).toBe(42); + expect(global.github.rest.pulls.get).toHaveBeenCalledWith({ + owner: "test-owner", + repo: "test-repo", + pull_number: 42, + }); + + delete global.context; + delete global.github; + }); + it("should set review context from target config when explicit PR number (e.g. workflow_dispatch)", async () => { const fetchedPR = { number: 99,