skip to content

In an EAS Submit Android profile, what do track, releaseStatus and rollout control, and which combinations does eas.json validation reject?

level: middleimportance: should knowfreq 35%

answer

  1. where, how, and how many
  2. track defaults to internal
  3. releaseStatus defaults to completed
  4. rollout is a fraction, 0 to 1
  5. rollout only with inProgress

basics

~20 s

track names the Google Play track (default internal), releaseStatus sets how the release is created (completed, draft, inProgress or halted; default completed), and rollout is a 0-1 fraction that is required with inProgress and rejected otherwise.

solid answer

~40 s

Three keys in the `android` block of an EAS Submit profile decide what Google Play does with the upload. `track` is the target track, a string that defaults to `"internal"`; `internal`, `alpha`, `beta` and `production` are the usual values. `releaseStatus` is one of `completed` (the default), `draft`, `inProgress` or `halted`. `rollout` is the staged-rollout fraction, a number from 0 to 1, so `0.1` means 10 %. The schema ties the last two together: `rollout` is **required** when `releaseStatus` is `inProgress` and **forbidden** with any other status, so `"rollout": 10` or a rollout on a draft fails validation before anything is uploaded. `changesNotSentForReview` (default `false`) is the other Play-side switch.

code

json · 14 lines
json
{
  "submit": {
    "internal": {
      "android": { "track": "internal" }
    },
    "production": {
      "android": {
        "track": "production",
        "releaseStatus": "inProgress",
        "rollout": 0.1
      }
    }
  }
}

go deeper

for a junior

Recall the two defaults, internal track and completed status, and that rollout is a fraction between 0 and 1.

for a middle

Explain the four releaseStatus values and the schema rule that rollout is required with inProgress and forbidden otherwise.

for a senior

Show you can predict a resolved profile, including shallow extends merging, and design profiles so production uploads are staged or drafted.

for a principal

Discuss which tracks and statuses an unattended pipeline may use, and where a person must promote or widen a rollout.

## The Android block of a submit profile An **EAS Submit** profile is a named object under `submit` in `eas.json`; its `android` block controls the upload to **Google Play**. The keys, as defined in the `@expo/eas-json` schema: | Key | Type | Default | Meaning | |---|---|---|---| | `track` | string | `"internal"` | the Play track that receives the release | | `releaseStatus` | `completed`, `draft`, `inProgress`, `halted` | `completed` | how the release is created on that track | | `rollout` | number 0-1 | none | staged-rollout fraction, only with `inProgress` | | `changesNotSentForReview` | boolean | `false` | ask Play not to send the edit for review automatically | | `serviceAccountKeyPath` | string | none | path to the Google Service Account JSON key | | `applicationId` | string | none | the package name, when the project cannot resolve one unambiguously | ## track: where the build goes `track` is a free-form string in the schema; the common values are `internal`, `alpha` (closed testing), `beta` (open testing) and `production`. The default, `internal`, is deliberately safe: an unconfigured submit reaches internal testers only. The build reaches the public only when a profile says `"track": "production"`. ## releaseStatus: how the release is created - **`completed`** — the release goes out to everyone on that track. This is the default. - **`draft`** — the release is created but not rolled out; someone promotes it in the Play Console. - **`inProgress`** — a **staged rollout**, reaching only the fraction given by `rollout`. - **`halted`** — a halted release. The default of `completed` matters: combined with the default `internal` track it means "internal testers get it now". Combined with `"track": "production"` it means a full release to every user once Google approves it, which is why teams that target production usually pair it with `inProgress` or `draft`. ## rollout: how many users `rollout` is a **fraction**, not a percentage: `0.1` is 10 %, and the schema's range is 0 to 1, so `10` fails validation. The schema also makes it **conditional**: 1. `releaseStatus: "inProgress"` without `rollout` is rejected — a staged rollout needs a size. 2. `rollout` with any other status is rejected — a completed or draft release has no fraction. Because validation runs when the profile is resolved, these mistakes fail the command before anything is uploaded. ## A trap with extends Submit profiles can inherit with `extends`, and the platform blocks are merged **shallowly**: the child's keys overwrite the parent's, and every other parent key is kept. A `draft` profile that extends a staged-rollout profile therefore inherits `rollout` and fails validation, because `rollout` is forbidden with `draft`. Either give the child its own complete `android` block or keep `rollout` out of the parent. ```json { "submit": { "production": { "android": { "track": "production", "releaseStatus": "inProgress", "rollout": 0.1 } }, "production-draft": { "extends": "production", "android": { "releaseStatus": "draft" } } } } ``` Here `production-draft` resolves to `draft` plus an inherited `rollout: 0.1` and is rejected. ## Reading a profile in an interview When asked "where will this build go?", read the three keys in order: the **track** (who can see it), the **status** (whether it is live, a draft or staged), and the **rollout** (what share of that audience). Missing keys fall back to `internal` and `completed`. The key `serviceAccountKeyPath` is optional: without it, EAS Submit uses a service account key uploaded to the project's EAS credentials. Remember too that Google's API cannot create an app's first release, so one manual upload in the Play Console comes before any of this works. A practical layout is one submit profile per destination rather than one profile edited before each release: an `internal` profile for every build, a `production` profile with a small staged rollout, and perhaps a draft profile for releases someone promotes by hand. Choosing the profile with `--profile`, or through `--auto-submit` naming, then becomes the release decision, and it is visible in the pipeline definition instead of hidden in a local edit. ## Summary - `track` answers **where**; the default is `internal`. - `releaseStatus` answers **how**; the default is `completed`. - `rollout` answers **how many**, as a fraction, and exists only for `inProgress`.

  • Why does a profile that extends a staged-rollout profile and sets releaseStatus to draft fail validation?
    `extends` merges the platform block shallowly, so the child keeps the parent's `rollout`. The schema forbids `rollout` unless `releaseStatus` is `inProgress`, so the resolved draft profile is rejected. Give the child a complete `android` block, or keep `rollout` out of the parent.
  • What happens if you submit to the production track and leave releaseStatus unset?
    The default `completed` applies, so the release goes to every production user once Google approves it, with no staged rollout. Teams targeting production usually set `inProgress` with a small `rollout`, or `draft` to promote by hand.

saying these in an interview costs you the question

  • rollout is a percentage, so 10 means ten percent
  • releaseStatus defaults to draft so nothing ships by accident
  • rollout can be combined with any releaseStatus
  • Android submissions default to the production track
  • A child profile drops the parent's rollout when it changes releaseStatus