← 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.