chore: 0.9.21+52, and stop the iOS embedded bundles shipping a stale version - #165
Conversation
…version Sets one version across every artefact — Android, the GitHub release, the iOS app, and (the part that was broken) the iOS widget extension and Watch app. THE BUG The iOS embedded bundles do NOT follow pubspec. The widget extension and the Watch app build their Info.plist from Xcode build settings (GENERATE_INFOPLIST_FILE = YES), so their version comes from MARKETING_VERSION / CURRENT_PROJECT_VERSION in project.pbxproj. Only the Runner target reads $(FLUTTER_BUILD_NAME), because Flutter's Generated.xcconfig is in scope for that target alone. So a pubspec bump moved the host app and left the embedded bundles behind. Built at pubspec 0.9.21+52 with the pbxproj untouched: main app 0.9.21 / 52 OpenStrapWidgetExtension.appex 0.9.20 / 51 OpenStrapWatch Watch App.app 0.9.20 / 51 Apple rejects that. An extension or WatchKit app whose CFBundleShortVersionString differs from its host fails App Store validation, so the upload bounces after the archive — which is why six pbxproj entries were all sitting at exactly 0.9.20/51: someone has been hand-editing them every release, and forgetting once is a failed submission. WHAT DIDN'T WORK Pointing them at $(FLUTTER_BUILD_NAME)/$(FLUTTER_BUILD_NUMBER) — the obvious fix, and already the pattern on three Runner configs. The variables are undefined for those targets, so the versions came out EMPTY. Caught by rebuilding and reading the compiled bundles rather than by assuming it worked; reverted. WHAT'S HERE - pubspec 0.9.20+51 → 0.9.21+52. - Every MARKETING_VERSION → 0.9.21 and CURRENT_PROJECT_VERSION → 52, including the three Runner configs that were still on a template-default 1.0/1. Those are inert (the build proves Runner follows pubspec), but uniform values let the guard below stay dumb and strict instead of special-casing targets. - A release guard beside the existing versionCode one: the tag build now fails if any pbxproj version disagrees with pubspec. Same spirit as the guard that already catches a missing +BUILD suffix — the failure mode is silent and only shows up at upload time, so CI should catch it. Verified on compiled bytes, not on intent: main app / widget / Watch → 0.9.21 / 52 Analyze clean.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThe release version advances to ChangesRelease version validation
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant Preflight
participant Pubspec as pubspec.yaml
participant Xcode as project.pbxproj
participant ReleaseJobs as Android and iOS jobs
Preflight->>Pubspec: Read versionName and BUILD
Preflight->>Xcode: Validate iOS version settings and Info.plist mappings
Xcode-->>Preflight: Return version values
Preflight->>ReleaseJobs: Permit packaging when guards pass
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
PR Reviewer Guide 🔍(Review updated until commit d110d9c)Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Explore these optional code suggestions:
|
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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 @.github/workflows/build.yml:
- Around line 83-97: Update the validation block using the pbx and bad symbols
to inspect every embedded widget and Watch target/configuration, not just the
unique values returned by grep. Require each embedded configuration to define a
literal numeric MARKETING_VERSION matching name and CURRENT_PROJECT_VERSION
matching build, and set bad=1 for missing, inherited, unresolved, or otherwise
non-numeric settings before exiting.
- Around line 68-99: Move the MARKETING_VERSION/CURRENT_PROJECT_VERSION
validation block into a dedicated preflight job that runs before releases. Add a
needs dependency from both the android and ios jobs to this preflight job,
ensuring either release is blocked when the guard fails while preserving the
existing version checks and error behavior.
🪄 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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: b95b76e7-b9ff-485c-8871-3dda465754d9
📒 Files selected for processing (3)
.github/workflows/build.ymlios/Runner.xcodeproj/project.pbxprojpubspec.yaml
…ery config Two review findings on the guard added in the previous commit, both real. The guard ran inside the `android` job while `ios` had no `needs:`, so the check protecting the iOS embedded bundles did not gate the iOS build — a mismatch would fail Android while iOS carried on and published the IPA. It now lives in a `preflight` job that both release jobs depend on. The pbxproj check also matched only values starting with a digit, so `MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)"` matched nothing and passed — and that is precisely the configuration proven to build EMPTY versions into the widget and Watch bundles. Verified against the old check: it reports clean on that input. Replaced with a per-build-configuration check. Configurations with GENERATE_INFOPLIST_FILE=YES generate their Info.plist from these settings and must carry literal versions matching pubspec; the Runner configurations have no such flag and correctly use $(FLUTTER_BUILD_*), because Runner's checked-in Info.plist substitutes them. It also fails when no such configuration is found, so the guard cannot quietly stop checking anything, and asserts Runner/Info.plist still tracks pubspec. Verified by extracting the step from the parsed YAML and running it: passes on the current tree (9 configurations), and exits 1 for a build variable on an embedded target, a stale literal, a deleted setting, and a Runner Info.plist that stops tracking pubspec.
|
Persistent review updated to latest commit 6534fd0 |
PR Code Suggestions ✨Explore these optional code suggestions:
|
There was a problem hiding this comment.
🧹 Nitpick comments (2)
.github/workflows/build.yml (2)
19-21: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winSet
persist-credentials: falseon this checkout.This step doesn't push or tag anything — it only reads tags for the versionCode guard — so there's no need to persist the checkout credential on disk.
🔒 Suggested fix
- uses: actions/checkout@v4 with: fetch-depth: 0 # all tags, so the versionCode guard can read prior releases' pubspec + persist-credentials: false🤖 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 @.github/workflows/build.yml around lines 19 - 21, Update the actions/checkout@v4 step to set persist-credentials to false while retaining fetch-depth: 0 for reading prior release tags.Source: Linters/SAST tools
155-158: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winComment and
fetch-depth: 0here are now stale — the guard they refer to lives inpreflight.The versionCode guard that needed full tag history has moved entirely to the new
preflightjob. Nothing else inandroidreads git tags, so keepingfetch-depth: 0here just makes this job's checkout slower/heavier for no benefit, and the comment now misdescribes what happens in this job. Also flagged by static analysis: this checkout doesn't setpersist-credentials: falseeither.🧹 Suggested fix
- uses: actions/checkout@v4 - with: - fetch-depth: 0 # need all tags so the versionCode guard can read prior releases' pubspec + with: + persist-credentials: falseIf anything else downstream in this job does rely on tag history, keep
fetch-depth: 0but drop the stale comment and still addpersist-credentials: false.🤖 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 @.github/workflows/build.yml around lines 155 - 158, Update the checkout step in the android job to remove the stale fetch-depth comment and use a shallow checkout instead of fetching all history, since tag history is now handled by preflight. Add persist-credentials: false to the actions/checkout@v4 configuration; retain full history only if another downstream android step demonstrably requires tags.Source: Linters/SAST tools
🤖 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 @.github/workflows/build.yml:
- Around line 19-21: Update the actions/checkout@v4 step to set
persist-credentials to false while retaining fetch-depth: 0 for reading prior
release tags.
- Around line 155-158: Update the checkout step in the android job to remove the
stale fetch-depth comment and use a shallow checkout instead of fetching all
history, since tag history is now handled by preflight. Add persist-credentials:
false to the actions/checkout@v4 configuration; retain full history only if
another downstream android step demonstrably requires tags.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: f23d0abd-d2dc-4d78-beb2-b2eb6e58271d
📒 Files selected for processing (1)
.github/workflows/build.yml
… credentials The `android` checkout still pulled full history with a comment about the versionCode guard, but that guard moved to `preflight` in the previous commit and nothing left in the job reads git. `generate_release_notes` builds its diff on GitHub's side from the tag rather than from local history — the ios job has always checked out shallow and attaches to the same release without trouble, which is the evidence that the deep fetch was doing nothing here. `preflight` keeps `fetch-depth: 0`, since it is the one that walks the tags. Also sets `persist-credentials: false` on all three checkouts. No step pushes or otherwise writes to the repo, so leaving the token in `.git/config` only widens what a compromised build dependency could read — and these jobs run third-party pub and pod code.
|
Both latest items looked at — one was right, one I have to push back on. CodeRabbit, stale PR Agent, "Fix crash when Info.plist key is absent" (importance 8) — this one is a false positive, and I checked before dismissing it. The claim is that Verified rather than argued — I deleted the Clean error, exit 1, no |
|
Persistent review updated to latest commit d110d9c |
PR Code Suggestions ✨No code suggestions found for the PR. |
User description
Aligns one version across every artefact — Android, the GitHub release, the iOS app, and the part that was actually broken: the iOS widget extension and Watch app.
The bug
The iOS embedded bundles don't follow pubspec. The widget extension and Watch app build their
Info.plistfrom Xcode build settings (GENERATE_INFOPLIST_FILE = YES), so their version comes fromMARKETING_VERSION/CURRENT_PROJECT_VERSIONinproject.pbxproj. Only the Runner target reads$(FLUTTER_BUILD_NAME)— Flutter'sGenerated.xcconfigis in scope for that target alone.So bumping pubspec moves the host app and leaves the embedded bundles behind. Built at pubspec
0.9.21+52with the pbxproj untouched:Apple rejects this. An extension or WatchKit app whose
CFBundleShortVersionStringdiffers from its host fails App Store validation, so the upload bounces after the archive. That's why six pbxproj entries were all sitting at exactly0.9.20/51— they've been hand-edited every release, and forgetting once costs a failed submission. Since store uploads are manual (no fastlane), nothing was catching it.What didn't work
Pointing them at
$(FLUTTER_BUILD_NAME)/$(FLUTTER_BUILD_NUMBER)— the obvious fix, and already the pattern on three Runner configs. The variables are undefined for those targets and the versions came out empty. Caught by rebuilding and reading the compiled bundles rather than assuming; reverted. Worth recording so nobody re-tries it.What's here
pubspec.yaml0.9.20+51 → 0.9.21+52MARKETING_VERSION→0.9.21, everyCURRENT_PROJECT_VERSION→52. That includes three Runner configs still on a template-default1.0/1— inert (the build proves Runner follows pubspec), but making them uniform lets the guard stay dumb and strict rather than special-casing targets.+BUILDsuffix — this failure mode is silent and only surfaces at upload time, so CI should own it.Verification
On compiled bytes, not intent —
flutter build ios --debug --no-codesign, then PlistBuddy on each bundle:Guard logic dry-run locally against the updated pbxproj: passes.
flutter analyzeclean. No Dart changed, sokAlgoVersionstays at 50.Release convention from here
pubspec.yamlis the single source of truth. Bump it, and the tagvX.Y.Zmust match its versionName — both already enforced. The new guard extends that to the iOS bundles, so App Store, Play Store and GitHub release all carry the same number by construction.🤖 Generated with Claude Code
PR Type
Bug fix, Other
Description
Bump app version from
0.9.20+51to0.9.21+52across all targetsFix iOS widget extension and Watch app shipping stale version numbers
MARKETING_VERSIONandCURRENT_PROJECT_VERSIONupdated for all embedded bundle configs (Debug/Release/Profile)1.0/1to0.9.21/52Add CI guard to catch future pubspec/pbxproj version drift before App Store rejection
Diagram Walkthrough
File Walkthrough
pubspec.yaml
Bump app version to 0.9.21+52pubspec.yaml
0.9.20+51to0.9.21+52MARKETING_VERSION/CURRENT_PROJECT_VERSIONinproject.pbxprojmust bemanually kept in sync
project.pbxproj
Align all iOS embedded bundle versions to 0.9.21/52ios/Runner.xcodeproj/project.pbxproj
MARKETING_VERSIONupdated to0.9.21for widget extension(Debug/Release/Profile) and Watch app (Debug/Release/Profile)
CURRENT_PROJECT_VERSIONupdated to52for widget extension and Watchapp configs
placeholder
1.0/1to0.9.21/52build.yml
Add CI guard preventing iOS embedded bundle version drift.github/workflows/build.yml
MARKETING_VERSIONandCURRENT_PROJECT_VERSIONvalues from
project.pbxprojthe pubspec
versionName/versionCode$(FLUTTER_BUILD_NAME)and why the guard is necessarySummary by CodeRabbit