skip to content

How does a fastlane beta lane get changelog text to iOS TestFlight testers versus Android testers?

level: middleimportance: should knowfreq 41%

answer

  1. one inline string, one plugin parameter
  2. per-build note versus app-level description
  3. no core Android beta action exists
  4. the note needs a processed iOS build

basics

~20 s

On iOS the changelog is an inline string: upload_to_testflight takes a changelog option, plus localized_build_info for per-language text. Android has no core beta action at all, so release notes ride on the third-party Firebase App Distribution plugin instead.

solid answer

~40 s

The two platforms are not symmetric. On iOS, `upload_to_testflight` (`pilot`) takes `changelog` — a plain string attached to the build being uploaded — and `localized_build_info` for per-language variants; `beta_app_description` and `beta_app_feedback_email` describe the beta app as a whole, not the individual build. In a script you read that string rather than hard-code it, for example from a file the release step writes for the bookbinding order app. On Android there is no core beta upload action to hang a changelog on: Firebase App Distribution is a third-party plugin and carries its own release-notes parameter, while the core Google Play route, `upload_to_play_store` (`supply`), is a store lane with a different notes model entirely.

code

ruby · 8 lines
ruby
notes = File.read("fastlane/changelog_ios.txt").strip
UI.user_error!("empty iOS changelog") if notes.empty?

upload_to_testflight(
  api_key_path: "fastlane/asc_api_key.json",
  app_identifier: "com.bindery.bookbindingorders",
  changelog: notes
)

go deeper

for a junior

Recall that the iOS beta note is an option on the upload action, not a file the tool discovers, and that Android does not share that option because it has no core beta action.

for a middle

Explain the difference between the per-build changelog and the app-level beta description, and show how you would read the text from a file or an environment variable instead of hard-coding it.

for a senior

Point at the failure mode: a lane that skips the processing wait cannot attach build-level information, so a carefully wired changelog silently never appears. Say how you would fail the lane on an empty note.

for a principal

Decide who owns tester-facing copy and how it is reviewed — a changelog written by the build script is a product surface, and leaving it to whoever pushed last is a policy gap.

## What a changelog is in a beta lane A beta changelog is not a release note for a store listing. It is the short, tester-facing text that says **what to exercise in this build** — for the bookbinding order app, something like *order intake now accepts partial binding batches; check the batch summary on the job card*. It is attached to one build, it changes on every upload, and it is the only part of the lane a human tester actually reads. Getting it there is a different mechanism on each platform, and that divergence is the whole content of the question. ## The iOS path: an option on the upload action On Apple platforms the note is an argument to `upload_to_testflight` — the canonical action behind the `pilot` and `testflight` aliases. The relevant options: - **`changelog`** — a plain string attached to the build being uploaded. This is the per-build note. - **`localized_build_info`** — per-language build information, for a beta audience that is not all reading one language. - **`beta_app_description`** — describes the **beta app as a whole**, not this build. Changing it does not tell a tester what is new. - **`beta_app_feedback_email`** — the address testers are pointed at; again app-level rather than per-build. The distinction between the per-build option and the app-level options is where candidates slip. A lane that rewrites `beta_app_description` on every upload has produced a note nobody associates with a particular build. There is a second trap: build-level information needs a **processed** build. If the same lane sets `skip_waiting_for_build_processing: true`, it returns before Apple has finished processing, and anything that has to attach to the processed build cannot happen in that run. A team that wires a beautiful changelog into a lane that never waits ships builds with no note at all. ## Sourcing the text from a script Hard-coding the string in the Fastfile means editing the Fastfile for every release, which is how changelogs go stale. Better patterns, in rough order of preference: 1. **Read a file** the release process writes — `changelog: File.read('fastlane/changelog_ios.txt')` — so the note is reviewed in the same pull request as the change it describes. 2. **Read an environment variable** the CI job sets, when the note is composed by whatever triggered the run. 3. **Derive it from the commit range**, but curate it. A raw dump of every commit subject is noise; testers stop reading a note that is mostly housekeeping lines. Whichever you choose, the lane should fail loudly on an empty note rather than uploading a build with a blank one. ## The Android path: there is no core action to configure fastlane ships **no core Android beta-distribution action**. Measured against the core action set there is no Firebase action at all: **Firebase App Distribution is a third-party plugin** (`fastlane-plugin-firebase_app_distribution`) that must be added to the project before its action and its options exist. Release notes on that route are therefore the plugin's own parameter, not a core fastlane option, and the plugin moves on its own release cadence. The other Android route is the core store lane `upload_to_play_store` (`supply`), which is aimed at a Play track and takes a Google service-account JSON key. That is a store deployment path with its own notes model rather than a beta option like `changelog`. | | iOS beta lane | Android beta lane | |---|---|---| | Upload action | `upload_to_testflight` (core) | Firebase App Distribution (plugin) | | Note mechanism | `changelog` option, inline string | the plugin's own release-notes parameter | | Per-language notes | `localized_build_info` | whatever the plugin supports | | Depends on processing | yes — needs a processed build | no Apple processing step exists | | Credential | App Store Connect API key or Apple ID | Google credentials, not an Apple key | ## What to avoid saying - Do not describe *one* changelog mechanism that covers both platforms. There is no shared beta action, so there is no shared option. - Do not reach for `metadata_path`. That belongs to the store lanes `upload_to_app_store` and `upload_to_play_store`, not to the TestFlight upload. - Do not confuse the per-build note with the app-level beta description; interviewers listen for exactly that distinction. - Do not promise per-language beta notes on Android with the confidence you can have about `localized_build_info` on iOS — the plugin is not a core surface and should be described as what it is.

  • How would you source the changelog for the bookbinding order app's iOS beta upload?
    Read it rather than hard-code it: `changelog: File.read('fastlane/changelog_ios.txt')`, or take it from an environment variable the CI job sets. Reading a file means the note is reviewed in the same pull request as the change it describes. Deriving it from commit subjects is possible but needs curation — testers ignore a note that is mostly housekeeping commits.
  • Can the beta changelog be per-language?
    On iOS, yes: alongside the single `changelog` string, `upload_to_testflight` accepts `localized_build_info` for per-language build text. On Android there is no core beta action at all, so per-language notes are whatever the third-party Firebase App Distribution plugin supports — not a fastlane core guarantee you should assert in an interview.

saying these in an interview costs you the question

  • Claiming one changelog option covers iOS and Android
  • Believing metadata_path feeds the TestFlight changelog
  • Confusing beta_app_description with the per-build note
  • Dumping raw commit subjects at testers as a changelog
  • Expecting the note to attach when the lane skips processing