expo / expo/eas-cli

`--no-auto-testflight-setup` is ignored when the submit profile has no `ascAppId`: `Team (Expo)` is still created and all Admins enrolled (opt-out lost in 22.0.0)

Open Beginner friendly
#4,397 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
1.4k
Forks
236
Avg merge
3d 1h
Merged PRs (30d)
91

Description

### Build/Submit details page URL

_No response_

### Summary

`eas submit -p ios --no-auto-testflight-setup` still creates the internal TestFlight group `Team (Expo)` and adds every Admin on the Apple team to it as a tester, whenever the submit profile has no `ascAppId`. The flag is only honoured on the `ascAppId` code path; the default path (app resolved by bundle identifier through `ensureAppStoreConnectAppExistsAsync`) calls the group setup unconditionally.

Expected: `--no-auto-testflight-setup` disables the group creation and the tester enrolment on every path, as its description says ("Set up an internal TestFlight group for the app (iOS only)", `allowNo: true`). On a team where the Admins are not the testers, enrolling them without being asked is a change to the team's TestFlight configuration that the user explicitly opted out of.

### Managed or bare?

Managed (continuous native generation; `ios/` and `android/` are gitignored prebuild output, which is why `expo-env-info` prints `Expo Workflow: bare` below).

### Environment

`eas-cli/24.0.0 darwin-arm64 node-v20.19.4` (global install via bun; `expo-env-info` does not see the bun global registry, so its `npmGlobalPackages` line is corrected by hand below).

```
expo-env-info 2.1.0 environment info:
System:
OS: macOS 26.5.2
Shell: 5.9 - /bin/zsh
Binaries:
Node: 20.19.4 - ~/.nvm/versions/node/v20.19.4/bin/node
Yarn: 1.22.22 - ~/.nvm/versions/node/v20.19.4/bin/yarn
npm: 10.8.2 - ~/.nvm/versions/node/v20.19.4/bin/npm
Managers:
CocoaPods: 1.16.2 - /opt/homebrew/bin/pod
SDKs:
iOS SDK:
Platforms: DriverKit 25.5, iOS 26.5, macOS 26.5, tvOS 26.5, visionOS 26.5, watchOS 26.5
IDEs:
Android Studio: 2025.2 AI-252.25557.131.2521.14344949
Xcode: 26.5/17F42 - /usr/bin/xcodebuild
npmPackages:
expo: ~57.0.20 => 57.0.20
expo-router: ~57.0.19 => 57.0.19
react: 19.2.3 => 19.2.3
react-dom: 19.2.3 => 19.2.3
react-native: 0.86.3 => 0.86.3
react-native-web: ~0.21.0 => 0.21.2
npmGlobalPackages:
eas-cli: 24.0.0 (bun global install)
Expo Workflow: bare <- see above; the project is managed
```

### Error output

No error. After a successful `eas submit --platform ios --profile ` (submit profile `{}`, no `ascAppId`, key on the EAS credentials service), App Store Connect > TestFlight shows a new internal group `Team (Expo)` with automatic distribution on and every Admin of the team enrolled as a tester. That run did not pass `--no-auto-testflight-setup`; that the flag would not have changed the outcome on this path is established from the source below, and the steps to observe it are in the repro section. (The messages for this step are `TestFlight group created: Team (Expo)` and `TestFlight access enabled for: ...`, from `ensureTestFlightGroup.ts`.)

### Where it happens (eas-cli `main`)

`packages/eas-cli/src/submit/ios/IosSubmitCommand.ts`, `resolveAscAppIdentifierAsync`, has two branches:

- With `ascAppId` in the profile (L175): `ensureTestFlightSetupForExistingAppAsync(this.ctx, ascAppId)`. That function checks the flag first (`packages/eas-cli/src/submit/ios/ensureTestFlightSetup.ts` L28: `if (!ctx.autoTestFlightSetup) return;`). Correct.
- Without `ascAppId` (L194): `ensureAppStoreConnectAppExistsAsync(this.ctx)` in `packages/eas-cli/src/submit/ios/AppProduce.ts`, which resolves or creates the app and then, at L97, calls `ensureTestFlightGroupExistsAsync(app, { nonInteractive })` inside a try/catch. Nothing on this path reads `ctx.autoTestFlightSetup`.

`ensureTestFlightGroupExistsAsync` (`packages/eas-cli/src/credentials/ios/appstore/ensureTestFlightGroup.ts`) then creates the `Team (Expo)` group with `hasAccessToAllBuilds: true` when the app has no groups, lists the team's users, filters to `UserRole.ADMIN`, and bulk-assigns them as beta testers.

The flag is plumbed into `SubmissionContext` (`packages/eas-cli/src/commands/submit.ts` L111-L113, L162, L233; `submit/context.ts`), so `ctx.autoTestFlightSetup` is available in `AppProduce.ts`; it is just not consulted there.

### How it got here (regression in 22.0.0)

- 14.6.0, #2839: `eas submit` starts auto-creating the internal group on the `AppProduce` path.
- 15.0.11, #2856: skip when groups already exist, and an opt-out: `EAS_NO_AUTO_TESTFLIGHT_SETUP`, checked at the top of `ensureTestFlightGroupExistsAsync`, so it covered every caller.
- 22.0.0, #4136: adds the `ascAppId` path and the `--[no-]auto-testflight-setup` flag, and removes the `EAS_NO_AUTO_TESTFLIGHT_SETUP` check from `ensureTestFlightGroupExistsAsync` (diff of #4136, `ensureTestFlightGroup.ts`). The flag is read only in the new `ensureTestFlightSetupForExistingAppAsync`.

So since 22.0.0 the default path has no opt-out at all: the env var is gone and the flag is not read. The 22.0.0 changelog entry ("with `--no-auto-testflight-setup` to disable it") reads as if the flag disables the behaviour; it disables it on one of the two paths.

### Docs

The behaviour is not described anywhere a user reads before their first submit. [Submit to the Apple App Store](https://docs.expo.dev/submit/ios/) does not mention it. [Distribute an iOS app with TestFlight](https://docs.expo.dev/submit/testflight/) says to create internal groups by hand in App Store Connect and target them with `--groups`, and does not mention that `eas submit` will create one and enrol the Admins on its own. The only mention is the flag's one-line description in the CLI reference.

### Reproducible demo or steps to reproduce from a blank project

1. `npx create-expo-app repro && cd repro && eas init && eas build:configure`. Set `ios.bundleIdentifier` to a bundle id on your Apple team whose App Store Connect app has **no** TestFlight groups yet (a fresh app record, or one where you removed the groups). Add `"submit": { "production": {} }` and do **not** set `ascAppId`.
2. Set up an App Store Connect API key for EAS Submit (`eas credentials -p ios`).
3. `eas build -p ios --profile production`.
4. `eas submit -p ios --profile production --latest --no-auto-testflight-setup`.
5. Observe `TestFlight group created: Team (Expo)` in the output and the group, with the team's Admins enrolled, in App Store Connect.

Control: add `"ascAppId": ""` to the submit profile, delete the group, and run step 4 again. No group is created; the flag is honoured on that path.

### Suggested fix

Move the check back into `ensureTestFlightGroupExistsAsync` (where the env var used to be) so every caller respects it (`commands/go.ts` L45 calls it too), or read `ctx.autoTestFlightSetup` in `createAppStoreConnectAppAsync` (`AppProduce.ts` L97). Restoring `EAS_NO_AUTO_TESTFLIGHT_SETUP` alongside the flag would also help CI, where a flag has to be threaded through every invocation and an env var does not.

Docs: a sentence in the TestFlight guide saying that the first `eas submit` creates `Team (Expo)` and enrols all Admins unless a group already exists or `--no-auto-testflight-setup` is passed.

### The default itself

Beyond the flag bug: as far as we could find, this default is undocumented, and it is an undesirable one. Every other thing `eas submit` does is scoped to the build it is submitting. This step reaches into the team: it creates a distribution group the user did not ask for, with automatic access to every future build, and enrols people by role rather than by choice, so each Admin gets a TestFlight invitation for an app they may have no part in testing, on the first submit, when whoever is running it is least likely to know a flag exists. Reading the Admin list also needs an Admin-role key, so the step silently depends on the key's role in a way the submission itself does not.

The behaviour the TestFlight guide describes is the right one: the team creates the groups it wants and `eas submit --groups` targets them. Please consider making the automatic group opt-in (`--auto-testflight-setup`, default off), or at least asking before creating it in interactive mode; and in either case documenting it in the TestFlight guide, not only as a flag description.

Contributor guide

Open the contributing guide

Research direction

Start in packages/eas-cli/src/submit/ios/AppProduce.ts and compare its default app-resolution path with ensureTestFlightSetup.ts and ensureTestFlightGroup.ts. Reproduce the issue with an empty submit profile and --no-auto-testflight-setup, then inspect the existing context plumbing in submit/context.ts. Done means the opt-out prevents group creation and Admin enrollment on both app-resolution paths, including callers from commands/go.ts.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
cli, mobile-dev
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.