Repository navigation
fix(target): harden native release preflight - #119
Conversation
|
Warning Review limit reached
Next review available in: 52 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughNative release identity validation now checks Android and iOS versions against ChangesNative release identity
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
docs/MULTI_TARGET_RELEASE_VERSIONING.md (1)
47-59: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winExample is not copy-pasteable and omits the now-required
APP_VERSION.Two problems in this block:
\\at the end of lines 56-57 is an escaped backslash insh, not a line continuation; use a single\.assertNativeReleaseIdentitynow hard-fails unlessAPP_VERSIONmatchesversionName/MARKETING_VERSION, but the example never exports it (andbuild-target.tsdefaults it to0.0.0).📝 Proposed doc fix
```sh +export APP_VERSION=1.4.0 + # Android export MPGD_TARGET_VERSION_NAME=1.4.0 export MPGD_TARGET_VERSION_CODE=42 # iOS export MPGD_TARGET_MARKETING_VERSION=1.4.0 export MPGD_TARGET_BUILD_NUMBER=42 -mpgd target build android production \\ - --targets-file ./mpgd.targets.json \\ +mpgd target build android production \ + --targets-file ./mpgd.targets.json \ --kit-path ../mpgd-kit</details> <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.In
@docs/MULTI_TARGET_RELEASE_VERSIONING.mdaround lines 47 - 59, Update the
shell example in MULTI_TARGET_RELEASE_VERSIONING to export APP_VERSION=1.4.0
alongside the platform version variables, and replace the doubled trailing
backslashes in the mpgd target build command with single shell line-continuation
backslashes so the example is copy-pasteable.</details> <!-- cr-comment:v1:e98e2395be2d625db16ab0a7 --> </blockquote></details> </blockquote></details>🧹 Nitpick comments (5)
tools/target/native-release-identity.test.ts (2)
72-83: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winSuffix fixture permanently clobbers the shared shell fixture.
writeAndroidReleaseSuffixoverwritesandroid/app/build.gradlein the sharedshellRootand never restores it, so this assertion must stay last forever; any test added after it inherits the suffix gradle and fails for the wrong reason. Use a dedicated temp shell (or re-runwriteShellFiles(shellRoot)afterwards).♻️ Suggested isolation
writeAndroidReleaseSuffix(shellRoot); assert.throws(() => assertNativeReleaseIdentity({ @@ }), /does not support applicationIdSuffix or versionNameSuffix/u); + writeShellFiles(shellRoot);Also applies to: 103-108
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tools/target/native-release-identity.test.ts` around lines 72 - 83, Isolate the suffix fixture so it cannot permanently modify the shared shellRoot used by later tests. Update the test around writeAndroidReleaseSuffix and the corresponding case at lines 103–108 to use a dedicated temporary shell, or restore writeShellFiles(shellRoot) immediately after each assertion, while preserving the existing error expectations.
99-101: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winGood fixture expansion — Debug and ShareExtension values now prove the Release/App scoping. Consider also adding an iOS
marketingVersionvsAPP_VERSIONmismatch case to mirror the Android coverage at lines 60-70.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tools/target/native-release-identity.test.ts` around lines 99 - 101, Add a fixture scenario in the native release identity tests that sets differing iOS marketingVersion and APP_VERSION values, mirroring the Android mismatch coverage near the existing version cases. Assert that the implementation uses the correct iOS Release/App-scoped value and preserves the expected behavior when the values differ.docs/MULTI_TARGET_RELEASE_VERSIONING.md (1)
61-72: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDocument the two new failure modes. The bullet list still describes only match-against-metadata; it omits that release builds are rejected when
applicationIdSuffix/versionNameSuffixare present and that only the iOSApptarget'sReleaseconfiguration is inspected.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/MULTI_TARGET_RELEASE_VERSIONING.md` around lines 61 - 72, The Android validation documentation should also state that release builds fail when applicationIdSuffix or versionNameSuffix is configured. Clarify that iOS validation inspects only the App target’s Release configuration, while preserving the existing metadata and release-ledger matching requirements.tools/target/native-release-identity.ts (1)
160-171: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winSuffix guard silently skips Kotlin DSL / non-literal release blocks.
readNamedGradleBlockonly matchesbuildTypes {+release {. Common forms such asrelease { ... }declared viagetByName("release") {,named("release") {, orbuildTypes.release { ... }returnundefined, so the guard passes and the mismatch it was meant to prevent can still ship. Since a missing block is indistinguishable from "no suffix", consider matching those forms too (or failing closed whenbuildTypesexists but the release block can't be parsed).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tools/target/native-release-identity.ts` around lines 160 - 171, The Android release suffix guard in assertNoAndroidReleaseIdentitySuffix currently misses Kotlin DSL and non-literal release block forms. Extend the release-block detection used by readNamedGradleBlock to recognize getByName("release"), named("release"), and buildTypes.release declarations, or fail closed when buildTypes exists but its release block cannot be parsed, while preserving the existing suffix checks..sampo/changesets/fix-native-release-identity.md (1)
1-6: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueChangeset present and scoped correctly; consider whether
patchundersells the behavior change. Builds that previously passed preflight (suffixed release identities, native version drift fromAPP_VERSION) now fail, so aminorbump plus a one-line note on the newAPP_VERSIONrequirement would set clearer expectations. As per coding guidelines, "add a Sampo changeset when a publishable package's public API, runtime behavior, package metadata, generated release artifacts, or user-facing integration contract changes."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.sampo/changesets/fix-native-release-identity.md around lines 1 - 6, Update the changeset for npm/@mpgd/cli from a patch to a minor release, and add a concise note stating that APP_VERSION must match the native Android and iOS release identities before packaging.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. Inline comments: In `@tools/target/native-release-identity.ts`: - Around line 144-158: The assertNativeVersionMatchesGameVersion validation must distinguish an absent APP_VERSION from the default 0.0.0 value. Update the function to require and compare a non-default APP_VERSION when present, while skipping App in Toss native validation only when APP_VERSION is absent; preserve normal Android and non-App-in-Toss validation behavior. - Around line 173-187: Update readIosAppReleaseBuildSettings so the App-target match accepts both unquoted and quoted name values, including name = "App";. Also avoid eagerly calling readPbxObject for every parsed object: filter or search for PBXNativeTarget candidates first and read blocks lazily within the lookup, while preserving the existing missing-target error. --- Outside diff comments: In `@docs/MULTI_TARGET_RELEASE_VERSIONING.md`: - Around line 47-59: Update the shell example in MULTI_TARGET_RELEASE_VERSIONING to export APP_VERSION=1.4.0 alongside the platform version variables, and replace the doubled trailing backslashes in the mpgd target build command with single shell line-continuation backslashes so the example is copy-pasteable. --- Nitpick comments: In @.sampo/changesets/fix-native-release-identity.md: - Around line 1-6: Update the changeset for npm/@mpgd/cli from a patch to a minor release, and add a concise note stating that APP_VERSION must match the native Android and iOS release identities before packaging. In `@docs/MULTI_TARGET_RELEASE_VERSIONING.md`: - Around line 61-72: The Android validation documentation should also state that release builds fail when applicationIdSuffix or versionNameSuffix is configured. Clarify that iOS validation inspects only the App target’s Release configuration, while preserving the existing metadata and release-ledger matching requirements. In `@tools/target/native-release-identity.test.ts`: - Around line 72-83: Isolate the suffix fixture so it cannot permanently modify the shared shellRoot used by later tests. Update the test around writeAndroidReleaseSuffix and the corresponding case at lines 103–108 to use a dedicated temporary shell, or restore writeShellFiles(shellRoot) immediately after each assertion, while preserving the existing error expectations. - Around line 99-101: Add a fixture scenario in the native release identity tests that sets differing iOS marketingVersion and APP_VERSION values, mirroring the Android mismatch coverage near the existing version cases. Assert that the implementation uses the correct iOS Release/App-scoped value and preserves the expected behavior when the values differ. In `@tools/target/native-release-identity.ts`: - Around line 160-171: The Android release suffix guard in assertNoAndroidReleaseIdentitySuffix currently misses Kotlin DSL and non-literal release block forms. Extend the release-block detection used by readNamedGradleBlock to recognize getByName("release"), named("release"), and buildTypes.release declarations, or fail closed when buildTypes exists but its release block cannot be parsed, while preserving the existing suffix checks.🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID:
ac594776-5b61-4bb7-9390-b9dff34e5e26📒 Files selected for processing (4)
.sampo/changesets/fix-native-release-identity.mddocs/MULTI_TARGET_RELEASE_VERSIONING.mdtools/target/native-release-identity.test.tstools/target/native-release-identity.ts
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fb876906f0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } | ||
|
|
||
| function assertNoAndroidReleaseIdentitySuffix(source: string, file: string): void { | ||
| const releaseBlock = readNamedGradleBlock(source, 'buildTypes', 'release'); |
There was a problem hiding this comment.
Reject suffixes assigned outside the nested release block
When a valid Groovy build assigns a suffix through a qualified expression such as buildTypes.release.applicationIdSuffix = ".store", this helper returns only the contents of buildTypes { release { ... } } and therefore misses the assignment. The preflight then validates the unsuffixed defaultConfig values and passes, while the bundleRelease invocation in tools/target/build-target.ts packages a different application ID. Search the complete Gradle source for these release-affecting assignments, or reject unsupported qualified configuration forms as well.
Useful? React with 👍 / 👎.
| const source = readRequiredFile(file, 'iOS Xcode project configuration'); | ||
| const appReleaseSettings = readIosAppReleaseBuildSettings(source, file); |
There was a problem hiding this comment.
Resolve inherited App Release settings
When the App target inherits an identity setting from the PBXProject Release configuration, or its target value is $(inherited), Xcode can resolve a valid effective bundle ID and version even though this extracted target buildSettings block has no matching literal. The subsequent assertions therefore reject a correctly configured archive before the build runs. Combine the target settings with their project/base configuration inheritance rather than requiring all three values directly in the target block.
Useful? React with 👍 / 👎.
| const globalExpression = expression.global | ||
| ? expression | ||
| : new RegExp(expression.source, `${expression.flags}g`); | ||
| const values = [...source.matchAll(globalExpression)].map((match) => match[1]?.trim()); |
There was a problem hiding this comment.
Exclude commented settings from identity matches
When build.gradle retains a commented-out old value such as // versionName "1.3.0", this raw-source matchAll includes it alongside the active versionName "1.4.0"; values.some(...) then reports an identity mismatch even though Gradle packages the expected release. Ignore comments and string contents, or parse only active Gradle declarations, before comparing all matches.
Useful? React with 👍 / 👎.
| mpgd target build android production \\ | ||
| --targets-file ./mpgd.targets.json \\ |
There was a problem hiding this comment.
Use single shell continuation backslashes
When a reader copies this documented command, each \\ escapes the other backslash instead of the newline, so mpgd receives a literal backslash argument and the following --targets-file and --kit-path lines are executed as separate commands. Use one trailing backslash on each continued shell line so the documented native release invocation is runnable.
Useful? React with 👍 / 👎.
| if (character === '{') { | ||
| depth += 1; |
There was a problem hiding this comment.
Ignore braces inside quoted PBX values
When any Xcode object contains an unmatched brace character inside a quoted value—commonly a PBXShellScriptBuildPhase with a command such as echo '{'—this counter treats the string character as structural nesting. Because App-target discovery eagerly calls readPbxObject for every object, an otherwise valid project then throws an "unclosed Xcode object" error and cannot pass release preflight. Parse quoted strings and comments separately, or locate the target without brace-scanning unrelated objects.
Useful? React with 👍 / 👎.
|
@codex review |
There was a problem hiding this comment.
🧹 Nitpick comments (4)
tools/target/native-release-identity.ts (4)
168-177: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueStatic-analysis ReDoS hints here are false positives.
expressionis always an internal literal regex; reconstructing it with thegflag is a correct fix formatchAll. No action needed. One nit:new RegExp(expression.source, ...)drops nothing today, but a cached module-levelWeakMap<RegExp, RegExp>would avoid recompiling per call if this ever runs in a loop over large sources.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tools/target/native-release-identity.ts` around lines 168 - 177, No code changes are needed for readSettingValues; retain the current regex reconstruction and matchAll handling. The caching suggestion is optional and outside this comment’s required scope.Source: Linters/SAST tools
208-228: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value
/\brelease\s*\{/gualso matches non-buildTypereleaseblocks.
signingConfigs { release { ... } }(and any other DSL block literally namedrelease) is collected as an "Android release Gradle block". Today that only widens the suffix check, so it is a false-positive risk rather than a miss — but the error message would then point at a block that has nothing to do with build types. Consider scoping block discovery to abuildTypes { ... }region before matching barerelease {.Also note
buildTypes.release { }is matched by both the first and third expression, yielding duplicate blocks.Also applies to: 340-361
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tools/target/native-release-identity.ts` around lines 208 - 228, Update release-block discovery used by assertNoAndroidReleaseIdentitySuffix to only collect bare release blocks within a buildTypes region, excluding signingConfigs and other unrelated DSL blocks. Also prevent buildTypes.release blocks from being collected twice when already matched by the qualified buildTypes.release expression, while preserving suffix validation for genuine Android release build configurations.
384-460: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoffShared scanner state duplicated between
readBracedBlockandstripComments.Both implement the same quote/line-comment/block-comment state machine with slightly different bodies. Extracting a small tokenizer (or a generator yielding
{ index, character, inCode }) would keep the two in sync when pbxproj/Gradle quirks are found later. Not blocking — current behavior looks correct, including quote-before-comment precedence.One edge case worth knowing: an unquoted pbxproj value containing
//(e.g. a bare URL) would be treated as a line comment byreadBracedBlock. Xcode quotes such values, so this is theoretical.Also applies to: 462-531
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tools/target/native-release-identity.ts` around lines 384 - 460, Extract the shared quote, line-comment, and block-comment scanning state machine from readBracedBlock and stripComments into a reusable tokenizer or scanner helper that exposes each character’s index and code/comment status. Refactor both functions to consume this shared state while preserving their current brace parsing and comment-stripping behavior, including quote-before-comment precedence.
263-310: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winDeduplicate the two full-project pbxproj scans.
findIosAppTargetBlockandfindIosProjectBlockare identical except for the final predicate, and each one callsreadBracedBlockfor every object in the file — so a realproject.pbxprojis walked (and brace-matched) twice end to end. A single pass that returns both blocks, with the cheapisatest applied before reading the full block, keeps preflight linear-ish and removes the copy/paste.♻️ Suggested single-pass scan
-function findIosAppTargetBlock(source: string, file: string): string | undefined { - for (const match of source.matchAll(/\b([A-F0-9]+)\s*\/\*\s*[^*]+\s*\*\/\s*=\s*\{/gu)) { +function findIosBlocks(source: string, file: string): { + appTarget: string | undefined; + project: string | undefined; +} { + let appTarget: string | undefined; + let project: string | undefined; + + for (const match of source.matchAll(/\b([A-F0-9]+)\s*\/\*\s*[^*]+\s*\*\/\s*=\s*\{/gu)) { const id = match[1]; if (id === undefined || match.index === undefined) { continue; } + if (appTarget !== undefined && project !== undefined) { + break; + } + const block = readBracedBlock( source, source.indexOf('{', match.index), `Xcode object ${id}`, file, ); - if ( - /\bisa\s*=\s*PBXNativeTarget;/u.test(block) - && /\bname\s*=\s*"?App"?\s*;/u.test(block) - ) { - return block; - } + if ( + appTarget === undefined + && /\bisa\s*=\s*PBXNativeTarget;/u.test(block) + && /\bname\s*=\s*"?App"?\s*;/u.test(block) + ) { + appTarget = block; + } else if (project === undefined && /\bisa\s*=\s*PBXProject;/u.test(block)) { + project = block; + } } - return undefined; -} + return { appTarget, project }; +}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tools/target/native-release-identity.ts` around lines 263 - 310, Replace the separate scans in findIosAppTargetBlock and findIosProjectBlock with one shared pass that examines each Xcode object once, checks the object’s cheap isa indicators before calling readBracedBlock, and returns both the PBXNativeTarget named App block and the PBXProject block together. Update callers to consume the paired result while preserving undefined behavior when either block is absent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@tools/target/native-release-identity.ts`:
- Around line 168-177: No code changes are needed for readSettingValues; retain
the current regex reconstruction and matchAll handling. The caching suggestion
is optional and outside this comment’s required scope.
- Around line 208-228: Update release-block discovery used by
assertNoAndroidReleaseIdentitySuffix to only collect bare release blocks within
a buildTypes region, excluding signingConfigs and other unrelated DSL blocks.
Also prevent buildTypes.release blocks from being collected twice when already
matched by the qualified buildTypes.release expression, while preserving suffix
validation for genuine Android release build configurations.
- Around line 384-460: Extract the shared quote, line-comment, and block-comment
scanning state machine from readBracedBlock and stripComments into a reusable
tokenizer or scanner helper that exposes each character’s index and code/comment
status. Refactor both functions to consume this shared state while preserving
their current brace parsing and comment-stripping behavior, including
quote-before-comment precedence.
- Around line 263-310: Replace the separate scans in findIosAppTargetBlock and
findIosProjectBlock with one shared pass that examines each Xcode object once,
checks the object’s cheap isa indicators before calling readBracedBlock, and
returns both the PBXNativeTarget named App block and the PBXProject block
together. Update callers to consume the paired result while preserving undefined
behavior when either block is absent.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 17561229-cba1-4f59-a1ad-7824c21036cd
📒 Files selected for processing (4)
.sampo/changesets/fix-native-release-identity.mddocs/MULTI_TARGET_RELEASE_VERSIONING.mdtools/target/native-release-identity.test.tstools/target/native-release-identity.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- .sampo/changesets/fix-native-release-identity.md
- tools/target/native-release-identity.test.ts
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fe895077b6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const targetValues = readSettingValues(settings.target, expression) | ||
| .filter((value) => value !== '$(inherited)'); | ||
| const values = targetValues.length > 0 | ||
| ? targetValues | ||
| : readSettingValues(settings.project, expression); |
There was a problem hiding this comment.
Inspect target xcconfigs before accepting project values
When the App target's Release configuration supplies an identity through its baseConfigurationReference, the inline target settings are absent or $(inherited), so this fallback validates only the project-level value. A target xcconfig can therefore override PRODUCT_BUNDLE_IDENTIFIER, MARKETING_VERSION, or CURRENT_PROJECT_VERSION with a different value while the preflight passes, allowing the archive identity to diverge from the release manifest.
Useful? React with 👍 / 👎.
| const qualifiedReleaseSuffixes = [ | ||
| /\bbuildTypes\s*\.\s*release\s*\.\s*(?:applicationIdSuffix|versionNameSuffix)\b/u, | ||
| /\bbuildTypes\s*\.\s*(?:getByName|named)\s*\(\s*["']release["']\s*\)\s*\.\s*(?:applicationIdSuffix|versionNameSuffix)\b/u, | ||
| ]; |
There was a problem hiding this comment.
Reject suffixes inherited from defaultConfig
When defaultConfig declares a valid versionNameSuffix or applicationIdSuffix, the release variant inherits that suffix, but these checks inspect only buildTypes.release forms. The ordinary identity regexes do not match the suffix property either, so preflight accepts the unsuffixed values and the generated bundleRelease can have an identity different from the recorded release metadata.
Useful? React with 👍 / 👎.
Summary
APP_VERSIONApptarget Release configuration in Xcode projectsRoot cause
The previous preflight validated broad source text and did not bind native marketing versions to the game release version. It also passed a non-global regular expression to
String.prototype.matchAll.Validation
pnpm exec ttsc --noEmit -p tsconfig.tools.jsonpnpm smoke:native-release-identitypnpm graph:preflightSAMPO_RELEASE_BRANCH=main sampo release --dry-runocr review --audience agent --commit fb87690Known baseline check
pnpm checkcurrently fails on two unrelated starter/template parity checks inpostOperationStore.tsandredditSandbox.ts; this PR does not change either file.Summary by CodeRabbit
package.jsonversion as the source of truth and added pre-build validation guidance.