skip to content

In fastlane, which action uploads an iOS build to TestFlight for beta testers?

level: juniorimportance: must knowfreq 68%

answer

  1. one core action, Apple only
  2. the binary comes from the build step
  3. the famous name is only an alias
  4. verb_noun form of pilot

basics

~20 s

The 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 lines
ruby
build_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

for a junior

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.

for a middle

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.

for a senior

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.

for a principal

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