diff --git a/.github/workflows/campaign-generator.lock.yml b/.github/workflows/campaign-generator.lock.yml index 1e5f44233a..992d4766a5 100644 --- a/.github/workflows/campaign-generator.lock.yml +++ b/.github/workflows/campaign-generator.lock.yml @@ -223,13 +223,12 @@ jobs: "type": "string" }, "item_number": { - "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Must be a valid existing item in the repository. Required.", + "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). If omitted, the tool will attempt to resolve the target from the current workflow context (triggering issue, PR, or discussion).", "type": "number" } }, "required": [ - "body", - "item_number" + "body" ], "type": "object" }, diff --git a/actions/setup/js/add_comment.cjs b/actions/setup/js/add_comment.cjs index ed9921cce4..cd3fe4ce1d 100644 --- a/actions/setup/js/add_comment.cjs +++ b/actions/setup/js/add_comment.cjs @@ -339,43 +339,59 @@ async function main(config = {}) { let itemNumber; let isDiscussion = false; - // Check if this is a discussion context - const isDiscussionContext = context.eventName === "discussion" || context.eventName === "discussion_comment"; - - if (isDiscussionContext) { - // For discussions, always use the discussion context - isDiscussion = true; - itemNumber = context.payload?.discussion?.number; - - if (!itemNumber) { - core.warning("Discussion context detected but no discussion number found"); + // Check if item_number was explicitly provided in the message + if (item.item_number !== undefined && item.item_number !== null) { + // Use the explicitly provided item_number + itemNumber = typeof item.item_number === "number" ? item.item_number : parseInt(String(item.item_number), 10); + + if (isNaN(itemNumber) || itemNumber <= 0) { + core.warning(`Invalid item_number specified: ${item.item_number}`); return { success: false, - error: "No discussion number available", + error: `Invalid item_number specified: ${item.item_number}`, }; } - - core.info(`Using discussion context: #${itemNumber}`); + + core.info(`Using explicitly provided item_number: #${itemNumber}`); } else { - // For issues/PRs, use the resolveTarget helper which respects target configuration - const targetResult = resolveTarget({ - targetConfig: commentTarget, - item: item, - context: context, - itemType: "add_comment", - supportsPR: true, // add_comment supports both issues and PRs - }); - - if (!targetResult.success) { - core.warning(targetResult.error); - return { - success: false, - error: targetResult.error, - }; - } + // Check if this is a discussion context + const isDiscussionContext = context.eventName === "discussion" || context.eventName === "discussion_comment"; + + if (isDiscussionContext) { + // For discussions, always use the discussion context + isDiscussion = true; + itemNumber = context.payload?.discussion?.number; + + if (!itemNumber) { + core.warning("Discussion context detected but no discussion number found"); + return { + success: false, + error: "No discussion number available", + }; + } + + core.info(`Using discussion context: #${itemNumber}`); + } else { + // For issues/PRs, use the resolveTarget helper which respects target configuration + const targetResult = resolveTarget({ + targetConfig: commentTarget, + item: item, + context: context, + itemType: "add_comment", + supportsPR: true, // add_comment supports both issues and PRs + }); - itemNumber = targetResult.number; - core.info(`Resolved target ${targetResult.contextType} #${itemNumber} (target config: ${commentTarget})`); + if (!targetResult.success) { + core.warning(targetResult.error); + return { + success: false, + error: targetResult.error, + }; + } + + itemNumber = targetResult.number; + core.info(`Resolved target ${targetResult.contextType} #${itemNumber} (target config: ${commentTarget})`); + } } // Replace temporary ID references in body diff --git a/actions/setup/js/add_comment.test.cjs b/actions/setup/js/add_comment.test.cjs index fdfc09c96b..67a7ec8979 100644 --- a/actions/setup/js/add_comment.test.cjs +++ b/actions/setup/js/add_comment.test.cjs @@ -192,6 +192,65 @@ describe("add_comment", () => { expect(result.error).toMatch(/no.*item_number/i); }); + it("should use explicit item_number even with triggering target", async () => { + const addCommentScript = fs.readFileSync(path.join(__dirname, "add_comment.cjs"), "utf8"); + + let capturedIssueNumber = null; + mockGithub.rest.issues.createComment = async params => { + capturedIssueNumber = params.issue_number; + return { + data: { + id: 12345, + html_url: `https://github.com/owner/repo/issues/${params.issue_number}#issuecomment-12345`, + }, + }; + }; + + // Execute the handler factory with target: "triggering" (default) + const handler = await eval(`(async () => { ${addCommentScript}; return await main({ target: 'triggering' }); })()`); + + const message = { + type: "add_comment", + item_number: 777, + body: "Test comment with explicit item_number", + }; + + const result = await handler(message, {}); + + expect(result.success).toBe(true); + expect(capturedIssueNumber).toBe(777); + expect(result.itemNumber).toBe(777); + }); + + it("should resolve from context when item_number is not provided", async () => { + const addCommentScript = fs.readFileSync(path.join(__dirname, "add_comment.cjs"), "utf8"); + + let capturedIssueNumber = null; + mockGithub.rest.issues.createComment = async params => { + capturedIssueNumber = params.issue_number; + return { + data: { + id: 12345, + html_url: `https://github.com/owner/repo/issues/${params.issue_number}#issuecomment-12345`, + }, + }; + }; + + // Execute the handler factory with target: "triggering" (default) + const handler = await eval(`(async () => { ${addCommentScript}; return await main({ target: 'triggering' }); })()`); + + const message = { + type: "add_comment", + body: "Test comment without item_number, should use PR from context", + }; + + const result = await handler(message, {}); + + expect(result.success).toBe(true); + expect(capturedIssueNumber).toBe(8535); // Should use PR number from mockContext + expect(result.itemNumber).toBe(8535); + }); + it("should use issue context when triggered by an issue", async () => { const addCommentScript = fs.readFileSync(path.join(__dirname, "add_comment.cjs"), "utf8"); diff --git a/actions/setup/js/safe_outputs_tools.json b/actions/setup/js/safe_outputs_tools.json index 9d1fc89a54..de28b698b5 100644 --- a/actions/setup/js/safe_outputs_tools.json +++ b/actions/setup/js/safe_outputs_tools.json @@ -160,7 +160,7 @@ "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead.", "inputSchema": { "type": "object", - "required": ["body", "item_number"], + "required": ["body"], "properties": { "body": { "type": "string", @@ -168,7 +168,7 @@ }, "item_number": { "type": "number", - "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Must be a valid existing item in the repository. Required." + "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). If omitted, the tool will attempt to resolve the target from the current workflow context (triggering issue, PR, or discussion)." } }, "additionalProperties": false diff --git a/pkg/workflow/js/safe_outputs_tools.json b/pkg/workflow/js/safe_outputs_tools.json index 24fc845279..0c0cad9e84 100644 --- a/pkg/workflow/js/safe_outputs_tools.json +++ b/pkg/workflow/js/safe_outputs_tools.json @@ -219,8 +219,7 @@ "inputSchema": { "type": "object", "required": [ - "body", - "item_number" + "body" ], "properties": { "body": { @@ -229,7 +228,7 @@ }, "item_number": { "type": "number", - "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). Must be a valid existing item in the repository. Required." + "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). If omitted, the tool will attempt to resolve the target from the current workflow context (triggering issue, PR, or discussion)." } }, "additionalProperties": false