local-expo-build 0.2.0
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 +99 -0
- package/LICENSE +21 -0
- package/README.md +372 -0
- package/bin/local-expo-build.js +2 -0
- package/dist/cli.js +33 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/build.js +121 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/doctor.js +606 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.js +125 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/keystore.js +47 -0
- package/dist/commands/keystore.js.map +1 -0
- package/dist/core/bumpVersion.js +70 -0
- package/dist/core/bumpVersion.js.map +1 -0
- package/dist/core/easLink.js +64 -0
- package/dist/core/easLink.js.map +1 -0
- package/dist/core/expoConfig.js +71 -0
- package/dist/core/expoConfig.js.map +1 -0
- package/dist/core/gradleRun.js +31 -0
- package/dist/core/gradleRun.js.map +1 -0
- package/dist/core/keystore/easFetch.js +109 -0
- package/dist/core/keystore/easFetch.js.map +1 -0
- package/dist/core/keystore/existing.js +135 -0
- package/dist/core/keystore/existing.js.map +1 -0
- package/dist/core/keystore/generate.js +72 -0
- package/dist/core/keystore/generate.js.map +1 -0
- package/dist/core/keystore/index.js +62 -0
- package/dist/core/keystore/index.js.map +1 -0
- package/dist/core/keystore/rehydrate.js +88 -0
- package/dist/core/keystore/rehydrate.js.map +1 -0
- package/dist/core/pinGradle.js +50 -0
- package/dist/core/pinGradle.js.map +1 -0
- package/dist/core/prebuild.js +16 -0
- package/dist/core/prebuild.js.map +1 -0
- package/dist/core/sdkDetect.js +26 -0
- package/dist/core/sdkDetect.js.map +1 -0
- package/dist/core/setupSigning.js +168 -0
- package/dist/core/setupSigning.js.map +1 -0
- package/dist/core/syncEasVersion.js +97 -0
- package/dist/core/syncEasVersion.js.map +1 -0
- package/dist/core/writeCredentialsJson.js +51 -0
- package/dist/core/writeCredentialsJson.js.map +1 -0
- package/dist/util/ctx.js +17 -0
- package/dist/util/ctx.js.map +1 -0
- package/dist/util/gitignore.js +23 -0
- package/dist/util/gitignore.js.map +1 -0
- package/dist/util/log.js +16 -0
- package/dist/util/log.js.map +1 -0
- package/package.json +64 -0
- package/templates/keystore.properties.example +4 -0
- package/templates/scripts/build.js +79 -0
- package/templates/scripts/bump-version.js +65 -0
- package/templates/scripts/pin-gradle.js +45 -0
- package/templates/scripts/print-artifact.js +36 -0
- package/templates/scripts/setup-signing.js +137 -0
- package/templates/scripts/sync-eas-version.js +59 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `local-expo-build` are documented here.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.2.0] — 2026-06-28
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Doctor as a setup wizard.** `doctor` now chains interactive auto-fixes for the
|
|
13
|
+
most common blockers: missing `expo.android.package`, missing EAS link
|
|
14
|
+
(`eas init`), missing `eas.json` (`eas build:configure`), and missing keystore
|
|
15
|
+
setup. Each step is gated on the previous outcome and the check rows are
|
|
16
|
+
mutated in place so the exit code reflects the post-fix state.
|
|
17
|
+
- **Dynamic config (`app.config.{js,ts,cjs,mjs}`) support.** `doctor`'s
|
|
18
|
+
`Android package` and `EAS project linked` checks now read the resolved
|
|
19
|
+
Expo config via `npx expo config --json --type public`, falling back to
|
|
20
|
+
`app.json` for static projects. Per-process cache keeps the cost down.
|
|
21
|
+
- **`keystore rehydrate --move` flag.** Deletes the source `.jks` after a
|
|
22
|
+
successful copy into `android/app/` (default is to leave the source as a
|
|
23
|
+
prebuild-survivable backup).
|
|
24
|
+
- **Root `.jks` backup for `keystore create` / `keystore import`.** Both
|
|
25
|
+
providers now also place the `.jks` at project root (gitignored via `*.jks`).
|
|
26
|
+
Closes the data-loss hole where `expo prebuild --clean` would leave a
|
|
27
|
+
freshly-generated keystore with no recoverable source.
|
|
28
|
+
- **Single-script orchestrator (`scripts/build.js`).** Replaces the long
|
|
29
|
+
`&&`-chained npm scripts with a single Node entry point. `package.json` now
|
|
30
|
+
carries `"build:android:apk": "node scripts/build.js apk"` instead of a
|
|
31
|
+
multi-step shell pipeline. Prints numbered progress and a total time.
|
|
32
|
+
- **Build artifact path + size printed at the end of every build.** Runner
|
|
33
|
+
mode and scaffold mode both surface the absolute path so you always know
|
|
34
|
+
where the APK / AAB landed.
|
|
35
|
+
- **Subtle contextual disclaimers** ("Local build · saves an EAS cloud build
|
|
36
|
+
credit · first run is slowest ~5 min") at the top of `build`, `init`, and
|
|
37
|
+
the build orchestrator. One dim line, not repeated mid-pipeline.
|
|
38
|
+
- **`keystore import` auto-detects matching `credentials.json`.** When the
|
|
39
|
+
user points at a `.jks` and a `credentials.json` exists describing the
|
|
40
|
+
same file (matched by absolute path OR sha1 content hash), the password +
|
|
41
|
+
alias prompts are skipped and the values are reused. Falls back to the
|
|
42
|
+
full prompt flow when no match — so this is purely an opt-in shortcut.
|
|
43
|
+
- **`--dry-run` honored by `build android`** (runner mode) and the scaffolded
|
|
44
|
+
`scripts/build.js` orchestrator. Prints every step's command + cwd without
|
|
45
|
+
executing them. Useful for screenshots, sanity-checking the pipeline order,
|
|
46
|
+
and CI plan-mode.
|
|
47
|
+
- **Pre-flight `doctor` in `init`.** `npx local-expo-build init` now runs
|
|
48
|
+
`doctor` first and only proceeds with scaffolding once the environment is
|
|
49
|
+
healthy. Use `--no-doctor` to skip.
|
|
50
|
+
- **Keystore `rehydrate` provider.** New `keystore rehydrate` subcommand (also
|
|
51
|
+
surfaced in the `keystore setup` picker when applicable) binds an existing
|
|
52
|
+
`credentials.json` + `.jks` pair to `keystore.properties` and copies the
|
|
53
|
+
`.jks` into `android/app/`. No password re-entry.
|
|
54
|
+
- **`credentials.json` scaffolder.** `ensureKeystore` now writes/maintains
|
|
55
|
+
`credentials.json` at project root from the same source as
|
|
56
|
+
`keystore.properties`, and gitignores it.
|
|
57
|
+
- **Build artifact path printed at the end of every build.** Both runner mode
|
|
58
|
+
and scaffolded `npm run build:android:*` now print the absolute path and size
|
|
59
|
+
of the produced APK / AAB.
|
|
60
|
+
- **`.jks` recovery in `setup-signing`.** If `expo prebuild --clean` wipes
|
|
61
|
+
`android/app/<storeFile>`, the build chain restores it from a stable source
|
|
62
|
+
(`credentials.json` keystorePath, `credentials/android/`, project root)
|
|
63
|
+
before invoking Gradle. Both `src/core/setupSigning.ts` and
|
|
64
|
+
`templates/scripts/setup-signing.js` carry the recovery.
|
|
65
|
+
- New doctor checks: `Android package (app.json)` (critical), `EAS project
|
|
66
|
+
linked` (yellow when half-linked), `keystore.properties`, `Signing keystore
|
|
67
|
+
(.jks)`, `credentials.json (EAS)`. Suggestions block prints a numbered
|
|
68
|
+
remediation list when anything is missing.
|
|
69
|
+
|
|
70
|
+
### Changed
|
|
71
|
+
|
|
72
|
+
- `keystore setup` picker now lists `Rehydrate from credentials.json` as the
|
|
73
|
+
top, recommended option whenever a complete `credentials.json` + `.jks` are
|
|
74
|
+
on disk.
|
|
75
|
+
- `fetchKeystoreFromEas` pre-flights both `expo.extra.eas.projectId` and
|
|
76
|
+
`eas.json` and offers to run `eas init` / `eas build:configure` interactively
|
|
77
|
+
before launching the EAS credentials menu.
|
|
78
|
+
- `.gitignore` entries created by `init` / `keystore setup` now include
|
|
79
|
+
`credentials.json` alongside `keystore.properties` and `*.jks`.
|
|
80
|
+
|
|
81
|
+
### Fixed
|
|
82
|
+
|
|
83
|
+
- Builds failing with `validateSigningRelease > Keystore file not found` after
|
|
84
|
+
`expo prebuild --clean` (or after accepting the "android project is malformed
|
|
85
|
+
— reinitialize?" prompt). The keystore is now restored from a stable source
|
|
86
|
+
before Gradle runs.
|
|
87
|
+
- Cryptic "credentials command failed" from `keystore fetch` on projects
|
|
88
|
+
without `eas.json` or a linked EAS project. Replaced with a guided
|
|
89
|
+
pre-flight.
|
|
90
|
+
- `templates/scripts/build.js` orchestrator was passing `shell: false` to
|
|
91
|
+
`execSync` on Unix, which is undefined behavior. Now omits the option so
|
|
92
|
+
Node uses the platform default (`/bin/sh` on Unix, `cmd.exe` on Windows).
|
|
93
|
+
Fully cross-platform.
|
|
94
|
+
|
|
95
|
+
## [0.1.0]
|
|
96
|
+
|
|
97
|
+
Initial release. Local Expo Android APK/AAB pipeline with `prebuild`, Gradle
|
|
98
|
+
wrapper pinning, version bump, signing config injection, Gradle build, and EAS
|
|
99
|
+
versionCode sync.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nikhil Dhawan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
# local-expo-build
|
|
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.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/local-expo-build)
|
|
6
|
+
[](https://www.npmjs.com/package/local-expo-build)
|
|
7
|
+
[](https://github.com/nikhild64/local-expo-build/blob/main/LICENSE)
|
|
8
|
+
[](https://nodejs.org/)
|
|
9
|
+
|
|
10
|
+
`local-expo-build` automates the painful parts of running `expo prebuild` + `gradlew bundleRelease` yourself:
|
|
11
|
+
|
|
12
|
+
- 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
|
+
- Bumps your app version and pulls the next `versionCode` from EAS so Play Store ingest doesn't reject the upload.
|
|
14
|
+
- Injects a release `signingConfig` into the generated `android/app/build.gradle` from a `keystore.properties` you control.
|
|
15
|
+
- Scaffolds `credentials.json` from the same source so EAS submit / cloud builds can reuse your local JKS.
|
|
16
|
+
- Restores your `.jks` into `android/app/` if `expo prebuild --clean` wipes it (no more `validateSigningRelease > Keystore file not found`).
|
|
17
|
+
- Runs `gradlew assembleRelease` / `bundleRelease` and prints the absolute path + size of the produced artifact.
|
|
18
|
+
- Pushes the new `versionCode` back to EAS via GraphQL so `eas build` / `eas submit` stay in sync.
|
|
19
|
+
|
|
20
|
+
**`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
|
+
|
|
22
|
+

|
|
23
|
+
|
|
24
|
+
Two modes:
|
|
25
|
+
|
|
26
|
+
- **Scaffold** _(recommended)_ — `npx local-expo-build init` drops reusable, committable scripts into your project; you run `npm run build:android:aab` from then on.
|
|
27
|
+
- **Runner** — `npx local-expo-build build android --aab`; one command, no files touched in your repo.
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm i -g local-expo-build
|
|
33
|
+
# or use it ad hoc
|
|
34
|
+
npx local-expo-build --help
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Quick start (recommended — scaffold mode)
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
cd <your-expo-project>
|
|
41
|
+
npx local-expo-build init
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`init` runs `doctor` first as a pre-flight, walks you through any missing setup (EAS link, `eas.json`, keystore), then drops the build scripts and adds the `build:android:apk` / `build:android:aab` entries to your `package.json`. Then:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm run build:android:aab # release AAB → android/app/build/outputs/bundle/release/app-release.aab
|
|
48
|
+
npm run build:android:apk # release APK → android/app/build/outputs/apk/release/app-release.apk
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
After the build finishes, the absolute path + size of the artifact is printed at the very end so you always know where it landed.
|
|
52
|
+
|
|
53
|
+
Skip the pre-flight in CI: `npx local-expo-build init --no-doctor --no-keystore`.
|
|
54
|
+
|
|
55
|
+
### Alternative — runner mode (no scaffold)
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npx local-expo-build doctor # env + setup wizard
|
|
59
|
+
npx local-expo-build build android --aab # full pipeline → .aab
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Useful if you don't want any files committed to your repo and prefer to drive the whole pipeline from a single CLI call each time.
|
|
63
|
+
|
|
64
|
+
## Commands
|
|
65
|
+
|
|
66
|
+
```text
|
|
67
|
+
local-expo-build init [--force] [--no-keystore] [--no-doctor]
|
|
68
|
+
Scaffold scripts + package.json entries
|
|
69
|
+
(runs `doctor` first by default)
|
|
70
|
+
|
|
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
|
+
local-expo-build doctor Env check + interactive auto-fix wizard
|
|
76
|
+
(eas init → eas build:configure →
|
|
77
|
+
keystore setup)
|
|
78
|
+
|
|
79
|
+
local-expo-build keystore setup Interactive picker:
|
|
80
|
+
rehydrate | existing | generate | EAS
|
|
81
|
+
local-expo-build keystore import Register an existing .jks
|
|
82
|
+
local-expo-build keystore create Generate a new keystore via keytool
|
|
83
|
+
local-expo-build keystore fetch Open `eas credentials` to download a .jks
|
|
84
|
+
local-expo-build keystore rehydrate [--move]
|
|
85
|
+
Bind credentials.json + .jks into
|
|
86
|
+
keystore.properties (no password re-entry).
|
|
87
|
+
--move deletes the source .jks after copy.
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Global flags: `--cwd <path>`, `--verbose`, `--dry-run`.
|
|
91
|
+
|
|
92
|
+
> **Dry-run** is wired into `build android` and the scaffolded orchestrator. Use it to preview the full pipeline (great for screenshots, sanity checks, CI plan-mode):
|
|
93
|
+
>
|
|
94
|
+
> ```bash
|
|
95
|
+
> npx local-expo-build --dry-run build android --aab # runner mode
|
|
96
|
+
> npm run build:android:aab -- --dry-run # scaffold mode (or: node scripts/build.js aab --dry-run)
|
|
97
|
+
> ```
|
|
98
|
+
|
|
99
|
+

|
|
100
|
+
|
|
101
|
+
## How it compares
|
|
102
|
+
|
|
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) |
|
|
112
|
+
|
|
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.
|
|
114
|
+
|
|
115
|
+
## Keystore sources
|
|
116
|
+
|
|
117
|
+
When `keystore setup` runs (or when `doctor` / `init` prompt for it), you'll see one of these. **Rehydrate** appears at the top conditionally — only when a complete `credentials.json` + `.jks` are already on disk:
|
|
118
|
+
|
|
119
|
+
| Source | What happens |
|
|
120
|
+
| --- | --- |
|
|
121
|
+
| **Rehydrate** | Reads `credentials.json` + the `.jks` it points at, copies the `.jks` into `android/app/<basename>`, writes `keystore.properties`. **No password re-entry.** Ideal after `keystore fetch`. |
|
|
122
|
+
| **Existing .jks** | You point to a file + provide alias/passwords. Copied into `android/app/`. If a matching `credentials.json` is on disk (same path or same content hash), the password prompts are skipped — values reused automatically. |
|
|
123
|
+
| **Generate new** | Wizard runs `keytool -genkeypair` with sane defaults (RSA 2048, 10000d). |
|
|
124
|
+
| **EAS** | Opens `eas credentials` so you can download the project's current keystore from EAS. |
|
|
125
|
+
|
|
126
|
+
In every case, after the keystore is registered the CLI also writes a matching `credentials.json` at the project root and adds `keystore.properties`, `*.jks`, and `credentials.json` to `.gitignore`.
|
|
127
|
+
|
|
128
|
+
## Bringing your own keystore (EAS-managed flow)
|
|
129
|
+
|
|
130
|
+
The lowest-friction path from a brand-new clone to a buildable project when your team already has a keystore on EAS. This is what `doctor` will walk you through.
|
|
131
|
+
|
|
132
|
+
### 1. Fetch the keystore via EAS CLI
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
npx local-expo-build keystore fetch
|
|
136
|
+
# Pre-flight: if eas.json or projectId is missing, we'll offer
|
|
137
|
+
# to run `eas init` / `eas build:configure` before launching EAS.
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
EAS opens its interactive menu. Pick:
|
|
141
|
+
|
|
142
|
+
```text
|
|
143
|
+
✔ Which build profile do you want to configure? › production
|
|
144
|
+
✔ What do you want to do? › Keystore: Manage everything needed to build your project
|
|
145
|
+
✔ What do you want to do? › Download existing keystore
|
|
146
|
+
✔ Display sensitive information? › Yes
|
|
147
|
+
✔ Go back
|
|
148
|
+
✔ What do you want to do? › credentials.json: Upload/Download credentials …
|
|
149
|
+
✔ What do you want to do? › Download credentials from EAS to credentials.json
|
|
150
|
+
✔ What do you want to do? › Exit
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
You now have:
|
|
154
|
+
|
|
155
|
+
- `<scope>__<project>.jks` at project root (from "Download existing keystore"),
|
|
156
|
+
- `credentials.json` at project root with all four required fields,
|
|
157
|
+
- `credentials/android/keystore.jks` (the file `credentials.json` actually references).
|
|
158
|
+
|
|
159
|
+
### 2. Bind everything in one step
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
npx local-expo-build keystore rehydrate
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
That copies the `.jks` referenced by `credentials.json` into `android/app/<basename>` and writes `keystore.properties` using the passwords already in `credentials.json` — **no re-typing**.
|
|
166
|
+
|
|
167
|
+
```text
|
|
168
|
+
✓ Copied credentials/android/keystore.jks → android/app/keystore.jks
|
|
169
|
+
✓ keystore.properties written from credentials.json (alias=6805615551f1…)
|
|
170
|
+
✓ Wrote credentials.json (keystorePath=android/app/keystore.jks)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### 3. Build
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
npm run build:android:aab # scaffold mode
|
|
177
|
+
# or
|
|
178
|
+
npx local-expo-build build android --aab # runner mode
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
> Tip: you can skip step 2 entirely — `doctor` detects the rehydrate state automatically and offers the same one-prompt fix inline.
|
|
182
|
+
|
|
183
|
+
## Files this CLI creates / touches
|
|
184
|
+
|
|
185
|
+
| Path | Purpose | Gitignored? |
|
|
186
|
+
| --- | --- | --- |
|
|
187
|
+
| `keystore.properties` (root) | Gradle release `signingConfig` source of truth: `storeFile`, `storePassword`, `keyAlias`, `keyPassword` | Yes (auto) |
|
|
188
|
+
| `credentials.json` (root) | EAS submit/cloud's local-credential pointer. Kept in sync with `keystore.properties`. | Yes (auto) |
|
|
189
|
+
| `android/app/<storeFile>` | The actual `.jks` that Gradle reads | Yes (`*.jks`, auto) |
|
|
190
|
+
| `eas.json` (root) | EAS build profile config (created by `eas build:configure`) | No — commit it |
|
|
191
|
+
| `app.json` → `expo.extra.eas.projectId` | EAS link (written by `eas init`) | No — commit it |
|
|
192
|
+
| `app.json` → `expo.android.package` | Android applicationId | No — commit it |
|
|
193
|
+
| `scripts/build.js` | (Scaffold mode only) single orchestrator entry point — what `npm run build:android:*` actually calls | No — commit it |
|
|
194
|
+
| `scripts/{pin-gradle,bump-version,setup-signing,sync-eas-version,print-artifact}.js` | (Scaffold mode only) per-step modules orchestrated by `build.js`; edit any one to customize that step for your project | No — commit them |
|
|
195
|
+
|
|
196
|
+
> **Security:** `keystore.properties` and `credentials.json` both contain plaintext keystore passwords. The CLI gitignores them automatically. **Don't commit them. Don't paste them into chat.** If you need them in CI, base64-encode and inject via secrets.
|
|
197
|
+
|
|
198
|
+
## Multi-SDK support
|
|
199
|
+
|
|
200
|
+
`local-expo-build` carries a small table of Gradle wrapper versions per SDK in [`src/core/pinGradle.ts`](src/core/pinGradle.ts):
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
export const GRADLE_PIN: Record<number, string | null> = {
|
|
204
|
+
50: null,
|
|
205
|
+
51: null,
|
|
206
|
+
52: null,
|
|
207
|
+
53: null,
|
|
208
|
+
54: null,
|
|
209
|
+
55: '8.13', // expo-manifests components.release workaround
|
|
210
|
+
56: null,
|
|
211
|
+
};
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
If your SDK isn't pinned, `pinGradle` is a no-op. Add a row + open a PR if a future SDK needs one.
|
|
215
|
+
|
|
216
|
+
## Requirements
|
|
217
|
+
|
|
218
|
+
- Node ≥ 18
|
|
219
|
+
- JDK 17 (recommended for Expo SDK 55)
|
|
220
|
+
- Android SDK + `ANDROID_HOME` env var
|
|
221
|
+
- `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
|
+
|
|
224
|
+
Run `local-expo-build doctor` to verify all of the above.
|
|
225
|
+
|
|
226
|
+
## How it works (pipeline)
|
|
227
|
+
|
|
228
|
+
```text
|
|
229
|
+
expo prebuild --platform android
|
|
230
|
+
→ pin Gradle wrapper (src/core/pinGradle.ts)
|
|
231
|
+
→ bump version + EAS code (src/core/bumpVersion.ts)
|
|
232
|
+
→ ensure keystore (src/core/keystore/*)
|
|
233
|
+
→ restore .jks into android/app (src/core/setupSigning.ts → ensureKeystoreInAndroidApp)
|
|
234
|
+
→ inject release signing (src/core/setupSigning.ts)
|
|
235
|
+
→ write/sync credentials.json (src/core/writeCredentialsJson.ts)
|
|
236
|
+
→ gradlew {assemble|bundle}Release
|
|
237
|
+
→ sync versionCode to EAS (src/core/syncEasVersion.ts)
|
|
238
|
+
→ print artifact path + size (templates/scripts/print-artifact.js)
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
The scaffolded `scripts/*.js` files mirror the same logic so they're vendorable and editable per-project.
|
|
242
|
+
|
|
243
|
+
### Doctor's auto-fix chain
|
|
244
|
+
|
|
245
|
+
```text
|
|
246
|
+
expo.android.package (app.json or app.config.*) → prompt + write to app.json
|
|
247
|
+
EAS link (expo.extra.eas.projectId) → offer `eas init`
|
|
248
|
+
eas.json → offer `eas build:configure --platform android`
|
|
249
|
+
keystore.properties → offer keystore picker
|
|
250
|
+
├─ credentials.json + .jks present → rehydrate (no password re-entry)
|
|
251
|
+
└─ else → existing | generate | EAS
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Each accepted step re-runs the affected checks in place. If everything ends up green, doctor exits 0; otherwise the remaining items are printed under **Suggested next steps to complete setup**.
|
|
255
|
+
|
|
256
|
+
Dynamic configs (`app.config.{js,ts,cjs,mjs}`) are supported: doctor shells out to `npx expo config --json --type public` and reads the resolved config. If the Expo CLI fails to resolve (e.g. you haven't `npm install`ed yet), the affected rows show a yellow warning instead of pretending. Auto-writes still target `app.json` only — we don't modify dynamic config files.
|
|
257
|
+
|
|
258
|
+
`init` runs the entire doctor chain as its pre-flight before scaffolding. Pass `--no-doctor` to skip.
|
|
259
|
+
|
|
260
|
+
Your `package.json` ends up with just two added lines, both pointing at the orchestrator:
|
|
261
|
+
|
|
262
|
+
```json
|
|
263
|
+
{
|
|
264
|
+
"scripts": {
|
|
265
|
+
"build:android:apk": "node scripts/build.js apk",
|
|
266
|
+
"build:android:aab": "node scripts/build.js aab"
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
The orchestrator (`scripts/build.js`) prints numbered progress (`▸ [3/7] bump version`, etc.) and a final total time. EAS version sync is treated as non-fatal — if your EAS login expires, the build still succeeds and you get a single warning line instead of a hard fail.
|
|
272
|
+
|
|
273
|
+
## Troubleshooting
|
|
274
|
+
|
|
275
|
+
### `validateSigningRelease > Keystore file '…/android/app/keystore.jks' not found`
|
|
276
|
+
|
|
277
|
+
This happens when `expo prebuild --clean` (or the "android project is malformed — reinitialize?" prompt) wipes `android/` between keystore setup and the Gradle build. The pipeline restores it automatically as of v0.2.0 — make sure your scaffolded `scripts/setup-signing.js` is up to date:
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
npx local-expo-build init --force
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
If the recovery itself fails, you'll get a clear list of paths it tried; the fix is usually:
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
npx local-expo-build keystore rehydrate # if you have credentials.json
|
|
287
|
+
# or
|
|
288
|
+
npx local-expo-build keystore import <path-to-jks>
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### `eas credentials failed: Command failed with exit code 1`
|
|
292
|
+
|
|
293
|
+
`eas credentials` refuses to start without `eas.json` and a linked `expo.extra.eas.projectId`. v0.2.0 pre-flights both and offers to run `eas init` / `eas build:configure` interactively. If you skipped those prompts, run them manually:
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
eas init
|
|
297
|
+
eas build:configure --platform android
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Then re-run `npx local-expo-build keystore fetch`.
|
|
301
|
+
|
|
302
|
+
### `Missing expo.android.package in app.json`
|
|
303
|
+
|
|
304
|
+
Doctor catches this as a critical check and offers to write it for you with a sensible default derived from `expo.slug` or `expo.ios.bundleIdentifier`. If you're using `app.config.{js,ts}`, doctor can't statically write to it — add the field by hand:
|
|
305
|
+
|
|
306
|
+
```json
|
|
307
|
+
{
|
|
308
|
+
"expo": {
|
|
309
|
+
"android": {
|
|
310
|
+
"package": "com.yourcompany.yourapp"
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### "The android project is malformed, would you like to clear and reinitialize?"
|
|
317
|
+
|
|
318
|
+
This is `expo prebuild` detecting that our injected `signingConfigs.release` block doesn't match what it would generate fresh. Accepting it is safe — the build chain re-injects signing and the recovery step puts the `.jks` back before Gradle runs.
|
|
319
|
+
|
|
320
|
+
### `expo CLI (in project) not found` in doctor
|
|
321
|
+
|
|
322
|
+
You haven't installed deps in the target Expo project yet. Run `npm install` (or `pnpm install` / `yarn`) in the project root.
|
|
323
|
+
|
|
324
|
+
### Build artifact is signed but Play Console rejects it
|
|
325
|
+
|
|
326
|
+
Two common causes:
|
|
327
|
+
|
|
328
|
+
1. **`versionCode` already used.** The `--no-sync` flag suppresses pushing the new `versionCode` back to EAS — if you used it and Play sees the same code as a previous upload, it'll reject. Don't pass `--no-sync` unless you're managing versions manually.
|
|
329
|
+
2. **Different keystore than the one originally registered.** Play Store requires the *same* keystore for all updates to a published app. Use `keystore fetch` + `keystore rehydrate` to get back the original.
|
|
330
|
+
|
|
331
|
+
## Testing the CLI locally in another Expo app
|
|
332
|
+
|
|
333
|
+
Three iteration loops, fastest to most-realistic:
|
|
334
|
+
|
|
335
|
+
```bash
|
|
336
|
+
# 1. npm link — fastest dev loop
|
|
337
|
+
cd local-expo-build && npm run build && npm link
|
|
338
|
+
cd ../my-test-app && npm link local-expo-build
|
|
339
|
+
# now changes in local-expo-build (with `npm run dev` watching) are picked up
|
|
340
|
+
# on the next `npx local-expo-build ...` call from my-test-app.
|
|
341
|
+
|
|
342
|
+
# 2. npm pack — exactly what end users will install
|
|
343
|
+
cd local-expo-build && npm run build && npm pack
|
|
344
|
+
cd ../my-test-app && npm i ../local-expo-build/local-expo-build-0.2.0.tgz
|
|
345
|
+
|
|
346
|
+
# 3. Direct invocation — no install at all
|
|
347
|
+
cd local-expo-build && npm run build
|
|
348
|
+
node /abs/path/local-expo-build/bin/local-expo-build.js doctor --cwd /abs/path/my-test-app
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
## Roadmap
|
|
352
|
+
|
|
353
|
+
- [ ] iOS local builds
|
|
354
|
+
- [ ] Auto-update `GRADLE_PIN` table from a hosted manifest
|
|
355
|
+
- [ ] Symbol upload (`mapping.txt` → Play Console / Sentry)
|
|
356
|
+
- [ ] CI presets (`init --ci` that scaffolds a GitHub Actions / GitLab CI workflow with base64-encoded secrets)
|
|
357
|
+
|
|
358
|
+
## Contributing
|
|
359
|
+
|
|
360
|
+
PRs welcome — see [CONTRIBUTING.md](CONTRIBUTING.md) for dev setup, code layout, and conventions.
|
|
361
|
+
|
|
362
|
+
## Changelog
|
|
363
|
+
|
|
364
|
+
See [CHANGELOG.md](CHANGELOG.md).
|
|
365
|
+
|
|
366
|
+
## License
|
|
367
|
+
|
|
368
|
+
[MIT](LICENSE) © Nikhil Dhawan
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
**Not affiliated with Expo or Google.** "Expo" and "EAS" are trademarks of 650 Industries, Inc. This project consumes EAS's public APIs (`api.expo.dev/graphql`, `eas-cli`) but is independently maintained.
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const commander_1 = require("commander");
|
|
7
|
+
const kleur_1 = __importDefault(require("kleur"));
|
|
8
|
+
const build_1 = require("./commands/build");
|
|
9
|
+
const init_1 = require("./commands/init");
|
|
10
|
+
const keystore_1 = require("./commands/keystore");
|
|
11
|
+
const doctor_1 = require("./commands/doctor");
|
|
12
|
+
const pkg = require('../package.json');
|
|
13
|
+
const program = new commander_1.Command();
|
|
14
|
+
program
|
|
15
|
+
.name('local-expo-build')
|
|
16
|
+
.description('Local Expo Android build CLI — bypasses EAS cloud builds. ' +
|
|
17
|
+
'Prebuild, pin Gradle, bump version, sign with your JKS, run gradlew, sync EAS.')
|
|
18
|
+
.version(pkg.version)
|
|
19
|
+
.option('--cwd <path>', 'project directory (default: process.cwd())')
|
|
20
|
+
.option('--verbose', 'verbose logging')
|
|
21
|
+
.option('--dry-run', 'print actions without executing destructive steps');
|
|
22
|
+
(0, build_1.registerBuildCommand)(program);
|
|
23
|
+
(0, init_1.registerInitCommand)(program);
|
|
24
|
+
(0, keystore_1.registerKeystoreCommand)(program);
|
|
25
|
+
(0, doctor_1.registerDoctorCommand)(program);
|
|
26
|
+
program
|
|
27
|
+
.parseAsync(process.argv)
|
|
28
|
+
.catch((err) => {
|
|
29
|
+
console.error(kleur_1.default.red('\nlocal-expo-build failed:'));
|
|
30
|
+
console.error(err?.stack || err?.message || err);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
});
|
|
33
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.registerBuildCommand = registerBuildCommand;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const ctx_1 = require("../util/ctx");
|
|
9
|
+
const log_1 = require("../util/log");
|
|
10
|
+
const sdkDetect_1 = require("../core/sdkDetect");
|
|
11
|
+
const prebuild_1 = require("../core/prebuild");
|
|
12
|
+
const pinGradle_1 = require("../core/pinGradle");
|
|
13
|
+
const bumpVersion_1 = require("../core/bumpVersion");
|
|
14
|
+
const setupSigning_1 = require("../core/setupSigning");
|
|
15
|
+
const gradleRun_1 = require("../core/gradleRun");
|
|
16
|
+
const syncEasVersion_1 = require("../core/syncEasVersion");
|
|
17
|
+
const keystore_1 = require("../core/keystore");
|
|
18
|
+
function registerBuildCommand(program) {
|
|
19
|
+
const build = program.command('build').description('Build commands');
|
|
20
|
+
build
|
|
21
|
+
.command('android')
|
|
22
|
+
.description('Build a local Android APK or AAB')
|
|
23
|
+
.option('--apk', 'build APK (assembleRelease)')
|
|
24
|
+
.option('--aab', 'build AAB (bundleRelease) — default')
|
|
25
|
+
.option('--profile <profile>', 'EAS profile for versionCode fetch', 'production')
|
|
26
|
+
.option('--clean', 'pass --clean to expo prebuild')
|
|
27
|
+
.option('--no-bump', 'skip version bump')
|
|
28
|
+
.option('--no-sync', 'skip EAS versionCode sync after build')
|
|
29
|
+
.option('--no-prebuild', 'skip expo prebuild step')
|
|
30
|
+
.action(async (opts, cmd) => {
|
|
31
|
+
const ctx = (0, ctx_1.getCtx)(cmd);
|
|
32
|
+
const task = opts.apk ? 'assembleRelease' : 'bundleRelease';
|
|
33
|
+
const kind = task === 'bundleRelease' ? 'AAB' : 'APK';
|
|
34
|
+
log_1.log.step(`local-expo-build android (${kind})`);
|
|
35
|
+
log_1.log.dim('Local build · runs on your machine · saves an EAS cloud build credit');
|
|
36
|
+
log_1.log.dim(`cwd: ${ctx.cwd}`);
|
|
37
|
+
if (ctx.dryRun) {
|
|
38
|
+
log_1.log.warn('DRY RUN — no files modified, no Gradle build executed.');
|
|
39
|
+
}
|
|
40
|
+
const sdk = (0, sdkDetect_1.detectExpoSdk)(ctx.cwd);
|
|
41
|
+
log_1.log.ok(`Detected Expo SDK ${sdk.major} (${sdk.raw})`);
|
|
42
|
+
if (opts.prebuild !== false) {
|
|
43
|
+
log_1.log.step('1/6 expo prebuild');
|
|
44
|
+
if (ctx.dryRun) {
|
|
45
|
+
log_1.log.dim(`[dry-run] would run: expo prebuild --platform android${opts.clean ? ' --clean' : ''}`);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
await (0, prebuild_1.prebuild)({ cwd: ctx.cwd, clean: Boolean(opts.clean) });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
log_1.log.dim('Skipping prebuild (--no-prebuild)');
|
|
53
|
+
}
|
|
54
|
+
log_1.log.step('2/6 pin Gradle wrapper');
|
|
55
|
+
if (ctx.dryRun) {
|
|
56
|
+
log_1.log.dim(`[dry-run] would pin Gradle wrapper for SDK ${sdk.major} (see src/core/pinGradle.ts)`);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
(0, pinGradle_1.pinGradle)({ cwd: ctx.cwd, sdk: sdk.major });
|
|
60
|
+
}
|
|
61
|
+
if (opts.bump !== false) {
|
|
62
|
+
log_1.log.step('3/6 bump version');
|
|
63
|
+
if (ctx.dryRun) {
|
|
64
|
+
log_1.log.dim(`[dry-run] would fetch next versionCode from EAS (profile=${opts.profile}) and write app.json + build.gradle`);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
(0, bumpVersion_1.bumpVersion)({ cwd: ctx.cwd, profile: opts.profile });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
log_1.log.dim('Skipping version bump (--no-bump)');
|
|
72
|
+
}
|
|
73
|
+
log_1.log.step('4/6 ensure keystore + inject signing config');
|
|
74
|
+
if (ctx.dryRun) {
|
|
75
|
+
log_1.log.dim('[dry-run] would ensure keystore.properties + .jks present, then inject release signingConfig into build.gradle');
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
await (0, keystore_1.ensureKeystore)(ctx.cwd);
|
|
79
|
+
(0, setupSigning_1.setupSigning)({ cwd: ctx.cwd });
|
|
80
|
+
}
|
|
81
|
+
log_1.log.step(`5/6 gradle ${task}`);
|
|
82
|
+
let artifact = '';
|
|
83
|
+
if (ctx.dryRun) {
|
|
84
|
+
const isWin = process.platform === 'win32';
|
|
85
|
+
const wrapper = isWin ? 'gradlew.bat' : './gradlew';
|
|
86
|
+
log_1.log.dim(`[dry-run] would run (cwd=android/): ${wrapper} ${task}`);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
artifact = await (0, gradleRun_1.gradleRun)({ cwd: ctx.cwd, task });
|
|
90
|
+
}
|
|
91
|
+
if (opts.sync !== false) {
|
|
92
|
+
log_1.log.step('6/6 sync EAS versionCode');
|
|
93
|
+
if (ctx.dryRun) {
|
|
94
|
+
log_1.log.dim('[dry-run] would POST new versionCode to api.expo.dev/graphql (non-fatal on failure)');
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
try {
|
|
98
|
+
await (0, syncEasVersion_1.syncEasVersion)({ cwd: ctx.cwd });
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
log_1.log.warn(`EAS sync failed (non-fatal): ${err?.message || err}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
log_1.log.dim('Skipping EAS sync (--no-sync)');
|
|
107
|
+
}
|
|
108
|
+
log_1.log.step('Done');
|
|
109
|
+
if (ctx.dryRun) {
|
|
110
|
+
log_1.log.ok(`DRY RUN complete — 6 steps shown for ${kind}. Re-run without --dry-run to actually build.`);
|
|
111
|
+
}
|
|
112
|
+
else if (fs_1.default.existsSync(artifact)) {
|
|
113
|
+
const sizeMb = (fs_1.default.statSync(artifact).size / 1024 / 1024).toFixed(2);
|
|
114
|
+
log_1.log.ok(`Build complete (${kind}, ${sizeMb} MB):\n ${artifact}`);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
log_1.log.warn(`Build finished but artifact not found at ${artifact}`);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=build.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"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"}
|