|
| 1 | +# OpenClaw iOS Versioning |
| 2 | + |
| 3 | +OpenClaw iOS uses a **pinned CalVer release version** instead of reading the current gateway version automatically on every build. |
| 4 | + |
| 5 | +## Goals |
| 6 | + |
| 7 | +- keep TestFlight submissions on one stable app version while iterating |
| 8 | +- change only `CFBundleVersion` during normal TestFlight iteration |
| 9 | +- promote the iOS release version to the current gateway version only when a maintainer chooses to do that |
| 10 | +- keep Apple bundle fields valid for App Store Connect |
| 11 | +- generate App Store release notes from an iOS-owned changelog |
| 12 | + |
| 13 | +## Version model |
| 14 | + |
| 15 | +The pinned iOS release version lives in `apps/ios/version.json`. |
| 16 | + |
| 17 | +Supported pinned format: |
| 18 | + |
| 19 | +- `YYYY.M.D` |
| 20 | + |
| 21 | +Examples: |
| 22 | + |
| 23 | +- `2026.4.6` |
| 24 | +- `2026.4.10` |
| 25 | + |
| 26 | +The root gateway version in `package.json` may still be one of: |
| 27 | + |
| 28 | +- `YYYY.M.D` |
| 29 | +- `YYYY.M.D-beta.N` |
| 30 | +- `YYYY.M.D-N` |
| 31 | + |
| 32 | +When you pin iOS from the gateway version, the iOS tooling strips the gateway suffix and keeps only the base CalVer. |
| 33 | + |
| 34 | +Examples: |
| 35 | + |
| 36 | +- gateway `2026.4.10` -> iOS `2026.4.10` |
| 37 | +- gateway `2026.4.10-beta.3` -> iOS `2026.4.10` |
| 38 | +- gateway `2026.4.10-2` -> iOS `2026.4.10` |
| 39 | + |
| 40 | +## Apple bundle mapping |
| 41 | + |
| 42 | +Pinned iOS version `2026.4.10` maps to: |
| 43 | + |
| 44 | +- `CFBundleShortVersionString = 2026.4.10` |
| 45 | +- `CFBundleVersion = numeric build number only` |
| 46 | + |
| 47 | +`CFBundleShortVersionString` stays fixed for a TestFlight train until you intentionally pin a newer iOS release version. |
| 48 | + |
| 49 | +## Source of truth and generated files |
| 50 | + |
| 51 | +### Source files |
| 52 | + |
| 53 | +- `apps/ios/version.json` |
| 54 | + - pinned iOS release version |
| 55 | +- `apps/ios/CHANGELOG.md` |
| 56 | + - iOS-only changelog and release-note source |
| 57 | +- `apps/ios/VERSIONING.md` |
| 58 | + - workflow and constraints |
| 59 | + |
| 60 | +### Generated or derived files |
| 61 | + |
| 62 | +- `apps/ios/Config/Version.xcconfig` |
| 63 | + - checked-in defaults derived from `apps/ios/version.json` |
| 64 | +- `apps/ios/fastlane/metadata/en-US/release_notes.txt` |
| 65 | + - generated from `apps/ios/CHANGELOG.md` |
| 66 | +- `apps/ios/build/Version.xcconfig` |
| 67 | + - local gitignored build override generated per build or beta prep |
| 68 | + |
| 69 | +## Tooling surfaces |
| 70 | + |
| 71 | +### Version parsing and sync tooling |
| 72 | + |
| 73 | +- `scripts/lib/ios-version.ts` |
| 74 | + - validates pinned iOS CalVer |
| 75 | + - normalizes gateway version -> pinned iOS CalVer |
| 76 | + - renders checked-in xcconfig and release notes |
| 77 | +- `scripts/ios-version.ts` |
| 78 | + - CLI for JSON, shell, or single-field version reads |
| 79 | +- `scripts/ios-sync-versioning.ts` |
| 80 | + - syncs checked-in derived files from the pinned iOS version |
| 81 | +- `scripts/ios-pin-version.ts` |
| 82 | + - explicitly pins iOS to a chosen release version or the current gateway version |
| 83 | + |
| 84 | +### Build and beta flow |
| 85 | + |
| 86 | +- `scripts/ios-write-version-xcconfig.sh` |
| 87 | + - reads the pinned iOS version |
| 88 | + - writes the local numeric build override file in `apps/ios/build/Version.xcconfig` |
| 89 | +- `scripts/ios-beta-prepare.sh` |
| 90 | + - prepares beta signing and bundle settings against the pinned iOS version |
| 91 | +- `apps/ios/fastlane/Fastfile` |
| 92 | + - resolves version metadata from the pinned iOS helper |
| 93 | + - increments TestFlight build numbers for the pinned short version |
| 94 | + |
| 95 | +## Release-note resolution order |
| 96 | + |
| 97 | +When generating `apps/ios/fastlane/metadata/en-US/release_notes.txt`, the tooling reads the first available changelog section in this order: |
| 98 | + |
| 99 | +1. exact pinned version, for example `## 2026.4.10` |
| 100 | +2. `## Unreleased` |
| 101 | + |
| 102 | +Recommended workflow: |
| 103 | + |
| 104 | +- while iterating on a TestFlight train, keep pending notes under `## Unreleased` |
| 105 | +- before the production release, move or copy the final notes under `## <pinned version>` and run sync again |
| 106 | + |
| 107 | +## Common commands |
| 108 | + |
| 109 | +```bash |
| 110 | +pnpm ios:version |
| 111 | +pnpm ios:version:check |
| 112 | +pnpm ios:version:sync |
| 113 | +pnpm ios:version:pin -- --from-gateway |
| 114 | +pnpm ios:version:pin -- --version 2026.4.10 |
| 115 | +``` |
| 116 | + |
| 117 | +## Normal TestFlight iteration workflow |
| 118 | + |
| 119 | +1. keep `apps/ios/version.json` pinned to the current TestFlight train version |
| 120 | +2. update `apps/ios/CHANGELOG.md` under `## Unreleased` while iterating |
| 121 | +3. upload more betas with the usual flow |
| 122 | +4. let Fastlane increment only `CFBundleVersion` |
| 123 | + |
| 124 | +This keeps the TestFlight version stable while review is in flight. |
| 125 | + |
| 126 | +## New release promotion workflow |
| 127 | + |
| 128 | +When you want the next production iOS release to align with the current gateway release: |
| 129 | + |
| 130 | +1. pin iOS from the root gateway version: |
| 131 | + |
| 132 | +```bash |
| 133 | +pnpm ios:version:pin -- --from-gateway |
| 134 | +``` |
| 135 | + |
| 136 | +2. review the generated changes in: |
| 137 | + - `apps/ios/version.json` |
| 138 | + - `apps/ios/Config/Version.xcconfig` |
| 139 | + - `apps/ios/fastlane/metadata/en-US/release_notes.txt` |
| 140 | +3. update `apps/ios/CHANGELOG.md` for the new release if needed |
| 141 | +4. run `pnpm ios:version:sync` again if the changelog changed |
| 142 | +5. submit the first TestFlight build for that newly pinned version |
| 143 | +6. keep iterating only by build number until the release candidate is ready |
| 144 | +7. release that reviewed TestFlight build to production |
| 145 | + |
| 146 | +## Important invariant |
| 147 | + |
| 148 | +Fastlane and Xcode should consume only the pinned iOS version from `apps/ios/version.json`. |
| 149 | + |
| 150 | +Changing `package.json.version` alone must not change the iOS app version until a maintainer explicitly runs the pin step. |
0 commit comments