crabpot/scripts/package-availability.mjs
Vincent Koc d68c82e8d7
fix(reports): flag missing externalized OpenClaw npm artifacts
Handle missing npm artifacts as P0s for externalized OpenClaw fixtures while source-packing bundled Matrix/Mattermost from the resolved OpenClaw checkout.
2026-05-04 20:21:43 -07:00

128 lines
4.5 KiB
JavaScript

import { existsSync, readFileSync } from "node:fs";
import { mkdir, writeFile } from "node:fs/promises";
import path from "node:path";
import { repoRoot } from "./manifest-lib.mjs";
export const defaultPackageAvailabilityPath = path.join(
repoRoot,
"reports/crabpot-package-availability.json",
);
export function packageAvailabilityCode() {
return "package-npm-pack-unavailable";
}
export function readPackageAvailabilityReport(reportPath = defaultPackageAvailabilityPath) {
if (!existsSync(reportPath)) {
return null;
}
return JSON.parse(readFileSync(reportPath, "utf8"));
}
export async function writePackageAvailabilityReport(report, reportPath = defaultPackageAvailabilityPath) {
await mkdir(path.dirname(reportPath), { recursive: true });
await writeFile(reportPath, `${JSON.stringify(normalizePackageAvailabilityReport(report), null, 2)}\n`, "utf8");
}
export function normalizePackageAvailabilityReport(report = {}) {
const failures = Array.isArray(report.failures) ? report.failures : [];
return {
generatedAt: report.generatedAt ?? "deterministic",
fixtureSet: report.fixtureSet ?? "all",
pluginTrack: report.pluginTrack ?? "manifest",
summary: {
failureCount: failures.length,
openclawFailureCount: failures.filter((failure) => failure.openclawPackage).length,
fallbackCount: failures.filter((failure) => failure.fallbackVersion).length,
},
failures,
};
}
export function packageAvailabilityActionableFailures(report, options = {}) {
const sourcePackFixtures = sourcePackFixtureIds(options.manifest);
return (report?.failures ?? [])
.filter((failure) => failure.openclawPackage)
.filter((failure) => failure.artifactSource !== "source-pack")
.filter((failure) => !sourcePackFixtures.has(failure.fixture));
}
export function packageAvailabilityIssues(report, options = {}) {
return packageAvailabilityActionableFailures(report, options)
.map((failure) => ({
id: issueIdForPackageFailure(failure),
fixture: failure.fixture,
severity: "P0",
owner: "plugin",
code: packageAvailabilityCode(),
decision: "plugin-release-fix",
status: "blocking",
issueClass: "live-issue",
live: true,
deprecated: false,
compatStatus: "none",
title: `${failure.fixture}: OpenClaw npm artifact is unavailable for ${failure.requestedTag ?? "requested track"}`,
evidence: [
`${failure.packageName}@${failure.requestedTag ?? failure.requestedVersion ?? "unknown"}`,
failure.message,
...(failure.fallbackVersion
? [`fallback:${failure.packageName}@${failure.fallbackVersion}`]
: []),
].filter(Boolean),
compatRecord: null,
runtimeCoverage: null,
}));
}
export function packageAvailabilityDecisions(report, options = {}) {
return packageAvailabilityActionableFailures(report, options)
.map((failure) => ({
fixture: failure.fixture,
decision: "plugin-release-fix",
seam: "npm-artifact",
action: `Restore the OpenClaw npm artifact for ${failure.packageName}@${failure.requestedTag ?? failure.requestedVersion ?? "requested track"} before trusting this track as release-complete.`,
evidence: failure.message,
}));
}
function sourcePackFixtureIds(manifest) {
return new Set(
(manifest?.fixtures ?? [])
.filter((fixture) => fixture.package?.artifactSource === "source-pack")
.map((fixture) => fixture.id),
);
}
export function mergePackageAvailabilityIntoSummary(summary, issues) {
const openIssues = issues.filter((issue) => issue.status !== "runtime-covered");
return {
...summary,
issueCount: issues.length,
openIssueCount: openIssues.length,
p0IssueCount: issues.filter((issue) => issue.severity === "P0").length,
openP0IssueCount: openIssues.filter((issue) => issue.severity === "P0").length,
liveIssueCount: issues.filter((issue) => issue.issueClass === "live-issue").length,
liveP0IssueCount: issues.filter((issue) => issue.issueClass === "live-issue" && issue.severity === "P0").length,
};
}
function issueIdForPackageFailure(failure) {
return `CRABPOT-${stableHash([
failure.fixture,
packageAvailabilityCode(),
failure.packageName,
failure.requestedTag ?? "",
failure.requestedVersion ?? "",
failure.message,
].join("\n"))}`;
}
function stableHash(value) {
let hash = 2166136261;
for (let index = 0; index < value.length; index += 1) {
hash ^= value.charCodeAt(index);
hash = Math.imul(hash, 16777619);
}
return (hash >>> 0).toString(16).toUpperCase().padStart(8, "0");
}