mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-19 09:01:39 -06:00
30bde936ef
* test(ci): temporarily omit Z.AI API Platform validation (#109246) * test(ci): temporarily omit Z.AI API Platform validation * test(ci): align disabled provider count (cherry picked from commit954c77d4ef) * fix(release): keep waived Z.AI probe out of broad shard Adapt the Z.AI shard portion of upstreamc4b2183db6(#109272). Exclude the waived API Platform file from the broad agents shard while retaining the dedicated Coding Plan proof. (cherry picked from commitc4b2183db6)
235 lines
7.8 KiB
TypeScript
235 lines
7.8 KiB
TypeScript
// Release Workflow Matrix Plan tests cover release workflow matrix plan script behavior.
|
|
import { readFileSync } from "node:fs";
|
|
import { describe, expect, it } from "vitest";
|
|
import { parse } from "yaml";
|
|
import { createReleaseWorkflowMatrixPlan } from "../../scripts/plan-release-workflow-matrix.mjs";
|
|
|
|
function workflow() {
|
|
return parse(readFileSync(".github/workflows/openclaw-live-and-e2e-checks-reusable.yml", "utf8"));
|
|
}
|
|
|
|
const PROFILE_GATED_STATIC_MATRIX_ALLOWLIST = [
|
|
"validate_live_provider_suites",
|
|
"validate_live_docker_provider_suites",
|
|
"validate_live_media_provider_suites",
|
|
];
|
|
|
|
// Direct dispatches build from the selected ref. Only trusted workflow callers
|
|
// may provide the complete immutable package artifact tuple.
|
|
const WORKFLOW_CALL_ONLY_INPUTS = new Set([
|
|
"package_artifact_name",
|
|
"package_artifact_id",
|
|
"package_artifact_digest",
|
|
"package_artifact_run_id",
|
|
"package_artifact_run_attempt",
|
|
"package_file_name",
|
|
"package_source_sha",
|
|
"package_sha256",
|
|
"package_version",
|
|
]);
|
|
|
|
const PROFILE_EXPECTATIONS = [
|
|
{
|
|
profile: "minimum",
|
|
dockerE2eChunks: ["package-update-openai", "package-update-anthropic", "package-update-core"],
|
|
liveModelProviders: ["openai"],
|
|
},
|
|
{
|
|
profile: "beta",
|
|
dockerE2eChunks: ["package-update-openai", "package-update-anthropic", "package-update-core"],
|
|
liveModelProviders: ["openai"],
|
|
},
|
|
{
|
|
profile: "stable",
|
|
dockerE2eChunks: [
|
|
"core",
|
|
"package-update-openai",
|
|
"package-update-anthropic",
|
|
"package-update-core",
|
|
"plugins-runtime-plugins",
|
|
"plugins-runtime-services",
|
|
"plugins-runtime-install-a",
|
|
"plugins-runtime-install-b",
|
|
"plugins-runtime-install-c",
|
|
"plugins-runtime-install-d",
|
|
"plugins-runtime-install-e",
|
|
"plugins-runtime-install-f",
|
|
"plugins-runtime-install-g",
|
|
"plugins-runtime-install-h",
|
|
],
|
|
liveModelProviders: ["anthropic", "google", "minimax", "openai"],
|
|
},
|
|
{
|
|
profile: "full",
|
|
dockerE2eChunks: [
|
|
"core",
|
|
"package-update-openai",
|
|
"package-update-anthropic",
|
|
"package-update-core",
|
|
"plugins-runtime-plugins",
|
|
"plugins-runtime-services",
|
|
"plugins-runtime-install-a",
|
|
"plugins-runtime-install-b",
|
|
"plugins-runtime-install-c",
|
|
"plugins-runtime-install-d",
|
|
"plugins-runtime-install-e",
|
|
"plugins-runtime-install-f",
|
|
"plugins-runtime-install-g",
|
|
"plugins-runtime-install-h",
|
|
],
|
|
liveModelProviders: [
|
|
"anthropic",
|
|
"google",
|
|
"minimax",
|
|
"moonshot",
|
|
"openai",
|
|
"opencode-go",
|
|
"openrouter",
|
|
"xai",
|
|
"fireworks",
|
|
],
|
|
},
|
|
];
|
|
|
|
function staticProfileMatrixJobs() {
|
|
return Object.entries(workflow().jobs)
|
|
.filter(([, job]) => {
|
|
const entries = job.strategy?.matrix?.include;
|
|
return Array.isArray(entries) && entries.some((entry) => "profiles" in entry);
|
|
})
|
|
.map(([jobName]) => jobName)
|
|
.toSorted((left, right) => left.localeCompare(right));
|
|
}
|
|
|
|
describe("scripts/plan-release-workflow-matrix.mjs", () => {
|
|
it("declares every job input for both workflow entry points", () => {
|
|
const definition = workflow();
|
|
const referencedInputs = new Set<string>();
|
|
for (const match of JSON.stringify(definition.jobs).matchAll(/\binputs\.([a-zA-Z0-9_]+)/gu)) {
|
|
if (match[1]) {
|
|
referencedInputs.add(match[1]);
|
|
}
|
|
}
|
|
|
|
expect(Object.keys(definition.on.workflow_call.inputs)).toEqual(
|
|
expect.arrayContaining([...referencedInputs]),
|
|
);
|
|
expect(Object.keys(definition.on.workflow_dispatch.inputs)).toEqual(
|
|
expect.arrayContaining(
|
|
[...referencedInputs].filter((input) => !WORKFLOW_CALL_ONLY_INPUTS.has(input)),
|
|
),
|
|
);
|
|
for (const input of WORKFLOW_CALL_ONLY_INPUTS) {
|
|
expect(definition.on.workflow_call.inputs).toHaveProperty(input);
|
|
expect(definition.on.workflow_dispatch.inputs).not.toHaveProperty(input);
|
|
}
|
|
});
|
|
|
|
it.each(PROFILE_EXPECTATIONS)(
|
|
"keeps $profile release jobs to profile-enabled Docker E2E chunks and live model providers",
|
|
({ profile, dockerE2eChunks, liveModelProviders }) => {
|
|
const plan = createReleaseWorkflowMatrixPlan({
|
|
includeLiveSuites: true,
|
|
includeReleasePathSuites: true,
|
|
releaseProfile: profile,
|
|
});
|
|
|
|
expect(plan.dockerE2e.matrix.include.map((entry) => entry.chunk_id)).toEqual(dockerE2eChunks);
|
|
expect(plan.liveModels.matrix.include.map((entry) => entry.providers)).toEqual(
|
|
liveModelProviders,
|
|
);
|
|
},
|
|
);
|
|
|
|
it("reports omitted lanes for release jobs excluded by the selected profile", () => {
|
|
const plan = createReleaseWorkflowMatrixPlan({
|
|
includeLiveSuites: true,
|
|
includeReleasePathSuites: true,
|
|
releaseProfile: "beta",
|
|
});
|
|
|
|
expect(plan.dockerE2e.omitted.map((entry) => entry.id)).toContain("core");
|
|
expect(plan.liveModels.omitted.map((entry) => entry.id)).toContain("anthropic");
|
|
});
|
|
|
|
it("keeps stable release jobs broad enough for stable-required lanes", () => {
|
|
const plan = createReleaseWorkflowMatrixPlan({
|
|
includeLiveSuites: true,
|
|
includeReleasePathSuites: true,
|
|
releaseProfile: "stable",
|
|
});
|
|
|
|
expect(plan.dockerE2e.count).toBe(14);
|
|
expect(plan.liveModels.matrix.include.map((entry) => entry.providers)).toEqual([
|
|
"anthropic",
|
|
"google",
|
|
"minimax",
|
|
"openai",
|
|
]);
|
|
expect(plan.liveModels.omitted.map((entry) => entry.id)).toEqual([
|
|
"moonshot",
|
|
"opencode-go",
|
|
"openrouter",
|
|
"xai",
|
|
"fireworks",
|
|
]);
|
|
});
|
|
|
|
it("limits MiniMax Docker live-model coverage to the stable M2.7 pair", () => {
|
|
const plan = createReleaseWorkflowMatrixPlan({
|
|
includeLiveSuites: true,
|
|
includeReleasePathSuites: true,
|
|
releaseProfile: "stable",
|
|
});
|
|
|
|
expect(plan.liveModels.matrix.include).toContainEqual({
|
|
provider_label: "MiniMax",
|
|
providers: "minimax",
|
|
models: "minimax/MiniMax-M2.7,minimax-portal/MiniMax-M2.7",
|
|
max_models: "2",
|
|
profiles: "stable full",
|
|
});
|
|
});
|
|
|
|
it("disables live model planning when focused recovery targets another live suite", () => {
|
|
const plan = createReleaseWorkflowMatrixPlan({
|
|
includeLiveSuites: true,
|
|
includeReleasePathSuites: true,
|
|
liveSuiteFilter: "live-cache",
|
|
releaseProfile: "full",
|
|
});
|
|
|
|
expect(plan.liveModels.count).toBe(0);
|
|
expect(plan.liveModels.omitted).toHaveLength(9);
|
|
expect(plan.liveModels.omitted[0]?.reason).toBe(
|
|
"Docker live model matrix disabled by input selection",
|
|
);
|
|
});
|
|
|
|
it("wires filtered matrices into the reusable live and E2E workflow", () => {
|
|
const jobs = workflow().jobs;
|
|
const planner = jobs.plan_release_workflow_matrices;
|
|
|
|
expect(planner.outputs.docker_e2e_matrix).toBe("${{ steps.plan.outputs.docker_e2e_matrix }}");
|
|
expect(planner.outputs.live_models_matrix).toBe("${{ steps.plan.outputs.live_models_matrix }}");
|
|
expect(jobs.validate_docker_e2e.needs).toContain("plan_release_workflow_matrices");
|
|
expect(jobs.validate_live_models_docker.needs).toContain("plan_release_workflow_matrices");
|
|
expect(jobs.validate_docker_e2e.strategy.matrix).toBe(
|
|
"${{ fromJson(needs.plan_release_workflow_matrices.outputs.docker_e2e_matrix) }}",
|
|
);
|
|
expect(jobs.validate_live_models_docker.strategy.matrix).toBe(
|
|
"${{ fromJson(needs.plan_release_workflow_matrices.outputs.live_models_matrix) }}",
|
|
);
|
|
expect(jobs.validate_live_models_docker.env.OPENCLAW_LIVE_MODELS).toBe(
|
|
"${{ matrix.models || 'modern' }}",
|
|
);
|
|
expect(jobs.validate_live_models_docker.env.OPENCLAW_LIVE_MAX_MODELS).toBe(
|
|
"${{ matrix.max_models || '6' }}",
|
|
);
|
|
});
|
|
|
|
it("requires new release-profile matrices to use a planner or an explicit allowlist", () => {
|
|
expect(staticProfileMatrixJobs()).toEqual(PROFILE_GATED_STATIC_MATRIX_ALLOWLIST.toSorted());
|
|
});
|
|
});
|