diff --git a/apps/ios/ActivityWidget/Info.plist b/apps/ios/ActivityWidget/Info.plist index 4c2d89e1566..4c965121bf9 100644 --- a/apps/ios/ActivityWidget/Info.plist +++ b/apps/ios/ActivityWidget/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 2026.3.9 + $(OPENCLAW_MARKETING_VERSION) CFBundleVersion - 20260308 + $(OPENCLAW_BUILD_VERSION) NSExtension NSExtensionPointIdentifier diff --git a/apps/ios/Config/Signing.xcconfig b/apps/ios/Config/Signing.xcconfig index 108dcf1a2f7..4fef287a09d 100644 --- a/apps/ios/Config/Signing.xcconfig +++ b/apps/ios/Config/Signing.xcconfig @@ -1,4 +1,6 @@ // Shared iOS signing defaults for local development + CI. +#include "Version.xcconfig" + OPENCLAW_IOS_DEFAULT_TEAM = Y5PE65HELJ OPENCLAW_IOS_SELECTED_TEAM = $(OPENCLAW_IOS_DEFAULT_TEAM) OPENCLAW_APP_BUNDLE_ID = ai.openclaw.client diff --git a/apps/ios/Config/Version.xcconfig b/apps/ios/Config/Version.xcconfig new file mode 100644 index 00000000000..db38e86df80 --- /dev/null +++ b/apps/ios/Config/Version.xcconfig @@ -0,0 +1,8 @@ +// Shared iOS version defaults. +// Generated overrides live in build/Version.xcconfig (git-ignored). + +OPENCLAW_GATEWAY_VERSION = 0.0.0 +OPENCLAW_MARKETING_VERSION = 0.0.0 +OPENCLAW_BUILD_VERSION = 0 + +#include? "../build/Version.xcconfig" diff --git a/apps/ios/README.md b/apps/ios/README.md index 03da0692c3a..d4bd94c9391 100644 --- a/apps/ios/README.md +++ b/apps/ios/README.md @@ -63,26 +63,27 @@ Release behavior: - Local development keeps using unique per-developer bundle IDs from `scripts/ios-configure-signing.sh`. - Beta release uses canonical `ai.openclaw.client*` bundle IDs through a temporary generated xcconfig in `apps/ios/build/BetaRelease.xcconfig`. - The beta flow does not modify `apps/ios/.local-signing.xcconfig` or `apps/ios/LocalSigning.xcconfig`. -- Version input `2026.3.9-beta.1` becomes: +- Root `package.json.version` is the only version source for iOS. +- A root version like `2026.3.9-beta.1` becomes: - `CFBundleShortVersionString = 2026.3.9` - `CFBundleVersion = next TestFlight build number for 2026.3.9` Archive without upload: ```bash -pnpm ios:beta:archive -- --version 2026.3.9-beta.1 +pnpm ios:beta:archive ``` Archive and upload to TestFlight: ```bash -pnpm ios:beta -- --version 2026.3.9-beta.1 +pnpm ios:beta ``` If you need to force a specific build number: ```bash -pnpm ios:beta -- --version 2026.3.9-beta.1 --build-number 7 +pnpm ios:beta -- --build-number 7 ``` ## APNs Expectations For Local/Manual Builds diff --git a/apps/ios/ShareExtension/Info.plist b/apps/ios/ShareExtension/Info.plist index 90a7e09e0fc..9469daa08a8 100644 --- a/apps/ios/ShareExtension/Info.plist +++ b/apps/ios/ShareExtension/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 2026.3.9 + $(OPENCLAW_MARKETING_VERSION) CFBundleVersion - 20260308 + $(OPENCLAW_BUILD_VERSION) NSExtension NSExtensionAttributes diff --git a/apps/ios/Signing.xcconfig b/apps/ios/Signing.xcconfig index 5966d6e2c2f..d6acc35dee8 100644 --- a/apps/ios/Signing.xcconfig +++ b/apps/ios/Signing.xcconfig @@ -2,6 +2,8 @@ // Auto-selected local team overrides live in .local-signing.xcconfig (git-ignored). // Manual local overrides can go in LocalSigning.xcconfig (git-ignored). +#include "Config/Version.xcconfig" + OPENCLAW_CODE_SIGN_STYLE = Manual OPENCLAW_DEVELOPMENT_TEAM = Y5PE65HELJ diff --git a/apps/ios/Sources/Info.plist b/apps/ios/Sources/Info.plist index 2f1f03d24a1..892d53e7ae9 100644 --- a/apps/ios/Sources/Info.plist +++ b/apps/ios/Sources/Info.plist @@ -23,7 +23,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 2026.3.9 + $(OPENCLAW_MARKETING_VERSION) CFBundleURLTypes @@ -36,7 +36,7 @@ CFBundleVersion - 20260308 + $(OPENCLAW_BUILD_VERSION) ITSAppUsesNonExemptEncryption NSAppTransportSecurity diff --git a/apps/ios/Tests/Info.plist b/apps/ios/Tests/Info.plist index 46e3fb97eb1..5bcf88ff5ad 100644 --- a/apps/ios/Tests/Info.plist +++ b/apps/ios/Tests/Info.plist @@ -17,8 +17,8 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 2026.3.9 + $(OPENCLAW_MARKETING_VERSION) CFBundleVersion - 20260308 + $(OPENCLAW_BUILD_VERSION) diff --git a/apps/ios/WatchApp/Info.plist b/apps/ios/WatchApp/Info.plist index fa45d719b9c..3eea1e6ff09 100644 --- a/apps/ios/WatchApp/Info.plist +++ b/apps/ios/WatchApp/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 2026.3.9 + $(OPENCLAW_MARKETING_VERSION) CFBundleVersion - 20260308 + $(OPENCLAW_BUILD_VERSION) WKCompanionAppBundleIdentifier $(OPENCLAW_APP_BUNDLE_ID) WKWatchKitApp diff --git a/apps/ios/WatchExtension/Info.plist b/apps/ios/WatchExtension/Info.plist index 1d898d43757..87313064945 100644 --- a/apps/ios/WatchExtension/Info.plist +++ b/apps/ios/WatchExtension/Info.plist @@ -15,9 +15,9 @@ CFBundleName $(PRODUCT_NAME) CFBundleShortVersionString - 2026.3.9 + $(OPENCLAW_MARKETING_VERSION) CFBundleVersion - 20260308 + $(OPENCLAW_BUILD_VERSION) NSExtension NSExtensionAttributes diff --git a/apps/ios/fastlane/Fastfile b/apps/ios/fastlane/Fastfile index 3bc3b4ba37d..ed00b3c5de2 100644 --- a/apps/ios/fastlane/Fastfile +++ b/apps/ios/fastlane/Fastfile @@ -1,5 +1,6 @@ require "shellwords" require "open3" +require "json" default_platform(:ios) @@ -94,18 +95,28 @@ def ios_root File.expand_path("..", __dir__) end -def normalize_beta_version(raw_value) +def normalize_release_version(raw_value) version = raw_value.to_s.strip.sub(/\Av/, "") - UI.user_error!("Missing IOS_BETA_VERSION. Example: IOS_BETA_VERSION=2026.3.9-beta.1 fastlane ios beta") unless env_present?(version) + UI.user_error!("Missing root package.json version.") unless env_present?(version) unless version.match?(/\A\d+\.\d+\.\d+(?:[.-]?beta[.-]\d+)?\z/i) - UI.user_error!("Invalid IOS_BETA_VERSION '#{raw_value}'. Expected 2026.3.9 or 2026.3.9-beta.1.") + UI.user_error!("Invalid package.json version '#{raw_value}'. Expected 2026.3.9 or 2026.3.9-beta.1.") end version end -def short_beta_version(version) - normalize_beta_version(version).sub(/([.-]?beta[.-]\d+)\z/i, "") +def read_root_package_version + package_json_path = File.join(repo_root, "package.json") + UI.user_error!("Missing package.json at #{package_json_path}.") unless File.exist?(package_json_path) + + parsed = JSON.parse(File.read(package_json_path)) + normalize_release_version(parsed["version"]) +rescue JSON::ParserError => e + UI.user_error!("Invalid package.json at #{package_json_path}: #{e.message}") +end + +def short_release_version(version) + normalize_release_version(version).sub(/([.-]?beta[.-]\d+)\z/i, "") end def shell_join(parts) @@ -120,7 +131,7 @@ def resolve_beta_build_number(api_key:, version:) return explicit end - short_version = short_beta_version(version) + short_version = short_release_version(version) latest_build = latest_testflight_build_number( api_key: api_key, app_identifier: BETA_APP_IDENTIFIER, @@ -135,7 +146,7 @@ end def prepare_beta_release!(version:, build_number:) script_path = File.join(repo_root, "scripts", "ios-beta-prepare.sh") UI.message("Preparing iOS beta release #{version} (build #{build_number}).") - sh(shell_join(["bash", script_path, "--version", version, "--build-number", build_number])) + sh(shell_join(["bash", script_path, "--build-number", build_number])) beta_xcconfig = File.join(ios_root, "build", "BetaRelease.xcconfig") UI.user_error!("Missing beta xcconfig at #{beta_xcconfig}.") unless File.exist?(beta_xcconfig) @@ -226,7 +237,7 @@ platform :ios do private_lane :prepare_beta_context do api_key = asc_api_key - version = normalize_beta_version(ENV["IOS_BETA_VERSION"]) + version = read_root_package_version build_number = resolve_beta_build_number(api_key: api_key, version: version) beta_xcconfig = prepare_beta_release!(version: version, build_number: build_number) @@ -234,7 +245,7 @@ platform :ios do api_key: api_key, beta_xcconfig: beta_xcconfig, build_number: build_number, - short_version: short_beta_version(version), + short_version: short_release_version(version), version: version } end diff --git a/apps/ios/fastlane/SETUP.md b/apps/ios/fastlane/SETUP.md index 14035f9f21e..b71a963c2e1 100644 --- a/apps/ios/fastlane/SETUP.md +++ b/apps/ios/fastlane/SETUP.md @@ -63,25 +63,26 @@ fastlane ios auth_check Archive locally without upload: ```bash -pnpm ios:beta:archive -- --version 2026.3.9-beta.1 +pnpm ios:beta:archive ``` Upload to TestFlight: ```bash -pnpm ios:beta -- --version 2026.3.9-beta.1 +pnpm ios:beta ``` Direct Fastlane entry point: ```bash cd apps/ios -IOS_BETA_VERSION=2026.3.9-beta.1 fastlane ios beta +fastlane ios beta ``` Versioning rules: -- Input release version uses CalVer beta format: `YYYY.M.D-beta.N` +- Root `package.json.version` is the single source of truth for iOS +- Use `YYYY.M.D` for stable versions and `YYYY.M.D-beta.N` for beta versions - Fastlane stamps `CFBundleShortVersionString` to `YYYY.M.D` - Fastlane resolves `CFBundleVersion` as the next integer TestFlight build number for that short version - The beta flow regenerates `apps/ios/OpenClaw.xcodeproj` from `apps/ios/project.yml` before archiving diff --git a/apps/ios/project.yml b/apps/ios/project.yml index 82985a6ec72..91b2a8e46d1 100644 --- a/apps/ios/project.yml +++ b/apps/ios/project.yml @@ -107,8 +107,8 @@ targets: - CFBundleURLName: ai.openclaw.ios CFBundleURLSchemes: - openclaw - CFBundleShortVersionString: "2026.3.9" - CFBundleVersion: "20260308" + CFBundleShortVersionString: "$(OPENCLAW_MARKETING_VERSION)" + CFBundleVersion: "$(OPENCLAW_BUILD_VERSION)" UILaunchScreen: {} UIApplicationSceneManifest: UIApplicationSupportsMultipleScenes: false @@ -168,8 +168,8 @@ targets: path: ShareExtension/Info.plist properties: CFBundleDisplayName: OpenClaw Share - CFBundleShortVersionString: "2026.3.9" - CFBundleVersion: "20260308" + CFBundleShortVersionString: "$(OPENCLAW_MARKETING_VERSION)" + CFBundleVersion: "$(OPENCLAW_BUILD_VERSION)" NSExtension: NSExtensionPointIdentifier: com.apple.share-services NSExtensionPrincipalClass: "$(PRODUCT_MODULE_NAME).ShareViewController" @@ -205,8 +205,8 @@ targets: path: ActivityWidget/Info.plist properties: CFBundleDisplayName: OpenClaw Activity - CFBundleShortVersionString: "2026.3.9" - CFBundleVersion: "20260308" + CFBundleShortVersionString: "$(OPENCLAW_MARKETING_VERSION)" + CFBundleVersion: "$(OPENCLAW_BUILD_VERSION)" NSSupportsLiveActivities: true NSExtension: NSExtensionPointIdentifier: com.apple.widgetkit-extension @@ -232,8 +232,8 @@ targets: path: WatchApp/Info.plist properties: CFBundleDisplayName: OpenClaw - CFBundleShortVersionString: "2026.3.9" - CFBundleVersion: "20260308" + CFBundleShortVersionString: "$(OPENCLAW_MARKETING_VERSION)" + CFBundleVersion: "$(OPENCLAW_BUILD_VERSION)" WKCompanionAppBundleIdentifier: "$(OPENCLAW_APP_BUNDLE_ID)" WKWatchKitApp: true @@ -257,8 +257,8 @@ targets: path: WatchExtension/Info.plist properties: CFBundleDisplayName: OpenClaw - CFBundleShortVersionString: "2026.3.9" - CFBundleVersion: "20260308" + CFBundleShortVersionString: "$(OPENCLAW_MARKETING_VERSION)" + CFBundleVersion: "$(OPENCLAW_BUILD_VERSION)" NSExtension: NSExtensionAttributes: WKAppBundleIdentifier: "$(OPENCLAW_WATCH_APP_BUNDLE_ID)" @@ -294,8 +294,8 @@ targets: path: Tests/Info.plist properties: CFBundleDisplayName: OpenClawTests - CFBundleShortVersionString: "2026.3.9" - CFBundleVersion: "20260308" + CFBundleShortVersionString: "$(OPENCLAW_MARKETING_VERSION)" + CFBundleVersion: "$(OPENCLAW_BUILD_VERSION)" OpenClawLogicTests: type: bundle.unit-test @@ -320,5 +320,5 @@ targets: path: Tests/Info.plist properties: CFBundleDisplayName: OpenClawLogicTests - CFBundleShortVersionString: "2026.3.9" - CFBundleVersion: "20260308" + CFBundleShortVersionString: "$(OPENCLAW_MARKETING_VERSION)" + CFBundleVersion: "$(OPENCLAW_BUILD_VERSION)" diff --git a/package.json b/package.json index a71c3dbbe40..f673633009c 100644 --- a/package.json +++ b/package.json @@ -265,10 +265,10 @@ "ios:beta": "bash scripts/ios-beta-release.sh", "ios:beta:archive": "bash scripts/ios-beta-archive.sh", "ios:beta:prepare": "bash scripts/ios-beta-prepare.sh", - "ios:build": "bash -lc './scripts/ios-configure-signing.sh && cd apps/ios && xcodegen generate && xcodebuild -project OpenClaw.xcodeproj -scheme OpenClaw -destination \"${IOS_DEST:-platform=iOS Simulator,name=iPhone 17}\" -configuration Debug build'", - "ios:gen": "bash -lc './scripts/ios-configure-signing.sh && cd apps/ios && xcodegen generate'", - "ios:open": "bash -lc './scripts/ios-configure-signing.sh && cd apps/ios && xcodegen generate && open OpenClaw.xcodeproj'", - "ios:run": "bash -lc './scripts/ios-configure-signing.sh && cd apps/ios && xcodegen generate && xcodebuild -project OpenClaw.xcodeproj -scheme OpenClaw -destination \"${IOS_DEST:-platform=iOS Simulator,name=iPhone 17}\" -configuration Debug build && xcrun simctl boot \"${IOS_SIM:-iPhone 17}\" || true && xcrun simctl launch booted ai.openclaw.ios'", + "ios:build": "bash -lc './scripts/ios-configure-signing.sh && ./scripts/ios-write-version-xcconfig.sh && cd apps/ios && xcodegen generate && xcodebuild -project OpenClaw.xcodeproj -scheme OpenClaw -destination \"${IOS_DEST:-platform=iOS Simulator,name=iPhone 17}\" -configuration Debug build'", + "ios:gen": "bash -lc './scripts/ios-configure-signing.sh && ./scripts/ios-write-version-xcconfig.sh && cd apps/ios && xcodegen generate'", + "ios:open": "bash -lc './scripts/ios-configure-signing.sh && ./scripts/ios-write-version-xcconfig.sh && cd apps/ios && xcodegen generate && open OpenClaw.xcodeproj'", + "ios:run": "bash -lc './scripts/ios-configure-signing.sh && ./scripts/ios-write-version-xcconfig.sh && cd apps/ios && xcodegen generate && xcodebuild -project OpenClaw.xcodeproj -scheme OpenClaw -destination \"${IOS_DEST:-platform=iOS Simulator,name=iPhone 17}\" -configuration Debug build && xcrun simctl boot \"${IOS_SIM:-iPhone 17}\" || true && xcrun simctl launch booted ai.openclaw.ios'", "lint": "oxlint --type-aware", "lint:agent:ingress-owner": "node scripts/check-ingress-agent-owner-context.mjs", "lint:all": "pnpm lint && pnpm lint:swift", diff --git a/scripts/ios-beta-archive.sh b/scripts/ios-beta-archive.sh index ab9c855ed22..c65e9991389 100755 --- a/scripts/ios-beta-archive.sh +++ b/scripts/ios-beta-archive.sh @@ -4,13 +4,12 @@ set -euo pipefail usage() { cat <<'EOF' Usage: - scripts/ios-beta-archive.sh --version 2026.3.9-beta.1 [--build-number 7] + scripts/ios-beta-archive.sh [--build-number 7] Archives and exports a beta-release IPA locally without uploading. EOF } -VERSION="${IOS_BETA_VERSION:-}" BUILD_NUMBER="${IOS_BETA_BUILD_NUMBER:-}" ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" @@ -19,10 +18,6 @@ while [[ $# -gt 0 ]]; do --) shift ;; - --version) - VERSION="${2:-}" - shift 2 - ;; --build-number) BUILD_NUMBER="${2:-}" shift 2 @@ -39,15 +34,7 @@ while [[ $# -gt 0 ]]; do esac done -if [[ -z "${VERSION}" ]]; then - echo "Missing required --version (or IOS_BETA_VERSION)." >&2 - usage - exit 1 -fi - ( cd "${ROOT_DIR}/apps/ios" - IOS_BETA_VERSION="${VERSION}" \ - IOS_BETA_BUILD_NUMBER="${BUILD_NUMBER}" \ - fastlane ios beta_archive + IOS_BETA_BUILD_NUMBER="${BUILD_NUMBER}" fastlane ios beta_archive ) diff --git a/scripts/ios-beta-prepare.sh b/scripts/ios-beta-prepare.sh index 38649a78f24..7114a03d7d6 100755 --- a/scripts/ios-beta-prepare.sh +++ b/scripts/ios-beta-prepare.sh @@ -4,10 +4,10 @@ set -euo pipefail usage() { cat <<'EOF' Usage: - scripts/ios-beta-prepare.sh --version 2026.3.9-beta.1 --build-number 7 [--team-id TEAMID] + scripts/ios-beta-prepare.sh --build-number 7 [--team-id TEAMID] Prepares local beta-release inputs without touching local signing overrides: -- stamps apps/ios/project.yml with the short version + build number +- reads package.json.version and writes apps/ios/build/Version.xcconfig - writes apps/ios/build/BetaRelease.xcconfig with canonical bundle IDs - regenerates apps/ios/OpenClaw.xcodeproj via xcodegen EOF @@ -17,20 +17,17 @@ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" IOS_DIR="${ROOT_DIR}/apps/ios" BETA_XCCONFIG="${IOS_DIR}/build/BetaRelease.xcconfig" TEAM_HELPER="${ROOT_DIR}/scripts/ios-team-id.sh" +VERSION_HELPER="${ROOT_DIR}/scripts/ios-write-version-xcconfig.sh" -VERSION="" BUILD_NUMBER="" TEAM_ID="${IOS_DEVELOPMENT_TEAM:-}" +PACKAGE_VERSION="$(cd "${ROOT_DIR}" && node -p "require('./package.json').version" 2>/dev/null || true)" while [[ $# -gt 0 ]]; do case "$1" in --) shift ;; - --version) - VERSION="${2:-}" - shift 2 - ;; --build-number) BUILD_NUMBER="${2:-}" shift 2 @@ -51,8 +48,8 @@ while [[ $# -gt 0 ]]; do esac done -if [[ -z "${VERSION}" || -z "${BUILD_NUMBER}" ]]; then - echo "Missing required --version or --build-number." >&2 +if [[ -z "${BUILD_NUMBER}" ]]; then + echo "Missing required --build-number." >&2 usage exit 1 fi @@ -64,10 +61,7 @@ fi mkdir -p "${IOS_DIR}/build" ( - cd "${ROOT_DIR}" - node --import tsx scripts/ios-sync-version.ts \ - --version "${VERSION}" \ - --build-number "${BUILD_NUMBER}" + bash "${VERSION_HELPER}" --build-number "${BUILD_NUMBER}" ) cat >"${BETA_XCCONFIG}" <&2 - usage - exit 1 -fi - ( cd "${ROOT_DIR}/apps/ios" - IOS_BETA_VERSION="${VERSION}" \ - IOS_BETA_BUILD_NUMBER="${BUILD_NUMBER}" \ - fastlane ios beta + IOS_BETA_BUILD_NUMBER="${BUILD_NUMBER}" fastlane ios beta ) diff --git a/scripts/ios-sync-version.ts b/scripts/ios-sync-version.ts deleted file mode 100755 index 17d302a6ebe..00000000000 --- a/scripts/ios-sync-version.ts +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env -S node --import tsx - -import { readFileSync, writeFileSync } from "node:fs"; -import { resolve } from "node:path"; - -type CliOptions = { - buildNumber: string; - version: string; -}; - -function parseArgs(argv: string[]): CliOptions { - const options = new Map(); - - for (let i = 0; i < argv.length; i += 1) { - const arg = argv[i]; - if (!arg.startsWith("--")) { - continue; - } - - const key = arg.slice(2); - const value = argv[i + 1]; - if (!value || value.startsWith("--")) { - throw new Error(`Missing value for --${key}`); - } - options.set(key, value); - i += 1; - } - - const version = options.get("version")?.trim(); - const buildNumber = options.get("build-number")?.trim(); - - if (!version) { - throw new Error("Missing required --version"); - } - if (!buildNumber) { - throw new Error("Missing required --build-number"); - } - if (!/^[0-9]+$/.test(buildNumber)) { - throw new Error(`Invalid --build-number '${buildNumber}'; expected digits only.`); - } - - return { buildNumber, version }; -} - -function toShortVersion(input: string): string { - const trimmed = input.trim().replace(/^v/, ""); - const shortVersion = trimmed.replace(/([.-]?beta[.-]\d+)$/i, ""); - if (!/^\d+\.\d+\.\d+$/.test(shortVersion)) { - throw new Error( - `Invalid --version '${input}'; expected CalVer like 2026.3.9 or 2026.3.9-beta.1.`, - ); - } - return shortVersion; -} - -function replaceAllExact(params: { content: string; pattern: RegExp; replacement: string }) { - const { content, pattern, replacement } = params; - const matches = [...content.matchAll(pattern)]; - if (matches.length === 0) { - throw new Error(`Pattern not found: ${pattern}`); - } - return content.replace(pattern, replacement); -} - -function main() { - const options = parseArgs(process.argv.slice(2)); - const shortVersion = toShortVersion(options.version); - const projectPath = resolve("apps/ios/project.yml"); - const original = readFileSync(projectPath, "utf8"); - - let updated = original; - updated = replaceAllExact({ - content: updated, - pattern: /(CFBundleShortVersionString:\s*")[^"]+(")/g, - replacement: `$1${shortVersion}$2`, - }); - updated = replaceAllExact({ - content: updated, - pattern: /(CFBundleVersion:\s*")[^"]+(")/g, - replacement: `$1${options.buildNumber}$2`, - }); - - if (updated === original) { - console.log(`iOS version already set: short=${shortVersion} build=${options.buildNumber}`); - return; - } - - writeFileSync(projectPath, updated); - console.log(`Updated iOS project version: short=${shortVersion} build=${options.buildNumber}`); -} - -try { - main(); -} catch (error) { - console.error(`ios-sync-version: ${(error as Error).message}`); - process.exit(1); -} diff --git a/scripts/ios-write-version-xcconfig.sh b/scripts/ios-write-version-xcconfig.sh new file mode 100755 index 00000000000..7022a21f134 --- /dev/null +++ b/scripts/ios-write-version-xcconfig.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +set -euo pipefail + +usage() { + cat <<'EOF' +Usage: + scripts/ios-write-version-xcconfig.sh [--build-number 7] + +Writes apps/ios/build/Version.xcconfig from root package.json.version: +- OPENCLAW_GATEWAY_VERSION = exact package.json version +- OPENCLAW_MARKETING_VERSION = short iOS/App Store version +- OPENCLAW_BUILD_VERSION = explicit build number or local numeric fallback +EOF +} + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +IOS_DIR="${ROOT_DIR}/apps/ios" +VERSION_XCCONFIG="${IOS_DIR}/build/Version.xcconfig" +PACKAGE_VERSION="$(cd "${ROOT_DIR}" && node -p "require('./package.json').version" 2>/dev/null || true)" +BUILD_NUMBER="" + +while [[ $# -gt 0 ]]; do + case "$1" in + --) + shift + ;; + --build-number) + BUILD_NUMBER="${2:-}" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown argument: $1" >&2 + usage + exit 1 + ;; + esac +done + +PACKAGE_VERSION="$(printf '%s' "${PACKAGE_VERSION}" | tr -d '\n' | xargs)" +if [[ -z "${PACKAGE_VERSION}" ]]; then + echo "Unable to read package.json.version from ${ROOT_DIR}/package.json." >&2 + exit 1 +fi + +if [[ "${PACKAGE_VERSION}" =~ ^([0-9]{4}\.[0-9]{1,2}\.[0-9]{1,2})([.-]?beta[.-][0-9]+)?$ ]]; then + MARKETING_VERSION="${BASH_REMATCH[1]}" +else + echo "Unsupported package.json.version '${PACKAGE_VERSION}'. Expected 2026.3.9 or 2026.3.9-beta.1." >&2 + exit 1 +fi + +if [[ -z "${BUILD_NUMBER}" ]]; then + BUILD_NUMBER="$(cd "${ROOT_DIR}" && git rev-list --count HEAD 2>/dev/null || printf '0')" +fi + +if [[ ! "${BUILD_NUMBER}" =~ ^[0-9]+$ ]]; then + echo "Invalid build number '${BUILD_NUMBER}'. Expected digits only." >&2 + exit 1 +fi + +mkdir -p "${IOS_DIR}/build" + +cat >"${VERSION_XCCONFIG}" <