In fastlane, which action uploads an iOS build to TestFlight for beta testers?
answer
- one core action, Apple only
- the binary comes from the build step
- the famous name is only an alias
- verb_noun form of pilot
basics
~20 sThe canonical action is upload_to_testflight, better known by its alias pilot. It takes a finished iOS binary, an App Store Connect credential and an optional changelog, and hands the build to Apple's beta service. Android has no core equivalent.
solid answer
~40 s`upload_to_testflight` is the canonical action; `pilot` and `testflight` are aliases for the same thing. For the bookbinding order app you point it at the Apple binary — `ipa` for an iOS app, `pkg` for a macOS package — and it defaults to the path `build_app` (`gym`) left in the lane context, so a build-then-upload lane usually passes no path at all. It authenticates with an App Store Connect API key (`api_key` / `api_key_path`) or an Apple ID `username`. `changelog` carries the tester-facing note for that build, and `app_identifier` disambiguates when a repository builds several bundles. There is no core Android counterpart: Firebase App Distribution ships as a third-party plugin, and the Google Play route is `upload_to_play_store` (`supply`).
code
ruby · 10 linesbuild_app(
scheme: "BookbindingOrders",
export_method: "app-store"
)
upload_to_testflight(
api_key_path: "fastlane/asc_api_key.json",
app_identifier: "com.bindery.bookbindingorders",
changelog: "Order intake now accepts partial binding batches."
)go deeper
Be ready to name the action and its alias in one breath — upload_to_testflight, also called pilot — and to say plainly that it is an Apple-only upload of an already-built binary.
Explain where the binary comes from: build_app leaves its archive path in the lane context and the upload action reads it. Name the options that identify the app and carry the changelog.
Show how the lane behaves unattended: an App Store Connect API key rather than an interactive Apple ID, an explicit app identifier in a multi-bundle repo, and a clear failure when the binary or credential is missing.
Own the policy question — one owned beta lane per platform versus every team hand-rolling an upload script — and decide who may change the tester-facing changelog contract.
## The action and its aliases fastlane runs **actions** from a Fastfile, and most of the famous fastlane names are aliases for a canonical `verb_noun` action. The iOS beta upload is one of them: the canonical action is **`upload_to_testflight`**, and `pilot` and `testflight` are aliases that resolve to it. Both spellings work in a Fastfile, but the canonical name is the one worth learning — it is how the action's own option list is filed, and it is what makes the family legible next to `build_app` (`gym`), `sync_code_signing` (`match`), `upload_to_app_store` (`deliver`) and `upload_to_play_store` (`supply`). Someone who only ever says *pilot* has learned a nickname, and will go looking for a `pilot` entry in the reference that is really just a pointer. The action does one job: it hands a **finished Apple binary** to Apple's beta service. It does not compile, it does not sign, and it does not decide who the testers are. ## What the upload step needs For the bookbinding order app — an iOS app a bindery uses to take and track custom binding orders — a beta upload needs four things: - **A binary.** `ipa` for an iOS app, `pkg` for a macOS package. One or the other, never both. - **A credential.** An App Store Connect API key through `api_key` or `api_key_path`, or an Apple ID through `username`. The API key is the form that works unattended on CI. - **An app identity.** `app_identifier` when the repository builds more than one bundle, and `app_platform` when the same identifier ships for more than one Apple platform. - **Optional tester-facing text.** `changelog` carries the note attached to that specific build; `localized_build_info` carries per-language variants of the build's text. Everything else in the option set — and it is a large set — governs *what happens after the bytes arrive*: whether to wait for Apple's processing, whether to distribute, which groups receive the build. ## Where the binary comes from The most common beginner move is to pass `ipa:` by hand. In a lane that builds and then uploads, you usually should not. fastlane actions communicate through a shared **lane context**: `build_app` (`gym`) writes the path of the archive it produced into `Actions.lane_context[SharedValues::IPA_OUTPUT_PATH]`, and `upload_to_testflight` uses that value as the default for `ipa`. A build-then-upload lane for the bookbinding order app therefore calls `build_app` and then the upload action with no path at all, and the two stay in step even when the output directory, the scheme or the configuration changes. Pass `ipa` explicitly when the binary did **not** come from a `build_app` call in the same run — a CI job that downloads an artifact produced in an earlier stage, a binary built by another tool, or a run that produced two products and you need to be precise about which one goes up. ## The platform split This action is **Apple-only**, and that asymmetry is the fact most often missed: | | Apple (iOS, macOS, tvOS) | Android | |---|---|---| | Core beta upload action | `upload_to_testflight` (`pilot`) | none | | Artifact it accepts | `.ipa` or `.pkg` | not accepted by this action | | Credential | App Store Connect API key, or Apple ID `username` | Google service-account JSON key | | Non-store beta route | third-party Firebase App Distribution plugin | third-party Firebase App Distribution plugin | | Core store route | `upload_to_app_store` (`deliver`) | `upload_to_play_store` (`supply`) | There is no Android branch hiding inside `upload_to_testflight`, and there is no core Android equivalent of it. **Firebase App Distribution is a third-party plugin** (`fastlane-plugin-firebase_app_distribution`) that has to be added to the project before its action exists at all. The other Android route is the Play upload lane `upload_to_play_store` (`supply`) aimed at a non-production track — a store lane rather than a beta lane, with a service-account key rather than an Apple credential. ## What the action does not do Three boundaries keep the answer honest: 1. It does not **build**. If the archive is missing, the defect is in the build step, not in the upload. 2. It does not **sign**. Signing assets come from `sync_code_signing` (`match`) and are consumed when the archive is exported. 3. It does not **create testers**. Options such as `groups` and `distribute_external` name an audience that already exists; who belongs to that audience is administered in Apple's console, not by the lane. Knowing exactly where the lane stops is what separates someone who has followed a fastlane tutorial from someone who owns the pipeline.
- If a lane builds and uploads in the same run, must you pass `ipa` to the upload action?No. `build_app` (`gym`) publishes the archive path into the lane context as `SharedValues::IPA_OUTPUT_PATH`, and `upload_to_testflight` defaults `ipa` to it. Pass the path explicitly only when the binary came from somewhere else — a downloaded CI artifact, another tool, or a run that produced more than one product and you need to name which one goes up.
- What uploads an Android beta build, given that this action is Apple-only?Not a core action. Firebase App Distribution is a third-party fastlane plugin (`fastlane-plugin-firebase_app_distribution`) that must be added to the project before its action exists. The core Android path is instead the Play upload lane `upload_to_play_store` (`supply`) pointed at a non-production track, authenticated with a Google service-account JSON key rather than an Apple credential.
saying these in an interview costs you the question
- Thinking pilot is a different tool from upload_to_testflight
- Assuming the same action can upload an Android artifact
- Believing Firebase App Distribution is a built-in fastlane action
- Expecting the upload action to build or sign the app
- Passing an Apple ID password where an API key is expected