Amba

Ship

Take your Expo app from code-complete to live on the App Store and Google Play with one config and one command — build, submit, store listing, and release, with every manual store gate detected and surfaced.

amba ship takes a code-complete Expo app all the way to live on the App Store and Google Play. One config file (amba.ship.json), one command, six idempotent phases. Re-runs skip work that's already done, so the real loop is clear a gate → re-run until you're live.

It's honest about what no API can do. Several launch steps — creating the store app record, accepting the Apple Paid Apps agreement, screenshots, store review — are not automatable. amba ship detects each one and stops with the exact console action and URL, instead of pretending it's done.

amba ship runs where your app and its build toolchain live (your machine or CI). It drives the Expo build/submit toolchain and your store credentials — Amba never sees your signing keys.

Quickstart

npx @layers/amba ship init        # scaffold amba.ship.json
# edit the app ids, then:
npx @layers/amba ship --phase preflight   # check your setup (read-only)
npx @layers/amba ship --dry-run           # print the whole plan, change nothing
npx @layers/amba ship                     # run the pipeline

amba ship init writes a starter config you edit in place. --dry-run prints the full plan and makes no changes or network calls. --force re-runs phases already recorded as done. --phase <name> runs a single phase; --platform ios|android narrows to one store.

The phases

amba ship runs these in order, recording per-app progress under .amba/ship-state/ so a re-run resumes where it stopped:

PhaseWhat it does
preflightRead-only checks: build toolchain installed + authenticated, config valid, store-record gates surfaced. Never mutates.
monetizationVerifies your in-app purchase catalog is in sync with Amba's monetization control plane and stops if it has drifted. Skipped unless monetization.enabled is set.
buildBuilds the production binary for each platform. Idempotent — reuses a recorded build for the same version unless --force.
submitUploads to TestFlight (iOS) and your Play track (Android). Won't re-submit a build that's already been submitted.
metadataPushes the App Store listing and surfaces the store-side checklist (screenshots, privacy, data safety).
releasePromotes the Android build to production; for iOS, stops at the manual Submit for Review gate (Apple review isn't automatable).

Config

amba.ship.json carries non-secret identifiers and the name of the environment variable holding your build token — never secret values. Your store credentials stay in your build config, where the build runs.

{
  "version": 1,
  "app": {
    "slug": "unbury",
    "name": "Unbury",
    "version": "1.0.0",
    "bundleId": "ai.uselayers.unbury",
    "androidPackage": "ai.uselayers.unbury"
  },
  "build": { "profile": "production", "platforms": ["ios", "android"] },
  "ios": { "ascAppId": null },
  "android": { "track": "internal" },
  "monetization": { "enabled": false },
  "secrets": { "expoTokenEnvVar": "EXPO_TOKEN" }
}

ios.ascAppId stays null until your App Store Connect app record exists — ship treats a missing one as a gate on the submit phase. android.track is the Play track the submit phase lands on; release promotes it to production. Bump app.version and the binary phases (build → release) re-run automatically.

Manual gates

These are not bugs — they're steps the stores require a human to do. amba ship detects each and prints the exact action and console URL, then exits cleanly so you can clear it and re-run:

  • Create the App Store Connect and Google Play app records (no create API).
  • Apple Paid Apps agreement + tax/banking, and the Play payments profile.
  • Screenshots, App Privacy / Data safety, and content rating questionnaires.
  • The first Play upload, published once manually (a Google Play limitation).
  • Submit for Review and the final release on both stores.

Monetization

If your app sells anything, set monetization.enabled to true and ship verifies your in-app purchase catalog against Amba's monetization control plane before building — gating if it has drifted so you apply the catalog with amba monetization apply first. Configure the catalog once; amba ship keeps the store in step with it.

  • amba monetization — the in-app purchase catalog ship verifies.
  • Sites — host your privacy/support pages at a public URL the stores require.
  • CLI overview — install, auth, and project setup.

On this page