Files
openclaw/scripts/ios-pin-version.ts
Nimrod Gutman 6681878339 feat(ios): pin calver release versioning (#63001)
* feat(ios): decouple app versioning from gateway

* feat(ios): pin calver release versioning

* refactor(ios): drop prerelease version helper fields

* docs(changelog): note pinned ios release versioning (#63001) (thanks @ngutman)
2026-04-08 11:25:35 +03:00

149 lines
4.3 KiB
TypeScript

import path from "node:path";
import {
normalizePinnedIosVersion,
resolveGatewayVersionForIosRelease,
resolveIosVersion,
syncIosVersioning,
writeIosVersionManifest,
} from "./lib/ios-version.ts";
type CliOptions = {
explicitVersion: string | null;
fromGateway: boolean;
rootDir: string;
sync: boolean;
};
export type PinIosVersionResult = {
previousVersion: string | null;
nextVersion: string;
packageVersion: string | null;
versionFilePath: string;
syncedPaths: string[];
};
function usage(): string {
return [
"Usage: node --import tsx scripts/ios-pin-version.ts (--from-gateway | --version <YYYY.M.D>) [--no-sync] [--root dir]",
"",
"Examples:",
" node --import tsx scripts/ios-pin-version.ts --from-gateway",
" node --import tsx scripts/ios-pin-version.ts --version 2026.4.10",
].join("\n");
}
export function parseArgs(argv: string[]): CliOptions {
let explicitVersion: string | null = null;
let fromGateway = false;
let rootDir = path.resolve(".");
let sync = true;
for (let index = 0; index < argv.length; index += 1) {
const arg = argv[index];
switch (arg) {
case "--from-gateway": {
fromGateway = true;
break;
}
case "--version": {
explicitVersion = argv[index + 1] ?? null;
index += 1;
break;
}
case "--no-sync": {
sync = false;
break;
}
case "--root": {
const value = argv[index + 1];
if (!value) {
throw new Error("Missing value for --root.");
}
rootDir = path.resolve(value);
index += 1;
break;
}
case "-h":
case "--help": {
console.log(`${usage()}\n`);
process.exit(0);
}
default: {
throw new Error(`Unknown argument: ${arg}`);
}
}
}
if (fromGateway === (explicitVersion !== null)) {
throw new Error("Choose exactly one of --from-gateway or --version <YYYY.M.D>.");
}
if (explicitVersion !== null && !explicitVersion.trim()) {
throw new Error("Missing value for --version.");
}
return { explicitVersion, fromGateway, rootDir, sync };
}
export function pinIosVersion(params: CliOptions): PinIosVersionResult {
const rootDir = path.resolve(params.rootDir);
let previousVersion: string | null = null;
try {
previousVersion = resolveIosVersion(rootDir).canonicalVersion;
} catch {
previousVersion = null;
}
const gatewayVersion = params.fromGateway ? resolveGatewayVersionForIosRelease(rootDir) : null;
const packageVersion = gatewayVersion?.packageVersion ?? null;
const nextVersion =
gatewayVersion?.pinnedIosVersion ?? normalizePinnedIosVersion(params.explicitVersion ?? "");
const versionFilePath = writeIosVersionManifest(nextVersion, rootDir);
const syncedPaths = params.sync ? syncIosVersioning({ mode: "write", rootDir }).updatedPaths : [];
return {
previousVersion,
nextVersion,
packageVersion,
versionFilePath,
syncedPaths,
};
}
export async function main(argv: string[]): Promise<number> {
try {
const options = parseArgs(argv);
const result = pinIosVersion(options);
const sourceText = result.packageVersion
? ` from gateway version ${result.packageVersion}`
: "";
process.stdout.write(`Pinned iOS version to ${result.nextVersion}${sourceText}.\n`);
if (result.previousVersion && result.previousVersion !== result.nextVersion) {
process.stdout.write(`Previous pinned iOS version: ${result.previousVersion}.\n`);
}
process.stdout.write(
`Updated version manifest: ${path.relative(process.cwd(), result.versionFilePath)}\n`,
);
if (options.sync) {
if (result.syncedPaths.length === 0) {
process.stdout.write("iOS versioning artifacts already up to date.\n");
} else {
process.stdout.write(
`Updated iOS versioning artifacts:\n- ${result.syncedPaths.map((filePath) => path.relative(process.cwd(), filePath)).join("\n- ")}\n`,
);
}
}
return 0;
} catch (error) {
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
return 1;
}
}
if (import.meta.url === `file://${process.argv[1]}`) {
const exitCode = await main(process.argv.slice(2));
if (exitCode !== 0) {
process.exit(exitCode);
}
}