diff --git a/CHANGELOG.md b/CHANGELOG.md index bd5b6ec..8daf3f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ ## Unreleased +- `apple build` / `apple upload`: `--allow-version-mismatch` is accepted (it was missing from the flag list); 1.0 and 1.0.0 count as the same version train, as on App Store Connect. + - Desktop: custom context menus everywhere (apps, releases, art, reviews, checks, fields, products, accounts, folders) with keyboard navigation, Shift+F10 and submenus; the WebView2 browser menu and browser shortcuts are off. - Desktop: each app shows once again (a folder project no longer appears a second time as a store-only app); a store app with a folder's bundle id or package is never listed separately. - Desktop UX pass on every screen: app tabs fit the window, slim app header, secondary actions in "…" menus, Ctrl+K lists app actions, Alt+Up/Down switches apps, accounts and folders decluttered. diff --git a/cli/data/flags.json b/cli/data/flags.json index af740bc..3d26219 100644 --- a/cli/data/flags.json +++ b/cli/data/flags.json @@ -68,6 +68,7 @@ "apple build": [ "allow-ssh", "allow-stale-web", + "allow-version-mismatch", "build-number", "configuration", "keychain", @@ -304,6 +305,7 @@ ], "apple upload": [ "allow-existing", + "allow-version-mismatch", "build", "csv", "group", @@ -648,11 +650,17 @@ "allow-stale-web", "dir", "flavor", + "include-drafts", "module", "no-prebuild", + "not-sent-for-review", "out", + "prune", + "src", + "variant", "version-code", - "version-name" + "version-name", + "write" ], "play data-safety": [ "csv", @@ -1018,6 +1026,7 @@ "allow-price-changes", "allow-ssh", "allow-stale-web", + "allow-version-mismatch", "alpha", "apk", "app", @@ -1265,6 +1274,7 @@ "storebound accounts [list] [--json] every profile: Apple key, Play account (masked), which", "storebound accounts import [--dir ] [--name ] [--dry] [--yes]", "storebound accounts use [--dir ] this project uses that profile (\"account\" in its", + "storebound accounts play-developer | --clear", "storebound accounts default | --clear the machine's default profile (a pointer file in the" ], "ads": [ diff --git a/cli/lib/xcode.ts b/cli/lib/xcode.ts index 2f0c3a6..6d01360 100644 --- a/cli/lib/xcode.ts +++ b/cli/lib/xcode.ts @@ -177,7 +177,9 @@ export function ipaInfo(ipa: string | Buffer): IpaInfo { * that ignores the version storebound or the release script intends exports MARKETING_VERSION 1.0), else null. * `wanted` is the --version of apple build or the editable App Store version; `source` names where it comes from. Pure. */ export function ipaVersionProblem(ipaVersion: string, wanted: string | null | undefined, source: string): string | null { - if (!wanted || ipaVersion === wanted) return null; + // Apple compares version strings numerically with missing parts as 0: 1.0 and 1.0.0 are the same train + const norm = (v: string) => v.trim().split('.').map(n => String(Number(n))).join('.').replace(/(\.0)+$/, ''); + if (!wanted || norm(ipaVersion) === norm(wanted)) return null; return `the IPA has version ${ipaVersion} (CFBundleShortVersionString), but ${source} is ${wanted}: the Xcode project sets MARKETING_VERSION itself, so the build lands in the wrong version train. Make the project read the intended version (xcodebuild MARKETING_VERSION=${wanted}, or set it in the project / Info.plist), build again, or pass --allow-version-mismatch to go on anyway.`; } diff --git a/tests/build-sign.test.ts b/tests/build-sign.test.ts index 9e3faa8..20e8327 100644 --- a/tests/build-sign.test.ts +++ b/tests/build-sign.test.ts @@ -559,6 +559,10 @@ describe('signed AAB, masked signing, IPA version train, release notes', () => { test('the IPA version must be the version train being prepared', () => { expect(ipaVersionProblem('1.0', '1.4.0', 'the editable App Store version')).toContain('--allow-version-mismatch'); expect(ipaVersionProblem('1.0', '1.4.0', 'x')).toContain('MARKETING_VERSION=1.4.0'); + // Apple compares numerically: 1.0 and 1.0.0 are one train + expect(ipaVersionProblem('1.0.0', '1.0', 'x')).toBeNull(); + expect(ipaVersionProblem('2.1', '2.1.0', 'x')).toBeNull(); + expect(ipaVersionProblem('2.1.1', '2.1', 'x')).not.toBeNull(); expect(ipaVersionProblem('1.4.0', '1.4.0', 'x')).toBeNull(); expect(ipaVersionProblem('1.0', undefined, 'x')).toBeNull(); });