Skip to content

feat(mobile): add worktree-aware debug identities - #2858

Merged
brow merged 3 commits into
mainfrom
tho/mobile-dev-labels
Jul 26, 2026
Merged

feat(mobile): add worktree-aware debug identities#2858
brow merged 3 commits into
mainfrom
tho/mobile-dev-labels

Conversation

@tellaho

@tellaho tellaho commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Category: improvement
User Impact: Developers can identify which worktree produced a mobile debug app, keep a bounded set of worktree builds installed side by side, and preserve each worktree app's login and local state while switching branches.

Problem: Mobile debug builds from every checkout currently appear as the same “Buzz” app and share one application identity, so the running source is ambiguous and one worktree build replaces another. A branch-keyed identity would avoid replacement but create stale installs and fresh app state on every branch switch.

Solution: Give each linked worktree a stable Debug-only application identity derived from its sanitized directory name. Show the sanitized branch name (or short commit SHA when detached) in the display label, persist generated native overrides for direct IDE builds, and leave Release/Profile identities unchanged. Worktree defaults remain lower precedence than a developer's iOS AppOverrides.xcconfig. just mobile-clean provides a safe cleanup path for suffixed worktree installs while preserving production Buzz.

File changes

.github/workflows/ci.yml
Runs the expanded worktree override contract when relevant mobile or native configuration changes.

AGENTS.md
Documents worktree-aware mobile development and cleanup for contributors and agents.

Justfile
Generates overrides before mobile development and Android debug builds, and exposes just mobile-clean.

mobile/README.md
Explains stable per-worktree identities, branch/SHA labels, direct IDE usage, cleanup, and Release/Profile guarantees.

mobile/android/.gitignore
Ignores generated worktree properties.

mobile/android/app/build.gradle.kts
Loads and validates generated properties, then applies the application ID suffix and display label to Android Debug only.

mobile/android/app/src/main/AndroidManifest.xml
Resolves the Android app label through an overridable string resource.

mobile/ios/.gitignore
Ignores generated iOS worktree settings.

mobile/ios/Flutter/Debug.xcconfig
Loads generated worktree defaults before developer AppOverrides, so personal signing overrides retain precedence.

mobile/ios/Flutter/Release.xcconfig
Pins the production display name and bundle identifier for Release/Profile builds.

mobile/ios/Runner/Info.plist
Resolves the visible iOS app name from build settings.

scripts/mobile-worktree-overrides.sh
Detects linked worktrees, derives a stable directory-keyed identity, sanitizes branch/SHA display context, writes native Debug overrides, and removes stale overrides in the main checkout.

scripts/mobile-worktree-clean.sh
Lists or removes suffixed Buzz worktree installs from booted iOS simulators and connected Android emulators without matching production IDs; supports --dry-run.

scripts/test-mobile-worktree-overrides.sh
Covers worktree detection, branch-switch identity stability, detached HEAD fallback, special-character sanitization, iOS override precedence, brace-aware Release/Profile purity, cleanup safety, ignores, and command integration.

Reproduction steps

  1. From a linked worktree, activate the repository toolchain and run just mobile-dev.
  2. Inspect the running app: its label should be Buzz (<sanitized-branch>), while its application ID suffix is derived from the worktree directory.
  3. Switch branches in the same worktree, rerun the override script, and confirm the application ID remains stable while the display label updates. In detached HEAD, confirm the label uses a short SHA.
  4. Build Debug from a second worktree and confirm both apps remain installed side by side with independent state.
  5. Build from Xcode after setting AppOverrides.xcconfig and confirm developer overrides still win over generated worktree defaults.
  6. Run just mobile-clean --dry-run, then just mobile-clean, and confirm suffixed worktree installs are targeted while the production app is preserved.
  7. Build Release/Profile and confirm the production name and application identity remain unchanged.
  8. Run scripts/test-mobile-worktree-overrides.sh, just mobile-check, just mobile-test, and just mobile-build-android.

Screenshots / demos

iOS — labeled app switcher iOS — side-by-side installs
Buzz worktree label in the iOS app switcher Buzz production and worktree debug apps installed side by side on iOS
Android — side-by-side installs Android — labeled app switcher
Buzz production and worktree debug apps installed side by side on Android Buzz worktree label in the Android app switcher

@tellaho tellaho changed the title improve mobile debug identity across worktrees feat(mobile): add worktree-aware debug identities Jul 26, 2026
@tellaho
tellaho force-pushed the tho/mobile-dev-labels branch from 18c1d74 to ec5390f Compare July 26, 2026 15:32
npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w and others added 3 commits July 26, 2026 08:46
Mirror the desktop dev experience on mobile: debug builds produced from a
git worktree get a branch label in the app name ("Buzz (my-branch)") and a
unique app identifier, so developers can tell which worktree produced a
running iOS/Android app and keep builds from multiple worktrees installed
side by side.

- scripts/mobile-worktree-env.sh derives the label/slugs from the branch
  (same worktree detection as scripts/instance-env.sh) and writes two
  gitignored override files: mobile/ios/Flutter/WorktreeOverrides.xcconfig
  and mobile/android/worktree.properties. In the main checkout it removes
  stale overrides.
- iOS: CFBundleDisplayName now resolves from a new APP_DISPLAY_NAME build
  setting; Debug.xcconfig includes WorktreeOverrides.xcconfig last so it
  wins in Debug only. Release/Profile keep production identity.
- Android: the manifest label resolves from an app_name resValue; the debug
  build type applies the worktree applicationIdSuffix and label from
  worktree.properties. Release never reads it.
- just mobile-dev and just mobile-build-android run the script
  automatically; direct Xcode/Android Studio use is documented in
  mobile/README.md.
- scripts/test-mobile-worktree-env.sh covers the contract (override
  generation, sanitization, debug-only wiring, gitignore) and runs in the
  CI mobile-changes contract job.

Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Gradle Kotlin DSL on CI resolves the lowercase `java` in
`java.util.Properties()` as Gradle's `java` extension, breaking
compilation of the worktree-aware debug identity block. Import the class
at the top of the script and use the bare `Properties()` constructor.

No behavior change; debug-only identity wiring untouched.

Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Address review on the worktree-aware debug identity:

- Key the install identity (bundle id / applicationIdSuffix) to the
  worktree DIRECTORY name instead of the branch, so installed apps and
  their login state are bounded by worktree count and never churn on
  branch switches.
- Keep the branch as a display-only label, falling back to the short
  SHA when the worktree is detached, and sanitize it to [A-Za-z0-9._-]
  so no valid Git ref (apostrophes, quotes, $) can break Android string
  resources or xcconfig values. Gradle now validates the label with the
  same rigor as the suffix.
- Include WorktreeOverrides.xcconfig BEFORE AppOverrides.xcconfig so a
  developer's app-specific overrides (e.g. a personal BUNDLE_IDENTIFIER
  for device signing) take precedence over worktree defaults.
- Fix the release-purity contract check: the old awk /release {/,/}/
  range ended at the first nested close brace and skipped most of the
  release block. Replace it with a brace-counting extractor and a
  self-test proving it scans past nested blocks.
- Add just mobile-clean + scripts/mobile-worktree-clean.sh to uninstall
  stale worktree-suffixed installs from booted iOS simulators and
  connected Android devices (--dry-run supported); production ids are
  never matched.
- Rename mobile-worktree-env.sh -> mobile-worktree-overrides.sh (it
  writes override files, not env), drop the hardcoded applicationId
  claim from its output, and expand the contract tests: branch-switch
  stability, detached HEAD, special-character refs, xcconfig precedence
  order, and cleanup safety.

Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
@tellaho
tellaho force-pushed the tho/mobile-dev-labels branch from ec5390f to e140f62 Compare July 26, 2026 16:00
@tellaho
tellaho marked this pull request as ready for review July 26, 2026 16:26
@tellaho
tellaho requested a review from a team as a code owner July 26, 2026 16:26
@brow
brow merged commit 1a56b7c into main Jul 26, 2026
26 checks passed
@brow
brow deleted the tho/mobile-dev-labels branch July 26, 2026 20:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants