Files
adolf/test/scripts/openclaw-npm-extended-stable-workflow.test.ts
alvis bedb527145
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Vendor OpenClaw source as Adolf fork baseline
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11),
free to diverge. Tree copied sans upstream .git; upstream remote added for
future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19.
Preserves docs/ARCHITECTURE.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
2026-07-05 09:36:54 +00:00

152 lines
6.3 KiB
TypeScript

import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";
import { parse } from "yaml";
const workflowPath = ".github/workflows/openclaw-npm-release.yml";
type Step = { env?: Record<string, string>; id?: string; if?: string; name?: string; run?: string };
type Job = { environment?: string; steps?: Step[] };
type Workflow = {
on?: {
workflow_dispatch?: {
inputs?: {
bypass_extended_stable_guard?: { default?: boolean; type?: string };
npm_dist_tag?: { options?: string[] };
};
};
};
jobs?: Record<string, Job>;
};
function workflow(): Workflow {
return parse(readFileSync(workflowPath, "utf8")) as Workflow;
}
function step(job: Job | undefined, name: string): Step {
const found = job?.steps?.find((candidate) => candidate.name === name);
if (!found) {
throw new Error(`Missing workflow step: ${name}`);
}
return found;
}
describe("minimal npm extended-stable workflow", () => {
it("adds extended-stable without adding policy or verifier contracts", () => {
const raw = readFileSync(workflowPath, "utf8");
const parsed = workflow();
expect(parsed.on?.workflow_dispatch?.inputs?.npm_dist_tag?.options).toEqual([
"alpha",
"beta",
"latest",
"extended-stable",
]);
for (const forbidden of [
"release-policy",
"policyMode",
"release-operation-verifier",
"external_contract_revision",
"stable-lines.json",
]) {
expect(raw).not.toContain(forbidden);
}
});
it("reuses the v1 preflight tarball and guards all three extended-stable gates", () => {
const parsed = workflow();
const raw = readFileSync(workflowPath, "utf8");
expect(raw).toContain("version: 1");
expect(raw).toContain("openclaw-npm-preflight-${{ inputs.tag }}");
expect(raw.match(/openclaw-npm-extended-stable-release\.mjs validate-request/g)).toHaveLength(
3,
);
expect(step(parsed.jobs?.preflight_openclaw_npm, "Validate npm release request").run).toContain(
"openclaw-npm-extended-stable-release.mjs validate-request",
);
expect(
step(parsed.jobs?.validate_publish_request, "Validate npm release request").run,
).toContain("openclaw-npm-extended-stable-release.mjs validate-request");
expect(step(parsed.jobs?.publish_openclaw_npm, "Recheck npm release request").run).toContain(
"openclaw-npm-extended-stable-release.mjs validate-request",
);
expect(
parsed.jobs?.validate_publish_request?.steps?.map((candidate) => candidate.name),
).not.toContain("Setup Node environment");
});
it("threads an explicit, default-off extended-stable bypass through every policy gate", () => {
const parsed = workflow();
const input = parsed.on?.workflow_dispatch?.inputs?.bypass_extended_stable_guard;
expect(input).toMatchObject({ default: false, type: "boolean" });
const policySteps = [
step(parsed.jobs?.preflight_openclaw_npm, "Validate npm release request"),
step(parsed.jobs?.validate_publish_request, "Validate npm release request"),
step(parsed.jobs?.publish_openclaw_npm, "Recheck npm release request"),
step(parsed.jobs?.publish_openclaw_npm, "Publish"),
];
for (const policyStep of policySteps) {
expect(policyStep.env?.BYPASS_EXTENDED_STABLE_GUARD).toBe(
"${{ inputs.bypass_extended_stable_guard }}",
);
}
const trustedRef = step(
parsed.jobs?.validate_publish_request,
"Require trusted workflow ref for publish",
);
expect(trustedRef.env?.BYPASS_EXTENDED_STABLE_GUARD).toBeUndefined();
expect(trustedRef.run).not.toContain("BYPASS_EXTENDED_STABLE_GUARD");
expect(trustedRef.run).toContain('"${WORKFLOW_REF}" == refs/heads/extended-stable/*');
const summary = step(
parsed.jobs?.publish_openclaw_npm,
"Summarize extended-stable npm publication",
);
expect(summary.env?.BYPASS_EXTENDED_STABLE_GUARD).toBe(
"${{ inputs.bypass_extended_stable_guard }}",
);
expect(summary.run).toContain("Extended-stable guard bypass: ${BYPASS_EXTENDED_STABLE_GUARD}");
});
it("authenticates exact extended-stable run and Full Validation identities", () => {
const raw = readFileSync(workflowPath, "utf8");
expect(raw).toContain("--json workflowName,headBranch,headSha,event,conclusion,url");
expect(raw).toContain("--json workflowName,headBranch,headSha,event,status,conclusion,url");
expect(raw.match(/openclaw-npm-extended-stable-release\.mjs verify-run/g)).toHaveLength(2);
expect(raw).toContain("openclaw-npm-extended-stable-release.mjs verify-manifest");
});
it("captures selector fail closed, publishes extended-stable, retries, and summarizes", () => {
const parsed = workflow();
const publish = parsed.jobs?.publish_openclaw_npm;
const capture = step(publish, "Capture previous extended-stable selector");
const readback = step(publish, "Verify extended-stable registry readback");
const summary = step(publish, "Summarize extended-stable npm publication");
expect(capture.run).toContain("openclaw-npm-extended-stable-release.mjs capture-selector");
expect(step(publish, "Publish").run).toContain("openclaw-npm-publish.sh");
expect(readback.run).toContain("openclaw-npm-extended-stable-release.mjs verify-readback");
expect(summary.if).toContain("always()");
expect(summary.run).toContain("openclaw-npm-extended-stable-release.mjs repair-command");
expect(summary.run).toContain('EXPECTED_VERSION="$RELEASE_TAG"');
expect(publish?.environment).toBe("npm-release");
});
it("publishes only the tarball path verified from the preflight manifest", () => {
const publish = workflow().jobs?.publish_openclaw_npm;
const provenance = step(publish, "Verify prepared tarball provenance");
const publishStep = step(publish, "Publish");
expect(provenance.run).toContain(
'ARTIFACT_TARBALL_PATH="preflight-tarball/$ARTIFACT_TARBALL_NAME"',
);
expect(provenance.run).toContain('echo "tarball_path=$ARTIFACT_TARBALL_PATH"');
expect(publishStep.env?.PUBLISH_TARBALL_PATH).toBe(
"${{ steps.preflight_provenance.outputs.tarball_path }}",
);
expect(publish?.steps?.map((candidate) => candidate.name)).not.toContain(
"Resolve publish tarball",
);
expect(readFileSync(workflowPath, "utf8")).not.toContain(
"find preflight-tarball -type f -name '*.tgz'",
);
});
});