mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-11-11 08:37:35 +00:00
* feat(ci): rebalance test shards from recorded timings * fix(ci): preserve artifact retries for shard timings Find shard-plan generation by its stable step ID and clarify the native runner cohort count (#3748). Note: pre-existing reference-oracle parser type failures in two test_oracle_assets cases remain outside this change. * fix(ci): keep retry timing snapshots immutable Require the existing snapshot on reruns so a failed download cannot refresh history and change shard assignments. Cover the workflow contract and document recovery when the artifact is unavailable. * docs(ci): document shard timing profile selection * fix(ci): balance cold shard estimates consistently --------- Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
57 lines
2.3 KiB
TypeScript
57 lines
2.3 KiB
TypeScript
import path from 'node:path';
|
|
import type { TestModule, TestRunEndReason, Vitest } from 'vitest/node';
|
|
import { TimingsReporter } from 'vitest-shard-balancer';
|
|
import { ALL_CROSS_PLATFORM } from './cross-platform-tests.js';
|
|
import { shardTimingKey, shardTimingsFile } from './shard-timings.js';
|
|
|
|
export default class ShardTimingsReporter extends TimingsReporter {
|
|
private context!: Vitest;
|
|
|
|
constructor() {
|
|
// The package's whole-config check cannot describe our native subset.
|
|
// Check the exact cohort below, before excluding skipped modules from learning.
|
|
super({ timingsFile: shardTimingsFile(), requireAllFiles: false });
|
|
}
|
|
|
|
override onInit(context: Vitest): void {
|
|
this.context = context;
|
|
super.onInit(context);
|
|
}
|
|
|
|
override async onTestRunEnd(
|
|
modules: readonly TestModule[],
|
|
errors: readonly unknown[] = [],
|
|
reason: TestRunEndReason = 'interrupted',
|
|
): Promise<void> {
|
|
if (!this.context.config.mergeReports) return;
|
|
if (errors.length || reason !== 'passed') {
|
|
throw new Error('Refusing to learn shard timings from an unsuccessful test run');
|
|
}
|
|
const root = this.context.config.root;
|
|
const native = (process.env.GITNEXUS_SHARD_PROFILE ?? 'coverage') !== 'coverage';
|
|
const nativeFiles = new Set<string>(ALL_CROSS_PLATFORM);
|
|
const specs = (await this.context.globTestSpecifications()).filter(
|
|
(spec) =>
|
|
!native || nativeFiles.has(path.relative(root, spec.moduleId).split(path.sep).join('/')),
|
|
);
|
|
const expected = new Set(
|
|
specs.map((spec) => shardTimingKey(root, spec.moduleId, spec.project.name)),
|
|
);
|
|
const actual = new Set<string>();
|
|
for (const module of modules) {
|
|
const key = shardTimingKey(root, module.moduleId, module.project.name);
|
|
if (actual.has(key) || !expected.has(key)) {
|
|
throw new Error(`Duplicate or unexpected test module in shard reports: ${key}`);
|
|
}
|
|
actual.add(key);
|
|
}
|
|
const missing = [...expected].filter((key) => !actual.has(key));
|
|
if (!actual.size || missing.length) {
|
|
throw new Error(
|
|
`Incomplete shard reports; missing ${missing.length} test modules: ${missing.join(', ')}`,
|
|
);
|
|
}
|
|
// A platform-specific skip must not turn a previously expensive file into zero work.
|
|
await super.onTestRunEnd(modules.filter((module) => module.state() === 'passed'));
|
|
}
|
|
}
|