diff --git a/actions/setup/js/add_comment.cjs b/actions/setup/js/add_comment.cjs index 1ed4a3c1945..8611d6ea52f 100644 --- a/actions/setup/js/add_comment.cjs +++ b/actions/setup/js/add_comment.cjs @@ -38,6 +38,38 @@ async function minimizeComment(github, nodeId, reason = "outdated") { }; } +/** + * Check if a comment should be hidden by the hideOlderComments function + * @param {string} commentBody - The comment body text + * @param {string} workflowId - The workflow ID to match + * @returns {boolean} true if the comment should be hidden, false otherwise + */ +function shouldHideComment(commentBody, workflowId) { + if (!commentBody) { + return false; + } + + // Comment must have the workflow-id marker + const hasWorkflowId = commentBody.includes(``); + if (!hasWorkflowId) { + return false; + } + + // Exclude reaction comments (activation comments) + const isReactionComment = commentBody.includes(``); + if (isReactionComment) { + return false; + } + + // Exclude append-only comments (should persist across runs) + const isAppendOnlyComment = commentBody.includes(``); + if (isAppendOnlyComment) { + return false; + } + + return true; +} + /** * Find comments on an issue/PR with a specific tracker-id * @param {any} github - GitHub REST API instance @@ -66,10 +98,8 @@ async function findCommentsWithTrackerId(github, owner, repo, issueNumber, workf break; } - // Filter comments that contain the workflow-id and are NOT reaction comments - const filteredComments = data - .filter(comment => comment.body?.includes(``) && !comment.body.includes(``)) - .map(({ id, node_id, body }) => ({ id, node_id, body })); + // Filter comments that should be hidden + const filteredComments = data.filter(comment => shouldHideComment(comment.body, workflowId)).map(({ id, node_id, body }) => ({ id, node_id, body })); comments.push(...filteredComments); @@ -122,9 +152,7 @@ async function findDiscussionCommentsWithTrackerId(github, owner, repo, discussi break; } - const filteredComments = result.repository.discussion.comments.nodes - .filter(comment => comment.body?.includes(``) && !comment.body.includes(``)) - .map(({ id, body }) => ({ id, body })); + const filteredComments = result.repository.discussion.comments.nodes.filter(comment => shouldHideComment(comment.body, workflowId)).map(({ id, body }) => ({ id, body })); comments.push(...filteredComments); @@ -414,6 +442,11 @@ async function main(config = {}) { processedBody += "\n\n" + trackerIDComment; } + // Add append-only marker if enabled to prevent hiding by future runs + if (appendOnlyComments) { + processedBody += "\n\n"; + } + const workflowName = process.env.GH_AW_WORKFLOW_NAME || "Workflow"; const runId = context.runId; const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`; diff --git a/actions/setup/js/add_comment.test.cjs b/actions/setup/js/add_comment.test.cjs index f5a9b7beb67..6a17d300d07 100644 --- a/actions/setup/js/add_comment.test.cjs +++ b/actions/setup/js/add_comment.test.cjs @@ -527,5 +527,114 @@ describe("add_comment", () => { // Clean up delete process.env.GITHUB_WORKFLOW; }); + + it("should include append-only marker in comment body when append-only-comments is enabled", async () => { + const addCommentScript = fs.readFileSync(path.join(__dirname, "add_comment.cjs"), "utf8"); + + // Set up environment variable for append-only-comments + process.env.GH_AW_SAFE_OUTPUT_MESSAGES = JSON.stringify({ + appendOnlyComments: true, + }); + process.env.GITHUB_WORKFLOW = "test-workflow"; + + let capturedComment = null; + mockGithub.rest.issues.createComment = async params => { + capturedComment = params; + return { + data: { + id: 12345, + html_url: `https://github.com/owner/repo/issues/${params.issue_number}#issuecomment-12345`, + }, + }; + }; + + const handler = await eval(`(async () => { ${addCommentScript}; return await main({}); })()`); + + const message = { + type: "add_comment", + body: "Append-only comment", + }; + + const result = await handler(message, {}); + + expect(result.success).toBe(true); + expect(capturedComment).toBeTruthy(); + expect(capturedComment.body).toContain(""); + + // Clean up + delete process.env.GH_AW_SAFE_OUTPUT_MESSAGES; + delete process.env.GITHUB_WORKFLOW; + }); + + it("should not hide append-only comments from previous runs", async () => { + const addCommentScript = fs.readFileSync(path.join(__dirname, "add_comment.cjs"), "utf8"); + + // Set up environment WITHOUT append-only-comments (new run) + delete process.env.GH_AW_SAFE_OUTPUT_MESSAGES; + process.env.GITHUB_WORKFLOW = "test-workflow"; + + let hideCommentsWasCalled = false; + let hiddenCommentIds = []; + + // Simulate finding an old append-only comment and a regular comment + mockGithub.rest.issues.listComments = async () => { + return { + data: [ + { + id: 888, + node_id: "IC_kwDOTest888", + body: "Old append-only comment ", + }, + { + id: 999, + node_id: "IC_kwDOTest999", + body: "Old regular comment ", + }, + ], + }; + }; + + mockGithub.graphql = async (query, variables) => { + if (query.includes("minimizeComment")) { + hideCommentsWasCalled = true; + hiddenCommentIds.push(variables.nodeId); + } + return { + minimizeComment: { + minimizedComment: { + isMinimized: true, + }, + }, + }; + }; + + mockGithub.rest.issues.createComment = async params => { + return { + data: { + id: 12345, + html_url: `https://github.com/owner/repo/issues/${params.issue_number}#issuecomment-12345`, + }, + }; + }; + + // Execute with hide-older-comments enabled (but not append-only) + const handler = await eval(`(async () => { ${addCommentScript}; return await main({ hide_older_comments: true }); })()`); + + const message = { + type: "add_comment", + body: "New comment", + }; + + const result = await handler(message, {}); + + expect(result.success).toBe(true); + expect(hideCommentsWasCalled).toBe(true); + // Should only hide the regular comment, not the append-only comment + expect(hiddenCommentIds).toEqual(["IC_kwDOTest999"]); + expect(hiddenCommentIds).not.toContain("IC_kwDOTest888"); + + // Clean up + delete process.env.GITHUB_WORKFLOW; + }); }); });