mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-11 11:53:16 +08:00
重构主控编排与运行时预热链路,统一工作区提示词/专家调度协议并补齐 wiki 记忆注入与写回闭环。
同时收敛启动与运维脚本默认行为(含 wiki worker)、更新 Admin 可观测性与相关测试,降低首轮时延并提高运行稳定性。 Made-with: Cursor
This commit is contained in:
parent
4a23b715a2
commit
dbbe3add6a
14438 changed files with 2693620 additions and 2546 deletions
218
openclaw/scripts/lib/ios-version.ts
Normal file
218
openclaw/scripts/lib/ios-version.ts
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
import { readFileSync, writeFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
export const IOS_VERSION_FILE = "apps/ios/version.json";
|
||||
export const IOS_CHANGELOG_FILE = "apps/ios/CHANGELOG.md";
|
||||
export const IOS_VERSION_XCCONFIG_FILE = "apps/ios/Config/Version.xcconfig";
|
||||
export const IOS_RELEASE_NOTES_FILE = "apps/ios/fastlane/metadata/en-US/release_notes.txt";
|
||||
|
||||
const PINNED_IOS_VERSION_PATTERN = /^(\d{4}\.\d{1,2}\.\d{1,2})$/u;
|
||||
const GATEWAY_VERSION_PATTERN = /^(\d{4}\.\d{1,2}\.\d{1,2})(?:-(?:beta\.\d+|\d+))?$/u;
|
||||
|
||||
export type IosVersionManifest = {
|
||||
version: string;
|
||||
};
|
||||
|
||||
export type ResolvedIosVersion = {
|
||||
canonicalVersion: string;
|
||||
marketingVersion: string;
|
||||
buildVersion: string;
|
||||
versionFilePath: string;
|
||||
changelogPath: string;
|
||||
versionXcconfigPath: string;
|
||||
releaseNotesPath: string;
|
||||
};
|
||||
|
||||
export type SyncIosVersioningMode = "check" | "write";
|
||||
|
||||
function normalizeTrailingNewline(value: string): string {
|
||||
return value.endsWith("\n") ? value : `${value}\n`;
|
||||
}
|
||||
|
||||
export function normalizePinnedIosVersion(rawVersion: string): string {
|
||||
const trimmed = rawVersion.trim();
|
||||
if (!trimmed) {
|
||||
throw new Error(`Missing iOS version in ${IOS_VERSION_FILE}.`);
|
||||
}
|
||||
|
||||
const match = PINNED_IOS_VERSION_PATTERN.exec(trimmed);
|
||||
if (!match) {
|
||||
throw new Error(`Invalid iOS version '${rawVersion}'. Expected pinned CalVer like 2026.4.6.`);
|
||||
}
|
||||
|
||||
return match[1] ?? trimmed;
|
||||
}
|
||||
|
||||
export function normalizeGatewayVersionToPinnedIosVersion(rawVersion: string): string {
|
||||
const trimmed = rawVersion.trim().replace(/^v/u, "");
|
||||
if (!trimmed) {
|
||||
throw new Error("Missing root package.json version.");
|
||||
}
|
||||
|
||||
const match = GATEWAY_VERSION_PATTERN.exec(trimmed);
|
||||
if (!match) {
|
||||
throw new Error(
|
||||
`Invalid gateway version '${rawVersion}'. Expected YYYY.M.D, YYYY.M.D-beta.N, or YYYY.M.D-N.`,
|
||||
);
|
||||
}
|
||||
|
||||
return match[1] ?? trimmed;
|
||||
}
|
||||
|
||||
export function readRootPackageVersion(rootDir = path.resolve(".")): string {
|
||||
const packageJsonPath = path.join(rootDir, "package.json");
|
||||
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 readIosVersionManifest(rootDir = path.resolve(".")): IosVersionManifest {
|
||||
const versionFilePath = path.join(rootDir, IOS_VERSION_FILE);
|
||||
return JSON.parse(readFileSync(versionFilePath, "utf8")) as IosVersionManifest;
|
||||
}
|
||||
|
||||
export function writeIosVersionManifest(version: string, rootDir = path.resolve(".")): string {
|
||||
const versionFilePath = path.join(rootDir, IOS_VERSION_FILE);
|
||||
const normalizedVersion = normalizePinnedIosVersion(version);
|
||||
const nextContent = `${JSON.stringify({ version: normalizedVersion }, null, 2)}\n`;
|
||||
writeFileSync(versionFilePath, nextContent, "utf8");
|
||||
return versionFilePath;
|
||||
}
|
||||
|
||||
export function resolveIosVersion(rootDir = path.resolve(".")): ResolvedIosVersion {
|
||||
const versionFilePath = path.join(rootDir, IOS_VERSION_FILE);
|
||||
const changelogPath = path.join(rootDir, IOS_CHANGELOG_FILE);
|
||||
const versionXcconfigPath = path.join(rootDir, IOS_VERSION_XCCONFIG_FILE);
|
||||
const releaseNotesPath = path.join(rootDir, IOS_RELEASE_NOTES_FILE);
|
||||
const manifest = readIosVersionManifest(rootDir);
|
||||
const canonicalVersion = normalizePinnedIosVersion(manifest.version ?? "");
|
||||
|
||||
return {
|
||||
canonicalVersion,
|
||||
marketingVersion: canonicalVersion,
|
||||
buildVersion: "1",
|
||||
versionFilePath,
|
||||
changelogPath,
|
||||
versionXcconfigPath,
|
||||
releaseNotesPath,
|
||||
};
|
||||
}
|
||||
|
||||
export function renderIosVersionXcconfig(version: ResolvedIosVersion): string {
|
||||
return `// Shared iOS version defaults.\n// Source of truth: apps/ios/version.json\n// Generated by scripts/ios-sync-versioning.ts.\n\nOPENCLAW_IOS_VERSION = ${version.canonicalVersion}\nOPENCLAW_MARKETING_VERSION = ${version.marketingVersion}\nOPENCLAW_BUILD_VERSION = ${version.buildVersion}\n\n#include? "../build/Version.xcconfig"\n`;
|
||||
}
|
||||
|
||||
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}.`,
|
||||
);
|
||||
}
|
||||
|
||||
function syncFile(params: {
|
||||
mode: SyncIosVersioningMode;
|
||||
path: string;
|
||||
nextContent: string;
|
||||
label: string;
|
||||
}): boolean {
|
||||
const nextContent = normalizeTrailingNewline(params.nextContent);
|
||||
const currentContent = readFileSync(params.path, "utf8");
|
||||
if (currentContent === nextContent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (params.mode === "check") {
|
||||
throw new Error(`${params.label} is stale: ${path.relative(process.cwd(), params.path)}`);
|
||||
}
|
||||
|
||||
writeFileSync(params.path, nextContent, "utf8");
|
||||
return true;
|
||||
}
|
||||
|
||||
export function syncIosVersioning(params?: { mode?: SyncIosVersioningMode; rootDir?: string }): {
|
||||
updatedPaths: string[];
|
||||
} {
|
||||
const mode = params?.mode ?? "write";
|
||||
const rootDir = path.resolve(params?.rootDir ?? ".");
|
||||
const version = resolveIosVersion(rootDir);
|
||||
const changelogContent = readFileSync(version.changelogPath, "utf8");
|
||||
const nextVersionXcconfig = renderIosVersionXcconfig(version);
|
||||
const nextReleaseNotes = renderIosReleaseNotes(version, changelogContent);
|
||||
const updatedPaths: string[] = [];
|
||||
|
||||
if (
|
||||
syncFile({
|
||||
mode,
|
||||
path: version.versionXcconfigPath,
|
||||
nextContent: nextVersionXcconfig,
|
||||
label: "iOS version xcconfig",
|
||||
})
|
||||
) {
|
||||
updatedPaths.push(version.versionXcconfigPath);
|
||||
}
|
||||
|
||||
if (
|
||||
syncFile({
|
||||
mode,
|
||||
path: version.releaseNotesPath,
|
||||
nextContent: nextReleaseNotes,
|
||||
label: "iOS release notes",
|
||||
})
|
||||
) {
|
||||
updatedPaths.push(version.releaseNotesPath);
|
||||
}
|
||||
|
||||
return { updatedPaths };
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue