mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-24 14:29:35 +00:00
Switch release train handling to YYYY.M.PATCH monthly patch numbering, preserve pre-transition compatibility, and pin the June 2026 stable/beta floor at 2026.6.5 after the published beta. Verification: - node scripts/run-vitest.mjs run test/appcast.test.ts test/release-check.test.ts test/scripts/package-mac-app.test.ts test/scripts/package-mac-dist.test.ts test/openclaw-npm-release-check.test.ts test/npm-publish-plan.test.ts src/infra/npm-registry-spec.test.ts src/infra/clawhub.test.ts src/plugins/clawhub.test.ts test/plugin-npm-release.test.ts test/scripts/ios-version.test.ts test/scripts/ios-pin-version.test.ts - node --import tsx scripts/plugin-npm-release-check.ts --base-ref origin/main --head-ref HEAD - node --import tsx scripts/plugin-clawhub-release-check.ts --base-ref origin/main --head-ref HEAD - git diff --check origin/main...HEAD - .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main --no-web-search
71 lines
2.7 KiB
TypeScript
71 lines
2.7 KiB
TypeScript
// Appcast tests validate generated update appcast metadata.
|
|
import { readFileSync } from "node:fs";
|
|
import { describe, expect, it } from "vitest";
|
|
import { canonicalSparkleBuildFromVersion } from "../scripts/sparkle-build.ts";
|
|
|
|
const APPCAST_URL = new URL("../appcast.xml", import.meta.url);
|
|
|
|
type AppcastItem = {
|
|
raw: string;
|
|
shortVersion: string | null;
|
|
sparkleVersion: number | null;
|
|
};
|
|
|
|
describe("canonicalSparkleBuildFromVersion", () => {
|
|
it("keeps pre-transition appcast builds on the legacy date key", () => {
|
|
expect(canonicalSparkleBuildFromVersion("2026.6.2")).toBe(2026060290);
|
|
});
|
|
|
|
it("uses monthly patch build keys from the June 2026 floor onward", () => {
|
|
expect(canonicalSparkleBuildFromVersion("2026.6.5-beta.2")).toBe(2606000502);
|
|
expect(canonicalSparkleBuildFromVersion("2026.6.32-beta.1")).toBe(2606003201);
|
|
expect(canonicalSparkleBuildFromVersion("2026.6.32")).toBe(2606003290);
|
|
});
|
|
});
|
|
|
|
function parseItems(appcast: string): AppcastItem[] {
|
|
return [...appcast.matchAll(/<item>([\s\S]*?)<\/item>/g)].map((match) => {
|
|
const raw = match[1] ?? "";
|
|
const shortVersion =
|
|
raw.match(/<sparkle:shortVersionString>([^<]+)<\/sparkle:shortVersionString>/)?.[1] ?? null;
|
|
const sparkleVersionText = raw.match(/<sparkle:version>([^<]+)<\/sparkle:version>/)?.[1] ?? "";
|
|
const sparkleVersion = Number.parseInt(sparkleVersionText, 10);
|
|
return {
|
|
raw,
|
|
shortVersion,
|
|
sparkleVersion: Number.isFinite(sparkleVersion) ? sparkleVersion : null,
|
|
};
|
|
});
|
|
}
|
|
|
|
describe("appcast.xml", () => {
|
|
it("keeps every appcast entry on the canonical sparkle build for its version", () => {
|
|
const appcast = readFileSync(APPCAST_URL, "utf8");
|
|
const items = parseItems(appcast);
|
|
expect(items.length).toBeGreaterThan(0);
|
|
|
|
for (const item of items) {
|
|
if (item.shortVersion === null || item.sparkleVersion === null) {
|
|
throw new Error(`Appcast entry missing version fields: ${item.raw}`);
|
|
}
|
|
expect(item.sparkleVersion).toBe(canonicalSparkleBuildFromVersion(item.shortVersion));
|
|
}
|
|
});
|
|
|
|
it("keeps the first stable appcast entry aligned with the newest stable build", () => {
|
|
const appcast = readFileSync(APPCAST_URL, "utf8");
|
|
const stableItems = parseItems(appcast).filter(
|
|
(item) => item.sparkleVersion !== null && item.sparkleVersion % 100 === 90,
|
|
);
|
|
|
|
expect(stableItems.length).toBeGreaterThan(0);
|
|
const firstStable = stableItems[0];
|
|
const newestStable = [...stableItems].toSorted(
|
|
(left, right) => (right.sparkleVersion ?? 0) - (left.sparkleVersion ?? 0),
|
|
)[0];
|
|
|
|
expect(firstStable.sparkleVersion).toBe(newestStable.sparkleVersion);
|
|
expect(firstStable.shortVersion).toBe(newestStable.shortVersion);
|
|
});
|
|
});
|