TaskJunkyDocs

Storeship

Ship any iOS or Android app's store presence from one config — capture, style, AI imagery, metadata, and publish to App Store Connect + Google Play.

What is Storeship?

Storeship ships any iOS or Android app's store presence from one config: capture screenshots, frame and caption them on your brand, generate AI marketing imagery, write metadata, and publish to App Store Connect and Google Play — no fastlane, no Firebase. The same pipeline powers a CLI, an HTTP API, and a web dashboard, so it scales from one command on your machine to a hosted service.

Install in one command

The fastest path installs the CLI and everything it needs (Python tooling via pipx) and prints guided next steps.

# macOS / Linux
curl -fsSL https://web.taskjunky.app/storeship/install.sh | sh
# Windows (PowerShell)
irm https://web.taskjunky.app/storeship/install.ps1 | iex

Or install manually — the CLI is a Python package (Python 3.9+):

pip install "storeship[all]"

Quickstart

storeship init scaffolds a storeship.yml plus a gitignored .env.local. Edit them, dry-run, then ship.

storeship init
storeship ship --config storeship.yml --platform ios --dry-run
storeship ship --config storeship.yml --platform ios

Or run the stages individually:

storeship capture     --config storeship.yml --platform android --out raw
storeship style       --config storeship.yml --raw raw --out styled
storeship metadata    --config storeship.yml --platform ios --dir metadata/en-US
storeship screenshots --config storeship.yml --platform ios --dir styled
storeship build       --config storeship.yml --platform ios
storeship upload      --config storeship.yml --platform ios --artifact App.ipa

Automatic screenshot capture

storeship capture boots a simulator/emulator and drives your app through a scripted sequence of screens, saving a PNG per step. On iOS it runs an Xcode UI test (macOS only). On Android it drives the emulator over adb — which works on Windows, macOS, and Linux, with no Xcode or Gradle. Each step lists actions (taps, swipes, text, key events) that run before the screenshot is taken.

android:
  package: com.example.app
  capture:
    avd: Pixel_8_API_34          # AVD to boot; omit to use a running device
    steps:
      - { name: "01_home", settle_ms: 2500 }
      - { name: "02_discover", actions: ["tap 540 1800"] }
      - { name: "03_search", actions: ["tap 320 140", "text vinyl", "key ENTER"] }

Platform support

StageiOSAndroid
capturemacOS only (xcodebuild UI test)Win / Mac / Linux (adb)
styleany OSany OS
metadata / screenshotsany OSany OS
buildmacOS only (xcodebuild)any OS (gradlew)
upload / publishany OSany OS

The Google Play path (style, metadata, screenshots, upload) is pure Python and runs anywhere — so you can ship Android entirely from a Windows PC. The iOS simulator, capture, and build rely on Apple's macOS-only toolchain (xcodebuild / simctl), so iOS requires a Mac or a cloud-Mac CI runner.

Configuration

One YAML describes a project. Secrets are referenced by path or ${ENV} var, never inlined — they live in .env.local, auto-loaded from the nearest directory upward.

name: What's Spinning
repo: ~/developer/whatisspinning
locales: [en-US]

ios:
  bundle_id: com.example.app
  scheme: App
  metadata_dir: fastlane/metadata/en-US
  screenshots: [fastlane/screenshots/en-US]
  asc:
    key_id: "${ASC_KEY_ID}"
    issuer_id: "${ASC_ISSUER_ID}"
    key_path: ~/.appstoreconnect/private_keys/AuthKey_XXXXXXXXXX.p8

android:
  package: com.example.app
  play:
    service_account_json: ~/.config/storeship/play-service-account.json

brand:
  paper: "#F4F1E9"
  ink:   "#16140E"
  accent: "#FF4A1F"

Connect from TaskJunky

Set up the integration in Settings → Storeship. Your connection points TaskJunky at a Storeship tenant; the credentials below are stored encrypted (AES‑256‑GCM) and are write‑only — once saved, a key is shown as •••• (saved) and never returned.

Connection

FieldRequiredWhat it is
API URLYesBase URL of your Storeship API (e.g. https://api.storeship.example.com).
Tenant API keyYesYour Storeship tenant key (sk_live_…). Used as x-api-key for every call.
Default projectNoProject id used when you trigger a ship from here. Required only to ship.

Narration

FieldRequiredWhat it is
ElevenLabs API keyOnly for narrationGenerates marketing‑video voiceovers. Leave blank if you don't use narration.

Admins can set an instance default (API URL + tenant key + default project) under the same panel; it applies to anyone who hasn't set their own connection. A personal connection always overrides the instance default.

Marketing-video narration

The app produces a silent replay clip; Storeship turns a config‑driven script into an ElevenLabs voiceover and muxes it onto the clip with ffmpeg — so ElevenLabs never lives in an app repo.

narration:
  voice_id: UgBBYS2sOqTuMpoF3BR0          # ElevenLabs voice
  model_id: eleven_multilingual_v2
  script: "Meet the app that…"            # or script_file: marketing/voiceover.txt
  api_key_env: ELEVEN_LABS_API_KEY        # the key itself is a secret, never inlined
  # music: marketing/bed.mp3              # optional background music, ducked under the voice
storeship narrate --config storeship.yml \
  --clip replay.mp4 --out marketing.mp4

Internal / TestFlight builds

Ship a non‑Release configuration (e.g. Internal, which defines INTERNAL_BUILD) straight to TestFlight for dogfooding — binary only, no store‑listing changes.

ios:
  internal:
    scheme: App Internal      # falls back to `scheme` if omitted
    configuration: Internal
storeship ship --config storeship.yml --internal

Secrets & encryption

Keys are never committed. There are three places config and secrets live:

WhereHoldsCommitted?
Project config (storeship.yml)non‑secret settings + ${ENV} / api_key_env referencesYes (no secret values)
.env.locallocal‑CLI secret values (ASC_*, ELEVEN_LABS_API_KEY, …)No — gitignored
Vault (DB)hosted secrets: ASC .p8, Play JSON, ElevenLabs keyNo — Fernet‑encrypted at rest

Vault secrets are encrypted with the STORESHIP_SECRET master key (server env only), so database access alone can't read or reuse them. App Store Connect API keys can't be created programmatically — mint the .p8 once in App Store Connect, then save it once at the developer level and every project inherits it.

Full config schema, the multi-tenant SaaS API, and the web dashboard live in the storeship repository.

On this page