asc-shots-pipeline

Orchestrate iOS screenshot automation from build through frame composition to App Store Connect upload. Build and launch apps on simulators with Xcode CLI tools, then drive UI interactions and capture screenshots using AXe plan files Frame screenshots deterministically with pinned Koubou 0.14.0, supporting six device types (iPhone Air, iPhone 17 Pro, iPhone 17 Pro Max, iPhone 16e, iPhone 17, Mac) Configure pipelines via JSON settings and capture plans; skip framing or upload stages as needed Capture multi-locale screenshots in parallel by mapping locales to dedicated simulator UDIDs, then batch-frame and upload to App Store Connect with per-locale version localization IDs

INSTALLATION
npx skills add https://github.com/rudrankriyam/app-store-connect-cli-skills --skill asc-shots-pipeline
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

$2b

Create or update .asc/shots.settings.json:

{

  "version": 1,

  "app": {

    "bundle_id": "com.example.app",

    "project": "MyApp.xcodeproj",

    "scheme": "MyApp",

    "simulator_udid": "booted"

  },

  "paths": {

    "plan": ".asc/screenshots.json",

    "raw_dir": "./screenshots/raw",

    "framed_dir": "./screenshots/framed"

  },

  "pipeline": {

    "frame_enabled": true,

    "upload_enabled": false

  },

  "upload": {

    "version_localization_id": "",

    "device_type": "IPHONE_65",

    "source_dir": "./screenshots/framed"

  }

}

If you intentionally skip framing, set:

  • "frame_enabled": false
  • "upload.source_dir": "./screenshots/raw"

2) Build and run app on simulator

Use Xcode CLI for build/install/launch:

xcrun simctl boot "$UDID" || true

xcodebuild \

  -project "MyApp.xcodeproj" \

  -scheme "MyApp" \

  -configuration Debug \

  -destination "platform=iOS Simulator,id=$UDID" \

  -derivedDataPath ".build/DerivedData" \

  build

xcrun simctl install "$UDID" ".build/DerivedData/Build/Products/Debug-iphonesimulator/MyApp.app"

xcrun simctl launch "$UDID" "com.example.app"

Use xcodebuild -showBuildSettings if the app bundle path differs from the default location.

3) Capture screenshots with AXe (or asc screenshots run )

Prefer plan-driven capture:

asc screenshots run --plan ".asc/screenshots.json" --udid "$UDID" --output json

Useful AXe primitives during plan authoring:

axe describe-ui --udid "$UDID"

axe tap --id "search_field" --udid "$UDID"

axe type "wwdc" --udid "$UDID"

axe screenshot --output "./screenshots/raw/home.png" --udid "$UDID"

Minimal .asc/screenshots.json example:

{

  "version": 1,

  "app": {

    "bundle_id": "com.example.app",

    "udid": "booted",

    "output_dir": "./screenshots/raw"

  },

  "steps": [

    { "action": "launch" },

    { "action": "wait", "duration_ms": 800 },

    { "action": "screenshot", "name": "home" }

  ]

}

4) Frame screenshots with asc screenshots frame

asc cli pins framing to Koubou 0.18.1.

Install and verify before running framing steps:

pip install koubou==0.18.1

kou --version  # expect 0.18.1

# If Koubou reports missing device frames, run once with network access:

kou setup-frames

List supported frame device values first:

asc screenshots list-frame-devices --output json

Frame one screenshot (defaults to iphone-air):

asc screenshots frame \

  --input "./screenshots/raw/home.png" \

  --output-dir "./screenshots/framed" \

  --device "iphone-air" \

  --output json

Supported --device values:

  • iphone-air (default)
  • iphone-17-pro
  • iphone-17-pro-max
  • iphone-16e
  • iphone-17
  • mac

5) Upload screenshots with asc

Generate and review artifacts before upload:

asc screenshots review-generate --framed-dir "./screenshots/framed" --output-dir "./screenshots/review"

asc screenshots review-open --output-dir "./screenshots/review"

asc screenshots review-approve --all-ready --output-dir "./screenshots/review"

Upload from the configured source directory (default ./screenshots/framed when framing is enabled):

asc screenshots upload \

  --version-localization "LOC_ID" \

  --path "./screenshots/framed" \

  --device-type "IPHONE_65" \

  --output json

List or validate before upload when needed:

asc screenshots sizes --output table

asc screenshots list --version-localization "LOC_ID" --output table

Agent behavior

  • Always confirm exact flags with --help before running commands.
  • Re-check command paths with asc screenshots --help because screenshot commands are evolving quickly.
  • Keep outputs deterministic: default to JSON for machine steps.
  • Prefer asc screenshots list-frame-devices --output json before selecting a frame device.
  • Ensure screenshot files exist before upload.
  • Use explicit long flags (--app, --output, --version-localization, etc.).
  • Treat screenshot-local automation as experimental and call it out in user-facing handoff notes.
  • If framing fails with a version error, re-install pinned Koubou: pip install koubou==0.18.1.
  • If framing fails because device frames are missing, run kou setup-frames once with network access.

6) Multi-locale capture (optional)

Do not use xcrun simctl launch ... -e AppleLanguages for localization.

-e is an environment variable pattern and does not reliably switch app language.

For this pipeline, use simulator-wide locale defaults per UDID. This works with

asc screenshots capture, which relaunches the app internally.

# Map each locale to a dedicated simulator UDID.

# (Create these simulators once with `xcrun simctl create`.)

declare -A LOCALE_UDID=(

  ["en-US"]="UDID_EN_US"

  ["de-DE"]="UDID_DE_DE"

  ["fr-FR"]="UDID_FR_FR"

  ["ja-JP"]="UDID_JA_JP"

)

set_simulator_locale() {

  local UDID="$1"

  local LOCALE="$2"            # e.g. de-DE

  local LANG="${LOCALE%%-*}"   # de

  local APPLE_LOCALE="${LOCALE/-/_}" # de_DE

  xcrun simctl boot "$UDID" || true

  xcrun simctl spawn "$UDID" defaults write NSGlobalDomain AppleLanguages -array "$LANG"

  xcrun simctl spawn "$UDID" defaults write NSGlobalDomain AppleLocale -string "$APPLE_LOCALE"

}

for LOCALE in "${!LOCALE_UDID[@]}"; do

  UDID="${LOCALE_UDID[$LOCALE]}"

  echo "Capturing $LOCALE on $UDID..."

  set_simulator_locale "$UDID" "$LOCALE"

  xcrun simctl terminate "$UDID" "com.example.app" || true

  asc screenshots capture \

    --bundle-id "com.example.app" \

    --name "home" \

    --udid "$UDID" \

    --output-dir "./screenshots/raw/$LOCALE" \

    --output json

done

If you launch manually (outside asc screenshots capture), use app launch arguments:

xcrun simctl launch "$UDID" "com.example.app" -AppleLanguages "(de)" -AppleLocale "de_DE"

7) Parallel execution for speed

Run one locale per simulator UDID in parallel:

#!/bin/bash

# parallel-capture.sh

declare -A LOCALE_UDID=(

  ["en-US"]="UDID_EN_US"

  ["de-DE"]="UDID_DE_DE"

  ["fr-FR"]="UDID_FR_FR"

  ["ja-JP"]="UDID_JA_JP"

)

capture_locale() {

  local LOCALE="$1"

  local UDID="$2"

  local LANG="${LOCALE%%-*}"

  local APPLE_LOCALE="${LOCALE/-/_}"

  echo "Starting $LOCALE on $UDID"

  xcrun simctl boot "$UDID" || true

  xcrun simctl spawn "$UDID" defaults write NSGlobalDomain AppleLanguages -array "$LANG"

  xcrun simctl spawn "$UDID" defaults write NSGlobalDomain AppleLocale -string "$APPLE_LOCALE"

  xcrun simctl terminate "$UDID" "com.example.app" || true

  asc screenshots capture \

    --bundle-id "com.example.app" \

    --name "home" \

    --udid "$UDID" \

    --output-dir "./screenshots/raw/$LOCALE" \

    --output json

  echo "Completed $LOCALE"

}

for LOCALE in "${!LOCALE_UDID[@]}"; do

  capture_locale "$LOCALE" "${LOCALE_UDID[$LOCALE]}" &

done

wait

echo "All captures done. Now framing..."

Or use xargs with locale:udid pairs:

printf "%s\n" \

  "en-US:UDID_EN_US" \

  "de-DE:UDID_DE_DE" \

  "fr-FR:UDID_FR_FR" \

  "ja-JP:UDID_JA_JP" | xargs -P 4 -I {} bash -c '

    PAIR="{}"

    LOCALE="${PAIR%%:*}"

    UDID="${PAIR##*:}"

    LANG="${LOCALE%%-*}"

    APPLE_LOCALE="${LOCALE/-/_}"

    xcrun simctl boot "$UDID" || true

    xcrun simctl spawn "$UDID" defaults write NSGlobalDomain AppleLanguages -array "$LANG"

    xcrun simctl spawn "$UDID" defaults write NSGlobalDomain AppleLocale -string "$APPLE_LOCALE"

    xcrun simctl terminate "$UDID" "com.example.app" || true

    asc screenshots capture --bundle-id "com.example.app" --name "home" --udid "$UDID" --output-dir "./screenshots/raw/$LOCALE" --output json

  '

8) Full multi-locale pipeline example

#!/bin/bash

# full-pipeline-multi-locale.sh

declare -A LOCALE_UDID=(

  ["en-US"]="UDID_EN_US"

  ["de-DE"]="UDID_DE_DE"

  ["fr-FR"]="UDID_FR_FR"

  ["es-ES"]="UDID_ES_ES"

  ["ja-JP"]="UDID_JA_JP"

)

DEVICE="iphone-air"

RAW_DIR="./screenshots/raw"

FRAMED_DIR="./screenshots/framed"

# Step 1: Parallel capture with per-simulator locale defaults

for LOCALE in "${!LOCALE_UDID[@]}"; do

  (

    UDID="${LOCALE_UDID[$LOCALE]}"

    LANG="${LOCALE%%-*}"

    APPLE_LOCALE="${LOCALE/-/_}"

    xcrun simctl boot "$UDID" || true

    xcrun simctl spawn "$UDID" defaults write NSGlobalDomain AppleLanguages -array "$LANG"

    xcrun simctl spawn "$UDID" defaults write NSGlobalDomain AppleLocale -string "$APPLE_LOCALE"

    xcrun simctl terminate "$UDID" "com.example.app" || true

    asc screenshots capture \

      --bundle-id "com.example.app" \

      --name "home" \

      --udid "$UDID" \

      --output-dir "$RAW_DIR/$LOCALE" \

      --output json

    echo "Captured $LOCALE"

  ) &

done

wait

# Step 2: Parallel framing

for LOCALE in "${!LOCALE_UDID[@]}"; do

  (

    asc screenshots frame \

      --input "$RAW_DIR/$LOCALE/home.png" \

      --output-dir "$FRAMED_DIR/$LOCALE" \

      --device "$DEVICE" \

      --output json

    echo "Framed $LOCALE"

  ) &

done

wait

# Step 3: Generate review (single run, aggregates all locales)

asc screenshots review-generate \

  --framed-dir "$FRAMED_DIR" \

  --output-dir "./screenshots/review"

# Step 4: Upload (run per locale if needed)

for LOCALE in "${!LOCALE_UDID[@]}"; do

  asc screenshots upload \

    --version-localization "LOC_ID_FOR_$LOCALE" \

    --path "$FRAMED_DIR/$LOCALE" \

    --device-type "IPHONE_65" \

    --output json

done
BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card