nightly-e2e #19
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 | |
| # | |
| # Nightly E2E tests: | |
| # | |
| # cloud-e2e Cloud inference (NVIDIA Endpoint API) on ubuntu-latest. | |
| # cloud-experimental-e2e Experimental cloud inference test (main script skips embedded | |
| # check-docs + final cleanup; follow-up steps run check-docs, | |
| # skip/05-network-policy.sh, then cleanup.sh --verify with if: always()). | |
| # messaging-providers-e2e Validates messaging credential provider/placeholder/L7-proxy chain | |
| # for Telegram + Discord. Uses fake tokens. See PR #1081. | |
| # token-rotation-e2e Validates that rotating a messaging token and re-running onboard | |
| # propagates the new credential to the sandbox. Combined Telegram + | |
| # Discord coverage with cross-talk assertions. See issue #1903. | |
| # sandbox-survival-e2e Sandbox survival across gateway restarts (onboard, inference, | |
| # gateway stop/start, verify sandbox + workspace + inference). | |
| # hermes-e2e Hermes Agent E2E — install → onboard --agent hermes → health | |
| # probe → live inference. Validates the multi-agent architecture. | |
| # skip-permissions-e2e Validates --dangerously-skip-permissions activates the permissive | |
| # policy (not stuck in Pending) and sandbox egress works (not 403). | |
| # gpu-e2e Local Ollama inference on a GPU self-hosted runner. | |
| # Controlled by the GPU_E2E_ENABLED repository variable. | |
| # Set vars.GPU_E2E_ENABLED to "true" in repo settings to enable. | |
| # notify-on-failure Auto-creates a GitHub issue when any E2E job fails. | |
| # | |
| # Runs directly on the runner (not inside Docker) because OpenShell bootstraps | |
| # a K3s cluster inside a privileged Docker container — nesting would break networking. | |
| # | |
| # NVIDIA_API_KEY for cloud-e2e and cloud-experimental-e2e: | |
| # - Repository secret: Settings → Secrets and variables → Actions → Repository secrets. | |
| # - Environment secret: only available if the job sets `environment: <that environment name>`. | |
| # (Storing the key under Environments / NVIDIA_API_KEY without `environment:` here leaves the | |
| # variable empty in the job — repository secrets and environment secrets are separate.) | |
| # Only runs on schedule and manual dispatch — never on PRs (secret protection). | |
| name: nightly-e2e | |
| on: | |
| schedule: | |
| - cron: "0 0 * * *" | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: nightly-e2e-${{ github.event_name }}-${{ github.event_name == 'workflow_dispatch' && github.ref || 'schedule' }} | |
| cancel-in-progress: true | |
| jobs: | |
| cloud-e2e: | |
| if: github.repository == 'NVIDIA/NemoClaw' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Run cloud E2E test | |
| env: | |
| NVIDIA_API_KEY: ${{ secrets.NVIDIA_API_KEY }} | |
| NEMOCLAW_NON_INTERACTIVE: "1" | |
| NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE: "1" | |
| NEMOCLAW_SANDBOX_NAME: "e2e-nightly" | |
| NEMOCLAW_RECREATE_SANDBOX: "1" | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: bash test/e2e/test-full-e2e.sh | |
| - name: Upload install log on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: install-log | |
| path: /tmp/nemoclaw-e2e-install.log | |
| if-no-files-found: ignore | |
| cloud-experimental-e2e: | |
| # DISABLED: CLI/docs command reference drift causes check-docs failures. | |
| # Landlock fix landed in OpenShell#810 (v0.0.32+). Re-enable once | |
| # docs-drift is resolved. | |
| if: github.repository == 'NVIDIA/NemoClaw' && vars.CLOUD_EXPERIMENTAL_E2E_ENABLED == 'true' | |
| runs-on: ubuntu-latest | |
| # Main suite + check-docs + network-policy skip script can exceed 45m on cold runners. | |
| timeout-minutes: 90 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| # Split Phase 5f (check-docs) and Phase 6 (cleanup) out of the main script so CI shows | |
| # failures in dedicated steps; tear-down always runs last (if: always()). | |
| - name: Run cloud-experimental E2E test | |
| env: | |
| NVIDIA_API_KEY: ${{ secrets.NVIDIA_API_KEY }} | |
| GITHUB_TOKEN: ${{ github.token }} | |
| # Non-interactive install (expect-driven Phase 3 optional). Runner has no expect; Phase 5e TUI skips if expect is absent. | |
| RUN_E2E_CLOUD_EXPERIMENTAL_INTERACTIVE_INSTALL: "0" | |
| NEMOCLAW_NON_INTERACTIVE: "1" | |
| NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE: "1" | |
| NEMOCLAW_RECREATE_SANDBOX: "1" | |
| NEMOCLAW_POLICY_MODE: "custom" | |
| NEMOCLAW_POLICY_PRESETS: "npm,pypi" | |
| RUN_E2E_CLOUD_EXPERIMENTAL_SKIP_FINAL_CLEANUP: "1" | |
| RUN_E2E_CLOUD_EXPERIMENTAL_SKIP_CHECK_DOCS: "1" | |
| run: bash test/e2e/test-e2e-cloud-experimental.sh | |
| - name: Documentation checks (check-docs.sh) | |
| if: always() | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: | | |
| set -euo pipefail | |
| if [ -f "$HOME/.bashrc" ]; then | |
| # shellcheck source=/dev/null | |
| source "$HOME/.bashrc" 2>/dev/null || true | |
| fi | |
| export NVM_DIR="${NVM_DIR:-$HOME/.nvm}" | |
| if [ -s "$NVM_DIR/nvm.sh" ]; then | |
| # shellcheck source=/dev/null | |
| . "$NVM_DIR/nvm.sh" | |
| fi | |
| if [ -d "$HOME/.local/bin" ] && [[ ":$PATH:" != *":$HOME/.local/bin:"* ]]; then | |
| export PATH="$HOME/.local/bin:$PATH" | |
| fi | |
| bash test/e2e/e2e-cloud-experimental/check-docs.sh | |
| - name: Network policy checks (skip/05-network-policy.sh) | |
| if: always() | |
| env: | |
| NVIDIA_API_KEY: ${{ secrets.NVIDIA_API_KEY }} | |
| GITHUB_TOKEN: ${{ github.token }} | |
| SANDBOX_NAME: e2e-cloud-experimental | |
| NEMOCLAW_SANDBOX_NAME: e2e-cloud-experimental | |
| run: | | |
| set -euo pipefail | |
| if [ -f "$HOME/.bashrc" ]; then | |
| # shellcheck source=/dev/null | |
| source "$HOME/.bashrc" 2>/dev/null || true | |
| fi | |
| export NVM_DIR="${NVM_DIR:-$HOME/.nvm}" | |
| if [ -s "$NVM_DIR/nvm.sh" ]; then | |
| # shellcheck source=/dev/null | |
| . "$NVM_DIR/nvm.sh" | |
| fi | |
| if [ -d "$HOME/.local/bin" ] && [[ ":$PATH:" != *":$HOME/.local/bin:"* ]]; then | |
| export PATH="$HOME/.local/bin:$PATH" | |
| fi | |
| bash test/e2e/e2e-cloud-experimental/skip/05-network-policy.sh | |
| - name: Tear down cloud-experimental sandbox (always) | |
| if: always() | |
| env: | |
| SANDBOX_NAME: e2e-cloud-experimental | |
| NEMOCLAW_SANDBOX_NAME: e2e-cloud-experimental | |
| run: | | |
| set -euo pipefail | |
| if [ -f "$HOME/.bashrc" ]; then | |
| # shellcheck source=/dev/null | |
| source "$HOME/.bashrc" 2>/dev/null || true | |
| fi | |
| export NVM_DIR="${NVM_DIR:-$HOME/.nvm}" | |
| if [ -s "$NVM_DIR/nvm.sh" ]; then | |
| # shellcheck source=/dev/null | |
| . "$NVM_DIR/nvm.sh" | |
| fi | |
| if [ -d "$HOME/.local/bin" ] && [[ ":$PATH:" != *":$HOME/.local/bin:"* ]]; then | |
| export PATH="$HOME/.local/bin:$PATH" | |
| fi | |
| bash test/e2e/e2e-cloud-experimental/cleanup.sh --verify | |
| - name: Upload install log on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: install-log-cloud-experimental | |
| path: /tmp/nemoclaw-e2e-cloud-experimental-install.log | |
| if-no-files-found: ignore | |
| # ── Messaging Providers E2E ────────────────────────────────── | |
| # Validates the full provider/placeholder/L7-proxy chain for messaging | |
| # credentials (Telegram, Discord). Uses fake tokens by default — the L7 | |
| # proxy rewrites placeholders and the real API returns 401, proving the | |
| # chain works. See: PR #1081 | |
| messaging-providers-e2e: | |
| if: github.repository == 'NVIDIA/NemoClaw' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Run messaging providers E2E test | |
| env: | |
| NVIDIA_API_KEY: ${{ secrets.NVIDIA_API_KEY }} | |
| NEMOCLAW_NON_INTERACTIVE: "1" | |
| NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE: "1" | |
| NEMOCLAW_POLICY_TIER: "open" | |
| NEMOCLAW_SANDBOX_NAME: "e2e-msg-provider" | |
| GITHUB_TOKEN: ${{ github.token }} | |
| TELEGRAM_BOT_TOKEN: "test-fake-telegram-token-e2e" | |
| DISCORD_BOT_TOKEN: "test-fake-discord-token-e2e" | |
| run: bash test/e2e/test-messaging-providers.sh | |
| - name: Upload install log on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: install-log-messaging-providers | |
| path: /tmp/nemoclaw-e2e-install.log | |
| if-no-files-found: ignore | |
| # ── Token rotation (credential propagation to L7 proxy) ───── | |
| # Validates that rotating a messaging token and re-running onboard | |
| # propagates the new credential to the sandbox. Uses two fake tokens | |
| # per provider (Telegram + Discord) to prove the sandbox is rebuilt on | |
| # rotation and reused when unchanged. | |
| # See: issue #1903 | |
| token-rotation-e2e: | |
| if: github.repository == 'NVIDIA/NemoClaw' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Run token rotation E2E test | |
| env: | |
| NVIDIA_API_KEY: ${{ secrets.NVIDIA_API_KEY }} | |
| NEMOCLAW_NON_INTERACTIVE: "1" | |
| NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE: "1" | |
| NEMOCLAW_POLICY_TIER: "open" | |
| GITHUB_TOKEN: ${{ github.token }} | |
| TELEGRAM_BOT_TOKEN_A: "test-fake-token-A-rotation-e2e" | |
| TELEGRAM_BOT_TOKEN_B: "test-fake-token-B-rotation-e2e" | |
| DISCORD_BOT_TOKEN_A: "test-fake-discord-A-rotation-e2e" | |
| DISCORD_BOT_TOKEN_B: "test-fake-discord-B-rotation-e2e" | |
| run: bash test/e2e/test-token-rotation.sh | |
| - name: Upload install log on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: install-log-token-rotation | |
| path: /tmp/nemoclaw-e2e-install.log | |
| if-no-files-found: ignore | |
| # ── Sandbox survival (gateway restart recovery) ────────────── | |
| sandbox-survival-e2e: | |
| if: github.repository == 'NVIDIA/NemoClaw' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Run sandbox survival E2E test | |
| env: | |
| NVIDIA_API_KEY: ${{ secrets.NVIDIA_API_KEY }} | |
| NEMOCLAW_NON_INTERACTIVE: "1" | |
| NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE: "1" | |
| NEMOCLAW_SANDBOX_NAME: "e2e-survival" | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: bash test/e2e/test-sandbox-survival.sh | |
| - name: Upload install log on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: sandbox-survival-install-log | |
| path: /tmp/nemoclaw-e2e-install.log | |
| if-no-files-found: ignore | |
| # ── Hermes Agent E2E ───────────────────────────────────────── | |
| # Validates the multi-agent architecture by onboarding with --agent hermes, | |
| # verifying the Hermes health probe, and running live inference through the | |
| # Hermes sandbox. See: PR #1618 | |
| hermes-e2e: | |
| if: github.repository == 'NVIDIA/NemoClaw' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Run Hermes Agent E2E test | |
| env: | |
| NVIDIA_API_KEY: ${{ secrets.NVIDIA_API_KEY }} | |
| NEMOCLAW_NON_INTERACTIVE: "1" | |
| NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE: "1" | |
| NEMOCLAW_SANDBOX_NAME: "e2e-hermes" | |
| NEMOCLAW_RECREATE_SANDBOX: "1" | |
| NEMOCLAW_AGENT: "hermes" | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: bash test/e2e/test-hermes-e2e.sh | |
| - name: Upload install log on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: hermes-e2e-install-log | |
| path: /tmp/nemoclaw-e2e-hermes-install.log | |
| if-no-files-found: ignore | |
| # ── Skip-permissions policy activation ──────────────────────── | |
| # Validates the --dangerously-skip-permissions onboard flag correctly | |
| # activates the permissive network policy (not stuck in Pending). | |
| # Reproduces the exact scenario from the bug report: onboard, verify | |
| # policy is Active, curl from inside sandbox must not return 403. | |
| skip-permissions-e2e: | |
| if: github.repository == 'NVIDIA/NemoClaw' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Run skip-permissions policy E2E test | |
| env: | |
| NVIDIA_API_KEY: ${{ secrets.NVIDIA_API_KEY }} | |
| NEMOCLAW_NON_INTERACTIVE: "1" | |
| NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE: "1" | |
| NEMOCLAW_SANDBOX_NAME: "e2e-skip-perms" | |
| NEMOCLAW_DANGEROUSLY_SKIP_PERMISSIONS: "1" | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: bash test/e2e/test-skip-permissions-policy.sh | |
| - name: Upload install log on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: skip-permissions-install-log | |
| path: /tmp/nemoclaw-e2e-install.log | |
| if-no-files-found: ignore | |
| # ── Sandbox operations (recovery + multi-sandbox isolation) ── | |
| # Validates sandbox list, connect, status, logs, destroy, gateway | |
| # auto-recovery after docker kill, registry rebuild, process recovery, | |
| # multi-sandbox metadata, and cross-sandbox network isolation. | |
| sandbox-operations-e2e: | |
| if: github.repository == 'NVIDIA/NemoClaw' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Run sandbox operations E2E test | |
| env: | |
| NVIDIA_API_KEY: ${{ secrets.NVIDIA_API_KEY }} | |
| NEMOCLAW_NON_INTERACTIVE: "1" | |
| NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE: "1" | |
| NEMOCLAW_POLICY_TIER: "open" | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: bash test/e2e/test-sandbox-operations.sh | |
| - name: Upload test log on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: sandbox-operations-test-log | |
| path: test-sandbox-operations-*.log | |
| if-no-files-found: ignore | |
| # ── Inference routing (credential isolation + error classification) ── | |
| # TC-INF-05: real API key absent from sandbox env/process/filesystem | |
| # TC-INF-06: invalid API key → classified credential error (PR-safe) | |
| # TC-INF-07: unreachable endpoint → classified transport error (PR-safe) | |
| inference-routing-e2e: | |
| if: github.repository == 'NVIDIA/NemoClaw' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Run inference error classification E2E test | |
| env: | |
| NVIDIA_API_KEY: ${{ secrets.NVIDIA_API_KEY }} | |
| NEMOCLAW_NON_INTERACTIVE: "1" | |
| NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE: "1" | |
| NEMOCLAW_POLICY_TIER: "open" | |
| run: bash test/e2e/test-inference-routing.sh | |
| - name: Upload test log on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: inference-routing-test-log | |
| path: test-inference-routing-*.log | |
| if-no-files-found: ignore | |
| # ── Network policy E2E ─────────────────────────────────────── | |
| # TC-NET-01..07, TC-NET-09: deny-by-default, whitelist, live policy-add, | |
| # dry-run, hot-reload, inference exemption, permissive mode, SSRF validation. | |
| network-policy-e2e: | |
| if: github.repository == 'NVIDIA/NemoClaw' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Run network policy E2E test | |
| env: | |
| NVIDIA_API_KEY: ${{ secrets.NVIDIA_API_KEY }} | |
| NEMOCLAW_NON_INTERACTIVE: "1" | |
| NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE: "1" | |
| NEMOCLAW_POLICY_TIER: "restricted" | |
| NEMOCLAW_RECREATE_SANDBOX: "1" | |
| run: bash test/e2e/test-network-policy.sh | |
| - name: Upload test log on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: network-policy-test-log | |
| path: test-network-policy-*.log | |
| if-no-files-found: ignore | |
| # ── Deployment & Services E2E ──────────────────────────────── | |
| # TC-STATE-02: backup-workspace.sh lifecycle (backup → destroy → restore) | |
| # TC-DEPLOY-01: nemoclaw start/stop (cloudflared tunnel) | |
| # TC-DEPLOY-03: uninstall --keep-openshell (destructive, runs last in script) | |
| deployment-services-e2e: | |
| if: github.repository == 'NVIDIA/NemoClaw' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Run deployment services E2E test | |
| env: | |
| NVIDIA_API_KEY: ${{ secrets.NVIDIA_API_KEY }} | |
| NEMOCLAW_NON_INTERACTIVE: "1" | |
| NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE: "1" | |
| NEMOCLAW_SANDBOX_NAME: "e2e-deploy-svc" | |
| run: bash test/e2e/test-deployment-services.sh | |
| - name: Upload test log on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: deployment-services-test-log | |
| path: test-deployment-services-*.log | |
| if-no-files-found: ignore | |
| # ── Diagnostics E2E ───────────────────────────────────────── | |
| # TC-DIAG-04: nemoclaw --version, TC-DIAG-02: debug --quick, | |
| # TC-DIAG-01: debug tarball + credential sanitization, | |
| # TC-DIAG-05: sandbox config, TC-DIAG-03: credentials list | |
| diagnostics-e2e: | |
| if: github.repository == 'NVIDIA/NemoClaw' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Run diagnostics E2E test | |
| env: | |
| NVIDIA_API_KEY: ${{ secrets.NVIDIA_API_KEY }} | |
| NEMOCLAW_NON_INTERACTIVE: "1" | |
| NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE: "1" | |
| NEMOCLAW_RECREATE_SANDBOX: "1" | |
| run: bash test/e2e/test-diagnostics.sh | |
| - name: Upload test log on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: diagnostics-test-log | |
| path: test-diagnostics-*.log | |
| if-no-files-found: ignore | |
| # ── Snapshot commands E2E ──────────────────────────────────── | |
| # Validates snapshot create/list/restore lifecycle: create a snapshot, | |
| # list it, delete state, restore from snapshot, verify state recovered. | |
| snapshot-commands-e2e: | |
| if: github.repository == 'NVIDIA/NemoClaw' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Run snapshot commands E2E test | |
| env: | |
| NVIDIA_API_KEY: ${{ secrets.NVIDIA_API_KEY }} | |
| NEMOCLAW_NON_INTERACTIVE: "1" | |
| NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE: "1" | |
| NEMOCLAW_SANDBOX_NAME: "e2e-snapshot" | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: bash test/e2e/test-snapshot-commands.sh | |
| - name: Upload install log on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: snapshot-commands-install-log | |
| path: /tmp/nemoclaw-e2e-install.log | |
| if-no-files-found: ignore | |
| # ── Shields & config lifecycle E2E ─────────────────────────── | |
| # Validates shields down/up controls config mutability, config get/set/ | |
| # rotate-token, audit trail, and auto-restore timer. | |
| shields-config-e2e: | |
| if: github.repository == 'NVIDIA/NemoClaw' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Run shields & config E2E test | |
| env: | |
| NVIDIA_API_KEY: ${{ secrets.NVIDIA_API_KEY }} | |
| NEMOCLAW_NON_INTERACTIVE: "1" | |
| NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE: "1" | |
| NEMOCLAW_SANDBOX_NAME: "e2e-shields" | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: bash test/e2e/test-shields-config.sh | |
| - name: Upload install log on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: shields-config-install-log | |
| path: /tmp/nemoclaw-e2e-shields-install.log | |
| if-no-files-found: ignore | |
| # ── OpenClaw rebuild upgrade E2E ───────────────────────────── | |
| # Reproduces NVBug 6076156: onboard with an older OpenClaw version, | |
| # then rebuild to verify workspace state survives the upgrade. | |
| rebuild-openclaw-e2e: | |
| if: github.repository == 'NVIDIA/NemoClaw' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Run OpenClaw rebuild upgrade E2E test | |
| env: | |
| NVIDIA_API_KEY: ${{ secrets.NVIDIA_API_KEY }} | |
| NEMOCLAW_NON_INTERACTIVE: "1" | |
| NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE: "1" | |
| NEMOCLAW_SANDBOX_NAME: "e2e-rebuild-oc" | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: bash test/e2e/test-rebuild-openclaw.sh | |
| - name: Upload install log on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: rebuild-openclaw-install-log | |
| path: /tmp/nemoclaw-e2e-install.log | |
| if-no-files-found: ignore | |
| # ── Issue #1904: stale sandbox after NemoClaw upgrade ──────── | |
| # Exact reproduction of the reporter's scenario: install an older | |
| # NemoClaw, create a sandbox, upgrade to current, verify the old | |
| # sandbox is detected as stale and rebuilt with the new image. | |
| upgrade-stale-sandbox-e2e: | |
| if: github.repository == 'NVIDIA/NemoClaw' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Run upgrade stale sandbox E2E test | |
| env: | |
| NVIDIA_API_KEY: ${{ secrets.NVIDIA_API_KEY }} | |
| NEMOCLAW_NON_INTERACTIVE: "1" | |
| NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE: "1" | |
| NEMOCLAW_SANDBOX_NAME: "e2e-upgrade-stale" | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: bash test/e2e/test-upgrade-stale-sandbox.sh | |
| - name: Upload install logs on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: upgrade-stale-sandbox-logs | |
| path: | | |
| /tmp/nemoclaw-e2e-old-install.log | |
| /tmp/nemoclaw-e2e-upgrade-install.log | |
| if-no-files-found: ignore | |
| # ── Hermes rebuild upgrade E2E ────────────────────────────── | |
| # Same upgrade scenario as OpenClaw but for Hermes Agent. | |
| rebuild-hermes-e2e: | |
| if: github.repository == 'NVIDIA/NemoClaw' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Run Hermes rebuild upgrade E2E test | |
| env: | |
| NVIDIA_API_KEY: ${{ secrets.NVIDIA_API_KEY }} | |
| NEMOCLAW_NON_INTERACTIVE: "1" | |
| NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE: "1" | |
| NEMOCLAW_SANDBOX_NAME: "e2e-rebuild-hm" | |
| NEMOCLAW_AGENT: "hermes" | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: bash test/e2e/test-rebuild-hermes.sh | |
| - name: Upload install log on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: rebuild-hermes-install-log | |
| path: /tmp/nemoclaw-e2e-install.log | |
| if-no-files-found: ignore | |
| # ── GPU E2E (Ollama local inference) ────────────────────────── | |
| # Enable by setting repository variable GPU_E2E_ENABLED=true | |
| # (Settings → Secrets and variables → Actions → Variables) | |
| # | |
| # Runner labels: using 'self-hosted' for now. Refine to | |
| # [self-hosted, linux, x64, gpu] once NVIDIA runner labels are confirmed. | |
| gpu-e2e: | |
| if: github.repository == 'NVIDIA/NemoClaw' && vars.GPU_E2E_ENABLED == 'true' | |
| runs-on: self-hosted | |
| timeout-minutes: 60 | |
| env: | |
| NEMOCLAW_NON_INTERACTIVE: "1" | |
| NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE: "1" | |
| NEMOCLAW_SANDBOX_NAME: "e2e-gpu-ollama" | |
| NEMOCLAW_RECREATE_SANDBOX: "1" | |
| NEMOCLAW_PROVIDER: "ollama" | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Verify GPU availability | |
| run: | | |
| echo "=== GPU Info ===" | |
| nvidia-smi | |
| echo "" | |
| echo "=== VRAM ===" | |
| nvidia-smi --query-gpu=name,memory.total --format=csv,noheader | |
| echo "" | |
| echo "=== Docker ===" | |
| docker info --format '{{.ServerVersion}}' | |
| - name: Run GPU E2E test (Ollama local inference) | |
| run: bash test/e2e/test-gpu-e2e.sh | |
| - name: Upload install log on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: gpu-e2e-install-log | |
| path: /tmp/nemoclaw-gpu-e2e-install.log | |
| if-no-files-found: ignore | |
| - name: Upload test log on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: gpu-e2e-test-log | |
| path: /tmp/nemoclaw-gpu-e2e-test.log | |
| if-no-files-found: ignore | |
| notify-on-failure: | |
| runs-on: ubuntu-latest | |
| needs: | |
| [ | |
| cloud-e2e, | |
| cloud-experimental-e2e, | |
| messaging-providers-e2e, | |
| token-rotation-e2e, | |
| sandbox-survival-e2e, | |
| hermes-e2e, | |
| skip-permissions-e2e, | |
| sandbox-operations-e2e, | |
| inference-routing-e2e, | |
| network-policy-e2e, | |
| deployment-services-e2e, | |
| diagnostics-e2e, | |
| snapshot-commands-e2e, | |
| shields-config-e2e, | |
| rebuild-openclaw-e2e, | |
| upgrade-stale-sandbox-e2e, | |
| rebuild-hermes-e2e, | |
| gpu-e2e, | |
| ] | |
| if: ${{ always() && (contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')) }} | |
| permissions: | |
| issues: write | |
| steps: | |
| - name: Create or update failure issue | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; | |
| const title = 'Nightly E2E failed'; | |
| const needs = ${{ toJSON(needs) }}; | |
| const failed = Object.entries(needs).filter(([, v]) => v.result === 'failure').map(([k]) => k); | |
| const cancelled = Object.entries(needs).filter(([, v]) => v.result === 'cancelled').map(([k]) => k); | |
| const summary = [ | |
| failed.length ? `**Failed:** ${failed.join(', ')}` : '', | |
| cancelled.length ? `**Cancelled:** ${cancelled.join(', ')}` : '', | |
| ].filter(Boolean).join('\n'); | |
| const { data: existing } = await github.rest.issues.listForRepo({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| state: 'open', | |
| labels: 'CI/CD', | |
| per_page: 100, | |
| }); | |
| const match = existing.find(i => !i.pull_request && i.title.startsWith(title)); | |
| if (match) { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: match.number, | |
| body: `Failed again on ${new Date().toISOString().split('T')[0]}.\n\n**Run:** ${runUrl}\n${summary}\n**Artifacts:** Check the run artifacts for install/test logs (artifact names vary by job).`, | |
| }); | |
| } else { | |
| await github.rest.issues.create({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| title: `${title} — ${new Date().toISOString().split('T')[0]}`, | |
| body: `The nightly E2E pipeline failed.\n\n**Run:** ${runUrl}\n${summary}\n**Artifacts:** Check the run artifacts for install/test logs (artifact names vary by job).`, | |
| labels: ['bug', 'CI/CD'], | |
| }); | |
| } |