perf(ci): split Telegram prerelease tests (#128459)

* perf(ci): split Telegram prerelease tests

* fix(ci): preserve Telegram test process recycling

* fix(ci): shard runnable Telegram tests

* fix(ci): cap Telegram prerelease jobs

* fix(ci): correct Telegram shard heredoc

* test(ci): type Telegram prerelease matrix

* docs(ci): explain Telegram prerelease shards
This commit is contained in:
Vincent Koc
2026-08-24 03:02:46 -07:00
committed by GitHub
parent 234df15a6d
commit cf4f112425
3 changed files with 327 additions and 10 deletions
+92 -9
View File
@@ -155,8 +155,9 @@ jobs:
FULL_RELEASE_VALIDATION: ${{ inputs.full_release_validation && 'true' || 'false' }}
run: |
node --import tsx --input-type=module <<'EOF'
import { appendFileSync, existsSync } from "node:fs";
import { appendFileSync, existsSync, globSync } from "node:fs";
import { execFileSync } from "node:child_process";
import path from "node:path";
const createMatrix = (include) => ({ include });
const outputPath = process.env.GITHUB_OUTPUT;
@@ -210,14 +211,27 @@ jobs:
}
try {
const { createExtensionTestShards, DEFAULT_EXTENSION_TEST_SHARD_COUNT } = await import(
targetPlanPath("extension-test-plan")
);
extensionShards = createExtensionTestShards({
const {
createExtensionTestShards,
DEFAULT_EXTENSION_TEST_SHARD_COUNT,
splitExtensionTestJobTargets,
} = await import(targetPlanPath("extension-test-plan"));
const allExtensionShards = createExtensionTestShards({
shardCount: DEFAULT_EXTENSION_TEST_SHARD_COUNT,
});
const telegramPlanGroup = allExtensionShards
.find((shard) => shard.extensionIds.includes("telegram"))
?.planGroups.find((group) => group.extensionIds.includes("telegram"));
const genericExtensionIds = allExtensionShards
.flatMap((shard) => shard.extensionIds)
.filter((extensionId) => extensionId !== "telegram");
const batchShards = createExtensionTestShards({
extensionIds: genericExtensionIds,
shardCount: DEFAULT_EXTENSION_TEST_SHARD_COUNT,
}).map((shard) => ({
check_name: shard.checkName,
extensions_csv: shard.extensionIds.join(","),
vitest_config: "",
vitest_max_workers: shard.extensionIds.some((extensionId) =>
extensionId.startsWith("memory-"),
)
@@ -225,12 +239,48 @@ jobs:
: 1,
runner: shard.extensionIds.some((extensionId) => extensionId.startsWith("memory-"))
? "blacksmith-16vcpu-ubuntu-2404"
: [0, 1, 2, 3].includes(shard.index)
? "blacksmith-8vcpu-ubuntu-2404"
: "blacksmith-4vcpu-ubuntu-2404",
: [0, 1, 2, 3].includes(shard.index)
? "blacksmith-8vcpu-ubuntu-2404"
: "blacksmith-4vcpu-ubuntu-2404",
shard_index: shard.index + 1,
task: "extensions-batch",
}));
const telegramVitestConfig = telegramPlanGroup
? (await import(`./${telegramPlanGroup.config}`)).default
: null;
const telegramTestConfig = telegramVitestConfig?.test ?? {};
const telegramTestDir = telegramTestConfig.dir ?? process.cwd();
const telegramTestExclude = (telegramTestConfig.exclude ?? []).map((pattern) =>
path.isAbsolute(pattern)
? path.relative(telegramTestDir, pattern).replaceAll("\\", "/")
: pattern,
);
const telegramTestFiles = globSync(telegramTestConfig.include ?? [], {
cwd: telegramTestDir,
exclude: telegramTestExclude,
})
.map((file) =>
path
.relative(process.cwd(), path.resolve(telegramTestDir, file))
.replaceAll("\\", "/"),
)
.sort();
const telegramJobTargets = telegramPlanGroup
? splitExtensionTestJobTargets(telegramPlanGroup.config, telegramTestFiles)
: [];
const telegramShards = telegramPlanGroup
? telegramJobTargets.map((includePatterns, index) => ({
check_name: `checks-node-extensions-telegram-shard-${index + 1}`,
extensions_csv: "telegram",
includePatterns,
vitest_config: telegramPlanGroup.config,
vitest_max_workers: 1,
runner: "blacksmith-8vcpu-ubuntu-2404",
shard_index: DEFAULT_EXTENSION_TEST_SHARD_COUNT + index + 1,
task: "extension-file-shard",
}))
: [];
extensionShards = [...batchShards, ...telegramShards];
} catch (error) {
const errorCode =
error && typeof error === "object" && "code" in error ? error.code : "";
@@ -473,6 +523,7 @@ jobs:
timeout-minutes: 60
strategy:
fail-fast: false
max-parallel: 12
matrix: ${{ fromJson(needs.preflight.outputs.plugin_prerelease_extension_matrix) }}
steps:
- name: Checkout
@@ -496,7 +547,39 @@ jobs:
OPENCLAW_EXTENSION_BATCH_PARALLEL: 2
OPENCLAW_VITEST_MAX_WORKERS: ${{ matrix.vitest_max_workers }}
OPENCLAW_EXTENSION_BATCH: ${{ matrix.extensions_csv }}
run: pnpm test:extensions:batch "$OPENCLAW_EXTENSION_BATCH" -- --retry=1 --exclude extensions/codex/src/app-server/run-attempt.test.ts
OPENCLAW_EXTENSION_INCLUDE_PATTERNS_JSON: ${{ toJson(matrix.includePatterns) }}
OPENCLAW_EXTENSION_TASK: ${{ matrix.task }}
OPENCLAW_EXTENSION_VITEST_CONFIG: ${{ matrix.vitest_config }}
shell: bash
run: |
set -euo pipefail
case "$OPENCLAW_EXTENSION_TASK" in
extensions-batch)
pnpm test:extensions:batch "$OPENCLAW_EXTENSION_BATCH" -- --retry=1 --exclude extensions/codex/src/app-server/run-attempt.test.ts
;;
extension-file-shard)
include_file="${RUNNER_TEMP}/telegram-test-include-${GITHUB_JOB}.json"
trap 'rm -f -- "$include_file"' EXIT
INCLUDE_FILE="$include_file" node --input-type=module <<'EOF'
import { writeFileSync } from "node:fs";
const patterns = JSON.parse(process.env.OPENCLAW_EXTENSION_INCLUDE_PATTERNS_JSON);
if (!Array.isArray(patterns) || patterns.length === 0) {
throw new Error("Invalid Telegram extension file shard");
}
writeFileSync(process.env.INCLUDE_FILE, JSON.stringify(patterns), "utf8");
EOF
OPENCLAW_TEST_PROJECTS_PARALLEL=2 \
OPENCLAW_VITEST_INCLUDE_FILE="$include_file" \
pnpm test -- "$OPENCLAW_EXTENSION_VITEST_CONFIG"
trap - EXIT
rm -f -- "$include_file"
;;
*)
echo "Unknown extension test task: $OPENCLAW_EXTENSION_TASK" >&2
exit 1
;;
esac
plugin-prerelease-inspector:
permissions: