mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-10 00:31:22 +00:00
refactor: simplify test workflow helpers
This commit is contained in:
@@ -1,182 +0,0 @@
|
||||
import { appendFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
|
||||
const WORKFLOWS = new Set(["ci", "install-smoke"]);
|
||||
|
||||
const parseArgs = (argv) => {
|
||||
const parsed = {
|
||||
workflow: "ci",
|
||||
};
|
||||
for (let index = 0; index < argv.length; index += 1) {
|
||||
const arg = argv[index];
|
||||
if (arg === "--workflow") {
|
||||
const nextValue = argv[index + 1] ?? "";
|
||||
if (!WORKFLOWS.has(nextValue)) {
|
||||
throw new Error(
|
||||
`Unsupported --workflow value "${String(nextValue || "<missing>")}". Supported values: ci, install-smoke.`,
|
||||
);
|
||||
}
|
||||
parsed.workflow = nextValue;
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
return parsed;
|
||||
};
|
||||
|
||||
const parseBooleanEnv = (value, defaultValue = false) => {
|
||||
if (value === undefined) {
|
||||
return defaultValue;
|
||||
}
|
||||
const normalized = value.trim().toLowerCase();
|
||||
if (normalized === "true" || normalized === "1") {
|
||||
return true;
|
||||
}
|
||||
if (normalized === "false" || normalized === "0" || normalized === "") {
|
||||
return false;
|
||||
}
|
||||
return defaultValue;
|
||||
};
|
||||
|
||||
const parseJsonEnv = (value, fallback) => {
|
||||
try {
|
||||
return value ? JSON.parse(value) : fallback;
|
||||
} catch {
|
||||
return fallback;
|
||||
}
|
||||
};
|
||||
|
||||
const createMatrix = (include) => ({ include });
|
||||
|
||||
export function buildWorkflowManifest(env = process.env, workflow = "ci") {
|
||||
const eventName = env.GITHUB_EVENT_NAME ?? "pull_request";
|
||||
const isPush = eventName === "push";
|
||||
const docsOnly = parseBooleanEnv(env.OPENCLAW_CI_DOCS_ONLY);
|
||||
const docsChanged = parseBooleanEnv(env.OPENCLAW_CI_DOCS_CHANGED);
|
||||
const runNode = parseBooleanEnv(env.OPENCLAW_CI_RUN_NODE);
|
||||
const runMacos = parseBooleanEnv(env.OPENCLAW_CI_RUN_MACOS);
|
||||
const runAndroid = parseBooleanEnv(env.OPENCLAW_CI_RUN_ANDROID);
|
||||
const runWindows = parseBooleanEnv(env.OPENCLAW_CI_RUN_WINDOWS);
|
||||
const runSkillsPython = parseBooleanEnv(env.OPENCLAW_CI_RUN_SKILLS_PYTHON);
|
||||
const hasChangedExtensions = parseBooleanEnv(env.OPENCLAW_CI_HAS_CHANGED_EXTENSIONS);
|
||||
const changedExtensionsMatrix = parseJsonEnv(env.OPENCLAW_CI_CHANGED_EXTENSIONS_MATRIX, {
|
||||
include: [],
|
||||
});
|
||||
const runChangedSmoke = parseBooleanEnv(env.OPENCLAW_CI_RUN_CHANGED_SMOKE);
|
||||
|
||||
const checksFastMatrix = createMatrix(
|
||||
runNode
|
||||
? [
|
||||
{ check_name: "checks-fast-bundled", runtime: "node", task: "bundled" },
|
||||
{ check_name: "checks-fast-extensions", runtime: "node", task: "extensions" },
|
||||
{
|
||||
check_name: "checks-fast-contracts-protocol",
|
||||
runtime: "node",
|
||||
task: "contracts-protocol",
|
||||
},
|
||||
]
|
||||
: [],
|
||||
);
|
||||
|
||||
const checksMatrixInclude = runNode
|
||||
? [
|
||||
{ check_name: "checks-node-test", runtime: "node", task: "test" },
|
||||
{ check_name: "checks-node-channels", runtime: "node", task: "channels" },
|
||||
...(isPush
|
||||
? [
|
||||
{
|
||||
check_name: "checks-node-compat-node22",
|
||||
runtime: "node",
|
||||
task: "compat-node22",
|
||||
node_version: "22.x",
|
||||
cache_key_suffix: "node22",
|
||||
},
|
||||
]
|
||||
: []),
|
||||
]
|
||||
: [];
|
||||
|
||||
const windowsMatrix = createMatrix(
|
||||
runWindows ? [{ check_name: "checks-windows-node-test", runtime: "node", task: "test" }] : [],
|
||||
);
|
||||
const macosNodeMatrix = createMatrix(
|
||||
runMacos ? [{ check_name: "macos-node", runtime: "node", task: "test" }] : [],
|
||||
);
|
||||
const androidMatrix = createMatrix(
|
||||
runAndroid
|
||||
? [
|
||||
{ check_name: "android-test-play", task: "test-play" },
|
||||
{ check_name: "android-test-third-party", task: "test-third-party" },
|
||||
{ check_name: "android-build-play", task: "build-play" },
|
||||
{ check_name: "android-build-third-party", task: "build-third-party" },
|
||||
]
|
||||
: [],
|
||||
);
|
||||
const extensionFastMatrix = createMatrix(
|
||||
hasChangedExtensions
|
||||
? (changedExtensionsMatrix.include ?? []).map((entry) => ({
|
||||
check_name: `extension-fast-${entry.extension}`,
|
||||
extension: entry.extension,
|
||||
}))
|
||||
: [],
|
||||
);
|
||||
|
||||
if (workflow === "install-smoke") {
|
||||
return {
|
||||
docs_only: docsOnly,
|
||||
run_install_smoke: !docsOnly && runChangedSmoke,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
docs_only: docsOnly,
|
||||
docs_changed: docsChanged,
|
||||
run_node: !docsOnly && runNode,
|
||||
run_macos: !docsOnly && runMacos,
|
||||
run_android: !docsOnly && runAndroid,
|
||||
run_skills_python: !docsOnly && runSkillsPython,
|
||||
run_windows: !docsOnly && runWindows,
|
||||
has_changed_extensions: !docsOnly && hasChangedExtensions,
|
||||
changed_extensions_matrix: changedExtensionsMatrix,
|
||||
run_build_artifacts: !docsOnly && runNode,
|
||||
run_checks_fast: !docsOnly && runNode,
|
||||
checks_fast_matrix: checksFastMatrix,
|
||||
run_checks: !docsOnly && runNode,
|
||||
checks_matrix: createMatrix(checksMatrixInclude),
|
||||
run_extension_fast: !docsOnly && hasChangedExtensions,
|
||||
extension_fast_matrix: extensionFastMatrix,
|
||||
run_check: !docsOnly && runNode,
|
||||
run_check_additional: !docsOnly && runNode,
|
||||
run_build_smoke: !docsOnly && runNode,
|
||||
run_check_docs: docsChanged,
|
||||
run_skills_python_job: !docsOnly && runSkillsPython,
|
||||
run_checks_windows: !docsOnly && runWindows,
|
||||
checks_windows_matrix: windowsMatrix,
|
||||
run_macos_node: !docsOnly && runMacos,
|
||||
macos_node_matrix: macosNodeMatrix,
|
||||
run_macos_swift: !docsOnly && runMacos,
|
||||
run_android_job: !docsOnly && runAndroid,
|
||||
android_matrix: androidMatrix,
|
||||
};
|
||||
}
|
||||
|
||||
const entryHref = process.argv[1] ? pathToFileURL(path.resolve(process.argv[1])).href : "";
|
||||
|
||||
if (import.meta.url === entryHref) {
|
||||
const outputPath = process.env.GITHUB_OUTPUT;
|
||||
|
||||
if (!outputPath) {
|
||||
throw new Error("GITHUB_OUTPUT is required");
|
||||
}
|
||||
|
||||
const { workflow } = parseArgs(process.argv.slice(2));
|
||||
const manifest = buildWorkflowManifest(process.env, workflow);
|
||||
|
||||
const writeOutput = (name, value) => {
|
||||
appendFileSync(outputPath, `${name}=${value}\n`, "utf8");
|
||||
};
|
||||
|
||||
for (const [key, value] of Object.entries(manifest)) {
|
||||
writeOutput(key, typeof value === "string" ? value : JSON.stringify(value));
|
||||
}
|
||||
}
|
||||
@@ -28,12 +28,8 @@ function normalizeRelative(inputPath) {
|
||||
return inputPath.split(path.sep).join("/");
|
||||
}
|
||||
|
||||
function isTestFile(filePath) {
|
||||
return filePath.endsWith(".test.ts") || filePath.endsWith(".test.tsx");
|
||||
}
|
||||
|
||||
function collectTestFiles(rootPath) {
|
||||
const results = [];
|
||||
function countTestFiles(rootPath) {
|
||||
let total = 0;
|
||||
const stack = [rootPath];
|
||||
|
||||
while (stack.length > 0) {
|
||||
@@ -50,13 +46,13 @@ function collectTestFiles(rootPath) {
|
||||
stack.push(fullPath);
|
||||
continue;
|
||||
}
|
||||
if (entry.isFile() && isTestFile(fullPath)) {
|
||||
results.push(fullPath);
|
||||
if (entry.isFile() && (fullPath.endsWith(".test.ts") || fullPath.endsWith(".test.tsx"))) {
|
||||
total += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results.toSorted((left, right) => left.localeCompare(right));
|
||||
return total;
|
||||
}
|
||||
|
||||
function hasGitCommit(ref) {
|
||||
@@ -224,22 +220,22 @@ export function resolveExtensionTestPlan(params = {}) {
|
||||
const pairedCoreRoot = path.join(repoRoot, "src", extensionId);
|
||||
if (fs.existsSync(pairedCoreRoot)) {
|
||||
const pairedRelativeRoot = normalizeRelative(path.relative(repoRoot, pairedCoreRoot));
|
||||
if (collectTestFiles(pairedCoreRoot).length > 0) {
|
||||
roots.push(pairedRelativeRoot);
|
||||
}
|
||||
roots.push(pairedRelativeRoot);
|
||||
}
|
||||
|
||||
const usesChannelConfig = roots.some((root) => channelTestRoots.includes(root));
|
||||
const config = usesChannelConfig ? "vitest.channels.config.ts" : "vitest.extensions.config.ts";
|
||||
const testFiles = roots
|
||||
.flatMap((root) => collectTestFiles(path.join(repoRoot, root)))
|
||||
.map((filePath) => normalizeRelative(path.relative(repoRoot, filePath)));
|
||||
const testFileCount = roots.reduce(
|
||||
(sum, root) => sum + countTestFiles(path.join(repoRoot, root)),
|
||||
0,
|
||||
);
|
||||
return {
|
||||
config,
|
||||
extensionDir: relativeExtensionDir,
|
||||
extensionId,
|
||||
hasTests: testFileCount > 0,
|
||||
roots,
|
||||
testFiles,
|
||||
testFileCount,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -247,7 +243,7 @@ async function runVitestBatch(params) {
|
||||
return await new Promise((resolve, reject) => {
|
||||
const child = spawn(
|
||||
pnpm,
|
||||
["exec", "vitest", "run", "--config", params.config, ...params.files, ...params.args],
|
||||
["exec", "vitest", "run", "--config", params.config, ...params.targets, ...params.args],
|
||||
{
|
||||
cwd: repoRoot,
|
||||
stdio: "inherit",
|
||||
@@ -382,23 +378,23 @@ async function run() {
|
||||
console.log(`[test-extension] ${plan.extensionId}`);
|
||||
console.log(`config: ${plan.config}`);
|
||||
console.log(`roots: ${plan.roots.join(", ")}`);
|
||||
console.log(`tests: ${plan.testFiles.length}`);
|
||||
console.log(`tests: ${plan.testFileCount}`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (plan.testFiles.length === 0) {
|
||||
if (!plan.hasTests) {
|
||||
process.exit(printNoTestsMessage(plan, requireTests));
|
||||
}
|
||||
|
||||
console.log(
|
||||
`[test-extension] Running ${plan.testFiles.length} test files for ${plan.extensionId} with ${plan.config}`,
|
||||
`[test-extension] Running ${plan.testFileCount} test files for ${plan.extensionId} with ${plan.config}`,
|
||||
);
|
||||
const exitCode = await runVitestBatch({
|
||||
args: passthroughArgs,
|
||||
config: plan.config,
|
||||
env: process.env,
|
||||
files: plan.testFiles,
|
||||
targets: plan.roots,
|
||||
});
|
||||
process.exit(exitCode);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user