mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-04 07:41:15 +08:00
172 lines
5.3 KiB
TypeScript
172 lines
5.3 KiB
TypeScript
// Ios Version script supports OpenClaw repository automation.
|
|
import { readFileSync } from "node:fs";
|
|
import path from "node:path";
|
|
import { parseReleaseVersion } from "./npm-publish-plan.mjs";
|
|
|
|
const IOS_CHANGELOG_FILE = "apps/ios/CHANGELOG.md";
|
|
|
|
type ResolvedIosVersion = {
|
|
canonicalVersion: string;
|
|
marketingVersion: string;
|
|
buildVersion: string;
|
|
changelogPath: string;
|
|
versionSource: "explicit" | "package";
|
|
versionSourcePath: string | null;
|
|
};
|
|
|
|
type SyncIosVersioningMode = "check" | "write";
|
|
|
|
function parsePinnedReleaseVersion(rawVersion: string): string | null {
|
|
const parsed = parseReleaseVersion(rawVersion.trim());
|
|
if (!parsed || parsed.version !== parsed.baseVersion) {
|
|
return null;
|
|
}
|
|
return parsed.baseVersion;
|
|
}
|
|
|
|
export function normalizePinnedIosVersion(rawVersion: string): string {
|
|
const trimmed = rawVersion.trim();
|
|
if (!trimmed) {
|
|
throw new Error("Missing iOS release version.");
|
|
}
|
|
|
|
const pinnedVersion = parsePinnedReleaseVersion(trimmed);
|
|
if (!pinnedVersion) {
|
|
throw new Error(`Invalid iOS version '${rawVersion}'. Expected release version like 2026.6.5.`);
|
|
}
|
|
|
|
return pinnedVersion;
|
|
}
|
|
|
|
export function normalizeGatewayVersionToPinnedIosVersion(rawVersion: string): string {
|
|
const trimmed = rawVersion.trim().replace(/^v/u, "");
|
|
if (!trimmed) {
|
|
throw new Error("Missing root package.json version.");
|
|
}
|
|
|
|
const parsed = parseReleaseVersion(trimmed);
|
|
if (!parsed) {
|
|
throw new Error(
|
|
`Invalid gateway version '${rawVersion}'. Expected YYYY.M.PATCH, YYYY.M.PATCH-alpha.N, YYYY.M.PATCH-beta.N, or YYYY.M.PATCH-N.`,
|
|
);
|
|
}
|
|
|
|
return parsed.baseVersion;
|
|
}
|
|
|
|
function rootPackageJsonPath(rootDir = path.resolve(".")): string {
|
|
return path.join(rootDir, "package.json");
|
|
}
|
|
|
|
function readRootPackageVersion(rootDir = path.resolve(".")): string {
|
|
const packageJsonPath = rootPackageJsonPath(rootDir);
|
|
const parsed = JSON.parse(readFileSync(packageJsonPath, "utf8")) as { version?: unknown };
|
|
const version = typeof parsed.version === "string" ? parsed.version.trim() : "";
|
|
if (!version) {
|
|
throw new Error(`Missing package.json version in ${packageJsonPath}.`);
|
|
}
|
|
return version;
|
|
}
|
|
|
|
export function resolveGatewayVersionForIosRelease(rootDir = path.resolve(".")): {
|
|
packageVersion: string;
|
|
pinnedIosVersion: string;
|
|
} {
|
|
const packageVersion = readRootPackageVersion(rootDir);
|
|
return {
|
|
packageVersion,
|
|
pinnedIosVersion: normalizeGatewayVersionToPinnedIosVersion(packageVersion),
|
|
};
|
|
}
|
|
|
|
export function resolveIosVersion(
|
|
rootDir = path.resolve("."),
|
|
options?: { releaseVersion?: string | null },
|
|
): ResolvedIosVersion {
|
|
const changelogPath = path.join(rootDir, IOS_CHANGELOG_FILE);
|
|
const explicitReleaseVersion = options?.releaseVersion?.trim() ?? "";
|
|
const canonicalVersion = explicitReleaseVersion
|
|
? normalizePinnedIosVersion(explicitReleaseVersion)
|
|
: resolveGatewayVersionForIosRelease(rootDir).pinnedIosVersion;
|
|
|
|
return {
|
|
canonicalVersion,
|
|
marketingVersion: canonicalVersion,
|
|
buildVersion: "1",
|
|
changelogPath,
|
|
versionSource: explicitReleaseVersion ? "explicit" : "package",
|
|
versionSourcePath: explicitReleaseVersion ? null : rootPackageJsonPath(rootDir),
|
|
};
|
|
}
|
|
|
|
function matchChangelogHeading(line: string, heading: string): boolean {
|
|
const normalized = line.trim();
|
|
return normalized === `## ${heading}` || normalized.startsWith(`## ${heading} - `);
|
|
}
|
|
|
|
export function extractChangelogSection(content: string, heading: string): string | null {
|
|
const lines = content.split(/\r?\n/);
|
|
const startIndex = lines.findIndex((line) => matchChangelogHeading(line, heading));
|
|
if (startIndex === -1) {
|
|
return null;
|
|
}
|
|
|
|
let endIndex = lines.length;
|
|
for (let index = startIndex + 1; index < lines.length; index += 1) {
|
|
if (lines[index]?.startsWith("## ")) {
|
|
endIndex = index;
|
|
break;
|
|
}
|
|
}
|
|
|
|
const body = lines
|
|
.slice(startIndex + 1, endIndex)
|
|
.join("\n")
|
|
.trim();
|
|
return body || null;
|
|
}
|
|
|
|
export function renderIosReleaseNotes(
|
|
version: ResolvedIosVersion,
|
|
changelogContent: string,
|
|
): string {
|
|
const candidateHeadings = [version.canonicalVersion, "Unreleased"];
|
|
|
|
for (const heading of candidateHeadings) {
|
|
const body = extractChangelogSection(changelogContent, heading);
|
|
if (body) {
|
|
return `${body}\n`;
|
|
}
|
|
}
|
|
|
|
throw new Error(
|
|
`Unable to find iOS changelog notes for ${version.canonicalVersion}. Add a matching section to ${IOS_CHANGELOG_FILE}.`,
|
|
);
|
|
}
|
|
|
|
export function syncIosVersioning(params?: {
|
|
mode?: SyncIosVersioningMode;
|
|
releaseVersion?: string | null;
|
|
rootDir?: string;
|
|
}): {
|
|
updatedPaths: string[];
|
|
} {
|
|
const rootDir = path.resolve(params?.rootDir ?? ".");
|
|
const releaseVersion = params?.releaseVersion;
|
|
const version = resolveIosVersion(rootDir, { releaseVersion });
|
|
const changelogContent = readFileSync(version.changelogPath, "utf8");
|
|
renderIosReleaseNotes(version, changelogContent);
|
|
|
|
return { updatedPaths: [] };
|
|
}
|
|
|
|
export function renderIosReleaseNotesForVersion(params?: {
|
|
releaseVersion?: string | null;
|
|
rootDir?: string;
|
|
}): string {
|
|
const rootDir = path.resolve(params?.rootDir ?? ".");
|
|
const version = resolveIosVersion(rootDir, { releaseVersion: params?.releaseVersion });
|
|
const changelogContent = readFileSync(version.changelogPath, "utf8");
|
|
return renderIosReleaseNotes(version, changelogContent);
|
|
}
|