From 1a31a43174c700fae126ed32f2d20aaebf1b8dc4 Mon Sep 17 00:00:00 2001 From: Julie Yaunches Date: Tue, 23 Jun 2026 16:16:34 -0400 Subject: [PATCH 1/4] ci(e2e): make Jetson Vitest explicit-only --- .github/workflows/e2e-vitest-scenarios.yaml | 3 +- .../jetson-workflow-boundary.test.ts | 35 +++++++++++++++++++ tools/e2e-scenarios/workflow-boundary.mts | 21 +++++++++-- 3 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 test/e2e-scenario/support-tests/jetson-workflow-boundary.test.ts diff --git a/.github/workflows/e2e-vitest-scenarios.yaml b/.github/workflows/e2e-vitest-scenarios.yaml index a1e0551e91f..a247bc8155a 100644 --- a/.github/workflows/e2e-vitest-scenarios.yaml +++ b/.github/workflows/e2e-vitest-scenarios.yaml @@ -2761,7 +2761,8 @@ jobs: jetson-nvmap-gpu-vitest: needs: generate-matrix - if: ${{ (inputs.jobs == '' && inputs.scenarios == '') || contains(format(',{0},', inputs.jobs), ',jetson-nvmap-gpu-vitest,') || contains(format(',{0},', inputs.scenarios), ',jetson-nvmap-gpu,') }} + # Explicit-only until a stable Jetson runner is available; otherwise full-suite dispatches remain queued forever. + if: ${{ contains(format(',{0},', inputs.jobs), ',jetson-nvmap-gpu-vitest,') || contains(format(',{0},', inputs.scenarios), ',jetson-nvmap-gpu,') }} runs-on: ${{ vars.JETSON_E2E_RUNNER_LABEL || 'linux-arm64-gpu-jetson-orin-latest-1' }} timeout-minutes: 60 env: diff --git a/test/e2e-scenario/support-tests/jetson-workflow-boundary.test.ts b/test/e2e-scenario/support-tests/jetson-workflow-boundary.test.ts new file mode 100644 index 00000000000..6bb33224a62 --- /dev/null +++ b/test/e2e-scenario/support-tests/jetson-workflow-boundary.test.ts @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { describe, expect, it } from "vitest"; +import { + evaluateE2eVitestWorkflowDispatchSelectors, + readFreeStandingJobsInventory, + validateE2eVitestScenariosWorkflowBoundary, +} from "../../../tools/e2e-scenarios/workflow-boundary.mts"; + +describe("Jetson nvmap GPU Vitest workflow boundary", () => { + it("keeps Jetson selectable but excluded from full-suite dispatch", () => { + const inventory = readFreeStandingJobsInventory(); + expect(validateE2eVitestScenariosWorkflowBoundary()).toEqual([]); + expect(inventory.allowedJobs).toContain("jetson-nvmap-gpu-vitest"); + expect(inventory.scenarioToJob.get("jetson-nvmap-gpu")).toBe("jetson-nvmap-gpu-vitest"); + expect(evaluateE2eVitestWorkflowDispatchSelectors({}).selectedFreeStandingJobs).not.toContain( + "jetson-nvmap-gpu-vitest", + ); + }); + + it("runs Jetson only when explicitly selected", () => { + for (const selector of [ + { scenarios: "jetson-nvmap-gpu" }, + { jobs: "jetson-nvmap-gpu-vitest" }, + ]) { + expect(evaluateE2eVitestWorkflowDispatchSelectors(selector)).toMatchObject({ + valid: true, + liveScenariosRuns: false, + selectedFreeStandingJobs: ["jetson-nvmap-gpu-vitest"], + registryScenarios: [], + }); + } + }); +}); diff --git a/tools/e2e-scenarios/workflow-boundary.mts b/tools/e2e-scenarios/workflow-boundary.mts index 8f24e15caec..8f3c1471f11 100644 --- a/tools/e2e-scenarios/workflow-boundary.mts +++ b/tools/e2e-scenarios/workflow-boundary.mts @@ -49,7 +49,9 @@ const COMMON_SECRET_ENV_NAMES = [ const FREE_STANDING_SELECTOR_SPECIAL_CASES = new Set([ "hermes-e2e-vitest", "hermes-root-entrypoint-smoke-vitest", + "jetson-nvmap-gpu-vitest", ]); +const FULL_SUITE_EXCLUDED_FREE_STANDING_JOBS = new Set(["jetson-nvmap-gpu-vitest"]); function asRecord(value: unknown): WorkflowRecord { return value && typeof value === "object" && !Array.isArray(value) @@ -275,7 +277,9 @@ export function evaluateE2eVitestWorkflowDispatchSelectors(input: { return { valid: true, errors: [], - selectedFreeStandingJobs: [...freeStandingVitestJobIds].sort(), + selectedFreeStandingJobs: freeStandingVitestJobIds + .filter((job) => !FULL_SUITE_EXCLUDED_FREE_STANDING_JOBS.has(job)) + .sort(), registryScenarios: [], liveScenariosRuns: true, }; @@ -450,6 +454,13 @@ function freeStandingJobIf(jobName: string, scenarioName?: string): string { return `\${{ (inputs.jobs == '' && inputs.scenarios == '') || contains(format(',{0},', inputs.jobs), ',${jobName},')${scenarioSelector} }}`; } +function explicitOnlyFreeStandingJobIf(jobName: string, scenarioName?: string): string { + const scenarioSelector = scenarioName + ? ` || contains(format(',{0},', inputs.scenarios), ',${scenarioName},')` + : ""; + return `\${{ contains(format(',{0},', inputs.jobs), ',${jobName},')${scenarioSelector} }}`; +} + function validateFreeStandingJobSelector( errors: string[], jobs: WorkflowRecord, @@ -7749,7 +7760,13 @@ export function validateE2eVitestScenariosWorkflowBoundary( "gateway-health-honest", ); - validateFreeStandingJobSelector(errors, jobs, "jetson-nvmap-gpu-vitest", "jetson-nvmap-gpu"); + const jetsonJob = asRecord(jobs["jetson-nvmap-gpu-vitest"]); + if (jetsonJob.needs !== "generate-matrix") { + errors.push("jetson-nvmap-gpu-vitest job must depend on generate-matrix"); + } + if (jetsonJob.if !== explicitOnlyFreeStandingJobIf("jetson-nvmap-gpu-vitest", "jetson-nvmap-gpu")) { + errors.push("jetson-nvmap-gpu-vitest job must run only when explicitly selected"); + } validateFreeStandingJobSelector( errors, From e75bb9cf02d3c6df05bc3e51cc571fce7b3d2c61 Mon Sep 17 00:00:00 2001 From: Julie Yaunches Date: Tue, 23 Jun 2026 16:34:49 -0400 Subject: [PATCH 2/4] ci(e2e): clarify Jetson default reporting --- .github/workflows/e2e-vitest-scenarios.yaml | 4 ++-- .../jetson-workflow-boundary.test.ts | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/.github/workflows/e2e-vitest-scenarios.yaml b/.github/workflows/e2e-vitest-scenarios.yaml index a247bc8155a..1e0ce65afea 100644 --- a/.github/workflows/e2e-vitest-scenarios.yaml +++ b/.github/workflows/e2e-vitest-scenarios.yaml @@ -5600,7 +5600,7 @@ jobs: ? '✅ All requested jobs passed' : selectiveDispatch ? '✅ All selected jobs passed' - : '✅ All jobs passed'; + : '✅ All default jobs passed'; const status = failed.length > 0 || missingRequested.length > 0 ? '❌ Some jobs failed' @@ -5626,7 +5626,7 @@ jobs: ? '**Requested jobs:** _(selector rejected by workflow validation)_' : requestedJobs ? `**Requested jobs:** \`${requestedJobs}\`` - : '**Requested jobs:** _(default — all free-standing when no scenarios are requested)_', + : '**Requested jobs:** _(default — all default-enabled free-standing jobs; explicit-only jobs such as `jetson-nvmap-gpu-vitest` are skipped unless selected)_', `**Summary:** ${passed.length} passed, ${failed.length} failed, ${cancelled.length} cancelled, ${skipped.length} skipped`, '', '| Job | Result |', diff --git a/test/e2e-scenario/support-tests/jetson-workflow-boundary.test.ts b/test/e2e-scenario/support-tests/jetson-workflow-boundary.test.ts index 6bb33224a62..f6bd6313e4d 100644 --- a/test/e2e-scenario/support-tests/jetson-workflow-boundary.test.ts +++ b/test/e2e-scenario/support-tests/jetson-workflow-boundary.test.ts @@ -1,6 +1,9 @@ // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import fs from "node:fs"; +import path from "node:path"; + import { describe, expect, it } from "vitest"; import { evaluateE2eVitestWorkflowDispatchSelectors, @@ -8,6 +11,8 @@ import { validateE2eVitestScenariosWorkflowBoundary, } from "../../../tools/e2e-scenarios/workflow-boundary.mts"; +const WORKFLOW_PATH = path.join(process.cwd(), ".github/workflows/e2e-vitest-scenarios.yaml"); + describe("Jetson nvmap GPU Vitest workflow boundary", () => { it("keeps Jetson selectable but excluded from full-suite dispatch", () => { const inventory = readFreeStandingJobsInventory(); @@ -32,4 +37,14 @@ describe("Jetson nvmap GPU Vitest workflow boundary", () => { }); } }); + + it("reports default jobs without claiming explicit-only Jetson ran", () => { + const workflow = fs.readFileSync(WORKFLOW_PATH, "utf8"); + expect(workflow).not.toContain("All jobs passed"); + expect(workflow).not.toContain("default — all free-standing when no scenarios are requested"); + expect(workflow).toContain("All default jobs passed"); + expect(workflow).toContain( + "default — all default-enabled free-standing jobs; explicit-only jobs such as `jetson-nvmap-gpu-vitest` are skipped unless selected", + ); + }); }); From 386658d45ae253edfeca9221d08010d00064bf14 Mon Sep 17 00:00:00 2001 From: Julie Yaunches Date: Tue, 23 Jun 2026 16:45:45 -0400 Subject: [PATCH 3/4] ci(e2e): clarify Vitest job selector docs --- .github/workflows/e2e-vitest-scenarios.yaml | 2 +- .../jetson-workflow-boundary.test.ts | 13 +------- tools/e2e-scenarios/workflow-boundary.mts | 30 +++++++++++++++++-- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/.github/workflows/e2e-vitest-scenarios.yaml b/.github/workflows/e2e-vitest-scenarios.yaml index 1e0ce65afea..eab8fea6d53 100644 --- a/.github/workflows/e2e-vitest-scenarios.yaml +++ b/.github/workflows/e2e-vitest-scenarios.yaml @@ -12,7 +12,7 @@ on: default: "" type: string jobs: - description: "Optional comma-separated free-standing live Vitest job ids. Empty runs all jobs only when scenarios is also empty." + description: "Optional comma-separated free-standing live Vitest job ids. Empty runs default-enabled jobs only when scenarios is also empty; explicit-only jobs such as jetson-nvmap-gpu-vitest are skipped unless selected." required: false default: "" type: string diff --git a/test/e2e-scenario/support-tests/jetson-workflow-boundary.test.ts b/test/e2e-scenario/support-tests/jetson-workflow-boundary.test.ts index f6bd6313e4d..c1ed8db75c5 100644 --- a/test/e2e-scenario/support-tests/jetson-workflow-boundary.test.ts +++ b/test/e2e-scenario/support-tests/jetson-workflow-boundary.test.ts @@ -1,9 +1,6 @@ // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import fs from "node:fs"; -import path from "node:path"; - import { describe, expect, it } from "vitest"; import { evaluateE2eVitestWorkflowDispatchSelectors, @@ -11,8 +8,6 @@ import { validateE2eVitestScenariosWorkflowBoundary, } from "../../../tools/e2e-scenarios/workflow-boundary.mts"; -const WORKFLOW_PATH = path.join(process.cwd(), ".github/workflows/e2e-vitest-scenarios.yaml"); - describe("Jetson nvmap GPU Vitest workflow boundary", () => { it("keeps Jetson selectable but excluded from full-suite dispatch", () => { const inventory = readFreeStandingJobsInventory(); @@ -39,12 +34,6 @@ describe("Jetson nvmap GPU Vitest workflow boundary", () => { }); it("reports default jobs without claiming explicit-only Jetson ran", () => { - const workflow = fs.readFileSync(WORKFLOW_PATH, "utf8"); - expect(workflow).not.toContain("All jobs passed"); - expect(workflow).not.toContain("default — all free-standing when no scenarios are requested"); - expect(workflow).toContain("All default jobs passed"); - expect(workflow).toContain( - "default — all default-enabled free-standing jobs; explicit-only jobs such as `jetson-nvmap-gpu-vitest` are skipped unless selected", - ); + expect(validateE2eVitestScenariosWorkflowBoundary()).toEqual([]); }); }); diff --git a/tools/e2e-scenarios/workflow-boundary.mts b/tools/e2e-scenarios/workflow-boundary.mts index 8f3c1471f11..ced5fee0561 100644 --- a/tools/e2e-scenarios/workflow-boundary.mts +++ b/tools/e2e-scenarios/workflow-boundary.mts @@ -323,9 +323,12 @@ function requireInput( errors: string[], inputs: WorkflowRecord, name: string, -): void { - if (!Object.hasOwn(inputs, name)) +): WorkflowRecord { + if (!Object.hasOwn(inputs, name)) { errors.push(`workflow_dispatch missing input: ${name}`); + return {}; + } + return asRecord(inputs[name]); } function requireStep( @@ -7361,7 +7364,18 @@ export function validateE2eVitestScenariosWorkflowBoundary( const dispatchInputs = asRecord(workflowDispatch.inputs); requireInput(errors, dispatchInputs, "scenarios"); - requireInput(errors, dispatchInputs, "jobs"); + const jobsInput = requireInput(errors, dispatchInputs, "jobs"); + const jobsDescription = stringValue(jobsInput.description); + if (!jobsDescription.includes("default-enabled jobs")) { + errors.push( + "workflow_dispatch jobs input description must say empty dispatch runs default-enabled jobs", + ); + } + if (!jobsDescription.includes("explicit-only jobs")) { + errors.push( + "workflow_dispatch jobs input description must say explicit-only jobs are skipped unless selected", + ); + } if (Object.hasOwn(dispatchInputs, "test_filter")) { errors.push("workflow_dispatch must not expose legacy test_filter input"); } @@ -7870,6 +7884,16 @@ export function validateE2eVitestScenariosWorkflowBoundary( "step 'Post Vitest scenario results to PR' run script must include **Requested scenarios:**", ); } + if (!reportScript.includes("All default jobs passed")) { + errors.push( + "step 'Post Vitest scenario results to PR' run script must label empty dispatch as default jobs passed", + ); + } + if (!reportScript.includes("default-enabled free-standing jobs")) { + errors.push( + "step 'Post Vitest scenario results to PR' run script must say empty dispatch uses default-enabled free-standing jobs", + ); + } for (const forbidden of [ "toJSON(inputs.pr_number)", "toJSON(inputs.scenarios)", From 41270206ae9bd3c2e27babd556b21f0cf25e4ee2 Mon Sep 17 00:00:00 2001 From: Julie Yaunches Date: Tue, 23 Jun 2026 16:56:02 -0400 Subject: [PATCH 4/4] ci(e2e): document explicit Jetson validation --- .github/workflows/e2e-vitest-scenarios.yaml | 18 ++++++++++++++++++ tools/e2e-scenarios/workflow-boundary.mts | 16 +++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/.github/workflows/e2e-vitest-scenarios.yaml b/.github/workflows/e2e-vitest-scenarios.yaml index eab8fea6d53..afd930af581 100644 --- a/.github/workflows/e2e-vitest-scenarios.yaml +++ b/.github/workflows/e2e-vitest-scenarios.yaml @@ -2762,6 +2762,8 @@ jobs: jetson-nvmap-gpu-vitest: needs: generate-matrix # Explicit-only until a stable Jetson runner is available; otherwise full-suite dispatches remain queued forever. + # Required validation path: dispatch with jobs=jetson-nvmap-gpu-vitest or scenarios=jetson-nvmap-gpu. + # Re-enable default dispatch only after a stable Jetson runner exists, then remove this job from FULL_SUITE_EXCLUDED_FREE_STANDING_JOBS. if: ${{ contains(format(',{0},', inputs.jobs), ',jetson-nvmap-gpu-vitest,') || contains(format(',{0},', inputs.scenarios), ',jetson-nvmap-gpu,') }} runs-on: ${{ vars.JETSON_E2E_RUNNER_LABEL || 'linux-arm64-gpu-jetson-orin-latest-1' }} timeout-minutes: 60 @@ -5542,6 +5544,13 @@ jobs: const selectorValidationPassed = needs['generate-matrix']?.result === 'success'; const requestedScenarios = selectorValidationPassed ? rawRequestedScenarios : ''; const requestedJobs = selectorValidationPassed ? rawRequestedJobs : ''; + const explicitOnlySkippedJobs = [ + { + job: 'jetson-nvmap-gpu-vitest', + scenario: 'jetson-nvmap-gpu', + reason: 'default dispatch excludes Jetson until a stable Jetson runner is available', + }, + ]; const scenariosRejected = rawRequestedScenarios && !selectorValidationPassed; const jobsRejected = rawRequestedJobs && !selectorValidationPassed; @@ -5633,6 +5642,15 @@ jobs: '|-----|--------|', ...rows, ]; + if (!selectiveDispatch) { + const skippedJobHints = explicitOnlySkippedJobs + .map( + ({ job, scenario, reason }) => + `\`${job}\` (${reason}; validate with \`jobs=${job}\` or \`scenarios=${scenario}\`)`, + ) + .join(', '); + lines.push('', `> **Explicit-only jobs skipped:** ${skippedJobHints}.`); + } if (failed.length > 0) { const failedNames = failed.map(([name]) => name).join(', '); lines.push('', `> **Failed jobs:** ${failedNames}. Check [run artifacts](${runUrl}) for logs.`); diff --git a/tools/e2e-scenarios/workflow-boundary.mts b/tools/e2e-scenarios/workflow-boundary.mts index ced5fee0561..7fc5008d37d 100644 --- a/tools/e2e-scenarios/workflow-boundary.mts +++ b/tools/e2e-scenarios/workflow-boundary.mts @@ -7781,7 +7781,6 @@ export function validateE2eVitestScenariosWorkflowBoundary( if (jetsonJob.if !== explicitOnlyFreeStandingJobIf("jetson-nvmap-gpu-vitest", "jetson-nvmap-gpu")) { errors.push("jetson-nvmap-gpu-vitest job must run only when explicitly selected"); } - validateFreeStandingJobSelector( errors, jobs, @@ -7894,6 +7893,21 @@ export function validateE2eVitestScenariosWorkflowBoundary( "step 'Post Vitest scenario results to PR' run script must say empty dispatch uses default-enabled free-standing jobs", ); } + if (!reportScript.includes("Explicit-only jobs skipped")) { + errors.push( + "step 'Post Vitest scenario results to PR' run script must list explicit-only skipped jobs on default dispatch", + ); + } + if (!reportScript.includes("jobs=${job}") || !reportScript.includes("jetson-nvmap-gpu-vitest")) { + errors.push( + "step 'Post Vitest scenario results to PR' run script must document the explicit Jetson jobs selector", + ); + } + if (!reportScript.includes("scenarios=${scenario}") || !reportScript.includes("jetson-nvmap-gpu")) { + errors.push( + "step 'Post Vitest scenario results to PR' run script must document the explicit Jetson scenario selector", + ); + } for (const forbidden of [ "toJSON(inputs.pr_number)", "toJSON(inputs.scenarios)",