diff --git a/.github/workflows/mantis-discord-status-reactions.yml b/.github/workflows/mantis-discord-status-reactions.yml index 2a52c38fde9b..860f63ef1999 100644 --- a/.github/workflows/mantis-discord-status-reactions.yml +++ b/.github/workflows/mantis-discord-status-reactions.yml @@ -22,8 +22,7 @@ on: permissions: contents: read - issues: write - pull-requests: write + pull-requests: read concurrency: group: mantis-discord-status-reactions-${{ github.event.issue.number || inputs.pr_number || inputs.candidate_ref || github.run_id }}-${{ github.run_attempt }} @@ -86,6 +85,7 @@ jobs: baseline_ref: ${{ steps.resolve.outputs.baseline_ref }} candidate_ref: ${{ steps.resolve.outputs.candidate_ref }} pr_number: ${{ steps.resolve.outputs.pr_number }} + reaction_id: ${{ steps.add_reaction.outputs.reaction_id }} request_source: ${{ steps.resolve.outputs.request_source }} should_run: ${{ steps.resolve.outputs.should_run }} steps: @@ -162,12 +162,35 @@ jobs: setOutput("pr_number", String(issue.number)); setOutput("request_source", "issue_comment"); - await github.rest.reactions.createForIssueComment({ - owner, - repo, - comment_id: context.payload.comment.id, - content: "eyes", - }).catch((error) => core.warning(`Could not add eyes reaction: ${error.message}`)); + - name: Create Mantis reaction GitHub App token + id: mantis_reaction_token + if: ${{ steps.resolve.outputs.request_source == 'issue_comment' }} + continue-on-error: true + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3 + with: + app-id: ${{ secrets.MANTIS_GITHUB_APP_ID }} + private-key: ${{ secrets.MANTIS_GITHUB_APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + repositories: ${{ github.event.repository.name }} + permission-issues: write + + - name: Add Mantis eyes reaction + id: add_reaction + if: ${{ steps.resolve.outputs.request_source == 'issue_comment' && steps.mantis_reaction_token.outcome == 'success' }} + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + with: + github-token: ${{ steps.mantis_reaction_token.outputs.token }} + script: | + const { owner, repo } = context.repo; + await github.rest.reactions + .createForIssueComment({ + owner, + repo, + comment_id: context.payload.comment.id, + content: "eyes", + }) + .then(({ data: reaction }) => core.setOutput("reaction_id", String(reaction.id))) + .catch((error) => core.warning(`Could not add eyes reaction: ${error.message}`)); validate_refs: name: Validate selected refs @@ -583,40 +606,33 @@ jobs: clear_issue_comment_reaction: name: Clear Mantis command reaction needs: [resolve_request, validate_refs, run_status_reactions] - if: ${{ always() && github.event_name == 'issue_comment' && needs.resolve_request.outputs.request_source == 'issue_comment' }} + if: ${{ always() && github.event_name == 'issue_comment' && needs.resolve_request.outputs.request_source == 'issue_comment' && needs.resolve_request.outputs.reaction_id != '' }} runs-on: ubuntu-24.04 - permissions: - issues: write + permissions: {} steps: - - name: Remove workflow eyes reaction - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + - name: Create Mantis cleanup GitHub App token + id: mantis_reaction_token + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3 with: + app-id: ${{ secrets.MANTIS_GITHUB_APP_ID }} + private-key: ${{ secrets.MANTIS_GITHUB_APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + repositories: ${{ github.event.repository.name }} + permission-issues: write + + - name: Remove Mantis eyes reaction + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + env: + REACTION_ID: ${{ needs.resolve_request.outputs.reaction_id }} + with: + github-token: ${{ steps.mantis_reaction_token.outputs.token }} script: | const { owner, repo } = context.repo; - const commentId = context.payload.comment?.id; - if (!commentId) { - core.info("No issue comment id found; skipping reaction cleanup."); - return; - } - - const reactions = await github.paginate(github.rest.reactions.listForIssueComment, { + const reactionId = Number(process.env.REACTION_ID); + await github.rest.reactions.deleteForIssueComment({ owner, repo, - comment_id: commentId, - per_page: 100, + comment_id: context.payload.comment.id, + reaction_id: reactionId, }); - const eyes = reactions.filter( - (reaction) => reaction.content === "eyes" && reaction.user?.login === "github-actions[bot]", - ); - for (const reaction of eyes) { - await github.rest.reactions.deleteForIssueComment({ - owner, - repo, - comment_id: commentId, - reaction_id: reaction.id, - }); - core.info(`Removed eyes reaction ${reaction.id} from comment ${commentId}.`); - } - if (eyes.length === 0) { - core.info(`No workflow eyes reaction found on comment ${commentId}.`); - } + core.info(`Removed Mantis eyes reaction ${reactionId}.`); diff --git a/.github/workflows/mantis-discord-thread-attachment.yml b/.github/workflows/mantis-discord-thread-attachment.yml index ab96d0b29373..aa49de79c812 100644 --- a/.github/workflows/mantis-discord-thread-attachment.yml +++ b/.github/workflows/mantis-discord-thread-attachment.yml @@ -22,8 +22,7 @@ on: permissions: contents: read - issues: write - pull-requests: write + pull-requests: read concurrency: group: mantis-discord-thread-attachment-${{ github.event.issue.number || inputs.pr_number || inputs.candidate_ref || github.run_id }}-${{ github.run_attempt }} @@ -86,6 +85,7 @@ jobs: baseline_ref: ${{ steps.resolve.outputs.baseline_ref }} candidate_ref: ${{ steps.resolve.outputs.candidate_ref }} pr_number: ${{ steps.resolve.outputs.pr_number }} + reaction_id: ${{ steps.add_reaction.outputs.reaction_id }} request_source: ${{ steps.resolve.outputs.request_source }} should_run: ${{ steps.resolve.outputs.should_run }} steps: @@ -161,12 +161,35 @@ jobs: setOutput("pr_number", String(issue.number)); setOutput("request_source", "issue_comment"); - await github.rest.reactions.createForIssueComment({ - owner, - repo, - comment_id: context.payload.comment.id, - content: "eyes", - }).catch((error) => core.warning(`Could not add eyes reaction: ${error.message}`)); + - name: Create Mantis reaction GitHub App token + id: mantis_reaction_token + if: ${{ steps.resolve.outputs.request_source == 'issue_comment' }} + continue-on-error: true + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3 + with: + app-id: ${{ secrets.MANTIS_GITHUB_APP_ID }} + private-key: ${{ secrets.MANTIS_GITHUB_APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + repositories: ${{ github.event.repository.name }} + permission-issues: write + + - name: Add Mantis eyes reaction + id: add_reaction + if: ${{ steps.resolve.outputs.request_source == 'issue_comment' && steps.mantis_reaction_token.outcome == 'success' }} + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + with: + github-token: ${{ steps.mantis_reaction_token.outputs.token }} + script: | + const { owner, repo } = context.repo; + await github.rest.reactions + .createForIssueComment({ + owner, + repo, + comment_id: context.payload.comment.id, + content: "eyes", + }) + .then(({ data: reaction }) => core.setOutput("reaction_id", String(reaction.id))) + .catch((error) => core.warning(`Could not add eyes reaction: ${error.message}`)); validate_candidate: name: Validate selected candidate @@ -605,40 +628,33 @@ jobs: clear_issue_comment_reaction: name: Clear Mantis command reaction needs: [resolve_request, validate_candidate, run_thread_attachment] - if: ${{ always() && github.event_name == 'issue_comment' && needs.resolve_request.outputs.request_source == 'issue_comment' }} + if: ${{ always() && github.event_name == 'issue_comment' && needs.resolve_request.outputs.request_source == 'issue_comment' && needs.resolve_request.outputs.reaction_id != '' }} runs-on: ubuntu-24.04 - permissions: - issues: write + permissions: {} steps: - - name: Remove workflow eyes reaction - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + - name: Create Mantis cleanup GitHub App token + id: mantis_reaction_token + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3 with: + app-id: ${{ secrets.MANTIS_GITHUB_APP_ID }} + private-key: ${{ secrets.MANTIS_GITHUB_APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + repositories: ${{ github.event.repository.name }} + permission-issues: write + + - name: Remove Mantis eyes reaction + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + env: + REACTION_ID: ${{ needs.resolve_request.outputs.reaction_id }} + with: + github-token: ${{ steps.mantis_reaction_token.outputs.token }} script: | const { owner, repo } = context.repo; - const commentId = context.payload.comment?.id; - if (!commentId) { - core.info("No issue comment id found; skipping reaction cleanup."); - return; - } - - const reactions = await github.paginate(github.rest.reactions.listForIssueComment, { + const reactionId = Number(process.env.REACTION_ID); + await github.rest.reactions.deleteForIssueComment({ owner, repo, - comment_id: commentId, - per_page: 100, + comment_id: context.payload.comment.id, + reaction_id: reactionId, }); - const eyes = reactions.filter( - (reaction) => reaction.content === "eyes" && reaction.user?.login === "github-actions[bot]", - ); - for (const reaction of eyes) { - await github.rest.reactions.deleteForIssueComment({ - owner, - repo, - comment_id: commentId, - reaction_id: reaction.id, - }); - core.info(`Removed eyes reaction ${reaction.id} from comment ${commentId}.`); - } - if (eyes.length === 0) { - core.info(`No workflow eyes reaction found on comment ${commentId}.`); - } + core.info(`Removed Mantis eyes reaction ${reactionId}.`); diff --git a/.github/workflows/mantis-telegram-live.yml b/.github/workflows/mantis-telegram-live.yml index 0533562fabad..87831ae9caf9 100644 --- a/.github/workflows/mantis-telegram-live.yml +++ b/.github/workflows/mantis-telegram-live.yml @@ -35,8 +35,7 @@ on: permissions: actions: read contents: read - issues: write - pull-requests: write + pull-requests: read env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" @@ -99,6 +98,7 @@ jobs: crabbox_provider: ${{ steps.resolve.outputs.crabbox_provider }} lease_id: ${{ steps.resolve.outputs.lease_id }} pr_number: ${{ steps.resolve.outputs.pr_number }} + reaction_id: ${{ steps.add_reaction.outputs.reaction_id }} request_source: ${{ steps.resolve.outputs.request_source }} scenario: ${{ steps.resolve.outputs.scenario }} should_run: ${{ steps.resolve.outputs.should_run }} @@ -193,12 +193,35 @@ jobs: setOutput("lease_id", leaseMatch?.[1] || ""); setOutput("request_source", "issue_comment"); - await github.rest.reactions.createForIssueComment({ - owner, - repo, - comment_id: context.payload.comment.id, - content: "eyes", - }).catch((error) => core.warning(`Could not add eyes reaction: ${error.message}`)); + - name: Create Mantis reaction GitHub App token + id: mantis_reaction_token + if: ${{ steps.resolve.outputs.request_source == 'issue_comment' }} + continue-on-error: true + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3 + with: + app-id: ${{ secrets.MANTIS_GITHUB_APP_ID }} + private-key: ${{ secrets.MANTIS_GITHUB_APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + repositories: ${{ github.event.repository.name }} + permission-issues: write + + - name: Add Mantis eyes reaction + id: add_reaction + if: ${{ steps.resolve.outputs.request_source == 'issue_comment' && steps.mantis_reaction_token.outcome == 'success' }} + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + with: + github-token: ${{ steps.mantis_reaction_token.outputs.token }} + script: | + const { owner, repo } = context.repo; + await github.rest.reactions + .createForIssueComment({ + owner, + repo, + comment_id: context.payload.comment.id, + content: "eyes", + }) + .then(({ data: reaction }) => core.setOutput("reaction_id", String(reaction.id))) + .catch((error) => core.warning(`Could not add eyes reaction: ${error.message}`)); validate_ref: name: Validate candidate ref @@ -566,40 +589,33 @@ jobs: clear_issue_comment_reaction: name: Clear Mantis command reaction needs: [resolve_request, validate_ref, run_telegram_live] - if: ${{ always() && github.event_name == 'issue_comment' && needs.resolve_request.outputs.request_source == 'issue_comment' }} + if: ${{ always() && github.event_name == 'issue_comment' && needs.resolve_request.outputs.request_source == 'issue_comment' && needs.resolve_request.outputs.reaction_id != '' }} runs-on: ubuntu-24.04 - permissions: - issues: write + permissions: {} steps: - - name: Remove workflow eyes reaction - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + - name: Create Mantis cleanup GitHub App token + id: mantis_reaction_token + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3 with: + app-id: ${{ secrets.MANTIS_GITHUB_APP_ID }} + private-key: ${{ secrets.MANTIS_GITHUB_APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + repositories: ${{ github.event.repository.name }} + permission-issues: write + + - name: Remove Mantis eyes reaction + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + env: + REACTION_ID: ${{ needs.resolve_request.outputs.reaction_id }} + with: + github-token: ${{ steps.mantis_reaction_token.outputs.token }} script: | const { owner, repo } = context.repo; - const commentId = context.payload.comment?.id; - if (!commentId) { - core.info("No issue comment id found; skipping reaction cleanup."); - return; - } - - const reactions = await github.paginate(github.rest.reactions.listForIssueComment, { + const reactionId = Number(process.env.REACTION_ID); + await github.rest.reactions.deleteForIssueComment({ owner, repo, - comment_id: commentId, - per_page: 100, + comment_id: context.payload.comment.id, + reaction_id: reactionId, }); - const eyes = reactions.filter( - (reaction) => reaction.content === "eyes" && reaction.user?.login === "github-actions[bot]", - ); - for (const reaction of eyes) { - await github.rest.reactions.deleteForIssueComment({ - owner, - repo, - comment_id: commentId, - reaction_id: reaction.id, - }); - core.info(`Removed eyes reaction ${reaction.id} from comment ${commentId}.`); - } - if (eyes.length === 0) { - core.info(`No workflow eyes reaction found on comment ${commentId}.`); - } + core.info(`Removed Mantis eyes reaction ${reactionId}.`); diff --git a/.github/workflows/mantis-web-ui-chat-proof.yml b/.github/workflows/mantis-web-ui-chat-proof.yml index d1fe2a2b3c6b..cb08a5dc1831 100644 --- a/.github/workflows/mantis-web-ui-chat-proof.yml +++ b/.github/workflows/mantis-web-ui-chat-proof.yml @@ -18,8 +18,7 @@ on: permissions: actions: read contents: read - issues: write - pull-requests: write + pull-requests: read concurrency: group: mantis-web-ui-chat-proof-${{ github.event.issue.number || inputs.pr_number || inputs.candidate_ref || github.run_id }}-${{ github.run_attempt }} @@ -79,6 +78,7 @@ jobs: outputs: candidate_ref: ${{ steps.resolve.outputs.candidate_ref }} pr_number: ${{ steps.resolve.outputs.pr_number }} + reaction_id: ${{ steps.add_reaction.outputs.reaction_id }} request_source: ${{ steps.resolve.outputs.request_source }} should_run: ${{ steps.resolve.outputs.should_run }} steps: @@ -150,12 +150,35 @@ jobs: setOutput("pr_number", String(issue.number)); setOutput("request_source", "issue_comment"); - await github.rest.reactions.createForIssueComment({ - owner, - repo, - comment_id: context.payload.comment.id, - content: "eyes", - }).catch((error) => core.warning(`Could not add eyes reaction: ${error.message}`)); + - name: Create Mantis reaction GitHub App token + id: mantis_reaction_token + if: ${{ steps.resolve.outputs.request_source == 'issue_comment' }} + continue-on-error: true + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3 + with: + app-id: ${{ secrets.MANTIS_GITHUB_APP_ID }} + private-key: ${{ secrets.MANTIS_GITHUB_APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + repositories: ${{ github.event.repository.name }} + permission-issues: write + + - name: Add Mantis eyes reaction + id: add_reaction + if: ${{ steps.resolve.outputs.request_source == 'issue_comment' && steps.mantis_reaction_token.outcome == 'success' }} + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + with: + github-token: ${{ steps.mantis_reaction_token.outputs.token }} + script: | + const { owner, repo } = context.repo; + await github.rest.reactions + .createForIssueComment({ + owner, + repo, + comment_id: context.payload.comment.id, + content: "eyes", + }) + .then(({ data: reaction }) => core.setOutput("reaction_id", String(reaction.id))) + .catch((error) => core.warning(`Could not add eyes reaction: ${error.message}`)); validate_candidate: name: Validate selected candidate @@ -371,40 +394,33 @@ jobs: clear_issue_comment_reaction: name: Clear Mantis command reaction needs: [resolve_request, validate_candidate, run_web_ui_chat, publish_evidence] - if: ${{ always() && github.event_name == 'issue_comment' && needs.resolve_request.outputs.request_source == 'issue_comment' }} + if: ${{ always() && github.event_name == 'issue_comment' && needs.resolve_request.outputs.request_source == 'issue_comment' && needs.resolve_request.outputs.reaction_id != '' }} runs-on: ubuntu-24.04 - permissions: - issues: write + permissions: {} steps: - - name: Remove workflow eyes reaction - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + - name: Create Mantis cleanup GitHub App token + id: mantis_reaction_token + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3 with: + app-id: ${{ secrets.MANTIS_GITHUB_APP_ID }} + private-key: ${{ secrets.MANTIS_GITHUB_APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + repositories: ${{ github.event.repository.name }} + permission-issues: write + + - name: Remove Mantis eyes reaction + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + env: + REACTION_ID: ${{ needs.resolve_request.outputs.reaction_id }} + with: + github-token: ${{ steps.mantis_reaction_token.outputs.token }} script: | const { owner, repo } = context.repo; - const commentId = context.payload.comment?.id; - if (!commentId) { - core.info("No issue comment id found; skipping reaction cleanup."); - return; - } - - const reactions = await github.paginate(github.rest.reactions.listForIssueComment, { + const reactionId = Number(process.env.REACTION_ID); + await github.rest.reactions.deleteForIssueComment({ owner, repo, - comment_id: commentId, - per_page: 100, + comment_id: context.payload.comment.id, + reaction_id: reactionId, }); - const eyes = reactions.filter( - (reaction) => reaction.content === "eyes" && reaction.user?.login === "github-actions[bot]", - ); - for (const reaction of eyes) { - await github.rest.reactions.deleteForIssueComment({ - owner, - repo, - comment_id: commentId, - reaction_id: reaction.id, - }); - core.info(`Removed eyes reaction ${reaction.id} from comment ${commentId}.`); - } - if (eyes.length === 0) { - core.info(`No workflow eyes reaction found on comment ${commentId}.`); - } + core.info(`Removed Mantis eyes reaction ${reactionId}.`); diff --git a/test/scripts/ci-workflow-guards.test.ts b/test/scripts/ci-workflow-guards.test.ts index 76221487f08a..77acbca74eee 100644 --- a/test/scripts/ci-workflow-guards.test.ts +++ b/test/scripts/ci-workflow-guards.test.ts @@ -29,6 +29,12 @@ const UPLOAD_ARTIFACT_V7 = "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64 const DOWNLOAD_ARTIFACT_V8 = "actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c"; const CREATE_GITHUB_APP_TOKEN_V3 = "actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1"; +const MANTIS_ISSUE_COMMENT_REACTION_WORKFLOWS = [ + ".github/workflows/mantis-web-ui-chat-proof.yml", + ".github/workflows/mantis-discord-status-reactions.yml", + ".github/workflows/mantis-discord-thread-attachment.yml", + ".github/workflows/mantis-telegram-live.yml", +] as const; const TRUFFLEHOG_V3_95_9 = "trufflesecurity/trufflehog@27b0417c16317ca9a472a9a8092acce143b49c55"; const MANTIS_GITHUB_APP_CLIENT_ID = "Iv23liPJCozR0uHm6P7G"; const OPENGREP_PR_DIFF_WORKFLOW = ".github/workflows/opengrep-precise.yml"; @@ -3774,6 +3780,75 @@ NODE ).toBe(true); }); + it.each(MANTIS_ISSUE_COMMENT_REACTION_WORKFLOWS)( + "keeps Mantis reaction ownership stable in %s", + (workflowPath) => { + const source = readFileSync(workflowPath, "utf8"); + const workflow = parse(source); + const resolveJob = workflow.jobs.resolve_request; + const resolveSteps = resolveJob.steps as WorkflowStep[]; + const cleanupJob = workflow.jobs.clear_issue_comment_reaction; + const cleanupSteps = cleanupJob.steps as WorkflowStep[]; + const findStep = (steps: WorkflowStep[], id: string) => + expectDefined( + steps.find((step) => step.id === id), + `${workflowPath} ${id}`, + ); + const createTokenStep = findStep(resolveSteps, "mantis_reaction_token"); + const createStep = findStep(resolveSteps, "add_reaction"); + const cleanupTokenStep = findStep(cleanupSteps, "mantis_reaction_token"); + const deleteStep = expectDefined( + cleanupSteps.find((step) => step.env?.REACTION_ID), + `${workflowPath} reaction cleanup step`, + ); + + expect(resolveJob.outputs.reaction_id, workflowPath).toBe( + "${{ steps.add_reaction.outputs.reaction_id }}", + ); + for (const [label, tokenStep] of [ + ["creation", createTokenStep], + ["cleanup", cleanupTokenStep], + ] as const) { + expect(tokenStep, `${workflowPath} ${label} token`).toMatchObject({ + uses: CREATE_GITHUB_APP_TOKEN_V3, + with: { + "app-id": "${{ secrets.MANTIS_GITHUB_APP_ID }}", + "private-key": "${{ secrets.MANTIS_GITHUB_APP_PRIVATE_KEY }}", + }, + }); + expect( + Object.entries(tokenStep.with ?? {}).filter(([key]) => key.startsWith("permission-")), + `${workflowPath} ${label} permissions`, + ).toEqual([["permission-issues", "write"]]); + } + expect(createStep, workflowPath).toMatchObject({ + if: "${{ steps.resolve.outputs.request_source == 'issue_comment' && steps.mantis_reaction_token.outcome == 'success' }}", + uses: "actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3", + with: { "github-token": "${{ steps.mantis_reaction_token.outputs.token }}" }, + }); + expect(createStep.with?.script, workflowPath).toContain("createForIssueComment"); + expect(createStep.with?.script, workflowPath).toContain( + 'core.setOutput("reaction_id", String(reaction.id))', + ); + expect(source.match(/createForIssueComment/gu), workflowPath).toHaveLength(1); + expect(cleanupJob.if, workflowPath).toContain( + "needs.resolve_request.outputs.reaction_id != ''", + ); + expect(cleanupJob.permissions, workflowPath).toEqual({}); + expect(deleteStep, workflowPath).toMatchObject({ + env: { REACTION_ID: "${{ needs.resolve_request.outputs.reaction_id }}" }, + uses: "actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3", + with: { "github-token": "${{ steps.mantis_reaction_token.outputs.token }}" }, + }); + expect(deleteStep.with?.script, workflowPath).toContain("deleteForIssueComment"); + expect(deleteStep.with?.script, workflowPath).toContain("Number(process.env.REACTION_ID)"); + expect(deleteStep.with?.script, workflowPath).toContain("reaction_id: reactionId"); + expect(JSON.stringify(cleanupJob), workflowPath).not.toMatch( + /listForIssueComment|\.filter\(|github-actions\[bot\]/u, + ); + }, + ); + it("bounds release ref validation fetches across checkout auth modes", () => { const resolveTargetSteps = readReleaseChecksWorkflow().jobs.resolve_target.steps;