feat(server,cli): Android APK previews with cross-platform share and run#9509
Merged
feat(server,cli): Android APK previews with cross-platform share and run#9509
Conversation
Users can now share Android APK builds through Tuist Previews, the same way they share iOS .ipa files today. Running `tuist share app.apk` extracts metadata via aapt2 and uploads the APK for distribution. Co-Authored-By: Claude Opus 4.6 <[email protected]>
The Android SDK's build-tools directory is never added to PATH by standard installation methods (mise, Homebrew). Instead of requiring users to manually add it, look for aapt2 in ANDROID_HOME, ANDROID_SDK_ROOT, and well-known installation paths before falling back to PATH. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Android APK builds use a SHA256 hash as binary_id, and there's no strict requirement to prevent re-uploading the same APK. Replace the unconditional unique index with a partial one that only applies to non-APK build types. Co-Authored-By: Claude Opus 4.6 <[email protected]>
55f4e5d to
a07ea97
Compare
… tuist run Extract share and run commands into cross-platform TuistShareCommand and TuistRunCommand modules (following the TuistBuildCommand pattern). Create TuistAndroid module with ADB device discovery, APK install, and app launch. - TuistAndroid: AdbController with device discovery, app install/launch using am start instead of monkey for reliable activity resolution - TuistShareCommand: moved from TuistKit, APK sharing always compiled, Apple builds behind #if os(macOS) - TuistRunCommand: moved from TuistKit, Android device selection and APK install/launch cross-platform, Apple simulator/device behind #if os(macOS) - Restructured PreviewsUploadService for cross-platform APK upload - Added GetPreviewInfoService, ServerPreviewInfo, PreviewUploadResult - Better error messages from adb commands (extracts stderr from CommandError) Co-Authored-By: Claude Opus 4.6 <[email protected]>
# Conflicts: # server/lib/tuist_web/components/previews/platform_icon.ex # server/priv/gettext/dashboard.pot
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Remove hardcoded fallback paths (mise, Android Studio, Homebrew) — rely on the standard ANDROID_HOME / ANDROID_SDK_ROOT environment variables and bare PATH lookup instead. Co-Authored-By: Claude Opus 4.6 <[email protected]>
fortmarek
commented
Feb 18, 2026
| let output: String | ||
| do { | ||
| output = try await commandRunner | ||
| .run(arguments: [adb, "devices", "-l"]) |
Member
Author
There was a problem hiding this comment.
yes, I also find it crazy that adb doesn't have a way to output json 🙃 at least not that I can tell
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Co-Authored-By: Claude Opus 4.6 <[email protected]>
…solution Removes the `servicePlatform` computed property from `Target+PlatformResolution.swift` and inlines the platform resolution logic at each call site in BuildService, TestService, and RunCommandService. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Co-Authored-By: Claude Opus 4.6 <[email protected]>
…ommandService Replaces platform-specific FileHandler/AbsolutePath.current branching with the cross-platform Environment.pathRelativeToWorkingDirectory helper. Co-Authored-By: Claude Opus 4.6 <[email protected]>
On Linux only preview URLs are supported, so --generate, --clean, --configuration, --os, --rosetta, and passthrough arguments are now only available on macOS. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Show platform-appropriate help for the runnable argument and reorder to list the most common options first. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Co-Authored-By: Claude Opus 4.6 <[email protected]>
…hods The cross-platform method only accepts path, runnable, and device. The macOS-only method adds generate, clean, configuration, osVersion, rosetta, and arguments parameters. Co-Authored-By: Claude Opus 4.6 <[email protected]>
…types Replace the macOS-only booted simulator shortcut with a cross-platform isReady check. Android devices and physical devices are always ready, simulators only when booted. If exactly one ready device exists, it is selected automatically without prompting. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Inline the platform checks directly at the call sites. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Remove custom ServerPreviewInfo and PreviewUploadResult wrapper types, using Components.Schemas.Preview from the generated OpenAPI client instead. Co-Authored-By: Claude Opus 4.6 <[email protected]>
…eview Rename the server operationId from downloadPreview to getPreview and consolidate into a single cross-platform GetPreviewService returning Components.Schemas.Preview. Remove the duplicate macOS-only service and update the app to convert at call sites. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Eliminate the separate APKPreviewUploadService by making PreviewsUploadService cross-platform. APK upload methods are always compiled while IPA/appBundle methods remain behind #if canImport(TuistCore). Git info is now passed as parameters instead of resolved internally, removing the TuistGit dependency from TuistServer. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Replace the failable `init?` with a throwing `init` that reports exactly which URL or date failed to parse via ServerPreviewError, instead of silently returning nil. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Move configuration, platforms, and derivedDataPath options behind the platform guard since they only apply to Apple builds. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Co-Authored-By: Claude Opus 4.6 <[email protected]>
f4e4f15 to
6f5abc2
Compare
6f5abc2 to
39fe3f2
Compare
…Command Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Update unique_constraint name in AppBuild to match new partial index - Add Credo safety-assured comment for migration drop_if_exists - Add APK/Android tests for app builds, previews controller, and run command - Use compactMap for supported platform mapping to handle unknown platforms - Fix DestinationType Android case string to "Android" - Add missing Path import in PreviewsUploadServiceTests - Extract gettext strings for dashboard_previews Co-Authored-By: Claude Opus 4.6 <[email protected]>
… hashing and fix share test mock CryptoKit is not available on Linux. Also reset gitController mock before re-registering to prevent stale return values in share_apk_with_git_info test. Co-Authored-By: Claude Opus 4.6 <[email protected]>
…APK hashing CryptoSwift was an implicit dependency. Use apple/swift-crypto (already a declared package dependency) which provides the same SHA256 API as CryptoKit but works cross-platform including Linux. Co-Authored-By: Claude Opus 4.6 <[email protected]>
…conService On Linux, URLSession/URLRequest live in FoundationNetworking, not Foundation. Co-Authored-By: Claude Opus 4.6 <[email protected]>
…mandService init The non-macOS designated init does not accept a commandRunner parameter. Co-Authored-By: Claude Opus 4.6 <[email protected]>
cschmatzler
reviewed
Feb 19, 2026
| else { return nil } | ||
| self.url = url | ||
| supportedPlatforms = appBuild.supported_platforms.map(DestinationType.init) | ||
| supportedPlatforms = appBuild.supported_platforms.compactMap(DestinationType.init) |
cschmatzler
reviewed
Feb 19, 2026
| ℹ︎ Uploading My App ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ 0% | ||
| ✔︎ My App uploaded [0.0s] | ||
| ✔ Success | ||
| Share My App with others using the following link: https://test.tuist.io No newline at end of file |
Member
Author
There was a problem hiding this comment.
I believe it's the other tests that it was following ... might fix it later, but not worth retriggering a CI 🙈
cschmatzler
approved these changes
Feb 19, 2026
…Linux compatibility The Command package only applies @mockable on macOS, so MockCommandRunning doesn't exist on Linux. Replace it with a local StubCommandRunner. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Co-Authored-By: Claude Opus 4.6 <[email protected]>
The Command package only applies @mockable on macOS, so MockCommandRunning is unavailable on Linux. Wrap the entire test file in #if os(macOS) to skip it on Linux instead of using a stub. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Co-Authored-By: Claude Opus 4.6 <[email protected]>
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.

What this means for users
tuist sharenow supports Android APKs. Developers can share Android app previews with their team usingtuist share app.apk, just like they already can with iOS.ipaand.appbundles. The APK metadata (package name, version, icon) is automatically extracted and uploaded alongside the build.tuist runnow supports Android previews. Runningtuist run <preview-url>with an Android preview will discover available Android emulators/devices via ADB, install the APK, and launch the app — mirroring the existing iOS simulator flow.The Tuist dashboard handles Android as a first-class platform. Android previews show the correct platform icon, display "Package name" instead of "Bundle identifier", and provide a QR code for direct APK download on Android devices.
Follow-up
Running Android previews from the macOS menu bar app will be added in a follow-up.
Summary
tuist shareandtuist runinto cross-platformTuistShareCommandandTuistRunCommandmodules (following theTuistBuildCommandpattern)TuistAndroidmodule with ADB device discovery, APK metadata parsing (viaaapt2), install, and app launchadb install→ launch viaam startPreviewsUploadServiceso APK upload path compiles cross-platformUploadPreviewIconServicecross-platformandroid_only?checks across server controller and LiveView templatesDestinationType.androidto the CLI and appTest plan
swift build --replace-scm-with-registry)tuist share app.apkuploads APK preview with icon successfullytuist run <android-preview-url>discovers emulator, installs APK, launches apptuist run <apple-preview-url>still works with iOS simulators🤖 Generated with Claude Code