Going Live
Releasing with the asc CLI
The manual checklist walks you through shipping ShipThatApp with the App Store Connect web console and Xcode's Organizer. That works — but it's a maze of point-and-click steps that you can't diff, can't replay, and can't run from CI. The automation track does the same flow from your terminal with asc, a fast, scriptable App Store Connect CLI. This page is its foundation: install asc, authenticate it, and learn the handful of flags and idioms every later command leans on.
asc does not replace the manual guide — it automates it. Everything in the shipping checklist about production secrets, the AI proxy, account deletion, Restore Purchases, and the camera usage string still applies. asc is the layer that turns "archive, upload, submit" into a command you can run twice and get the same result.
What asc talks to
asc is a client for Apple's App Store Connect API. It authenticates with an API key you generate in App Store Connect — not your Apple ID password — so it never touches your personal credentials and is safe to run unattended in CI. The local build commands (asc xcode …) wrap xcodebuild and are macOS-only; everything else is pure API and runs anywhere.
Why the CLI Over the Web Console
The web console is fine for a one-off. The CLI earns its keep the moment you ship more than once:
- Reproducible. A release becomes a script you can read, review, and re-run. No "wait, which dropdown did I change last time?"
- Scriptable & CI-friendly. API-key auth and environment variables mean GitHub Actions or Xcode Cloud can archive, upload, and submit without a human clicking through Safari.
- Fast. No page loads, no spinners —
asc builds listanswers in the time it takes a browser tab to open. - Diffable. Metadata, pricing, and localizations can live as files in your repo (see Metadata & localizations, authored alongside this track), so changes show up in pull requests.
- Deterministic artifacts.
asc xcode archive/exportproduce an.xcarchiveand.ipaat exact paths you choose, which the upload and submit commands consume directly.
You'll still open the console occasionally — to eyeball a build in TestFlight or read a rejection — but the shipping path itself becomes code.
Install asc
The recommended install is via Homebrew, using the project's tap:
brew install rudrankriyam/tap/asc
There's also an install script for macOS and Linux:
curl -fsSL https://asccli.sh/install | bash
Windows users (and anyone who wants a pinned version) can download signed binaries from the GitHub releases page.
Confirm the binary works before you wire up auth:
asc version
asc --help
asc version should print the version you installed (this guide is written against 2.2.0). asc --help prints the full command map — auth and getting-started commands at the top, then app management, TestFlight & builds, review & release, monetization, and signing.
The local-build commands need a Mac
asc xcode archive and asc xcode export shell out to xcodebuild, so they only execute on macOS — but the subcommands are visible on every platform so docs and CI definitions stay consistent. The API commands (apps, builds, publish, status, metadata, …) work on any OS.
Create an App Store Connect API Key
asc authenticates with an App Store Connect API key — a three-part credential:
- a Key ID (e.g.
ABC123DEFG), - an Issuer ID (a UUID for your account),
- and a private key file (
AuthKey_XXXX.p8) that you download once.
Generate one at appstoreconnect.apple.com/access/integrations/api:
- Go to Users and Access → Integrations → App Store Connect API.
- Create a key with a role that covers what you'll do. App Manager is enough to manage builds, versions, and submissions; Admin is required for some account-wide operations.
- Note the Issuer ID (top of the page) and the new key's Key ID.
- Download the
.p8file immediately — Apple lets you download it exactly once. Store it somewhere safe (e.g.~/.asc/AuthKey_ABC123.p8); if you lose it you must revoke the key and make a new one.
Treat the .p8 like a password
The private key grants programmatic access to your App Store Connect account. Never commit it to git, never paste it into a screenshot, and prefer a key scoped to the narrowest role that does the job. For CI, store it as an encrypted secret (see the env-var section below), not as a file in the repo.
Authenticate with asc auth login
asc auth login registers the key and stores it in your system keychain (with a config-file fallback), so commands keep working even if you later move the original .p8:
asc auth login \
--name "ShipThatApp" \
--key-id "ABC123DEFG" \
--issuer-id "12345678-abcd-1234-abcd-123456789012" \
--private-key ~/.asc/AuthKey_ABC123.p8 \
--network
The flags, all confirmed via asc auth login --help:
--name— a friendly label for this profile (you can register several and switch between them withasc auth switch).--key-id,--issuer-id,--private-key— the three parts of the credential.--key-type—team(default) orindividual.--network— validate the credential against the API as part of login, so you catch a bad key immediately instead of on your first real command.
For a CI runner or any headless machine where the keychain isn't available, store credentials in a config file instead:
asc auth login \
--bypass-keychain \
--name "ShipThatApp-CI" \
--key-id "ABC123DEFG" \
--issuer-id "12345678-abcd-1234-abcd-123456789012" \
--private-key ~/.asc/AuthKey_ABC123.p8
--bypass-keychain writes to ~/.asc/config.json (restricted permissions); add --local to write a repo-scoped ./.asc/config.json instead, which takes precedence over everything else when present.
Verify: asc auth status and asc doctor
Two commands tell you whether auth is healthy. Run both after your first login:
asc auth status --validate
asc doctor
asc auth status shows the active profile and every stored credential; --validate performs a live network check on each one. asc doctor (an alias for asc auth doctor) runs a broader health check across keychain availability, config files, stored profiles, the private-key files, and environment variables — and will tell you exactly what's missing or misconfigured. If something's wrong, start here:
asc doctor --output json
A clean doctor is your green light
Before you run any release command, asc auth status --validate should report your profile as valid and asc doctor should come back clean. If doctor flags a keychain problem on a CI box, switch to the env-var or --bypass-keychain flow below.
Environment-Variable Auth for CI
In CI you don't run an interactive login — you inject the credential as environment variables. asc reads these as a fallback for any field a stored profile doesn't supply:
export ASC_KEY_ID="ABC123DEFG"
export ASC_ISSUER_ID="12345678-abcd-1234-abcd-123456789012"
export ASC_PRIVATE_KEY_PATH="$HOME/.asc/AuthKey_ABC123.p8"
If your CI provider can't write a file to disk, pass the key inline as base64 instead of a path:
export ASC_PRIVATE_KEY_B64="LS0tLS1CRUdJTi..."
The credential-resolution order is: the selected profile (keychain or config) first, then environment variables for any missing fields. Set ASC_STRICT_AUTH=true (or pass --strict-auth) if you want asc to fail when sources are mixed, rather than silently filling gaps from the environment — a good safety net in CI so you know exactly which credential is in play.
Set a default app with ASC_APP_ID
Almost every release command takes an --app flag — the App Store Connect app ID, bundle ID, or exact app name. Set it once as an environment variable and you can omit --app everywhere:
export ASC_APP_ID="1234567890"
With ASC_APP_ID exported, asc status and asc publish appstore (and the rest) fall back to it when --app is absent. The flag, when given, always wins.
Don't echo secrets into your shell history or logs
In CI, set ASC_PRIVATE_KEY_B64 (or the others) through your provider's encrypted-secrets mechanism, not an export in a committed script. Locally, prefer the keychain (asc auth login) so the key material never sits in a plaintext env var at all.
Output Formats, Confirmation, and Pagination
A few global idioms recur in every later page. Learn them once here.
--output table | json | markdown
asc is TTY-aware: when stdout is a terminal it prints human-friendly output, and when piped it defaults to JSON so you can parse it. Be explicit when it matters:
asc apps list --output table # human-readable columns
asc apps list --output json --pretty # parseable, indented
asc builds list --app "1234567890" --output markdown # paste into a PR or doc
Use json (optionally --pretty) in scripts, table when you're reading at the keyboard, and markdown when you're pasting results into a pull request or these docs. You can set a personal default with ASC_DEFAULT_OUTPUT, but an explicit --output always wins.
--confirm gates destructive operations
Anything that mutates App Store Connect — submitting for review, applying a price change, deleting a tester — requires --confirm. Without it, asc plans the change and stops, which makes dry runs safe by default. The canonical ship command is the clearest example:
# Plans the publish + submit, but does nothing irreversible:
asc publish appstore --app "1234567890" --ipa app.ipa --version "1.0.0" --submit --dry-run
# Actually submits for review:
asc publish appstore --app "1234567890" --ipa app.ipa --version "1.0.0" --submit --confirm
--submit requires --confirm; many commands also accept --dry-run to preview the exact mutation sequence before you commit to it.
--paginate fetches every page
List commands page their results. When you need the whole set — every build, every tester — add --paginate:
asc builds list --app "1234567890" --paginate --output table
Discovering Commands
Two habits keep you accurate without guessing at flags:
--helpon anything. Every command and subcommand documents its own flags and shows worked examples. When in doubt, append--help—asc publish appstore --help,asc xcode archive --help,asc builds list --help. This is the source of truth; don't assume a flag exists until--helpconfirms it.asc docsships embedded guides you can read offline:
asc docs list # api-notes, reference, workflows
asc docs show workflows # task-based recipes for release/TestFlight/Xcode
asc docs show api-notes # API quirks: date formats, sandbox testers, finance
asc docs init (or asc init) can also drop an ASC.md quick reference into your repo so your whole team — and your AI assistant — works from the same command map.
The command spine you'll use to ship
The release path is just a few commands, each detailed on its own page in this track:
- Build locally:
asc xcode version …(set the version/build number),asc xcode archive,asc xcode export→ a deterministic.ipa. - Ship it:
asc publish appstore --app … --ipa … --version … --submit --confirm. - Watch it:
asc status --app …for the release dashboard,asc builds listfor processing state. - Stage without submitting:
asc release stage …andasc validate …for a readiness report.
Read Building & uploading with asc next, then Submitting & monitoring with asc.
Related
- Shipping to the App Store — the manual/GUI checklist this automation track mirrors. Read it first for the production secrets, Supabase, RevenueCat, AI proxy, and review-trap context that
ascdoes not change. - Building & uploading with asc —
asc xcodeversion/archive/export and uploading the IPA. - Submitting & monitoring with asc —
asc publish appstore,asc validate, and watching review status withasc status. - Configuration & Secrets — where the bundle id, product ids, and
Config.xcconfigvalues live.