mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-03 17:53:39 +00:00
* 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
84 lines
3 KiB
JavaScript
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.");
|
|
}
|
|
}
|