fix(ci): avoid Windows shell arg overflow in unit-fast
This commit is contained in:
parent
3db2cfef07
commit
829beced04
@ -28,6 +28,25 @@ const pnpm = "pnpm";
|
||||
const behaviorManifest = loadTestRunnerBehavior();
|
||||
const existingFiles = (entries) =>
|
||||
entries.map((entry) => entry.file).filter((file) => fs.existsSync(file));
|
||||
let tempArtifactDir = null;
|
||||
const ensureTempArtifactDir = () => {
|
||||
if (tempArtifactDir === null) {
|
||||
tempArtifactDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-test-parallel-"));
|
||||
}
|
||||
return tempArtifactDir;
|
||||
};
|
||||
const writeTempJsonArtifact = (name, value) => {
|
||||
const filePath = path.join(ensureTempArtifactDir(), `${name}.json`);
|
||||
fs.writeFileSync(filePath, `${JSON.stringify(value)}\n`, "utf8");
|
||||
return filePath;
|
||||
};
|
||||
const cleanupTempArtifacts = () => {
|
||||
if (tempArtifactDir === null) {
|
||||
return;
|
||||
}
|
||||
fs.rmSync(tempArtifactDir, { recursive: true, force: true });
|
||||
tempArtifactDir = null;
|
||||
};
|
||||
const existingUnitConfigFiles = (entries) => existingFiles(entries).filter(isUnitConfigTestFile);
|
||||
const unitBehaviorIsolatedFiles = existingUnitConfigFiles(behaviorManifest.unit.isolated);
|
||||
const unitSingletonIsolatedFiles = existingUnitConfigFiles(behaviorManifest.unit.singletonIsolated);
|
||||
@ -333,6 +352,10 @@ const unitFastExcludedFiles = [
|
||||
const unitAutoSingletonFiles = [
|
||||
...new Set([...unitSingletonIsolatedFiles, ...memoryHeavyUnitFiles]),
|
||||
];
|
||||
const unitFastExtraExcludeFile =
|
||||
unitFastExcludedFiles.length > 0
|
||||
? writeTempJsonArtifact("vitest-unit-fast-excludes", unitFastExcludedFiles)
|
||||
: null;
|
||||
const estimateUnitDurationMs = (file) =>
|
||||
unitTimingManifest.files[file]?.durationMs ?? unitTimingManifest.defaultDurationMs;
|
||||
const heavyUnitBuckets = packFilesByDuration(
|
||||
@ -349,6 +372,12 @@ const baseRuns = [
|
||||
? [
|
||||
{
|
||||
name: "unit-fast",
|
||||
env:
|
||||
unitFastExtraExcludeFile === null
|
||||
? undefined
|
||||
: {
|
||||
OPENCLAW_VITEST_EXTRA_EXCLUDE_FILE: unitFastExtraExcludeFile,
|
||||
},
|
||||
args: [
|
||||
"vitest",
|
||||
"run",
|
||||
@ -356,7 +385,6 @@ const baseRuns = [
|
||||
"vitest.unit.config.ts",
|
||||
`--pool=${useVmForks ? "vmForks" : "forks"}`,
|
||||
...(disableIsolation ? ["--isolate=false"] : []),
|
||||
...unitFastExcludedFiles.flatMap((file) => ["--exclude", file]),
|
||||
],
|
||||
},
|
||||
...(unitBehaviorIsolatedFiles.length > 0
|
||||
@ -982,7 +1010,12 @@ const runOnce = (entry, extraArgs = []) =>
|
||||
try {
|
||||
child = spawn(pnpm, args, {
|
||||
stdio: ["inherit", "pipe", "pipe"],
|
||||
env: { ...process.env, VITEST_GROUP: entry.name, NODE_OPTIONS: resolvedNodeOptions },
|
||||
env: {
|
||||
...process.env,
|
||||
...entry.env,
|
||||
VITEST_GROUP: entry.name,
|
||||
NODE_OPTIONS: resolvedNodeOptions,
|
||||
},
|
||||
shell: isWindows,
|
||||
});
|
||||
captureTreeSample("spawn");
|
||||
@ -1134,6 +1167,7 @@ const shutdown = (signal) => {
|
||||
|
||||
process.on("SIGINT", () => shutdown("SIGINT"));
|
||||
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
||||
process.on("exit", cleanupTempArtifacts);
|
||||
|
||||
if (process.env.OPENCLAW_TEST_LIST_LANES === "1") {
|
||||
const entriesToPrint = targetedEntries.length > 0 ? targetedEntries : runs;
|
||||
|
||||
53
test/vitest-unit-config.test.ts
Normal file
53
test/vitest-unit-config.test.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { loadExtraExcludePatternsFromEnv } from "../vitest.unit.config.ts";
|
||||
|
||||
const tempDirs = new Set<string>();
|
||||
|
||||
afterEach(() => {
|
||||
for (const dir of tempDirs) {
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
tempDirs.clear();
|
||||
});
|
||||
|
||||
const writeExcludeFile = (value: unknown) => {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-vitest-unit-config-"));
|
||||
tempDirs.add(dir);
|
||||
const filePath = path.join(dir, "extra-exclude.json");
|
||||
fs.writeFileSync(filePath, `${JSON.stringify(value)}\n`, "utf8");
|
||||
return filePath;
|
||||
};
|
||||
|
||||
describe("loadExtraExcludePatternsFromEnv", () => {
|
||||
it("returns an empty list when no extra exclude file is configured", () => {
|
||||
expect(loadExtraExcludePatternsFromEnv({})).toEqual([]);
|
||||
});
|
||||
|
||||
it("loads extra exclude patterns from a JSON file", () => {
|
||||
const filePath = writeExcludeFile([
|
||||
"src/infra/update-runner.test.ts",
|
||||
42,
|
||||
"",
|
||||
"ui/src/ui/views/chat.test.ts",
|
||||
]);
|
||||
|
||||
expect(
|
||||
loadExtraExcludePatternsFromEnv({
|
||||
OPENCLAW_VITEST_EXTRA_EXCLUDE_FILE: filePath,
|
||||
}),
|
||||
).toEqual(["src/infra/update-runner.test.ts", "ui/src/ui/views/chat.test.ts"]);
|
||||
});
|
||||
|
||||
it("throws when the configured file is not a JSON array", () => {
|
||||
const filePath = writeExcludeFile({ exclude: ["src/infra/update-runner.test.ts"] });
|
||||
|
||||
expect(() =>
|
||||
loadExtraExcludePatternsFromEnv({
|
||||
OPENCLAW_VITEST_EXTRA_EXCLUDE_FILE: filePath,
|
||||
}),
|
||||
).toThrow(/JSON array/u);
|
||||
});
|
||||
});
|
||||
@ -1,3 +1,4 @@
|
||||
import fs from "node:fs";
|
||||
import { defineConfig } from "vitest/config";
|
||||
import baseConfig from "./vitest.config.ts";
|
||||
import {
|
||||
@ -8,12 +9,33 @@ import {
|
||||
const base = baseConfig as unknown as Record<string, unknown>;
|
||||
const baseTest = (baseConfig as { test?: { include?: string[]; exclude?: string[] } }).test ?? {};
|
||||
const exclude = baseTest.exclude ?? [];
|
||||
export function loadExtraExcludePatternsFromEnv(
|
||||
env: Record<string, string | undefined> = process.env,
|
||||
): string[] {
|
||||
const extraExcludeFile = env.OPENCLAW_VITEST_EXTRA_EXCLUDE_FILE?.trim();
|
||||
if (!extraExcludeFile) {
|
||||
return [];
|
||||
}
|
||||
const parsed = JSON.parse(fs.readFileSync(extraExcludeFile, "utf8")) as unknown;
|
||||
if (!Array.isArray(parsed)) {
|
||||
throw new TypeError(
|
||||
`OPENCLAW_VITEST_EXTRA_EXCLUDE_FILE must point to a JSON array: ${extraExcludeFile}`,
|
||||
);
|
||||
}
|
||||
return parsed.filter((value): value is string => typeof value === "string" && value.length > 0);
|
||||
}
|
||||
|
||||
export default defineConfig({
|
||||
...base,
|
||||
test: {
|
||||
...baseTest,
|
||||
include: unitTestIncludePatterns,
|
||||
exclude: [...exclude, ...unitTestAdditionalExcludePatterns],
|
||||
exclude: [
|
||||
...new Set([
|
||||
...exclude,
|
||||
...unitTestAdditionalExcludePatterns,
|
||||
...loadExtraExcludePatternsFromEnv(),
|
||||
]),
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user