fix(release): enforce lane floor for calver appcast entries

This commit is contained in:
Ayaan Zaidi
2026-02-28 10:18:10 +05:30
committed by Ayaan Zaidi
parent 10f1be1072
commit f29c642c13
2 changed files with 48 additions and 7 deletions

View File

@@ -3,6 +3,7 @@
import { execSync } from "node:child_process";
import { readdirSync, readFileSync } from "node:fs";
import { join, resolve } from "node:path";
import { pathToFileURL } from "node:url";
import { sparkleBuildFloorsFromShortVersion, type SparkleBuildFloors } from "./sparkle-build.ts";
type PackFile = { path: string };
@@ -17,6 +18,8 @@ const requiredPathGroups = [
];
const forbiddenPrefixes = ["dist/OpenClaw.app/"];
const appcastPath = resolve("appcast.xml");
const laneBuildMin = 1_000_000_000;
const laneFloorAdoptionDateKey = 20260227;
type PackageJson = {
name?: string;
@@ -95,8 +98,7 @@ function extractTag(item: string, tag: string): string | null {
return regex.exec(item)?.[1]?.trim() ?? null;
}
function checkAppcastSparkleVersions() {
const xml = readFileSync(appcastPath, "utf8");
export function collectAppcastSparkleVersionErrors(xml: string): string[] {
const itemMatches = [...xml.matchAll(/<item>([\s\S]*?)<\/item>/g)];
const errors: string[] = [];
const calverItems: Array<{ title: string; sparkleBuild: number; floors: SparkleBuildFloors }> =
@@ -131,15 +133,18 @@ function checkAppcastSparkleVersions() {
calverItems.push({ title, sparkleBuild: Number(sparkleVersion), floors });
}
const adoptionDateKey = calverItems
.filter((item) => item.sparkleBuild >= 1_000_000_000)
const observedLaneAdoptionDateKey = calverItems
.filter((item) => item.sparkleBuild >= laneBuildMin)
.map((item) => item.floors.dateKey)
.toSorted((a, b) => a - b)[0];
const effectiveLaneAdoptionDateKey =
typeof observedLaneAdoptionDateKey === "number"
? Math.min(observedLaneAdoptionDateKey, laneFloorAdoptionDateKey)
: laneFloorAdoptionDateKey;
for (const item of calverItems) {
const expectLaneFloor =
item.sparkleBuild >= 1_000_000_000 ||
(typeof adoptionDateKey === "number" && item.floors.dateKey >= adoptionDateKey);
item.sparkleBuild >= laneBuildMin || item.floors.dateKey >= effectiveLaneAdoptionDateKey;
const floor = expectLaneFloor ? item.floors.laneFloor : item.floors.legacyFloor;
if (item.sparkleBuild < floor) {
const floorLabel = expectLaneFloor ? "lane floor" : "legacy floor";
@@ -149,6 +154,12 @@ function checkAppcastSparkleVersions() {
}
}
return errors;
}
function checkAppcastSparkleVersions() {
const xml = readFileSync(appcastPath, "utf8");
const errors = collectAppcastSparkleVersionErrors(xml);
if (errors.length > 0) {
console.error("release-check: appcast sparkle version validation failed:");
for (const error of errors) {
@@ -197,4 +208,6 @@ function main() {
console.log("release-check: npm pack contents look OK.");
}
main();
if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
main();
}