fix: SHA-256 version checks for iOS/Android drivers and app binary cache#3140
Open
qwertey6 wants to merge 3 commits intomobile-dev-inc:mainfrom
Open
fix: SHA-256 version checks for iOS/Android drivers and app binary cache#3140qwertey6 wants to merge 3 commits intomobile-dev-inc:mainfrom
qwertey6 wants to merge 3 commits intomobile-dev-inc:mainfrom
Conversation
iOS simulators share the host's localhost, causing port collisions when
multiple Maestro processes target different sims simultaneously. Session
tracking was per-platform, so two processes on different devices would
interfere with each other's sessions.
Changes:
- Per-device session tracking: SessionStore keys are now
"{platform}_{deviceId}_{sessionId}" instead of "{platform}_{sessionId}"
- Add --driver-host-port CLI flag for explicit XCTest server port
- Auto-select available ports with isPortAvailable() check
- Refactor SessionStore from singleton to injectable class (DI)
- Add shouldCloseSession(platform, deviceId) for per-device shutdown
instead of global activeSessions().isEmpty()
- Add cross-process file locking to KeyValueStore (~/.maestro/sessions)
- Append PID to debug log directory to prevent parallel race
- Enable useJUnitPlatform() in maestro-cli (was missing)
- Add SessionStoreTest with 8 tests covering isolation and lifecycle
Verified: 3 iOS simulators + Android emulator running simultaneously,
all passing. Both --driver-host-port (explicit) and auto-port-selection
work correctly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Default --reinstall-driver to false: reuse a healthy running driver instead of killing and reinstalling on every run (~40s saved on iOS) - XCTestDriverClient checks isChannelAlive() before reinstalling — if the user explicitly passes --reinstall-driver, honor it - Cache extracted iOS build products per-device in ~/.maestro/build-products/<deviceId>/ with SHA-256 hash validation: skips extraction when source matches cache, re-extracts on upgrade - Reduce XCTest status check HTTP read timeout from 100s to 3s - Remove Thread.sleep(1000) heartbeat delay hack (no longer needed with per-device session tracking) Single device: ~52s → ~10-12s. Three devices parallel: ~54s → ~18s. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- iOS XCTest runner: add isVersionMatch() to XCTestInstaller interface. LocalXCTestInstaller compares SHA-256 hash of build products against a .running-hash marker written at startup. restartXCTestRunner now checks both isChannelAlive() AND isVersionMatch() — stale runners from a previous Maestro version are replaced automatically. - Android driver: add isDriverVersionCurrent() that hashes the bundled maestro-app.apk and maestro-server.apk, compares against stored hash in ~/.maestro/android-driver-hash. On mismatch, APKs are reinstalled even when reinstallDriver=false. - App binary cache (clearAppState): getCachedAppBinary now compares Info.plist of cached vs installed app. Stale cache from app updates is detected and refreshed before reinstall. Per-device cache dirs (~/.maestro/app-cache/<deviceId>/) prevent parallel races. - Add XCTestDriverClientTest (4 tests) and LocalSimulatorUtilsTest (3 tests) covering version mismatch, reuse, and cache behavior. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This was referenced Apr 4, 2026
Open
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Proposed changes
Makes
reinstallDriver=falsesafe for production. PR #3139 skips driver reinstallation for ~4x faster startup, but without version checks, upgrading Maestro could leave stale drivers on devices — causing silent failures or incompatible API responses. This PR adds SHA-256 hash validation to both platforms.Impact
clearAppStateno longer silently downgrades apps on cloned simulatorsclearStatecommand doesn't reliably clear UserDefaults on iOS #1601 —clearStatereliability on iOS (the cloned simulator binary race)What could go wrong without this PR
If a user upgrades Maestro and runs
maestro test:isChannelAlive()finds the old XCTest runner still responding → skips reinstall → old runner has incompatible API → mysterious failuresisPackageInstalled()finds old APKs → skips install → old gRPC server has different protocol → mysterious failuresclearAppState: App binary cached from v1, user installs v2,clearAppStatereinstalls from cache → app silently reverted to v1Changes
iOS XCTest runner version check:
XCTestInstallerinterface gainsisVersionMatch(): Boolean(defaulttruefor backward compat)LocalXCTestInstallercomputes SHA-256 of build products, writes.running-hashmarker on startup, compares on reuseXCTestDriverClient.restartXCTestRunner()checksisChannelAlive() && isVersionMatch()— mismatch triggers fresh startAndroid driver version check:
AndroidDriver.computeDriverHash()hashes bundledmaestro-app.apk+maestro-server.apk~/.maestro/android-driver-hashafter installinstallMaestroDriverApp()/installMaestroServerApp()check hash before skipping — mismatch forces reinstallApp binary cache staleness:
getCachedAppBinary()comparesInfo.plistof cached vs installed app~/.maestro/app-cache/<deviceId>/) prevent parallel races on cloned simulatorsVerification
--reinstall-driver--reinstall-driverclearAppStatecalledclearAppStateTesting
XCTestDriverClientTestusingFakeXCTestInstaller(fakes, not mocks — per CONTRIBUTING.md)LocalSimulatorUtilsTestcovering cache staleness detectionIssues fixed
Closes #1601