Skip to content
Mobile
Skill

/local-build

Build a release APK and a simulator .app file locally for a React Native Expo app using gradlew and xcodebuild. Use when the user wants to produce installable artifacts without EAS (for e2e testing, sharing a build with a teammate, or quick iteration on a real device/simulator).

BOOST
From plugin
code-with-beto-skills
1434 skills
Install
$ npx -y skills add Code-with-Beto/skills --skill local-build --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/local-build

Context preview

The summary Claude sees to decide when to auto-load this skill.

Build a release APK and a simulator .app file locally for a React Native Expo app using gradlew and xcodebuild. Use when the user wants to produce installable artifacts without EAS (for e2e testing, sharing a build with a teammate, or quick iteration on a real device/simulator).

SKILL.md

local-build.SKILL.md
name: local-build
description: Build a release APK and a simulator .app file locally for a React Native Expo app using gradlew and xcodebuild. Use when the user wants to produce installable artifacts without EAS (for e2e testing, sharing a build with a teammate, or quick iteration on a real device/simulator). Generates two shell scripts wired up to the user's actual app name, scheme, and bundle identifier.

Local Build Workflow (APK and .app)

Overview

Generate two shell scripts that build release artifacts for an Expo / React Native project using the platform toolchains directly:

  • `scripts/build-android.sh` → runs `./gradlew assembleRelease` and copies the APK into `builds/`
  • `scripts/build-ios.sh` → runs `xcodebuild` for the iOS Simulator and copies the `.app` into `builds/`

Both scripts replace the previous artifact on each run, so the output path is always predictable.

**Why this exists:** Debug builds on an Expo project include `expo-dev-client` and launch the "Development Servers" picker instead of the app UI. For e2e runs, teammate previews, or real device installs, you need a **Release** build, and that's what these scripts produce.

Step 0: Confirm the project is a React Native / Expo app

Before doing anything else, verify the current directory is an Expo/React Native project:

1. Check for `package.json` with `expo` in `dependencies`. 2. Check for `app.json` or `app.config.ts` / `app.config.js`.

If neither exists, stop and tell the user the skill only works inside an Expo project.

Step 1: Read the app identity from the Expo config

The scripts need three values specific to the user's project. **Do not hardcode "YourApp" or assume any name.** Read them from the config.

Values to extract

| Value | Where to find it | Used for | |-------|------------------|----------| | `name` | `app.config.ts` / `app.config.js` / `app.json` → `expo.name` | iOS scheme and `.app` filename | | `slug` | `expo.slug` | APK filename (fallback: lowercased `name`) | | `ios.bundleIdentifier` | `expo.ios.bundleIdentifier` | `xcrun simctl launch` command | | `android.package` | `expo.android.package` | `adb shell am start` command |

How to read each config format

  • **`app.json`** — plain JSON, parse directly. The top-level key is usually `expo`.
  • **`app.config.ts` / `app.config.js`** — exports a function that returns the config. Read the file and extract the literal values from the returned object. Do not try to execute it. If a value is computed dynamically, ask the user what to use.

Verify the iOS scheme exists on disk

The iOS scheme file is generated from `expo.name` during `expo prebuild`. Confirm it's there before wiring it into the script:

ls ios/*.xcworkspace 2>/dev/null
ls ios/*.xcodeproj/xcshareddata/xcschemes/*.xcscheme 2>/dev/null

The `.xcworkspace` name and the `.xcscheme` filename (without the extension) are what you pass to `xcodebuild -workspace` and `-scheme`. They usually match `expo.name`, but if they don't, **use what's on disk**.

If `ios/` or `android/` doesn't exist yet, run:

npx expo prebuild

Then re-check the scheme.

Step 2: Create the Android build script

Write `scripts/build-android.sh`. Replace `<SLUG>` with the value from Step 1.

#!/usr/bin/env bash
set -euo pipefail

HERE="$(cd "$(dirname "$0")" && pwd)"
ROOT="$(cd "$HERE/.." && pwd)"
ARTIFACT="$ROOT/android/app/build/outputs/apk/release/app-release.apk"
DEST_DIR="$ROOT/builds"
DEST="$DEST_DIR/<SLUG>.apk"

echo "==> Building Android release APK"
cd "$ROOT/android"
./gradlew assembleRelease

if [ ! -f "$ARTIFACT" ]; then
  echo "Build did not produce $ARTIFACT" >&2
  exit 1
fi

mkdir -p "$DEST_DIR"
rm -f "$DEST"
cp "$ARTIFACT" "$DEST"

echo "Done: $DEST"

**Notes:**

  • `assembleRelease` is required. `assembleDebug` launches `expo-dev-client` instead of the app.
  • If the project has no release signing configured, Gradle falls back to the debug keystore, which is fine for local testing but **not** acceptable for Play Store submission.

Step 3: Create the iOS build script

Write `scripts/build-ios.sh`. Replace `<SCHEME>` with the value from Step 1 (it's the same string used for `-workspace <SCHEME>.xcworkspace` and `-scheme <SCHEME>`, and the output file is `<SCHEME>.app`).

#!/usr/bin/env bash
set -euo pipefail

HERE="$(cd "$(dirname "$0")" && pwd)"
ROOT="$(cd "$HERE/.." && pwd)"
ARTIFACT="$ROOT/ios/build/Build/Products/Release-iphonesimulator/<SCHEME>.app"
DEST_DIR="$ROOT/builds"
DEST="$DEST_DIR/<SCHEME>.app"

echo "==> Building iOS Simulator .app (Release)"
cd "$ROOT/ios"
xcodebuild \
  -workspace <SCHEME>.xcworkspace \
  -scheme <SCHEME> \
  -configuration Release \
  -sdk iphonesimulator \
  -derivedDataPath build \
  CODE_SIGNING_ALLOWED=NO \
  -quiet

if [ ! -d "$ARTIFACT" ]; then
  echo "Build did not produce $ARTIFACT" >&2
  exit 1
fi

mkdir -p "$DEST_DIR"
rm -rf "$DEST"
cp -R "$ARTIFACT" "$DEST"

echo "Done: $DEST"

**Notes:**

  • `-sdk iphonesimulator` produces a simulator `.app`. It will **not** run on a physical device.
  • `CODE_SIGNING_ALLOWED=NO` is safe for simulator builds and avoids the need for a signing identity.
  • A `.app` is a directory bundle, not a file, so `cp -R` is required.

Step 4: Make the scripts executable

chmod +x scripts/build-android.sh scripts/build-ios.sh

Step 5: Add `builds/` to `.gitignore`

Append this to `.gitignore` (check it's not already there):

# Local build artifacts
builds/

Step 6: Tell the user how to use them

Give the user this summary:

Built two scripts for you:

  ./scripts/build-android.sh   → builds/<slug>.apk
  ./scripts/build-ios.sh       → builds/<scheme>.app

Install and run:

  # Android (connected device or emulator)
  adb install -r builds/<slug>.apk
  adb shell am start -n <android.package>/.MainActivity

  # iOS Simulator
  xcrun simctl boot "iPhone 16" 2>/dev/null; open -a Simulator
  xcrun simctl install booted builds/<scheme>.app
Read more
Ships withcode-with-beto-skills

Professional agent skills and plugins to help mobile developers ship better apps faster with AI assistance.

Get the whole plugin
Stats
143
Stars
13
Forks
Maintained
Maintenance
TypeScript
Language
3mo ago
Last commit
8mo ago
Created
4h ago
Added

Repo: Code-with-Beto/skills

Other skills on code-with-beto-skills.