| name | ns-ios-deploy-app |
| description | One-shot NowStack Mobile iOS deployment with asc App Store Connect CLI. Use when preparing TestFlight/App Store releases, uploading IPAs, syncing screenshots/metadata, validating review readiness, or submitting the iOS app without EAS Submit. |
ns-ios-deploy-app - App Store Connect deployment for NowStack Mobile
Ship NowStack Mobile to App Store Connect with `asc` as the default Apple deployment tool.
This skill does not use EAS Submit for Apple uploads. The target flow is:
- Build or obtain a signed production
.ipa.
- Authenticate
asc with App Store Connect.
- Use
asc to resolve the app, upload/publish the build, sync screenshots/metadata where possible, validate readiness, and submit.
Build artifact generation is OS-aware: macOS may use local Xcode or local EAS, while Windows/Linux must load ns-setup-expo and use EAS cloud to produce the .ipa. App Store Connect automation belongs to asc.
<when_to_use>
Use this workflow when:
- Shipping NowStack Mobile to TestFlight or App Review.
- Preparing the first iOS App Store release.
- Re-running an iOS submit after metadata, screenshots, IAP, privacy, or build changes.
- The user says: "deploy iOS", "publish iOS", "submit to App Store", "use asc", "asccli", "App Store Connect CLI", or asks for the iOS store deployment skill.
Do NOT use this for:
- Android/Google Play submissions (use
ns-deploy-android-app).
- Local simulator/dev builds (
npm run ios, npx expo start).
- EAS Submit unless the user explicitly overrides this skill.
- Creating or submitting real App Store resources without a dry run and explicit confirmation.
</when_to_use>
<state_variables>
Persist these values across the workflow:
| Variable | Type | Description |
|---|
{app_name} | string | App name from SiteConfig.title / Expo config. |
{slug} | string | Expo slug and URL scheme from SiteConfig.slug. |
{bundle_id} | string | iOS bundle identifier from SiteConfig.bundleId and ios.bundleIdentifier. |
{apple_team_id} | string | Apple Developer Team ID from SiteConfig.appleTeamId. |
{app_store_app_id} | string | Numeric App Store Connect app ID / Apple ID. |
{version} | string | App Store version string, usually from mobile-app/package.json / Expo config. |
{build_id} | string | App Store Connect build ID after upload/processing. |
{ipa_path} | string | Signed production .ipa path to upload. |
{convex_prod_url} | string | Production Convex cloud URL for EXPO_PUBLIC_CONVEX_URL. |
{convex_prod_site_url} | string | Production Convex site URL for EXPO_PUBLIC_CONVEX_SITE_URL. |
{iap_product_id} | string | Apple IAP product ID from SiteConfig.payment.iapProductId. |
{asc_key_id} | string | App Store Connect API key ID. Secret metadata; do not commit. |
{asc_issuer_id} | string | App Store Connect API issuer ID. Secret metadata; do not commit. |
{asc_p8_path} | string | Local path to the .p8 API private key. Never commit. |
{dry_run} | boolean | If true, print plans and run dry-run commands only. |
| </state_variables> | | |
<critical_safety>
- Never commit App Store Connect
.p8 keys, Apple account passwords, app-specific passwords, certificates, provisioning profiles, export option files with secrets, or .env files containing secrets.
- Never print full secret values. Mask key IDs, issuer IDs, tokens, and file paths when summarizing.
- Do not run
asc publish appstore --submit --confirm, asc review submit --confirm, or any asc web ... --confirm command until the user explicitly confirms the app ID, bundle ID, Apple team, version, IPA path, production Convex URLs, screenshots, metadata, and IAP product.
- Prefer
asc dry runs and validation before mutation.
- Treat
asc web ... commands as optional escape hatches for App Store Connect web-only gaps. Use them only when the user is knowingly operating with their Apple session.
</critical_safety>
Run these checks from the repository root unless noted. Stop if deploy-relevant state is dirty or placeholders would ship.
gh repo view --json nameWithOwner,defaultBranchRef || true
git status --short --branch
node --version
npm --version
command -v asc && asc version
asc auth status --validate || asc auth doctor || true
cd mobile-app && npx expo config --type public
Read and verify these files:
site-config.ts
mobile-app/app.config.ts
mobile-app/package.json
mobile-app/lib/iap.ts
convex/payments/iap.ts
docs/templates/app-store-metadata.example.json
docs/templates/store-screenshot-manifest.example.json
Confirm:
SiteConfig.bundleId equals ios.bundleIdentifier in generated Expo config.
SiteConfig.slug equals the Expo scheme used by screenshot/deep-link automation.
SiteConfig.appleTeamId belongs to the intended Apple Developer team.
SiteConfig.easProjectId being placeholder is not a blocker if this flow is not using EAS.
- Production Convex URLs are known before the IPA is built.
SiteConfig.payment.iapProductId, StoreKit config, and convex/payments/iap.ts are aligned.
- Screenshots exist, are fresh, and passed AI visual QA.
- App Store metadata/review details/privacy/IAP status are ready enough for
asc validate.
<install_and_auth>
If asc is missing, install it:
brew install asc
or:
curl -fsSL https://asccli.sh/install | bash
If {asc_key_id}, {asc_issuer_id}, or {asc_p8_path} are unknown, run the ns-find-asc-credentials skill first — it locates .p8 keys on disk, reads the issuer ID from the user's signed-in browser session (App Store Connect web UI, via dev-browser CDP), and verifies the key is active before login.
Authenticate with an App Store Connect API key:
asc auth login \
--name "NowStack" \
--key-id "KEY_ID" \
--issuer-id "ISSUER_ID" \
--private-key "/secure/local/AuthKey_KEY_ID.p8" \
--network
For CI/headless shells where keychain access is not available:
asc auth login \
--bypass-keychain \
--name "NowStack CI" \
--key-id "KEY_ID" \
--issuer-id "ISSUER_ID" \
--private-key "/secure/local/AuthKey_KEY_ID.p8"
Then validate:
asc auth status --validate
asc auth doctor
asc apps list --output table
</install_and_auth>
<app_store_connect_setup>
Use asc as the Apple control plane:
asc apps list --output table to resolve {app_store_app_id}.
asc status --app "{app_store_app_id}" --output table for release state.
asc localizations list --app "{app_store_app_id}" for metadata/localization status.
asc screenshots list --app "{app_store_app_id}" for store media status.
asc validate --app "{app_store_app_id}" --version "{version}" --platform IOS --output table for readiness.
asc validate iap --app "{app_store_app_id}" --output table when IAP is enabled.
asc validate subscriptions --app "{app_store_app_id}" --output table if subscriptions are used.
Manual or web-session gates that may still exist:
- Apple Developer enrollment and team/legal agreements.
- First app record creation if no public/API path is available for the current account.
- App Privacy publish state if public API validation cannot prove it.
- First-review IAP selection if Apple requires selecting the IAP on the app-version page.
</app_store_connect_setup>
<metadata_and_screenshot_automation>
Before binary publish or review submission, run .agents/skills/ns-generate-store-screenshots and require its manifest to be upload-ready.
Automation target:
ns-generate-store-screenshots creates iOS PNGs and manifest.json.
docs/templates/app-store-metadata.example.json remains the repo source template for copy/review details.
asc is the preferred tool for App Store Connect metadata, localizations, screenshots, build upload, validation, and submission.
- Keep raw screenshots separate from framed marketing screenshots. Upload only the user-approved set.
</metadata_and_screenshot_automation>
<build_ipa>
This skill needs a signed production .ipa. Detect the OS before choosing the build path:
uname -s || node -p "process.platform"
- macOS / Darwin: local Xcode/Expo or local EAS builds are allowed when signing/tooling are ready. Prefer local/Xcode or Xcode Cloud when the user wants no EAS cloud credits.
- Windows/Linux: do not attempt local iOS production builds. Load/follow
.agents/skills/ns-setup-expo/SKILL.md, verify eas-cli, eas whoami, linked easProjectId, and expo-doctor, then use EAS cloud to produce the .ipa.
Local Expo/Xcode path:
cd mobile-app
npm run env:prod
npx expo prebuild --clean --platform ios
Then archive/export with Xcode or xcodebuild. Keep ExportOptions.plist and signing material free of secrets and confirm the exact workspace/scheme before running destructive clean/archive steps.
Optional Xcode Cloud path:
asc xcode-cloud run --app "{app_store_app_id}" --workflow "CI" --branch "main" --wait
Windows/Linux required cloud path, and optional macOS fallback only if the user explicitly allows EAS cloud for build artifact generation:
cd mobile-app
npm run env:prod
npx eas build --platform ios --profile production --non-interactive
Do not use npx eas submit. Once the .ipa exists, deployment continues with asc.
</build_ipa>
<asc_publish_flow>
Before any mutation, run a dry run:
asc publish appstore \
--app "{app_store_app_id}" \
--ipa "{ipa_path}" \
--version "{version}" \
--submit \
--dry-run \
--output table
If the dry run is clean and the user confirms:
asc publish appstore \
--app "{app_store_app_id}" \
--ipa "{ipa_path}" \
--version "{version}" \
--submit \
--confirm
Add --wait when the command should wait for build processing before attaching/submitting:
asc publish appstore \
--app "{app_store_app_id}" \
--ipa "{ipa_path}" \
--version "{version}" \
--submit \
--wait \
--confirm
If the user wants a checkpoint before review submission, stage first:
asc release stage \
--app "{app_store_app_id}" \
--version "{version}" \
--build "{build_id}" \
--metadata-dir "./metadata/version/{version}" \
--dry-run \
--output table
asc release stage \
--app "{app_store_app_id}" \
--version "{version}" \
--build "{build_id}" \
--metadata-dir "./metadata/version/{version}" \
--confirm
Then submit explicitly:
asc review submit --app "{app_store_app_id}" --version "{version}" --build "{build_id}" --dry-run --output table
asc review submit --app "{app_store_app_id}" --version "{version}" --build "{build_id}" --confirm
</asc_publish_flow>
<post_submit_checklist>
After asc publish appstore or asc review submit succeeds:
- Run
asc status --app "{app_store_app_id}" --output table.
- Run
asc submit status --version-id "{version_id}" or asc submit status --id "{submission_id}" if available.
- Confirm the build appears in TestFlight/App Store Connect.
- Resolve processing, export compliance, App Privacy, age rating, pricing/availability, screenshot, review-detail, IAP, or subscription warnings.
- Test Apple Sign In, onboarding, Convex connectivity, paywall, purchase restore/finish behavior, and account deletion.
- Record the app ID, version, build ID, submission ID, IPA path, release notes, and
asc command outputs in the release summary.
- Restore dev env if
npm run env:prod changed local files:
cd mobile-app
npm run env:dev
</post_submit_checklist>
<failure_modes>
asc is missing: install with brew install asc or the install script.
asc auth status --validate fails: API key ID, issuer ID, private key path, keychain storage, or network access is wrong.
- Build uploads to the wrong app:
{app_store_app_id}, bundle ID, or Apple team is mismatched.
asc publish appstore cannot attach/submit: build processing is incomplete; rerun with --wait or monitor with asc status.
asc validate blocks submission: fix metadata, screenshots, review details, content rights, encryption, app availability, App Privacy, IAP, or subscription readiness.
- App Review rejects payments: iOS production uses Stripe instead of Apple IAP, or IAP/subscription products are incomplete.
- First-review IAP/subscription gaps: use
asc validate iap, asc validate subscriptions, and the relevant attach/review commands; some first-time selections may still require App Store Connect UI or asc web ... with user approval.
- Production app points at dev backend: the IPA was built before production Convex URLs were set.
- Credentials leak risk:
.p8, passwords, app-specific passwords, or signing materials were stored in tracked files.
</failure_modes>
<success_metrics>
- A signed production
.ipa exists for the intended bundle ID/team.
asc auth status --validate succeeds.
asc publish appstore --dry-run or asc review submit --dry-run is clean before mutation.
asc publish appstore --submit --confirm or asc review submit --confirm completes for the intended app/version.
- App Store Connect/TestFlight shows the build under the intended app/version.
- IAP product ID, bundle ID, Convex prod URLs, privacy/support URLs, screenshots, review details, and metadata are aligned.
- No secrets are committed or printed.
</success_metrics>