> ## Documentation Index
> Fetch the complete documentation index at: https://www.minitap.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Upload a build

> Upload an .ipa, .apk, .apks or .aab from the dashboard or the CLI, or point physical iPhones at a TestFlight link.

Built your app on your laptop or your own CI? Upload the file and Mini runs your scenarios on it. This page covers which file fits which device, how to upload from the dashboard and the CLI, Android App Bundles, TestFlight and Ad Hoc builds for physical iPhones, and how to check which build a run used. Prefer Mini to compile from a commit? See [Builds from your repository](https://www.minitap.ai/docs/minitest/setup/builds-from-repo.md).

## Which file for which device

| Runs on | Upload |
| - | - |
| iOS cloud simulator | A **simulator build**: `.app.zip` or `.ipa` built for "Any iOS Simulator Device". |
| Physical iPhone | An **Ad Hoc or Development .ipa** signed for the iPhones of your workspace, or a **TestFlight link**. See [Physical iPhones](#physical-iphones-testflight-and-ad-hoc). |
| Android cloud emulator | The **`.aab`** you ship to Google Play (recommended), or an `.apk` / `.apks` with `x86_64` or `arm64-v8a` code. |
| Physical Android phone | The **`.aab`**, or an `.apk` / `.apks` with `arm64-v8a` (or `armeabi-v7a`) code. |

Not sure which devices you'll use? See [Devices](https://www.minitap.ai/docs/minitest/setup/devices.md).

## From the dashboard

In **Run tests**, each platform row has a **Build** cell. Open it and click **Upload a build…**, then pick the file. The build cell also lists **Recent builds**, split into **Your builds** and **Team builds**, and **View all builds** searches the rest.

The dashboard reads the file and tells you where it can run. Upload a device `.ipa` while the iOS row is on the simulator, and it says the build runs on physical iPhones and offers to **Switch**. A build marked **Flagged as broken** can still be picked.

## From the CLI

The platform is detected from the extension. The CLI prints the build ID to start a run with.

```bash theme={"theme":{"light":"github-light","dark":"vesper"}}
minitest --app <app_id> build upload ./MyApp-simulator.app.zip
minitest --app <app_id> build upload ./app-release.aab
minitest --app <app_id> build upload ./app-debug.apk

# A device .ipa for physical iPhones needs --physical
minitest --app <app_id> build upload ./MyApp-device.ipa --physical
```

Then start a run on that build:

```bash theme={"theme":{"light":"github-light","dark":"vesper"}}
minitest --app <app_id> run all --ios-build <build_id>
minitest --app <app_id> run all --ios-build <build_id> --ios-physical
minitest --app <app_id> run all --android-build <build_id> --android-physical
```

* Pass `--platform ios` or `--platform android` when the file name doesn't end in a known extension.
* A device `.ipa` uploaded without `--physical` is rejected with a hint to add the flag, and a simulator build uploaded with `--physical` is rejected too.
* `--ios-physical` and `--android-physical` need physical devices on your workspace, see [Devices](https://www.minitap.ai/docs/minitest/setup/devices.md#get-physical-phones).

Every CLI command is in the [CLI reference](https://www.minitap.ai/docs/minitest/reference/cli-commands.md). Build environment variables and build options don't apply to uploads: your file already has everything baked in.

## Android App Bundles (.aab)

Upload the same `.aab` you send to Google Play, from the dashboard or the CLI:

```bash theme={"theme":{"light":"github-light","dark":"vesper"}}
minitest --app <app_id> build upload ./app-release.aab
```

Mini turns the bundle into the APKs a device installs. That takes a minute or two, so the build shows as converting first. The CLI waits until the build is ready, so your CI can start a run right after (`--no-wait` skips the wait). If the bundle can't be converted, the build is marked failed with the reason.

One upload runs everywhere. A bundle without asset packs becomes one build for all devices. A bundle with Play Asset Delivery packs becomes an **Emulator** version and a **Phone** version shown as one upload, and the fast-follow and on-demand packs are installed the way Google Play's local testing does it. Start runs with the ID the upload printed: Mini installs the right version on each device. A bundle with no `arm64` code has no Phone version, and the build picker says so.

Mini signs the generated APKs with your workspace's own signing key. Two things follow:

* **Google sign-in** checks the app's signing certificate. Register the key's SHA-1 in your Firebase project, or as an Android OAuth client in Google Cloud. `minitest build signing-certificate` prints it.
* **Play Billing and Play Integrity** only trust builds signed by Google Play, so they don't work on these builds.

## Physical iPhones: TestFlight and Ad Hoc

Physical iPhones can't install a simulator build. They get your app in one of two ways.

> [tab] TestFlight link
>
> Minitap's iPhones install your app from TestFlight, so there is no build to upload. New builds and new iPhones need nothing more from you.
>
> 1. In **Run tests**, set the iOS row's **Run on** to **Physical device**.
> 2. Open the **Build** cell and click **Add a new TestFlight link**.
> 3. The first time, the dialog shows an Apple ID. In App Store Connect, open your app's **TestFlight** tab, add that Apple ID as an external tester (or share a public link), then click **I added the tester**.
> 4. Paste the **TestFlight public link** (`https://testflight.apple.com/join/...`). Minitap asks for the **Bundle id** only if it doesn't know it yet. Click **Use this TestFlight link**.
>
> The link then shows in the build list with a **TestFlight** badge, and you can only pick it while the iOS row is on a physical device. The iPhone installs whatever build TestFlight serves for that link at the time of the run. If the run fails with a TestFlight error, the beta is usually full or the tester invitation hasn't been accepted yet.

> [tab] Ad Hoc or Development .ipa
>
> Export the build for the iPhones assigned to your workspace and upload it like any other build.
>
> 1. Register those iPhones in your Apple Developer account, in **developer.apple.com › Devices**. Ask the Minitap team for their UDIDs.
> 2. Regenerate your **Ad Hoc** or **Development** profile so it includes them, and archive with the team that owns the app in App Store Connect.
> 3. Upload the `.ipa` from the dashboard, or with `--physical` from the CLI.
>
> Minitap reads the provisioning profile of every `.ipa` you upload. Runs only use the iPhones listed in it, and you're told before launch if none of yours is. An `.ipa` signed for the App Store can't install this way, and neither can one whose profile expired.
>
> Every new build, and every new iPhone in your workspace, means a new profile and a new upload. TestFlight skips both.

## Which build a run used

* **Runs list**: the **Build Version** column shows the app version and build number, like `v2.3.1 (142)`, with the branch, pull request or commit underneath. For a file you uploaded, hover to see its name.
* **Run report**: under the recording, a run on a physical iPhone from TestFlight shows the build the iPhone installed, like "Tested 2.3.1 (57) via TestFlight", next to the phone model and iOS version.
* **CLI**: `minitest build get <build_id>` returns the build's `appVersion`, `buildNumber` and `commitSha`.

Runs started from a pull request always use the build of the commit that triggered them.

## Next steps

> [card] [Devices](https://www.minitap.ai/docs/minitest/setup/devices.md)
>
> Simulators, emulators and physical phones, and how a run picks one.

> [card] [Builds from your repository](https://www.minitap.ai/docs/minitest/setup/builds-from-repo.md)
>
> Let Mini compile the app from the commit each run needs.

> [card] [Start a run](https://www.minitap.ai/docs/minitest/runs/triggering-a-run.md)
>
> From the dashboard, a pull request, Slack or the CLI.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.