local-expo-build 0.2.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,126 @@ All notable changes to `local-expo-build` are documented here.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.4.1] — 2026-06-28
9
+
10
+ ### Added
11
+
12
+ - **Interactive `--clean` prompt.** When you run `build android` / `build
13
+ ios` (runner mode) or `npm run build:android:*` (scaffold mode) in an
14
+ interactive terminal, the build asks once at the top whether to clean.
15
+ Hint text explains when to say yes (Expo SDK upgrade, plugin change,
16
+ "android project is malformed" / "MainActivity not found" errors).
17
+ Default is **No** — most builds don't need clean, and forgetting to
18
+ clean fails loud (errors) while forgetting NOT to clean wastes 1-2 min
19
+ silently every time.
20
+ - **`--no-clean` flag** to skip the prompt and force no-clean. Useful when
21
+ scripting / aliasing the build invocation.
22
+ - **Scaffold mode now honors `--clean` / `--no-clean`.** Previously these
23
+ flags only worked in runner mode; the templated `scripts/build.js`
24
+ silently ignored them. Now they're parsed from argv and propagated to
25
+ `expo prebuild`. Pass via `npm run build:android:aab -- --clean`.
26
+
27
+ ### Changed
28
+
29
+ - All templated scripts bumped to `v0.4.1` version stamp. Users on v0.4.0
30
+ will see them as outdated when running `npx local-expo-build update-scripts`.
31
+
32
+ ### Behavior in non-interactive environments
33
+
34
+ - CI / non-TTY: defaults to no-clean (unchanged from v0.4.0).
35
+ - `--dry-run`: defaults to no-clean (so the dry-run output is deterministic).
36
+ - Explicit `--clean` or `--no-clean` always wins, no prompt.
37
+
38
+ ## [0.4.0] — 2026-06-28
39
+
40
+ ### Added
41
+
42
+ - **iOS build support (EXPERIMENTAL, macOS only).** New `local-expo-build
43
+ build ios` subcommand orchestrates `expo prebuild --platform ios` →
44
+ `xcodebuild archive` → `xcodebuild -exportArchive` → signed `.ipa`. Five
45
+ steps with the same numbered progress + dry-run support as the Android
46
+ pipeline. Flags: `--method app-store|ad-hoc|development|enterprise`,
47
+ `--scheme`, `--configuration`, `--team-id`, `--bundle-id`,
48
+ `--profile-name`, `--clean`, `--no-bump`, `--no-prebuild`.
49
+ - **`src/core/ios/` module suite.** `xcodebuild.ts` (archive + export
50
+ wrappers), `exportOptions.ts` (generates `export-options.plist` per
51
+ build), `credentials.ts` (reads the `ios` section of `credentials.json`),
52
+ `detect.ts` (auto-detects `ios/<Workspace>.xcworkspace`).
53
+ - **`assertMacOS` guard.** Every iOS code path hard-throws on non-Darwin
54
+ hosts with an actionable message (no cryptic `command not found` for
55
+ `xcodebuild`).
56
+ - **Loud experimental banner.** iOS commands print a yellow warning line
57
+ before doing anything so users know to expect rough edges and to file
58
+ issues. No opt-in flag required (would be annoying after the first run).
59
+ - **Doctor adds iOS prerequisite checks** on macOS (`xcodebuild`,
60
+ `xcrun`). Non-macOS hosts get a single dim "skipped — iOS builds require
61
+ macOS" row instead of failures.
62
+ - **14 new `node:test` cases** for the iOS-testable bits: plist
63
+ generation, credentials.json parsing, workspace detection,
64
+ `assertMacOS` guard. Total: 37 tests across 7 suites.
65
+
66
+ ### Changed
67
+
68
+ - **README restructured** with platform-tab sections — Android (stable)
69
+ and iOS (experimental) have separate Quick Start blocks, "Bringing your
70
+ own credentials" walkthroughs, and file tables.
71
+ - **Comparison table + Roadmap updated** to reflect iOS support and to
72
+ retire the closed items (iOS basic, remote `GRADLE_PIN` manifest).
73
+ - **Package keywords + description** now mention iOS / `.ipa` /
74
+ `xcodebuild`.
75
+
76
+ ### Known limitations of iOS support (read before reporting)
77
+
78
+ The iOS pipeline was implemented from Apple's documented `xcodebuild`
79
+ interface but **not validated on macOS by the maintainer** (Windows-only
80
+ dev environment). Code is correct in theory; please file issues with
81
+ your `eas-cli` version, `xcodebuild -version` output, and the failing
82
+ command if something doesn't work. Specifically not yet supported:
83
+
84
+ - Automated `.p12` keychain import (you double-click the file).
85
+ - Automated provisioning profile install (you double-click the file).
86
+ - TestFlight / App Store upload (use `xcrun altool` manually after the build).
87
+ - iOS-specific `CFBundleVersion` (buildNumber) bump.
88
+ - Multi-bundle apps (extensions / widgets / watch apps).
89
+
90
+ All of the above are on the roadmap for future releases.
91
+
92
+ ## [0.3.0] — 2026-06-28
93
+
94
+ ### Added
95
+
96
+ - **Live `GRADLE_PIN` manifest.** `pinGradle` now fetches the latest pin table
97
+ from `manifest/gradle-pins.json` in the GitHub repo (3 s timeout, silent
98
+ fallback to the bundled table). New Expo SDK shipping a problematic Gradle
99
+ default → edit the manifest, commit, and every existing CLI user gets the
100
+ new pin on their next build. No `npm publish` required.
101
+ - **`local-expo-build update-scripts` subcommand.** Diffs scaffolded
102
+ `scripts/*.js` against the bundled templates by version stamp + content.
103
+ Shows a table of up-to-date / outdated / missing scripts. Prompts before
104
+ overwriting (or pass `--yes`). Honors `--dry-run`. Every scaffolded script
105
+ now carries a `// Generated by local-expo-build vX.Y.Z` header.
106
+ - **Multi-strategy `signingConfigs.release` injection.** `setupSigning` now
107
+ tries three strategies in order: exact-match → tolerant block-inject →
108
+ synthesize new block inside `android { }`. Survives whitespace drift,
109
+ comments, and even Expo dropping the default debug signing block from
110
+ prebuild output.
111
+ - **`local-expo-build` test suite.** 23 tests across 5 suites using
112
+ `node:test` (no new devDep). Covers gradle injection, buildType rewiring,
113
+ EAS link detection, and rehydrate candidate matching — the bits that
114
+ break first when Expo shifts. Run with `npm test`.
115
+ - **CI matrix:** 3 OS × 2 Node versions = 6 jobs per PR
116
+ (ubuntu/macos/windows × Node 20/22). Catches Windows-specific regressions
117
+ before users do.
118
+
119
+ ### Changed
120
+
121
+ - **Node 20 minimum** (`engines.node`). Node 18 reached EOL in April 2025;
122
+ bumping the floor lets us use native `fetch` for the manifest and
123
+ `node:test` for the test suite without polyfills.
124
+ - Doctor's `Node` check now requires `>= 20` instead of `>= 18`.
125
+ - `pinGradle` is now async (was sync). The single internal caller in
126
+ `build.ts` was updated.
127
+
8
128
  ## [0.2.0] — 2026-06-28
9
129
 
10
130
  ### Added
package/README.md CHANGED
@@ -1,13 +1,15 @@
1
1
  # local-expo-build
2
2
 
3
- > One-stop CLI for **local** Expo Android APK / AAB builds. Bypass EAS cloud builds, keep full control of signing, and stop waiting in queues.
3
+ > One-stop CLI for **local** Expo Android APK / AAB builds **works on Windows, macOS, and Linux** (unlike `eas build --local`, which rejects Windows). Own your signing, skip the EAS cloud queue, no eas.json required.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/local-expo-build.svg)](https://www.npmjs.com/package/local-expo-build)
6
6
  [![npm downloads](https://img.shields.io/npm/dm/local-expo-build.svg)](https://www.npmjs.com/package/local-expo-build)
7
7
  [![license](https://img.shields.io/npm/l/local-expo-build.svg)](https://github.com/nikhild64/local-expo-build/blob/main/LICENSE)
8
8
  [![node](https://img.shields.io/node/v/local-expo-build.svg)](https://nodejs.org/)
9
9
 
10
- `local-expo-build` automates the painful parts of running `expo prebuild` + `gradlew bundleRelease` yourself:
10
+ `local-expo-build` automates the painful parts of running `expo prebuild` + `gradlew bundleRelease` (or `xcodebuild archive`) yourself:
11
+
12
+ **Android (stable):**
11
13
 
12
14
  - Detects your Expo SDK and pins the Gradle wrapper to a version that actually works (e.g. SDK 55 → Gradle 8.13, working around the `expo-manifests` `components.release` bug).
13
15
  - Bumps your app version and pulls the next `versionCode` from EAS so Play Store ingest doesn't reject the upload.
@@ -17,6 +19,13 @@
17
19
  - Runs `gradlew assembleRelease` / `bundleRelease` and prints the absolute path + size of the produced artifact.
18
20
  - Pushes the new `versionCode` back to EAS via GraphQL so `eas build` / `eas submit` stay in sync.
19
21
 
22
+ **iOS (experimental, macOS only — community-tested):**
23
+
24
+ - Orchestrates `xcodebuild archive` + `-exportArchive` for `.ipa` output.
25
+ - Auto-detects the workspace + scheme produced by `expo prebuild --platform ios`.
26
+ - Generates `export-options.plist` per build with sensible defaults for `app-store` / `ad-hoc` / `development` / `enterprise` distribution methods.
27
+ - Reads `.p12` + provisioning profile paths from `credentials.json` (same file format EAS downloads).
28
+
20
29
  **`doctor` is a setup wizard, not just a health check.** It detects missing pieces (`expo.android.package`, EAS link, `eas.json`, keystore) and offers to fix each one interactively — `eas init`, `eas build:configure`, keystore picker (with one-prompt `rehydrate` from `credentials.json` when possible), all chained.
21
30
 
22
31
  ![local-expo-build init: doctor pre-flight + scaffolding in one command](https://raw.githubusercontent.com/nikhild64/local-expo-build/main/assets/screenshots/setup_init.png)
@@ -68,14 +77,15 @@ local-expo-build init [--force] [--no-keystore] [--no-doctor]
68
77
  Scaffold scripts + package.json entries
69
78
  (runs `doctor` first by default)
70
79
 
71
- local-expo-build build android [--apk|--aab] [--profile <name>]
72
- [--clean] [--no-bump] [--no-sync] [--no-prebuild]
73
- Run the full pipeline → .aab|.apk
74
-
75
80
  local-expo-build doctor Env check + interactive auto-fix wizard
76
81
  (eas init → eas build:configure →
77
82
  keystore setup)
78
83
 
84
+ # ── Android (stable) ──
85
+ local-expo-build build android [--apk|--aab] [--profile <name>]
86
+ [--clean] [--no-bump] [--no-sync] [--no-prebuild]
87
+ Run the full Android pipeline → .aab|.apk
88
+
79
89
  local-expo-build keystore setup Interactive picker:
80
90
  rehydrate | existing | generate | EAS
81
91
  local-expo-build keystore import Register an existing .jks
@@ -84,7 +94,19 @@ local-expo-build keystore fetch Open `eas credentials` to download a
84
94
  local-expo-build keystore rehydrate [--move]
85
95
  Bind credentials.json + .jks into
86
96
  keystore.properties (no password re-entry).
87
- --move deletes the source .jks after copy.
97
+
98
+ # ── iOS (experimental, macOS only) ──
99
+ local-expo-build build ios [--method <m>] [--scheme <s>] [--configuration <c>]
100
+ [--team-id <id>] [--profile-name <n>] [--bundle-id <id>]
101
+ [--clean] [--no-bump] [--no-prebuild]
102
+ Run the full iOS pipeline → .ipa
103
+ --method = app-store | ad-hoc |
104
+ development | enterprise
105
+
106
+ # ── Shared ──
107
+ local-expo-build update-scripts [-y|--yes]
108
+ Refresh scaffolded scripts/*.js to the
109
+ version bundled with this CLI.
88
110
  ```
89
111
 
90
112
  Global flags: `--cwd <path>`, `--verbose`, `--dry-run`.
@@ -95,22 +117,52 @@ Global flags: `--cwd <path>`, `--verbose`, `--dry-run`.
95
117
  > npx local-expo-build --dry-run build android --aab # runner mode
96
118
  > npm run build:android:aab -- --dry-run # scaffold mode (or: node scripts/build.js aab --dry-run)
97
119
  > ```
120
+ >
121
+ > **`--clean`** prompts interactively when neither `--clean` nor `--no-clean` is passed. Default is **No** (faster). Say yes after an Expo SDK upgrade, plugin change, or `MainActivity not found` / "android project is malformed" errors. Pass `--clean` or `--no-clean` explicitly to skip the prompt.
98
122
 
99
123
  ![Dry-run output: the full 7-step build pipeline with no side effects](https://raw.githubusercontent.com/nikhild64/local-expo-build/main/assets/screenshots/dryrun-build.png)
100
124
 
101
125
  ## How it compares
102
126
 
103
- | | `eas build` (cloud) | `npx expo run:android` | `local-expo-build` |
104
- | --- | --- | --- | --- |
105
- | Runs locally | No | Yes | **Yes** |
106
- | Produces a signed release `.aab` / `.apk` | Yes | No (debug) | **Yes** |
107
- | Manages release `signingConfig` for you | Yes | No | **Yes** |
108
- | Bumps `versionCode` from EAS automatically | Yes | No | **Yes** |
109
- | Wait in cloud queue | Sometimes | Never | Never |
110
- | Works offline | No | Yes | **Yes** (after first prebuild) |
111
- | Needs `eas-cli` | Yes | No | Optional (only for version sync / EAS keystore) |
127
+ | | `eas build` (cloud) | `eas build --local` | `npx expo run:android/ios` | `local-expo-build` |
128
+ | --- | --- | --- | --- | --- |
129
+ | Runs locally | No | Yes | Yes | **Yes** |
130
+ | **Works on Windows** | Yes (cloud) | **No** ¹ | Yes (debug only) | **Yes** |
131
+ | Works on macOS / Linux | Yes (cloud) | Yes | Yes | **Yes** |
132
+ | Produces a signed release `.aab` / `.apk` | Yes | Yes | No (debug) | **Yes (Android)** |
133
+ | Produces a signed release `.ipa` | Yes | Yes | No (debug) | **Yes (iOS, experimental)** |
134
+ | Counts against EAS free build quota | **Yes** | No | No | No |
135
+ | Manages release signing config for you | Yes | Yes | No | **Yes** |
136
+ | Bumps `versionCode` from EAS automatically | Yes | Yes | No | **Yes (Android)** |
137
+ | Wait in cloud queue | Sometimes | Never | Never | Never |
138
+ | Needs `eas-cli` installed | Yes | **Yes** | No | Optional ² |
139
+ | Needs `eas.json` set up | Yes | **Yes** | No | No |
140
+ | Needs EAS account / login | Yes | **Yes** | No | Optional ² |
141
+ | Pipeline editable per-project | No | No | No | **Yes** (scaffold mode) |
142
+
143
+ ¹ `eas build --platform android --local` errors with `"Unsupported platform, macOS or Linux is required to build apps for Android"` on Windows — EAS's local build infrastructure uses Unix-only tooling.
144
+
145
+ ² Only needed for: EAS keystore fetch, versionCode sync back to EAS, or doctor's `eas init` / `eas build:configure` auto-fixes. The build itself runs without any EAS dependency.
146
+
147
+ **TL;DR:**
112
148
 
113
- If you're happy with cloud builds, use `eas build`. This CLI is for teams who want the EAS workflow (managed signing, synced `versionCode`) but the speed and control of building on their own machine.
149
+ - **On Windows?** `local-expo-build` is the only option in the Expo ecosystem to build Android locally.
150
+ - **On Mac/Linux + happy with EAS?** Use `eas build --local` — it's the official path with managed credentials + `eas submit` integration.
151
+ - **On Mac/Linux + want lightweight / no EAS lock-in?** `local-expo-build` is your fit — no eas.json, no login, fully editable pipeline.
152
+
153
+ ## Why not just use `eas build --local`?
154
+
155
+ If you're on Mac or Linux **and** already invested in EAS (account, eas.json, profiles, `eas submit` workflow), `eas build --platform android --local` is the official Expo path and you should use it. It integrates seamlessly with `eas submit`, managed credentials, and the rest of the EAS toolbox.
156
+
157
+ Use `local-expo-build` instead when **any** of these is true:
158
+
159
+ 1. **You're on Windows.** `eas build --local` literally won't run — it errors out with `"Unsupported platform, macOS or Linux is required to build apps for Android"`. This is the killer use case: there's no other path to local Expo Android builds on Windows.
160
+ 2. **You don't want an EAS account.** No login, no `eas.json`, no project-link ceremony. Just point at any Expo project and get a signed `.aab`.
161
+ 3. **You want to vendor + edit the build pipeline in your repo.** Scaffold mode drops 6 readable JS files into `scripts/` that you can customize per-project (custom version bumping, project-specific signing tweaks, pre/post hooks). `eas build` is a black box by design.
162
+ 4. **Your project pre-dates EAS** (SDK 50 era) and you never set up the EAS link. `local-expo-build` works on any Expo SDK ≥ 50 with no migration needed.
163
+ 5. **You want to script around predictable artifact paths.** Output always lands at `android/app/build/outputs/{apk,bundle}/release/...`. EAS local copies through temp dirs.
164
+
165
+ Both tools can coexist. The CLI also writes a `credentials.json` at project root, which `eas submit` (and `eas build` cloud, if you ever want to use it) reads directly — your local-built `.aab` can still be uploaded via `eas submit --path /path/to/app.aab` without re-signing.
114
166
 
115
167
  ## Keystore sources
116
168
 
@@ -180,6 +232,97 @@ npx local-expo-build build android --aab # runner mode
180
232
 
181
233
  > Tip: you can skip step 2 entirely — `doctor` detects the rehydrate state automatically and offers the same one-prompt fix inline.
182
234
 
235
+ ## iOS (experimental, macOS only)
236
+
237
+ > **Status: community-tested.** The iOS pipeline ships behind an experimental banner because the maintainer develops on Windows and can't validate every build configuration. The code is built on Apple's documented `xcodebuild` interface and follows the same patterns as the Android side, but please [file issues](https://github.com/nikhild64/local-expo-build/issues) when you hit something — and PRs are very welcome.
238
+
239
+ ### Prerequisites
240
+
241
+ iOS local builds are constrained by Apple and require all of:
242
+
243
+ - **macOS** (Xcode is macOS-only — Apple does not ship `xcodebuild` for Linux or Windows)
244
+ - **Xcode** 14+ with Command Line Tools (`xcode-select --install`)
245
+ - **Apple Developer account** ($99/yr — required for distribution signing certificates)
246
+ - **A distribution `.p12`** + **provisioning profile** installed in your keychain (drag the `.p12` into Keychain Access; double-click the `.mobileprovision` to install)
247
+
248
+ `local-expo-build doctor` checks for Xcode / `xcodebuild` automatically on macOS hosts and skips the checks elsewhere.
249
+
250
+ ### Quick start (iOS)
251
+
252
+ ```bash
253
+ # 1. Download credentials from EAS (same flow as Android)
254
+ npx local-expo-build keystore fetch
255
+ # In the EAS menu pick: iOS → Download credentials to credentials.json
256
+ # This produces:
257
+ # - credentials.json with an `ios` section
258
+ # - ios/certs/dist.p12 (distribution certificate)
259
+ # - ios/certs/profile.mobileprovision (provisioning profile)
260
+
261
+ # 2. (one-time, manual) Import the .p12 into your login keychain
262
+ # and double-click the .mobileprovision file. Both go into ~/Library/...
263
+ # Xcode does this automatically on double-click.
264
+
265
+ # 3. Build the .ipa
266
+ npx local-expo-build build ios --method app-store \
267
+ --team-id ABCDE12345 \
268
+ --bundle-id com.yourcompany.yourapp \
269
+ --profile-name "Your Profile Name"
270
+ ```
271
+
272
+ The build runs through these 5 steps:
273
+
274
+ ```text
275
+ 1/5 expo prebuild (ios)
276
+ 2/5 bump version
277
+ 3/5 detect Xcode workspace + credentials
278
+ 4/5 xcodebuild archive
279
+ 5/5 xcodebuild -exportArchive (method=app-store)
280
+
281
+ Build complete (.ipa, 14.3 MB):
282
+ /path/to/your/app/ios/build/export/YourApp.ipa
283
+ ```
284
+
285
+ Use `--dry-run` to preview the 5 steps without invoking xcodebuild.
286
+
287
+ ### What the CLI does NOT handle (yet)
288
+
289
+ By design, the experimental iOS support keeps a tight scope so we don't ship untested Apple-keychain code that could leave your machine in a weird state. You're still responsible for:
290
+
291
+ - **Keychain `.p12` import.** Double-click the file in Finder, or `security import dist.p12 -k ~/Library/Keychains/login.keychain-db -P <password>`.
292
+ - **Provisioning profile install.** Double-click the `.mobileprovision`, or copy to `~/Library/MobileDevice/Provisioning Profiles/<UUID>.mobileprovision`.
293
+ - **TestFlight / App Store upload.** Use `xcrun altool --upload-app` or Apple's Transporter app after the `.ipa` is built.
294
+
295
+ These are on the roadmap (full `certs setup` flow + EAS cert fetch + TestFlight upload) for a future release once there's been enough community testing of the core build flow.
296
+
297
+ ### iOS-specific files
298
+
299
+ | Path | Purpose | Gitignored? |
300
+ | --- | --- | --- |
301
+ | `credentials.json` → `ios` block | EAS local-credentials shape for iOS (`distributionCertificate.path/password`, `provisioningProfilePath`) | Yes (auto via the Android setup) |
302
+ | `ios/build/export-options.plist` | Regenerated per build with the chosen distribution method | No — derived per-build, safe to commit or gitignore as you prefer |
303
+ | `ios/build/<Scheme>.xcarchive` | Intermediate archive produced by `xcodebuild archive` | Yes (recommend adding `ios/build/` to `.gitignore`) |
304
+ | `ios/build/export/<Scheme>.ipa` | The final `.ipa` | Yes (recommend adding `ios/build/` to `.gitignore`) |
305
+
306
+ ### Known caveats
307
+
308
+ - **Auto-detected scheme is the workspace basename.** Works for Expo's default prebuild output; pass `--scheme MyOtherScheme` if your project has multiple schemes.
309
+ - **Manual signing requires `--team-id`.** Without it, `xcodebuild` falls back to automatic signing, which only works for `development` builds if your Apple ID is logged into Xcode.
310
+ - **Multi-bundle apps not handled.** If your project has app extensions (widgets, watch app, share extension), each needs its own provisioning profile and the current `--profile-name` flag only sets one. File an issue with your `app.json` `extra.eas.build.experimental.ios.appExtensions` setup if you hit this.
311
+ - **No `bumpVersion` for iOS-specific `buildNumber`.** Today the bump step still updates `versionCode` in `android/app/build.gradle` (no-op on iOS-only builds). Expo's prebuild reads `expo.version` from `app.json` and writes it into `Info.plist`'s `CFBundleShortVersionString` — for `CFBundleVersion` (the iOS equivalent of `versionCode`) you currently need to manage that in `app.json` → `ios.buildNumber` manually. PRs to wire this up are welcome.
312
+
313
+ ### How it works (iOS pipeline)
314
+
315
+ ```text
316
+ expo prebuild --platform ios
317
+ → (you import .p12 + provisioning profile manually, one-time)
318
+ → bump app.json version (src/core/bumpVersion.ts)
319
+ → detect ios/<Workspace>.xcworkspace (src/core/ios/detect.ts)
320
+ → read credentials.json `ios` section (src/core/ios/credentials.ts)
321
+ → write ios/build/export-options.plist (src/core/ios/exportOptions.ts)
322
+ → xcodebuild archive (src/core/ios/xcodebuild.ts)
323
+ → xcodebuild -exportArchive → .ipa
324
+ ```
325
+
183
326
  ## Files this CLI creates / touches
184
327
 
185
328
  | Path | Purpose | Gitignored? |
@@ -215,13 +358,24 @@ If your SDK isn't pinned, `pinGradle` is a no-op. Add a row + open a PR if a fut
215
358
 
216
359
  ## Requirements
217
360
 
218
- - Node ≥ 18
361
+ **All platforms:**
362
+
363
+ - Node ≥ 20 (Node 18 reached EOL in April 2025)
364
+ - `eas-cli` is **optional** — only needed for EAS version sync, EAS credentials fetch, or doctor's `eas init` / `eas build:configure` auto-fixes
365
+
366
+ **Android builds (cross-platform):**
367
+
219
368
  - JDK 17 (recommended for Expo SDK 55)
220
369
  - Android SDK + `ANDROID_HOME` env var
221
370
  - `keytool` on `PATH` (ships with the JDK)
222
- - `eas-cli` is **optional** — only needed for EAS version sync, EAS keystore fetch, or doctor's `eas init` / `eas build:configure` auto-fixes
223
371
 
224
- Run `local-expo-build doctor` to verify all of the above.
372
+ **iOS builds (macOS only):**
373
+
374
+ - macOS (Apple does not ship `xcodebuild` for Linux or Windows)
375
+ - Xcode 14+ with Command Line Tools (`xcode-select --install`)
376
+ - Apple Developer account ($99/yr — for distribution signing certificates)
377
+
378
+ Run `local-expo-build doctor` to verify all of the above — iOS-specific checks are auto-skipped on non-macOS hosts.
225
379
 
226
380
  ## How it works (pipeline)
227
381
 
@@ -350,9 +504,12 @@ node /abs/path/local-expo-build/bin/local-expo-build.js doctor --cwd /abs/path/m
350
504
 
351
505
  ## Roadmap
352
506
 
353
- - [ ] iOS local builds
354
- - [ ] Auto-update `GRADLE_PIN` table from a hosted manifest
355
- - [ ] Symbol upload (`mapping.txt` Play Console / Sentry)
507
+ - [x] iOS local builds (experimental in v0.4.0 — awaiting community testing)
508
+ - [x] Auto-update `GRADLE_PIN` table from a hosted manifest (v0.3.0)
509
+ - [ ] `certs setup` interactive flow for iOS (.p12 import + provisioning profile install via `security` / Keychain Access automation)
510
+ - [ ] iOS `buildNumber` (CFBundleVersion) bump + EAS sync — parity with Android's `versionCode` flow
511
+ - [ ] TestFlight / App Store Connect upload (`xcrun altool` / `notarytool` wrapper)
512
+ - [ ] Symbol upload (`mapping.txt` → Play Console / Sentry; iOS `.dSYM` to Sentry)
356
513
  - [ ] CI presets (`init --ci` that scaffolds a GitHub Actions / GitLab CI workflow with base64-encoded secrets)
357
514
 
358
515
  ## Contributing
package/dist/cli.js CHANGED
@@ -9,6 +9,7 @@ const build_1 = require("./commands/build");
9
9
  const init_1 = require("./commands/init");
10
10
  const keystore_1 = require("./commands/keystore");
11
11
  const doctor_1 = require("./commands/doctor");
12
+ const update_1 = require("./commands/update");
12
13
  const pkg = require('../package.json');
13
14
  const program = new commander_1.Command();
14
15
  program
@@ -23,6 +24,7 @@ program
23
24
  (0, init_1.registerInitCommand)(program);
24
25
  (0, keystore_1.registerKeystoreCommand)(program);
25
26
  (0, doctor_1.registerDoctorCommand)(program);
27
+ (0, update_1.registerUpdateCommand)(program);
26
28
  program
27
29
  .parseAsync(process.argv)
28
30
  .catch((err) => {
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;;;;AAAA,yCAAoC;AACpC,kDAA0B;AAC1B,4CAAwD;AACxD,0CAAsD;AACtD,kDAA8D;AAC9D,8CAA0D;AAE1D,MAAM,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAEvC,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,kBAAkB,CAAC;KACxB,WAAW,CACV,4DAA4D;IAC1D,gFAAgF,CACnF;KACA,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;KACpB,MAAM,CAAC,cAAc,EAAE,4CAA4C,CAAC;KACpE,MAAM,CAAC,WAAW,EAAE,iBAAiB,CAAC;KACtC,MAAM,CAAC,WAAW,EAAE,mDAAmD,CAAC,CAAC;AAE5E,IAAA,4BAAoB,EAAC,OAAO,CAAC,CAAC;AAC9B,IAAA,0BAAmB,EAAC,OAAO,CAAC,CAAC;AAC7B,IAAA,kCAAuB,EAAC,OAAO,CAAC,CAAC;AACjC,IAAA,8BAAqB,EAAC,OAAO,CAAC,CAAC;AAE/B,OAAO;KACJ,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC;KACxB,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACb,OAAO,CAAC,KAAK,CAAC,eAAK,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC,CAAC;IACvD,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,IAAI,GAAG,EAAE,OAAO,IAAI,GAAG,CAAC,CAAC;IACjD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;;;;AAAA,yCAAoC;AACpC,kDAA0B;AAC1B,4CAAwD;AACxD,0CAAsD;AACtD,kDAA8D;AAC9D,8CAA0D;AAC1D,8CAA0D;AAE1D,MAAM,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAEvC,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,kBAAkB,CAAC;KACxB,WAAW,CACV,4DAA4D;IAC1D,gFAAgF,CACnF;KACA,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;KACpB,MAAM,CAAC,cAAc,EAAE,4CAA4C,CAAC;KACpE,MAAM,CAAC,WAAW,EAAE,iBAAiB,CAAC;KACtC,MAAM,CAAC,WAAW,EAAE,mDAAmD,CAAC,CAAC;AAE5E,IAAA,4BAAoB,EAAC,OAAO,CAAC,CAAC;AAC9B,IAAA,0BAAmB,EAAC,OAAO,CAAC,CAAC;AAC7B,IAAA,kCAAuB,EAAC,OAAO,CAAC,CAAC;AACjC,IAAA,8BAAqB,EAAC,OAAO,CAAC,CAAC;AAC/B,IAAA,8BAAqB,EAAC,OAAO,CAAC,CAAC;AAE/B,OAAO;KACJ,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC;KACxB,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACb,OAAO,CAAC,KAAK,CAAC,eAAK,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC,CAAC;IACvD,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,IAAI,GAAG,EAAE,OAAO,IAAI,GAAG,CAAC,CAAC;IACjD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}