Repository navigation
perf(ci): cut CLI coverage shard critical path #11098
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
989ce48
383dfa3
0574153
f229e37
195c46b
ee6ac63
4df2007
f52d79f
a92f3f1
563aa14
ea2c571
be14520
3725f4a
ef52644
6727699
804ce7b
42946d4
5c401c6
acffdc4
3a3fbfe
7cfb75d
7b2a2ad
9d8921c
fab7f50
851d7d9
933cb4e
824af6f
6457319
5d18469
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -176,6 +176,16 @@ | |
| "test": "provides the package token only to trusted main dependency installation", | ||
| "category": "security" | ||
| }, | ||
| { | ||
| "file": "test/automation/pull-requests/pr-workflow-contract.test.ts", | ||
| "test": "keeps %s plugin test typechecking after the trusted production build", | ||
| "category": "security" | ||
| }, | ||
| { | ||
| "file": "test/repository/plugin-vitest-project.test.ts", | ||
| "test": "keeps standalone plugin %s locked to the root test toolchain", | ||
| "category": "compatibility" | ||
| }, | ||
|
Comment on lines
+179
to
+188
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove source-shape tests instead of adding exceptions, please |
||
| { | ||
| "file": "test/automation/pull-requests/pr-workflow-contract.test.ts", | ||
| "test": "passes only the base-packaged SDK archive to pull request dependency jobs", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| interface TimingHintSource { | ||
| runId: number; | ||
| artifactId: number; | ||
| headSha: string; | ||
| recordedAt: string; | ||
| } | ||
|
|
||
| export interface CliTestTimingHints { | ||
| schemaVersion: 2; | ||
| defaultDurationMs: number; | ||
| sources: readonly TimingHintSource[]; | ||
| files: Readonly<Record<string, number>>; | ||
| } | ||
|
|
||
| function isRecord(value: unknown): value is Record<string, unknown> { | ||
| return typeof value === "object" && value !== null && !Array.isArray(value); | ||
| } | ||
|
|
||
| export function parseCliTestTimingHints(value: unknown): CliTestTimingHints { | ||
| if (!isRecord(value) || value.schemaVersion !== 2) { | ||
| throw new Error("CLI test timing hints must use schemaVersion 2"); | ||
| } | ||
| if (!Number.isSafeInteger(value.defaultDurationMs) || Number(value.defaultDurationMs) <= 0) { | ||
| throw new Error("CLI test timing hints require a positive integer defaultDurationMs"); | ||
| } | ||
| if (!Array.isArray(value.sources) || value.sources.length === 0) { | ||
| throw new Error("CLI test timing hints require source metadata"); | ||
| } | ||
| const sources = value.sources.map((source) => { | ||
| if (!isRecord(source)) throw new Error("Invalid CLI test timing hint source"); | ||
| const { runId, artifactId, headSha, recordedAt } = source; | ||
| if ( | ||
| !Number.isSafeInteger(runId) || | ||
| Number(runId) <= 0 || | ||
| !Number.isSafeInteger(artifactId) || | ||
| Number(artifactId) <= 0 || | ||
| typeof headSha !== "string" || | ||
| !/^[0-9a-f]{40}$/u.test(headSha) || | ||
| typeof recordedAt !== "string" || | ||
| Number.isNaN(Date.parse(recordedAt)) | ||
| ) { | ||
| throw new Error("Invalid CLI test timing hint source"); | ||
| } | ||
| return { runId: Number(runId), artifactId: Number(artifactId), headSha, recordedAt }; | ||
| }); | ||
| if (!isRecord(value.files)) { | ||
| throw new Error("CLI test timing hints require a files map"); | ||
| } | ||
|
|
||
| const defaultDurationMs = Number(value.defaultDurationMs); | ||
| const files: Record<string, number> = {}; | ||
| for (const [file, durationMs] of Object.entries(value.files)) { | ||
| const segments = file.split("/"); | ||
| if ( | ||
| file.length === 0 || | ||
| file.startsWith("/") || | ||
| file.includes("\\") || | ||
| segments.includes("..") || | ||
| !Number.isSafeInteger(durationMs) || | ||
| Number(durationMs) <= defaultDurationMs | ||
| ) { | ||
| throw new Error(`Invalid CLI test timing hint: ${file}`); | ||
| } | ||
| files[file] = Number(durationMs); | ||
| } | ||
|
|
||
| return { | ||
| schemaVersion: 2, | ||
| defaultDurationMs, | ||
| sources, | ||
| files, | ||
| }; | ||
| } |
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's this adding that you can't get from the vitest reports themselves? |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,169 @@ | ||
| #!/usr/bin/env -S npx tsx | ||
| // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| import { appendFileSync, readFileSync } from "node:fs"; | ||
| import path from "node:path"; | ||
| import { fileURLToPath } from "node:url"; | ||
|
|
||
| import { parseCliTestTimingHints } from "./cli-test-timing-hints.mts"; | ||
|
|
||
| type VitestFileResult = { | ||
| name?: unknown; | ||
| startTime?: unknown; | ||
| endTime?: unknown; | ||
| }; | ||
|
|
||
| type VitestReport = { | ||
| testResults?: unknown; | ||
| }; | ||
|
|
||
| export type TimingDrift = { | ||
| file: string; | ||
| hintMs?: number; | ||
| kind: "faster" | "slower" | "unprofiled"; | ||
| observedMs: number; | ||
| }; | ||
|
|
||
| const MINIMUM_DELTA_MS = 10_000; | ||
| const HINT_RATIO = 2; | ||
| const UNPROFILED_RATIO = 3; | ||
|
|
||
| function isRecord(value: unknown): value is Record<string, unknown> { | ||
| return typeof value === "object" && value !== null && !Array.isArray(value); | ||
| } | ||
|
|
||
| function normalizeReportPath(name: string, repoRoot: string): string | undefined { | ||
| const absoluteName = path.isAbsolute(name) ? path.resolve(name) : path.resolve(repoRoot, name); | ||
| const relativeName = path.relative(repoRoot, absoluteName).split(path.sep).join("/"); | ||
| if (relativeName === "" || relativeName === ".." || relativeName.startsWith("../")) { | ||
| return undefined; | ||
| } | ||
| return relativeName; | ||
| } | ||
|
|
||
| function parseFileResults(value: VitestReport, repoRoot: string): Map<string, number> { | ||
| if (!Array.isArray(value.testResults)) { | ||
| throw new Error("Invalid Vitest timing report"); | ||
| } | ||
|
|
||
| const durations = new Map<string, number>(); | ||
| for (const [index, candidate] of value.testResults.entries()) { | ||
| if (!isRecord(candidate)) { | ||
| throw new Error(`Invalid Vitest timing report entry at index ${index}`); | ||
| } | ||
| const { name, startTime, endTime } = candidate as VitestFileResult; | ||
| if ( | ||
| typeof name !== "string" || | ||
| typeof startTime !== "number" || | ||
| !Number.isFinite(startTime) || | ||
| typeof endTime !== "number" || | ||
| !Number.isFinite(endTime) || | ||
| endTime < startTime | ||
| ) { | ||
| throw new Error(`Invalid Vitest timing report entry at index ${index}`); | ||
| } | ||
| const file = normalizeReportPath(name, repoRoot); | ||
| if (!file) continue; | ||
| durations.set(file, Math.max(durations.get(file) ?? 0, Math.round(endTime - startTime))); | ||
| } | ||
| return durations; | ||
| } | ||
|
|
||
| export function findCliTestTimingDrift( | ||
| reportValue: VitestReport, | ||
| hintsValue: unknown, | ||
| repoRoot: string, | ||
| ): TimingDrift[] { | ||
| const hints = parseCliTestTimingHints(hintsValue); | ||
| const durations = parseFileResults(reportValue, path.resolve(repoRoot)); | ||
| const drift: TimingDrift[] = []; | ||
|
|
||
| for (const [file, observedMs] of durations) { | ||
| const hintMs = hints.files[file]; | ||
| if (hintMs === undefined) { | ||
| if (observedMs >= hints.defaultDurationMs * UNPROFILED_RATIO) { | ||
| drift.push({ file, kind: "unprofiled", observedMs }); | ||
| } | ||
| continue; | ||
| } | ||
|
|
||
| const deltaMs = Math.abs(observedMs - hintMs); | ||
| if (deltaMs < MINIMUM_DELTA_MS) continue; | ||
| if (observedMs >= hintMs * HINT_RATIO) { | ||
| drift.push({ file, hintMs, kind: "slower", observedMs }); | ||
| } else if (hintMs >= observedMs * HINT_RATIO) { | ||
| drift.push({ file, hintMs, kind: "faster", observedMs }); | ||
| } | ||
| } | ||
|
|
||
| return drift.sort( | ||
| (left, right) => right.observedMs - left.observedMs || left.file.localeCompare(right.file), | ||
| ); | ||
| } | ||
|
|
||
| function escapeWorkflowCommand(value: string): string { | ||
| return value.replaceAll("%", "%25").replaceAll("\r", "%0D").replaceAll("\n", "%0A"); | ||
| } | ||
|
|
||
| function formatDuration(milliseconds: number): string { | ||
| return `${(milliseconds / 1_000).toFixed(1)}s`; | ||
| } | ||
|
|
||
| function driftMessage(item: TimingDrift): string { | ||
| if (item.kind === "unprofiled") { | ||
| return `Unprofiled test file took ${formatDuration(item.observedMs)}; refresh CLI timing hints`; | ||
| } | ||
| return `Test file ran ${item.kind} than its timing hint: observed ${formatDuration(item.observedMs)}, hint ${formatDuration(item.hintMs!)}`; | ||
| } | ||
|
|
||
| export function formatCliTestTimingDriftSummary(drift: readonly TimingDrift[]): string { | ||
| if (drift.length === 0) { | ||
| return "### CLI test timing drift\n\nNo material timing-hint drift detected.\n"; | ||
| } | ||
| const rows = drift | ||
| .slice(0, 20) | ||
| .map( | ||
| (item) => | ||
| `| \`${item.file}\` | ${item.kind} | ${formatDuration(item.observedMs)} | ${item.hintMs === undefined ? "—" : formatDuration(item.hintMs)} |`, | ||
| ) | ||
| .join("\n"); | ||
| return `### CLI test timing drift\n\n${drift.length} file(s) need a timing-hint refresh. This report is advisory.\n\n| File | Drift | Observed | Hint |\n| --- | --- | ---: | ---: |\n${rows}\n`; | ||
| } | ||
|
|
||
| function loadJson(file: string): unknown { | ||
| try { | ||
| return JSON.parse(readFileSync(path.resolve(file), "utf8")); | ||
| } catch (cause) { | ||
| throw new Error(`Failed to load ${file}`, { cause }); | ||
| } | ||
| } | ||
|
|
||
| function main(): void { | ||
| const [reportFile, hintsFile] = process.argv.slice(2); | ||
| if (!reportFile || !hintsFile) { | ||
| throw new Error( | ||
| "Usage: report-cli-test-timing-drift.mts <vitest-results.json> <cli-test-timing-hints.json>", | ||
| ); | ||
| } | ||
|
|
||
| const drift = findCliTestTimingDrift( | ||
| loadJson(reportFile) as VitestReport, | ||
| loadJson(hintsFile), | ||
| process.cwd(), | ||
| ); | ||
| const summary = formatCliTestTimingDriftSummary(drift); | ||
| process.stdout.write(summary); | ||
| for (const item of drift) { | ||
| process.stdout.write( | ||
| `::warning file=${escapeWorkflowCommand(item.file)},title=CLI test timing drift::${escapeWorkflowCommand(driftMessage(item))}\n`, | ||
| ); | ||
| } | ||
| if (process.env.GITHUB_STEP_SUMMARY) { | ||
| appendFileSync(process.env.GITHUB_STEP_SUMMARY, summary); | ||
| } | ||
| } | ||
|
|
||
| if (fileURLToPath(import.meta.url) === path.resolve(process.argv[1] ?? "")) { | ||
| main(); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why?