E2E Gate workflow_run 30733542874 #24234
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | |
| # SPDX-License-Identifier: Apache-2.0 | |
| name: E2E / PR Gate Controller | |
| run-name: >- | |
| ${{ github.event_name == 'pull_request_target' && | |
| format('E2E Gate PR #{0} head {1} base {2} gate {3}', | |
| github.event.pull_request.number, github.event.pull_request.head.sha, | |
| github.event.pull_request.base.sha, | |
| github.event.action != 'closed') || | |
| format('E2E Gate {0} {1}', github.event_name, github.run_id) }} | |
| on: | |
| workflow_run: | |
| workflows: ["CI / Pull Request"] | |
| types: [completed] | |
| pull_request_target: | |
| types: [opened, synchronize, reopened, ready_for_review, edited, closed] | |
| workflow_dispatch: | |
| inputs: | |
| operation: | |
| description: E2E gate action to perform. | |
| required: true | |
| default: approve-e2e | |
| type: choice | |
| options: | |
| - approve-e2e | |
| pr_number: | |
| description: Pull request number for the selected E2E gate action. | |
| required: true | |
| type: string | |
| expected_head_sha: | |
| description: Current 40-character PR head SHA reviewed by the maintainer. | |
| required: true | |
| type: string | |
| expected_base_sha: | |
| description: Current 40-character PR base SHA reviewed by the maintainer. | |
| required: true | |
| type: string | |
| review_reason: | |
| description: Why this PR may run credentialed E2E. | |
| required: true | |
| type: string | |
| permissions: {} | |
| jobs: | |
| initialize: | |
| if: ${{ github.run_attempt == 1 && github.event_name == 'pull_request_target' && github.repository == 'NVIDIA/NemoClaw' && github.event.action != 'closed' && (github.event.action != 'edited' || github.event.changes.base != null) }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| permissions: | |
| checks: write | |
| contents: read | |
| pull-requests: read | |
| concurrency: | |
| group: pr-e2e-gate-${{ github.repository }}-${{ github.event.pull_request.number }}-${{ github.event.pull_request.head.sha }}-${{ github.event.pull_request.base.sha }} | |
| queue: max | |
| cancel-in-progress: false | |
| steps: | |
| - name: Checkout controller | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| ref: ${{ github.workflow_sha }} | |
| persist-credentials: false | |
| - name: Setup Node | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| node-version: "22" | |
| - name: Install controller dependencies | |
| run: npm ci --ignore-scripts --no-audit --no-fund | |
| - name: Reserve PR/base SHA gate | |
| env: | |
| BASE_SHA: ${{ github.event.pull_request.base.sha }} | |
| GITHUB_TOKEN: ${{ github.token }} | |
| HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| run: >- | |
| node --experimental-strip-types tools/e2e/pr-e2e-gate.mts | |
| --mode seed | |
| --pr "$PR_NUMBER" | |
| --head "$HEAD_SHA" | |
| --base "$BASE_SHA" | |
| required: | |
| # GitHub treats a skipped required job as passing, so only the running observer can use the required check name. | |
| name: >- | |
| ${{ github.event_name == 'pull_request_target' && | |
| github.repository == 'NVIDIA/NemoClaw' && | |
| github.event.action != 'closed' && | |
| 'E2E / PR Gate' || 'E2E / PR Gate (not applicable)' }} | |
| if: ${{ github.event_name == 'pull_request_target' && github.repository == 'NVIDIA/NemoClaw' && github.event.action != 'closed' }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 360 | |
| permissions: | |
| checks: read | |
| contents: read | |
| pull-requests: read | |
| concurrency: | |
| group: pr-e2e-required-${{ github.event.pull_request.number }} | |
| cancel-in-progress: true | |
| steps: | |
| - name: Checkout observer | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| ref: ${{ github.workflow_sha }} | |
| persist-credentials: false | |
| - name: Setup Node | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| node-version: "22" | |
| - name: Wait for trusted PR/base SHA verdict | |
| env: | |
| BASE_SHA: ${{ github.event.pull_request.base.sha }} | |
| GITHUB_TOKEN: ${{ github.token }} | |
| HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| run: >- | |
| node --experimental-strip-types tools/e2e/pr-e2e-required.mts | |
| --pr "$PR_NUMBER" | |
| --head "$HEAD_SHA" | |
| --base "$BASE_SHA" | |
| --timeout-seconds 21480 | |
| cancel-superseded: | |
| if: ${{ github.run_attempt == 1 && github.event_name == 'pull_request_target' && github.repository == 'NVIDIA/NemoClaw' && (github.event.action != 'edited' || github.event.changes.base != null) }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| permissions: | |
| actions: write | |
| checks: write | |
| contents: read | |
| pull-requests: read | |
| steps: | |
| - name: Checkout controller | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| ref: ${{ github.workflow_sha }} | |
| persist-credentials: false | |
| - name: Setup Node | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| node-version: "22" | |
| - name: Install controller dependencies | |
| run: npm ci --ignore-scripts --no-audit --no-fund | |
| - name: Cancel superseded E2E runs | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| SUPERSEDED_HEAD_SHA: ${{ github.event.before || github.event.pull_request.head.sha }} | |
| run: >- | |
| node --experimental-strip-types tools/e2e/pr-e2e-gate.mts | |
| --mode cancel | |
| --pr "$PR_NUMBER" | |
| --head "$HEAD_SHA" | |
| --superseded-head "$SUPERSEDED_HEAD_SHA" | |
| coordinate: | |
| if: ${{ github.run_attempt == 1 && github.repository == 'NVIDIA/NemoClaw' && ((github.event_name == 'workflow_run' && github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.path == '.github/workflows/pr.yaml' && endsWith(github.event.workflow_run.display_title, ' gate true')) || (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main' && inputs.operation == 'approve-e2e')) }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 330 | |
| permissions: | |
| actions: write | |
| checks: write | |
| contents: read | |
| pull-requests: read | |
| concurrency: | |
| group: pr-e2e-gate-${{ github.repository }}-${{ github.event_name == 'workflow_run' && github.event.workflow_run.pull_requests[0].number || inputs.pr_number }}-${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_sha || inputs.expected_head_sha }}-${{ github.event_name == 'workflow_run' && github.event.workflow_run.pull_requests[0].base.sha || inputs.expected_base_sha }} | |
| queue: max | |
| # Let the previous coordinator observe E2E cancellation and close its check. | |
| cancel-in-progress: false | |
| steps: | |
| - name: Checkout controller | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| ref: ${{ github.workflow_sha }} | |
| persist-credentials: false | |
| - name: Setup Node | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| node-version: "22" | |
| - name: Install controller dependencies | |
| run: npm ci --ignore-scripts --no-audit --no-fund | |
| - id: workspace | |
| name: Create private workspace | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| work_dir="$(mktemp -d "${RUNNER_TEMP}/nemoclaw-pr-e2e-gate.XXXXXX")" | |
| chmod 700 "$work_dir" | |
| printf 'work_dir=%s\n' "$work_dir" >> "$GITHUB_OUTPUT" | |
| - id: start | |
| name: Start evaluation | |
| env: | |
| CI_CONCLUSION: ${{ github.event.workflow_run.conclusion }} | |
| CI_DISPLAY_TITLE: ${{ github.event.workflow_run.display_title }} | |
| CI_RUN_ATTEMPT: ${{ github.event.workflow_run.run_attempt }} | |
| CI_RUN_ID: ${{ github.event.workflow_run.id }} | |
| GATE_RUN_ID: ${{ github.run_id }} | |
| GITHUB_TOKEN: ${{ github.token }} | |
| EVENT_NAME: ${{ github.event_name }} | |
| HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }} | |
| HEAD_REPOSITORY: ${{ github.event.workflow_run.head_repository.full_name }} | |
| HEAD_SHA: ${{ github.event.workflow_run.head_sha }} | |
| MAINTAINER: ${{ github.triggering_actor }} | |
| MANUAL_BASE_SHA: ${{ inputs.expected_base_sha }} | |
| MANUAL_HEAD_SHA: ${{ inputs.expected_head_sha }} | |
| MANUAL_PR_NUMBER: ${{ inputs.pr_number }} | |
| PR_NUMBER: ${{ github.event.workflow_run.pull_requests[0].number }} | |
| REVIEW_REASON: ${{ inputs.review_reason }} | |
| WORKFLOW_SHA: ${{ github.workflow_sha }} | |
| WORKFLOW_RUN_ATTEMPT: ${{ github.run_attempt }} | |
| WORK_DIR: ${{ steps.workspace.outputs.work_dir }} | |
| run: | | |
| set -euo pipefail | |
| if [ "$EVENT_NAME" = "workflow_dispatch" ]; then | |
| node --experimental-strip-types tools/e2e/pr-e2e-gate.mts \ | |
| --mode approve-e2e \ | |
| --pr "$MANUAL_PR_NUMBER" \ | |
| --head "$MANUAL_HEAD_SHA" \ | |
| --base "$MANUAL_BASE_SHA" \ | |
| --workflow-sha "$WORKFLOW_SHA" \ | |
| --maintainer "$MAINTAINER" \ | |
| --reason "$REVIEW_REASON" \ | |
| --gate-run-id "$GATE_RUN_ID" \ | |
| --workflow-run-attempt "$WORKFLOW_RUN_ATTEMPT" \ | |
| --work-dir "$WORK_DIR" | |
| else | |
| node --experimental-strip-types tools/e2e/pr-e2e-gate.mts \ | |
| --mode start \ | |
| --head "$HEAD_SHA" \ | |
| --head-repo "$HEAD_REPOSITORY" \ | |
| --head-branch "$HEAD_BRANCH" \ | |
| --workflow-sha "$WORKFLOW_SHA" \ | |
| --ci-conclusion "$CI_CONCLUSION" \ | |
| --ci-display-title "$CI_DISPLAY_TITLE" \ | |
| --ci-run-attempt "$CI_RUN_ATTEMPT" \ | |
| --ci-run-id "$CI_RUN_ID" \ | |
| --gate-run-id "$GATE_RUN_ID" \ | |
| --pr "$PR_NUMBER" \ | |
| --work-dir "$WORK_DIR" | |
| fi | |
| - name: Upload risk plan | |
| if: ${{ always() && steps.workspace.outputs.work_dir != '' }} | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: pr-e2e-risk-plan-${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_sha || inputs.expected_head_sha }} | |
| path: ${{ steps.workspace.outputs.work_dir }}/risk-plan.json | |
| if-no-files-found: ignore | |
| retention-days: 14 | |
| - id: wait | |
| name: Wait for E2E run | |
| if: ${{ steps.start.outputs.dispatched == 'true' }} | |
| continue-on-error: true | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: >- | |
| node --experimental-strip-types tools/e2e/pr-e2e-gate.mts | |
| --mode wait | |
| --run-id "${{ steps.start.outputs.run_id }}" | |
| - id: evidence | |
| name: Download evidence | |
| if: ${{ always() && steps.start.outputs.dispatched == 'true' }} | |
| continue-on-error: true | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: >- | |
| node --experimental-strip-types tools/e2e/pr-e2e-gate.mts | |
| --mode download | |
| --work-dir "${{ steps.workspace.outputs.work_dir }}" | |
| --run-id "${{ steps.start.outputs.run_id }}" | |
| - id: finish | |
| name: Verify evidence | |
| if: ${{ always() && steps.start.outputs.dispatched == 'true' }} | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: >- | |
| node --experimental-strip-types tools/e2e/pr-e2e-gate.mts | |
| --mode finish | |
| --work-dir "${{ steps.workspace.outputs.work_dir }}" | |
| --state-hash "${{ steps.start.outputs.state_hash }}" | |
| --check-id "${{ steps.start.outputs.check_id }}" | |
| --run-id "${{ steps.start.outputs.run_id }}" | |
| --evidence-outcome "${{ steps.evidence.outcome }}" | |
| - id: retry | |
| name: Retry after hosted runner loss | |
| if: ${{ always() && steps.finish.outputs.runner_loss_retry_authorized == 'true' && github.run_attempt == 1 }} | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: >- | |
| node --experimental-strip-types tools/e2e/pr-e2e-gate.mts | |
| --mode retry-runner-loss | |
| --work-dir "${{ steps.workspace.outputs.work_dir }}" | |
| --state-hash "${{ steps.start.outputs.state_hash }}" | |
| --check-id "${{ steps.start.outputs.check_id }}" | |
| --run-id "${{ steps.start.outputs.run_id }}" | |
| --workflow-run-attempt "${{ github.run_attempt }}" | |
| - id: retry_wait | |
| name: Wait for retry E2E run | |
| if: ${{ steps.retry.outputs.dispatched == 'true' }} | |
| continue-on-error: true | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: >- | |
| node --experimental-strip-types tools/e2e/pr-e2e-gate.mts | |
| --mode wait | |
| --run-id "${{ steps.retry.outputs.run_id }}" | |
| - id: retry_evidence | |
| name: Download retry evidence | |
| if: ${{ always() && steps.retry.outputs.dispatched == 'true' }} | |
| continue-on-error: true | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: >- | |
| node --experimental-strip-types tools/e2e/pr-e2e-gate.mts | |
| --mode download | |
| --slot runner-loss-retry | |
| --work-dir "${{ steps.workspace.outputs.work_dir }}" | |
| --run-id "${{ steps.retry.outputs.run_id }}" | |
| - id: retry_finish | |
| name: Verify retry evidence | |
| if: ${{ always() && steps.retry.outputs.dispatched == 'true' }} | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: >- | |
| node --experimental-strip-types tools/e2e/pr-e2e-gate.mts | |
| --mode finish | |
| --slot runner-loss-retry | |
| --work-dir "${{ steps.workspace.outputs.work_dir }}" | |
| --state-hash "${{ steps.retry.outputs.state_hash }}" | |
| --check-id "${{ steps.retry.outputs.check_id }}" | |
| --run-id "${{ steps.retry.outputs.run_id }}" | |
| --evidence-outcome "${{ steps.retry_evidence.outcome }}" | |
| - name: Close incomplete retry check | |
| if: ${{ always() && steps.retry.outputs.check_id != '' && steps.retry.outputs.finalized != 'true' && steps.retry_finish.outputs.finalized != 'true' }} | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: >- | |
| node --experimental-strip-types tools/e2e/pr-e2e-gate.mts | |
| --mode abandon | |
| --check-id "${{ steps.retry.outputs.check_id }}" | |
| --run-id "${{ steps.retry.outputs.run_id }}" | |
| - name: Terminalize interrupted retry setup | |
| if: ${{ always() && steps.finish.outputs.runner_loss_retry_authorized == 'true' && steps.retry.outcome != 'success' && steps.retry.outputs.check_id == '' }} | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: >- | |
| node --experimental-strip-types tools/e2e/pr-e2e-gate.mts | |
| --mode abandon-runner-loss-retry | |
| --check-id "${{ steps.start.outputs.check_id }}" | |
| --run-id "${{ steps.start.outputs.run_id }}" | |
| --workflow-run-attempt "${{ github.run_attempt }}" | |
| - name: Close incomplete check | |
| if: ${{ always() && steps.start.outputs.check_id != '' && steps.start.outputs.finalized != 'true' && steps.finish.outputs.finalized != 'true' }} | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: >- | |
| node --experimental-strip-types tools/e2e/pr-e2e-gate.mts | |
| --mode abandon | |
| --check-id "${{ steps.start.outputs.check_id }}" | |
| --run-id "${{ steps.start.outputs.run_id }}" | |
| - name: Remove private workspace | |
| if: ${{ always() && steps.workspace.outputs.work_dir != '' }} | |
| run: rm -rf -- "${{ steps.workspace.outputs.work_dir }}" |