← back to Nineoh Guide
apps/mobile/EAS-BUILD-NOTES.md
108 lines
# EAS internal (ad-hoc) build notes
App: **Unofficial 90210 Guide** · bundle `com.abrams.nineohguide` · EAS project `d1c16075-337b-4f74-b818-582a9bac7f67` · Apple team `3VAV3KMNZK` (Steven Abrams, Individual)
## Getting an internal build onto a physical device — the one-shot recipe
Internal distribution = ad-hoc `.ipa`, installable only on devices whose UDID is
**embedded in the provisioning profile at build time**. Registering a device is
NOT enough — it has to be *selected into the profile* when the build runs.
1. **Register each device** (once per device):
```
npx eas-cli device:create
```
- iPhone/iPad → choose **Website** → open the printed URL in **Safari on the device** → install the profile.
- This Apple-silicon Mac → choose **Current Machine**.
- ⚠️ Devices often register **nameless** (Name = None). In the build's device
checklist they show as just the raw UDID and are easy to skip.
2. **Build, and SELECT ALL DEVICES:**
```
npx eas-cli build --profile preview --platform ios
```
- Run it in a **real terminal** (interactive Apple login / 2FA needed).
- reuse distribution certificate → **yes**
- At the device checklist: **press `a` (select ALL), then Enter.**
`a` toggles every registered device at once, so the nameless iPhone can't be missed.
3. **Verify** the finished `.ipa` actually carries both UDIDs:
```
curl -sL -o app.ipa "<applicationArchiveUrl>"
unzip -o -q app.ipa -d app_ipa
security cms -D -i "$(find app_ipa -name embedded.mobileprovision)" > prof.plist
/usr/libexec/PlistBuddy -c "Print :ProvisionedDevices" prof.plist
```
4. **Install:** open the build page in Safari on the phone (or scan its QR) → Install
→ first launch, trust the dev under Settings › General › VPN & Device Management.
Mac: `npx eas-cli build:run -p ios`.
## Gotchas that cost us a morning (2026-07-27)
- **"integrity could not be verified"** on install = the device's UDID is **not** in
that `.ipa`'s embedded profile. Fix by rebuilding with the device selected.
- **`--refresh-ad-hoc-provisioning-profile` is NON-INTERACTIVE-ONLY** and requires an
**App Store Connect API key** (issuer id + key id + `.p8`). It errors instantly in
interactive mode, so it can't rescue an interactive build. If a key gets set up,
`eas build … --non-interactive --refresh-ad-hoc-provisioning-profile` pulls in every
registered device with no checkbox — the ideal headless path.
- Interactive EAS commands need a real TTY — they fail under the Claude `!` prefix and
under the Bash tool ("stdin is not readable"). Run them in a plain terminal tab.
- Registered devices: Mac `00006032-000C692614DA401C`, iPhone `00008150-001039662E3B401C`.
## App Store / TestFlight (production) path — TK-12
Ad-hoc (above) and store are DIFFERENT credential worlds. Store needs a **Distribution
Certificate** + an **App Store provisioning profile** (not the ad-hoc profile), and an
**App Store Connect app record** (`ascAppId`). `eas.json` `production` profile is ready
(`distribution: store`, `autoIncrement: true`); `submit.production.ios` is pre-filled with
`appleId: steveabramsdesigns@gmail.com` + `appleTeamId: 3VAV3KMNZK`; only `ascAppId` is
still `SET-AT-SUBMIT` (it doesn't exist until the ASC app record is made).
### The one-time unlock: App Store Connect API key (recommended — makes build+submit headless)
Interactive Apple credential setup fails under the Bash/`!` tool (no TTY). An **ASC API key**
removes every prompt for BOTH build and submit:
1. App Store Connect → **Users and Access → Integrations → App Store Connect API** →
generate a **Team key** (role: App Manager or Admin). Download the `.p8` **once**
(non-recoverable), note the **Key ID** + **Issuer ID**.
2. Route it via the `secrets` skill; reference in `eas.json` (or `credentials.json`) so
`eas build`/`eas submit --non-interactive` run with zero prompts.
⚠️ Interactive alt (no key): run `npx eas-cli build -p ios --profile production` in a
REAL terminal, Apple login + 2FA once. This **consumes 1 of only 2** Individual-account
Distribution-cert slots — Steve's call.
### Pre-build hygiene (recommended, do before the FIRST production build)
`expo-doctor` flags `react-native 0.76.5 → 0.76.9` (Expo SDK 52's expected patch). The
preview cloud builds all succeeded on **0.76.5**, so it is NOT a hard blocker — but align
it on the first production build and confirm the build passes before trusting it:
```
cd ~/Projects/nineoh-guide/apps/mobile
npx expo install react-native@0.76.9 # then the production build below VALIDATES it
```
(Left at 0.76.5 in git deliberately — an unvalidated bump shouldn't ride a config-only prep commit.)
### Build (production, store-signed .ipa — one EAS build slot)
```
cd ~/Projects/nineoh-guide/apps/mobile
npx eas-cli build -p ios --profile production # add --non-interactive once ASC key is set
npx eas-cli build:view <id> # poll until finished
```
### Submit → TestFlight ⛔ GATED (App Store submit — Steve-approved only)
```
npx eas-cli submit -p ios --profile production --latest
```
Uploads to App Store Connect → TestFlight after Apple processing (~5–30 min).
⚠️ **`ascAppId` is still the non-numeric sentinel `SET-AT-SUBMIT`.** Before running submit,
EITHER **delete the `ascAppId` line** from `eas.json` (then `eas submit` auto-detects the app
by bundle id, or creates the ASC app record on first run) **OR** paste the real numeric id.
Do NOT run `eas submit` with the sentinel string in place — behavior is EAS-CLI-version-
dependent and errors confusingly. After the first submit, capture the numeric `ascAppId` it
prints back into `eas.json` for subsequent runs.
### ⚠️ App Review risk to surface
"Unofficial 90210 Guide" references a trademarked TV property. Guideline 4.1 (Copycats) /
5.2 (Intellectual Property) — unofficial third-party-IP guide apps are a known rejection
risk. Worth a disclaimer + original-content framing before submit.