mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
fix(release): enforce lane floor for calver appcast entries
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
import { execSync } from "node:child_process";
|
import { execSync } from "node:child_process";
|
||||||
import { readdirSync, readFileSync } from "node:fs";
|
import { readdirSync, readFileSync } from "node:fs";
|
||||||
import { join, resolve } from "node:path";
|
import { join, resolve } from "node:path";
|
||||||
|
import { pathToFileURL } from "node:url";
|
||||||
import { sparkleBuildFloorsFromShortVersion, type SparkleBuildFloors } from "./sparkle-build.ts";
|
import { sparkleBuildFloorsFromShortVersion, type SparkleBuildFloors } from "./sparkle-build.ts";
|
||||||
|
|
||||||
type PackFile = { path: string };
|
type PackFile = { path: string };
|
||||||
@@ -17,6 +18,8 @@ const requiredPathGroups = [
|
|||||||
];
|
];
|
||||||
const forbiddenPrefixes = ["dist/OpenClaw.app/"];
|
const forbiddenPrefixes = ["dist/OpenClaw.app/"];
|
||||||
const appcastPath = resolve("appcast.xml");
|
const appcastPath = resolve("appcast.xml");
|
||||||
|
const laneBuildMin = 1_000_000_000;
|
||||||
|
const laneFloorAdoptionDateKey = 20260227;
|
||||||
|
|
||||||
type PackageJson = {
|
type PackageJson = {
|
||||||
name?: string;
|
name?: string;
|
||||||
@@ -95,8 +98,7 @@ function extractTag(item: string, tag: string): string | null {
|
|||||||
return regex.exec(item)?.[1]?.trim() ?? null;
|
return regex.exec(item)?.[1]?.trim() ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkAppcastSparkleVersions() {
|
export function collectAppcastSparkleVersionErrors(xml: string): string[] {
|
||||||
const xml = readFileSync(appcastPath, "utf8");
|
|
||||||
const itemMatches = [...xml.matchAll(/<item>([\s\S]*?)<\/item>/g)];
|
const itemMatches = [...xml.matchAll(/<item>([\s\S]*?)<\/item>/g)];
|
||||||
const errors: string[] = [];
|
const errors: string[] = [];
|
||||||
const calverItems: Array<{ title: string; sparkleBuild: number; floors: SparkleBuildFloors }> =
|
const calverItems: Array<{ title: string; sparkleBuild: number; floors: SparkleBuildFloors }> =
|
||||||
@@ -131,15 +133,18 @@ function checkAppcastSparkleVersions() {
|
|||||||
calverItems.push({ title, sparkleBuild: Number(sparkleVersion), floors });
|
calverItems.push({ title, sparkleBuild: Number(sparkleVersion), floors });
|
||||||
}
|
}
|
||||||
|
|
||||||
const adoptionDateKey = calverItems
|
const observedLaneAdoptionDateKey = calverItems
|
||||||
.filter((item) => item.sparkleBuild >= 1_000_000_000)
|
.filter((item) => item.sparkleBuild >= laneBuildMin)
|
||||||
.map((item) => item.floors.dateKey)
|
.map((item) => item.floors.dateKey)
|
||||||
.toSorted((a, b) => a - b)[0];
|
.toSorted((a, b) => a - b)[0];
|
||||||
|
const effectiveLaneAdoptionDateKey =
|
||||||
|
typeof observedLaneAdoptionDateKey === "number"
|
||||||
|
? Math.min(observedLaneAdoptionDateKey, laneFloorAdoptionDateKey)
|
||||||
|
: laneFloorAdoptionDateKey;
|
||||||
|
|
||||||
for (const item of calverItems) {
|
for (const item of calverItems) {
|
||||||
const expectLaneFloor =
|
const expectLaneFloor =
|
||||||
item.sparkleBuild >= 1_000_000_000 ||
|
item.sparkleBuild >= laneBuildMin || item.floors.dateKey >= effectiveLaneAdoptionDateKey;
|
||||||
(typeof adoptionDateKey === "number" && item.floors.dateKey >= adoptionDateKey);
|
|
||||||
const floor = expectLaneFloor ? item.floors.laneFloor : item.floors.legacyFloor;
|
const floor = expectLaneFloor ? item.floors.laneFloor : item.floors.legacyFloor;
|
||||||
if (item.sparkleBuild < floor) {
|
if (item.sparkleBuild < floor) {
|
||||||
const floorLabel = expectLaneFloor ? "lane floor" : "legacy 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) {
|
if (errors.length > 0) {
|
||||||
console.error("release-check: appcast sparkle version validation failed:");
|
console.error("release-check: appcast sparkle version validation failed:");
|
||||||
for (const error of errors) {
|
for (const error of errors) {
|
||||||
@@ -197,4 +208,6 @@ function main() {
|
|||||||
console.log("release-check: npm pack contents look OK.");
|
console.log("release-check: npm pack contents look OK.");
|
||||||
}
|
}
|
||||||
|
|
||||||
main();
|
if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
|
||||||
|
main();
|
||||||
|
}
|
||||||
|
|||||||
28
test/release-check.test.ts
Normal file
28
test/release-check.test.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { collectAppcastSparkleVersionErrors } from "../scripts/release-check.ts";
|
||||||
|
|
||||||
|
function makeItem(shortVersion: string, sparkleVersion: string): string {
|
||||||
|
return `<item><title>${shortVersion}</title><sparkle:shortVersionString>${shortVersion}</sparkle:shortVersionString><sparkle:version>${sparkleVersion}</sparkle:version></item>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("collectAppcastSparkleVersionErrors", () => {
|
||||||
|
it("accepts legacy 9-digit calver builds before lane-floor cutover", () => {
|
||||||
|
const xml = `<rss><channel>${makeItem("2026.2.26", "202602260")}</channel></rss>`;
|
||||||
|
|
||||||
|
expect(collectAppcastSparkleVersionErrors(xml)).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("requires lane-floor builds on and after lane-floor cutover", () => {
|
||||||
|
const xml = `<rss><channel>${makeItem("2026.2.27", "202602270")}</channel></rss>`;
|
||||||
|
|
||||||
|
expect(collectAppcastSparkleVersionErrors(xml)).toEqual([
|
||||||
|
"appcast item '2026.2.27' has sparkle:version 202602270 below lane floor 2026022790.",
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("accepts canonical stable lane builds on and after lane-floor cutover", () => {
|
||||||
|
const xml = `<rss><channel>${makeItem("2026.2.27", "2026022790")}</channel></rss>`;
|
||||||
|
|
||||||
|
expect(collectAppcastSparkleVersionErrors(xml)).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user