ci: split plugin prerelease validation

This commit is contained in:
Peter Steinberger
2026-04-29 08:20:06 +01:00
parent 9b1967e5ef
commit a0fd105e5e
9 changed files with 632 additions and 346 deletions

View File

@@ -17,8 +17,12 @@ function readFullReleaseValidationWorkflow() {
return parse(readFileSync(".github/workflows/full-release-validation.yml", "utf8"));
}
function readPluginPrereleaseWorkflow() {
return parse(readFileSync(".github/workflows/plugin-prerelease.yml", "utf8"));
}
describe("scripts/lib/plugin-prerelease-test-plan.mjs", () => {
it("covers every pre-release plugin skill surface in mega CI", () => {
it("covers every pre-release plugin skill surface in the plugin prerelease plan", () => {
const plan = assertPluginPrereleaseTestPlanComplete();
expect(plan.surfaces).toEqual(
@@ -107,77 +111,103 @@ describe("scripts/lib/plugin-prerelease-test-plan.mjs", () => {
expect(script).toContain("scan_logs_for_unexpected_errors");
});
it("wires the full plugin prerelease plan into the mega CI workflow", () => {
it("wires the full plugin prerelease plan into its release workflow", () => {
const workflow = readCiWorkflow();
const preflight = workflow.jobs.preflight;
const extensionShard = workflow.jobs["checks-node-extensions-shard"];
const extensionSuite = workflow.jobs["checks-node-extensions"];
const staticShard = workflow.jobs["plugin-prerelease-static-shard"];
const dockerSuite = workflow.jobs["plugin-prerelease-docker-suite"];
const suite = workflow.jobs["plugin-prerelease-suite"];
const pluginWorkflow = readPluginPrereleaseWorkflow();
const pluginPreflight = pluginWorkflow.jobs.preflight;
const staticShard = pluginWorkflow.jobs["plugin-prerelease-static-shard"];
const nodeShard = pluginWorkflow.jobs["plugin-prerelease-node-shard"];
const extensionShard = pluginWorkflow.jobs["plugin-prerelease-extension-shard"];
const dockerSuite = pluginWorkflow.jobs["plugin-prerelease-docker-suite"];
const suite = pluginWorkflow.jobs["plugin-prerelease-suite"];
const releaseWorkflow = readFullReleaseValidationWorkflow();
const manifestScript = preflight.steps.find((step) => step.name === "Build CI manifest").run;
const manifestEnv = preflight.steps.find((step) => step.name === "Build CI manifest").env;
const pluginManifestScript = pluginPreflight.steps.find(
(step) => step.name === "Build plugin prerelease manifest",
).run;
const normalCiScript = releaseWorkflow.jobs.normal_ci.steps.find(
(step) => step.name === "Dispatch and monitor CI",
).run;
const pluginPrereleaseScript = releaseWorkflow.jobs.plugin_prerelease.steps.find(
(step) => step.name === "Dispatch and monitor plugin prerelease",
).run;
expect(preflight.outputs).toMatchObject({
plugin_prerelease_docker_lanes:
"${{ steps.manifest.outputs.plugin_prerelease_docker_lanes }}",
plugin_prerelease_ref: "${{ steps.manifest.outputs.plugin_prerelease_ref }}",
plugin_prerelease_static_matrix:
"${{ steps.manifest.outputs.plugin_prerelease_static_matrix }}",
run_plugin_prerelease_suite: "${{ steps.manifest.outputs.run_plugin_prerelease_suite }}",
run_checks_node_extensions: "${{ steps.manifest.outputs.run_checks_node_extensions }}",
});
expect(workflow.jobs["plugin-prerelease-static-shard"]).toBeUndefined();
expect(workflow.jobs["plugin-prerelease-docker-suite"]).toBeUndefined();
expect(workflow.jobs["plugin-prerelease-suite"]).toBeUndefined();
expect(workflow.jobs["checks-node-extensions-shard"]).toBeUndefined();
expect(preflight.outputs).not.toHaveProperty("run_plugin_prerelease_suite");
expect(preflight.outputs).not.toHaveProperty("run_checks_node_extensions");
expect(staticShard).toMatchObject({
name: "${{ matrix.check_name }}",
"runs-on": "blacksmith-8vcpu-ubuntu-2404",
});
expect(workflow.on.workflow_dispatch.inputs.full_release_validation).toMatchObject({
default: false,
type: "boolean",
});
expect(workflow.on.workflow_dispatch.inputs.full_release_validation).toBeUndefined();
expect(workflow.on.workflow_dispatch.inputs.include_android).toMatchObject({
default: false,
type: "boolean",
});
expect(manifestEnv).toMatchObject({
OPENCLAW_CI_FULL_RELEASE_VALIDATION:
"${{ github.event_name == 'workflow_dispatch' && inputs.full_release_validation && 'true' || 'false' }}",
OPENCLAW_CI_RUN_ANDROID:
"${{ github.event_name == 'workflow_dispatch' && (inputs.full_release_validation || inputs.include_android) && 'true' || steps.changed_scope.outputs.run_android || 'false' }}",
"${{ github.event_name == 'workflow_dispatch' && inputs.include_android && 'true' || steps.changed_scope.outputs.run_android || 'false' }}",
});
expect(manifestScript).toContain("const isFullReleaseValidationCiRun =");
expect(manifestScript).toContain(
"parseBoolean(process.env.OPENCLAW_CI_FULL_RELEASE_VALIDATION)",
);
expect(manifestScript).toContain(
"let runPluginPrereleaseSuite =\n isFullReleaseValidationCiRun && runNodeFull && isCanonicalRepository;",
);
expect(manifestScript).toContain("run_checks_node_extensions: runReleaseOnlyPluginSuites");
expect(manifestEnv).not.toHaveProperty("OPENCLAW_CI_FULL_RELEASE_VALIDATION");
expect(manifestScript).toContain("includeReleaseOnlyPluginShards: false");
expect(manifestScript).not.toContain("plugin-prerelease-test-plan.mjs");
expect(normalCiScript).toContain(
'dispatch_and_wait ci.yml -f target_ref="$TARGET_SHA" -f full_release_validation=true',
'dispatch_and_wait ci.yml -f target_ref="$TARGET_SHA" -f include_android=true',
);
expect(manifestScript).toContain("await import(");
expect(manifestScript).toContain('"./scripts/lib/plugin-prerelease-test-plan.mjs"');
expect(manifestScript).not.toContain('} from "./scripts/lib/plugin-prerelease-test-plan.mjs";');
expect(manifestScript).toContain(
"Plugin prerelease plan unavailable in target ref; skipping plugin prerelease suite.",
expect(normalCiScript).not.toContain("full_release_validation=true");
expect(pluginPrereleaseScript).toContain(
'dispatch_and_wait plugin-prerelease.yml -f target_ref="$TARGET_SHA" -f expected_sha="$TARGET_SHA"',
);
expect(pluginManifestScript).toContain("await import(");
expect(pluginManifestScript).toContain('"./scripts/lib/plugin-prerelease-test-plan.mjs"');
expect(pluginManifestScript).toContain('"./scripts/lib/extension-test-plan.mjs"');
expect(pluginManifestScript).toContain('"./scripts/lib/ci-node-test-plan.mjs"');
expect(pluginManifestScript).toContain('shard.shardName === "agentic-plugins"');
expect(pluginManifestScript).toContain(
"Plugin prerelease plan unavailable in target ref; skipping static and Docker plugin prerelease lanes.",
);
expect(pluginWorkflow.on.workflow_dispatch.inputs.target_ref).toMatchObject({
default: "main",
type: "string",
});
expect(pluginPreflight.outputs).toMatchObject({
checkout_revision: "${{ steps.manifest.outputs.checkout_revision }}",
plugin_prerelease_docker_lanes:
"${{ steps.manifest.outputs.plugin_prerelease_docker_lanes }}",
plugin_prerelease_extension_matrix:
"${{ steps.manifest.outputs.plugin_prerelease_extension_matrix }}",
plugin_prerelease_node_matrix: "${{ steps.manifest.outputs.plugin_prerelease_node_matrix }}",
plugin_prerelease_static_matrix:
"${{ steps.manifest.outputs.plugin_prerelease_static_matrix }}",
run_plugin_prerelease_docker: "${{ steps.manifest.outputs.run_plugin_prerelease_docker }}",
run_plugin_prerelease_extensions:
"${{ steps.manifest.outputs.run_plugin_prerelease_extensions }}",
run_plugin_prerelease_node: "${{ steps.manifest.outputs.run_plugin_prerelease_node }}",
run_plugin_prerelease_static: "${{ steps.manifest.outputs.run_plugin_prerelease_static }}",
run_plugin_prerelease_suite: "${{ steps.manifest.outputs.run_plugin_prerelease_suite }}",
});
expect(staticShard.strategy.matrix).toBe(
"${{ fromJson(needs.preflight.outputs.plugin_prerelease_static_matrix) }}",
);
expect(extensionShard.if).toBe("needs.preflight.outputs.run_checks_node_extensions == 'true'");
expect(extensionSuite.if).toBe(
"${{ !cancelled() && always() && needs.preflight.outputs.run_checks_node_extensions == 'true' }}",
expect(nodeShard.strategy.matrix).toBe(
"${{ fromJson(needs.preflight.outputs.plugin_prerelease_node_matrix) }}",
);
expect(extensionShard.if).toBe(
"needs.preflight.outputs.run_plugin_prerelease_extensions == 'true'",
);
expect(extensionShard.strategy.matrix).toBe(
"${{ fromJson(needs.preflight.outputs.plugin_prerelease_extension_matrix) }}",
);
expect(
staticShard.steps.find((step) => step.name === "Run plugin prerelease static shard").run,
).toContain('bash -c "$PLUGIN_PRERELEASE_COMMAND"');
expect(dockerSuite).toMatchObject({
if: "needs.preflight.outputs.run_plugin_prerelease_suite == 'true'",
if: "needs.preflight.outputs.run_plugin_prerelease_docker == 'true'",
needs: ["preflight"],
uses: "./.github/workflows/openclaw-live-and-e2e-checks-reusable.yml",
with: {
@@ -187,13 +217,15 @@ describe("scripts/lib/plugin-prerelease-test-plan.mjs", () => {
include_release_path_suites: false,
include_repo_e2e: false,
live_models_only: false,
ref: "${{ needs.preflight.outputs.plugin_prerelease_ref }}",
ref: "${{ needs.preflight.outputs.checkout_revision }}",
},
});
expect(dockerSuite.secrets).toBeUndefined();
expect(suite.needs).toEqual([
"preflight",
"plugin-prerelease-static-shard",
"plugin-prerelease-node-shard",
"plugin-prerelease-extension-shard",
"plugin-prerelease-docker-suite",
]);
});