diff --git a/.github/scripts/wait-for-workflow-artifact.cjs b/.github/scripts/wait-for-workflow-artifact.cjs index 1d3620e88..6704a53d1 100644 --- a/.github/scripts/wait-for-workflow-artifact.cjs +++ b/.github/scripts/wait-for-workflow-artifact.cjs @@ -6,7 +6,6 @@ const DEFAULT_TIMEOUT_MS = 110 * 60_000; async function pollWorkflowArtifact({ artifactName, listArtifacts, - getTargetJob = async () => null, getWorkflowRun, now = Date.now, sleep = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)), @@ -27,14 +26,6 @@ async function pollWorkflowArtifact({ }; } - const targetJob = await getTargetJob(); - if (targetJob?.status === 'completed') { - return { - artifactAvailable: false, - conclusion: targetJob.conclusion || 'failure', - }; - } - const workflowRun = await getWorkflowRun(); if (workflowRun.status === 'completed') { return { @@ -59,7 +50,6 @@ async function waitForWorkflowArtifact({ context, core, artifactName, - targetJobName, pollIntervalMs = DEFAULT_POLL_INTERVAL_MS, timeoutMs = DEFAULT_TIMEOUT_MS, }) { @@ -87,19 +77,6 @@ async function waitForWorkflowArtifact({ }); return response.data.artifacts; }, - getTargetJob: async () => { - if (!targetJobName) { - return null; - } - const response = await github.rest.actions.listJobsForWorkflowRun({ - owner, - repo, - run_id: runId, - filter: 'latest', - per_page: 100, - }); - return response.data.jobs.find((job) => job.name === targetJobName) || null; - }, getWorkflowRun: async () => { const response = await github.rest.actions.getWorkflowRun({ owner, @@ -110,15 +87,12 @@ async function waitForWorkflowArtifact({ }, }); - const runUrl = run.html_url || `https://github.com/${owner}/${repo}/actions/runs/${runId}`; core.setOutput('artifact_available', String(result.artifactAvailable)); core.setOutput('conclusion', result.conclusion); - core.setOutput('run_id', String(runId)); - core.setOutput('run_url', runUrl); if (!result.artifactAvailable) { core.notice( - `The target job or workflow run ${runId} finished, or the wait timed out, before ${artifactName} was uploaded.` + `Workflow run ${runId} finished or the wait timed out before ${artifactName} was uploaded.` ); } } diff --git a/.github/scripts/wait-for-workflow-artifact.test.cjs b/.github/scripts/wait-for-workflow-artifact.test.cjs index d4d1ec072..ce640ee51 100644 --- a/.github/scripts/wait-for-workflow-artifact.test.cjs +++ b/.github/scripts/wait-for-workflow-artifact.test.cjs @@ -57,22 +57,6 @@ test('reports a terminal workflow that never uploaded the artifact', async () => assert.deepEqual(result, { artifactAvailable: false, conclusion: 'failure' }); }); -test('does not wait for unrelated jobs after the target job finishes', async () => { - let workflowLookups = 0; - const result = await pollWorkflowArtifact({ - artifactName: 'benchmark-results', - listArtifacts: async () => [], - getTargetJob: async () => ({ status: 'completed', conclusion: 'skipped' }), - getWorkflowRun: async () => { - workflowLookups += 1; - return { status: 'in_progress', conclusion: null }; - }, - }); - - assert.deepEqual(result, { artifactAvailable: false, conclusion: 'skipped' }); - assert.equal(workflowLookups, 0); -}); - test('ignores expired artifacts and eventually times out', async () => { let currentTime = 0; const result = await pollWorkflowArtifact({ diff --git a/.github/workflows/sequential-navigation-soak-comment.yml b/.github/workflows/sequential-navigation-soak-comment.yml index 44c2ce636..14be14aea 100644 --- a/.github/workflows/sequential-navigation-soak-comment.yml +++ b/.github/workflows/sequential-navigation-soak-comment.yml @@ -38,7 +38,6 @@ jobs: uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7 env: ARTIFACT_NAME: sequential-navigation-soak-results - TARGET_JOB_NAME: Sequential navigation soak ยท 200 A/B with: retries: 3 script: | @@ -50,7 +49,6 @@ jobs: context, core, artifactName: process.env.ARTIFACT_NAME, - targetJobName: process.env.TARGET_JOB_NAME, }); - name: Download soak evidence @@ -62,14 +60,14 @@ jobs: name: sequential-navigation-soak-results path: sequential-navigation-soak-results github-token: ${{ secrets.GITHUB_TOKEN }} - run-id: ${{ steps.wait.outputs.run_id }} + run-id: ${{ github.event.workflow_run.id }} - name: Render trusted PR comment if: steps.wait.outputs.conclusion != 'cancelled' id: render env: PYTHONPATH: moli-benchmark - RUN_URL: ${{ steps.wait.outputs.run_url }} + RUN_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }} CONCLUSION: ${{ steps.wait.outputs.conclusion || 'failure' }} EVIDENCE_OUTCOME: ${{ steps.evidence.outcome }} run: | diff --git a/.github/workflows/spider-bench-comment.yml b/.github/workflows/spider-bench-comment.yml index e53bddf89..cdf0e83ab 100644 --- a/.github/workflows/spider-bench-comment.yml +++ b/.github/workflows/spider-bench-comment.yml @@ -11,7 +11,7 @@ concurrency: # This trusted workflow starts with CI, then waits only for the immutable v4 # artifact. It runs from the default branch, never executes PR code, and renders -# only bounded numeric fields into an Actions summary and a PR comment. +# only bounded numeric fields into an Actions summary and an upserted PR comment. permissions: actions: read contents: read @@ -38,7 +38,6 @@ jobs: uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7 env: ARTIFACT_NAME: spider-bench-results - TARGET_JOB_NAME: Spider public-web A/B with: retries: 3 script: | @@ -50,7 +49,6 @@ jobs: context, core, artifactName: process.env.ARTIFACT_NAME, - targetJobName: process.env.TARGET_JOB_NAME, }); - name: Download benchmark evidence @@ -62,7 +60,7 @@ jobs: name: spider-bench-results path: spider-bench-results github-token: ${{ secrets.GITHUB_TOKEN }} - run-id: ${{ steps.wait.outputs.run_id }} + run-id: ${{ github.event.workflow_run.id }} - name: Install Node.js if: steps.wait.outputs.conclusion != 'cancelled' @@ -74,7 +72,7 @@ jobs: if: steps.wait.outputs.conclusion != 'cancelled' id: render env: - RUN_URL: ${{ steps.wait.outputs.run_url }} + RUN_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }} CONCLUSION: ${{ steps.wait.outputs.conclusion || 'failure' }} EVIDENCE_OUTCOME: ${{ steps.evidence.outcome }} run: | @@ -144,9 +142,34 @@ jobs: } const body = fs.readFileSync(process.env.COMMENT_PATH, 'utf8'); - await github.rest.issues.createComment({ + const marker = ''; + if (!body.includes(marker) || Buffer.byteLength(body, 'utf8') > 32 * 1024) { + core.setFailed('Rendered Spider Bench comment is missing its marker or exceeds 32 KiB.'); + return; + } + const comments = await github.paginate(github.rest.issues.listComments, { owner: context.repo.owner, repo: context.repo.repo, issue_number: association.number, - body, + per_page: 100, }); + const existing = comments.find( + (comment) => + comment.user?.login === 'github-actions[bot]' && + comment.body?.includes(marker) + ); + if (existing) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existing.id, + body, + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: association.number, + body, + }); + }