asc-shots-pipeline

asc-shots-pipeline

热门

使用 xcodebuild/simctl 进行构建与运行、AXe 执行 UI 操作、JSON 配置文件与 Plan 执行计划、基于 Koubou 的带壳合成(`asc screenshots frame`)以及截图上传(`asc screenshots upload`),编排 iOS 截图自动化流程。当用户提出自动化截图抓取、AXe 驱动的模拟器操作流、套壳合成或“截图到上传”全流程管线需求时使用。

946Star
50Fork
更新于 2026/7/31
SKILL.md
只读
名称
asc-shots-pipeline
描述

使用 xcodebuild/simctl 进行构建与运行、AXe 执行 UI 操作、JSON 配置文件与 Plan 执行计划、基于 Koubou 的带壳合成(`asc screenshots frame`)以及截图上传(`asc screenshots upload`),编排 iOS 截图自动化流程。当用户提出自动化截图抓取、AXe 驱动的模拟器操作流、套壳合成或“截图到上传”全流程管线需求时使用。

asc screenshots 流水线 (xcodebuild -> AXe -> frame -> asc)

当需要执行由 Agent 驱动的截图工作流(通过 Xcode CLI 工具构建和启动 App,使用 AXe 驱动 UI 操作,并使用 asc 上传截图)时,使用此 Skill。

当前功能范围

  • 已实现:构建/运行、AXe 计划抓取、带壳合成(Framing)以及上传。
  • 内置设备发现功能,可直接使用 asc screenshots list-frame-devices
  • asc CLI 中的本地截图自动化命令目前处于实验阶段。
  • 带壳合成功能锁定使用 Koubou 0.18.1,以确保输出效果可预测且一致。
  • 问题反馈与 Issue 提交:https://github.com/rorkai/App-Store-Connect-CLI/issues/new/choose

默认配置

  • 配置文件路径:.asc/shots.settings.json
  • 抓取计划路径:.asc/screenshots.json
  • 原始截图目录:./screenshots/raw
  • 带壳截图目录:./screenshots/framed
  • 默认合成外壳设备:iphone-air

1) 首先创建 settings JSON 配置文件

创建或更新 .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"
  }
}

如果打算跳过带壳合成步骤,请设置:

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

2) 在模拟器上构建并运行 App

使用 Xcode CLI 命令进行构建、安装与启动:

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"

如果 App 构建包路径与默认位置不一致,可以使用 xcodebuild -showBuildSettings 进行查看。

3) 使用 AXe 抓取截图(或使用 asc screenshots run

推荐优先使用基于 Plan(计划文件)的抓取方式:

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

在编写 Plan 计划文件时常用的 AXe 原语操作:

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"

最简 .asc/screenshots.json 示例:

{
  "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) 使用 asc screenshots frame 进行带壳合成

asc CLI 将带壳合成依赖锁定为 Koubou 0.18.1
在执行带壳合成步骤之前,请先安装并验证:

pip install koubou==0.18.1
kou --version  # 应输出 0.18.1
# 如果 Koubou 提示缺少设备外壳(device frames),请在联网状态下运行一次:
kou setup-frames

首先列出支持的外壳设备列表:

asc screenshots list-frame-devices --output json

对单张截图进行带壳合成(默认设备为 iphone-air):

asc screenshots frame \
  --input "./screenshots/raw/home.png" \
  --output-dir "./screenshots/framed" \
  --device "iphone-air" \
  --output json

支持的 --device 参数值:

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

5) 使用 asc 上传截图

在上传前生成并审查产物:

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"

对于已完成审查的多语言截图集,推荐优先使用 plan/apply 工作流,以便在上传前将线上已有的截图数量纳入计算:

asc screenshots plan --app "APP_ID" --version "1.2.3" --review-output-dir "./screenshots/review" --output json
asc screenshots apply --app "APP_ID" --version "1.2.3" --review-output-dir "./screenshots/review" --confirm --output json

从配置的源目录中上传(当开启带壳合成时,默认目录为 ./screenshots/framed):

asc screenshots upload \
  --version-localization "LOC_ID" \
  --path "./screenshots/framed" \
  --device-type "IPHONE_65" \
  --output json

在需要时,可在上传前先列表查看或校验:

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

Agent 行为规范

  • 执行命令前,始终先通过 --help 确认精准的参数 Flag。
  • 由于截图相关命令迭代更新较快,请通过 asc screenshots --help 重新确认命令路径。
  • 确保输出结果可预测:对于机器处理步骤,默认使用 JSON 格式。
  • 选择外壳设备前,优先运行 asc screenshots list-frame-devices --output json 查看可选设备。
  • 在上传之前,确保截图文件确实存在。
  • 尽量使用明确的完整 Flag 长参数(如 --app--output--version-localization 等)。
  • 将本地截图自动化功能视为实验性功能,并在向用户交付的说明笔记中予以特别标注。
  • 当需要对线上已有截图设置追加限制熔断防护时,针对审查后的批量截图使用 asc screenshots plan / asc screenshots apply
  • 如果带壳合成因版本错误失败,请重新安装指定版本的 Koubou:pip install koubou==0.18.1
  • 如果带壳合成因缺失设备外壳(device frames)而失败,请在联网环境下运行一次 kou setup-frames

6) 多语言截图抓取(可选)

请勿使用 xcrun simctl launch ... -e AppleLanguages 来切换语言。
-e 属于环境变量模式,无法稳定可靠地切换 App 语言。

在此流水线中,建议为每个模拟器 UDID 设置全系统级别的语言环境默认值。这种方式支持与 asc screenshots capture 配合使用(该命令会在内部重新启动 App)。

# 将每种语言映射到专属的模拟器 UDID。
# (可以通过 `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"            # 例如: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

如果是手动启动 App(而非在 asc screenshots capture 内部),请使用 App 启动参数:

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

7) 并行执行提升速度

在不同的模拟器 UDID 上并行运行各个语言的截图抓取:

#!/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..."

或者对 locale:udid 组合使用 xargs

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) 完整的多语言流水线示例

#!/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"

# 步骤 1:利用各模拟器专属的环境设置进行并行抓取
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

# 步骤 2:并行带壳合成
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

# 步骤 3:生成审查报告(单次运行,自动汇总所有语言)
asc screenshots review-generate \
  --framed-dir "$FRAMED_DIR" \
  --output-dir "./screenshots/review"

# 步骤 4:上传(按需为各个语言分别运行)
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