openclaw/scripts/plugin-npm-bootstrap-approval.mjs
Peter Steinberger dcb4847e48
feat(release): check publication gates before dispatch (#152470)
* feat(release): check publication gates before dispatch

* fix(release): accept empty draft bodies in publish preflight

* fix(release): wire publish preflight command and shared gate proof

* fix(release): discover draft state and await committed archive proof

* fix(ci): preserve release metadata types and catalog test lifetime

* fix(release): reuse resume authority and settle UI test phases

* style(release): satisfy typed metadata and UI fixture lint
2026-09-19 01:41:43 -07:00

84 lines
3 KiB
JavaScript

import { evaluateReleaseBootstrapGate } from "./lib/release-publish-gates.mts";
const SHA = /^[a-f0-9]{40}$/u;
const PACKAGE = /^@openclaw\/[a-z0-9][a-z0-9._-]*$/u;
export function createStablePluginNpmBootstrapApproval(input) {
const stableSoakWaiver = typeof input.stableSoakWaiver === "string" ? input.stableSoakWaiver : "";
const eligibility = evaluateReleaseBootstrapGate(input);
if (eligibility.status === "FAIL") {
throw new Error(eligibility.message);
}
if (
input.repository !== "openclaw/openclaw" ||
!SHA.test(input.targetSha ?? "") ||
!SHA.test(input.parentWorkflowSha ?? "") ||
!new RegExp(`^release-publish/${input.parentWorkflowSha.slice(0, 12)}-[1-9][0-9]*$`, "u").test(
input.workflowBranch,
) ||
input.workflowFullRef !== `refs/tags/${input.workflowBranch}` ||
!/^[1-9][0-9]*$/u.test(input.parentRunId ?? "") ||
!Number.isSafeInteger(input.parentRunAttempt) ||
input.parentRunAttempt < 1 ||
!/^[1-9][0-9]*$/u.test(input.validationRunId ?? "") ||
!Number.isSafeInteger(input.validationRunAttempt) ||
input.validationRunAttempt < 1
) {
throw new Error(
"Stable npm bootstrap requires an exact protected parent and validation tuple.",
);
}
if (
!Array.isArray(input.packages) ||
input.packages.length === 0 ||
input.packages.some((name) => typeof name !== "string" || !PACKAGE.test(name)) ||
new Set(input.packages).size !== input.packages.length
) {
throw new Error("Stable npm bootstrap requires a unique publishable @openclaw package set.");
}
return {
version: 1,
kind: "npm-stable-bootstrap",
repository: input.repository,
parentRunId: input.parentRunId,
parentRunAttempt: input.parentRunAttempt,
workflowBranch: input.workflowBranch,
workflowFullRef: input.workflowFullRef,
parentWorkflowSha: input.parentWorkflowSha,
releaseTag: input.releaseTag,
targetSha: input.targetSha,
publishTag: input.publishTag,
releaseProfile: input.releaseProfile,
stableSoakWaiver,
validationRunId: input.validationRunId,
validationRunAttempt: input.validationRunAttempt,
packages: input.packages.toSorted(),
};
}
export function validateStablePluginNpmBootstrapApproval(approval, expected) {
const canonical = createStablePluginNpmBootstrapApproval(approval);
if (JSON.stringify(approval) !== JSON.stringify(canonical)) {
throw new Error("Stable npm bootstrap approval is not canonical.");
}
for (const key of [
"repository",
"parentRunId",
"parentRunAttempt",
"workflowBranch",
"workflowFullRef",
"parentWorkflowSha",
"targetSha",
"publishTag",
]) {
if (approval[key] !== expected[key]) {
throw new Error(`Stable npm bootstrap approval ${key} does not match this publication.`);
}
}
if (
approval.releaseTag !== `v${expected.packageVersion}` ||
!approval.packages.includes(expected.packageName)
) {
throw new Error("Stable npm bootstrap approval does not cover this package and version.");
}
}