ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

App-Store-Connect-CLI 工作流实战:用 .asc/workflow.json 编排可复现的 Xcode→TestFlight 发布流水线

App-Store-Connect-CLI 工作流实战:用 .asc/workflow.json 编排可复现的 Xcode→TestFlight 发布流水线 【免费下载链接】App-Store-Connect-CLIFast, scriptable CLI for the App Store Connect API. Automate TestFlight, builds, submissions, signing, analytics, screenshots, subscriptions, and more项目地址https://gitcode.com/gh_mirrors/ap/App-Store-Connect-CLI点击查看免费下载本篇技术指南聚焦 App-Store-Connect-CLI 的高层工作流能力如何在asc workflow中组合已有的asc命令与任意 shell 命令将「取下一个构建号 → 注入部署元数据 → 归档 → 导出 IPA → 上传并分发到 TestFlight 组」这条已验证的本地发布链路固化为可重复运行的流水线。读完本文你将掌握.asc/workflow.json的完整格式、asc workflow validate/run/--resume的正确用法、步间输出传递与 JSON 路径提取、可恢复的上传分发拆分方式以及带固定延迟重试与单次超时的有界执行策略并能理解这些行为背后的源码级实现。三种高层工作流入口先选对路径docs/WORKFLOWS.md建议在动手写工作流前先明确目标再选择对应的高层命令面workflow surfaceasc publish appstore规范的 App Store 上架发布路径asc publish testflight规范的高层 TestFlight 发布路径asc workflow面向仓库特定流水线的用户自定义编排。asc workflow的定位是编排层当你已经确定了走哪条顶层路径之后用它把现有的asc命令和 shell 命令组合成可重复执行的发布流水线。命令行入口在 internal/cli/workflow/workflow.go提供run、validate、list三个子命令其底层执行器是一个独立、零业务依赖的internal/workflow包只依赖 Go 标准库与tidwall/jsonc用于支持 JSONC 注释。已验证的本地 Xcode → TestFlow 发布链路docs/WORKFLOWS.md中记录的这条链路已针对真实应用验证过是asc workflow的推荐起点其命令序列为asc builds next-build-number为指定版本选出下一个构建号asc xcode inject归档前把部署元数据写入生成的 Xcode plist/配置文件与资源路径asc xcode build针对明确的模拟器或设备目的地编译 schemeasc xcode archive生成确定性的.xcarchiveasc xcode export生成确定性的.ipaasc publish testflight --group ... --wait上传、等待处理完成并把构建加入 TestFlight 组目标为外部组、需要触发 beta 审核提交时追加--submit --confirm。下面逐段展开各环节的实操细节与参数约束。模拟器编译检查不改动项目文件的xcode build在不想改动项目文件的前提下做模拟器编译检查使用类型化目的地typed destination加显式无签名标志。此时--no-code-signing会以CODE_SIGNING_ALLOWEDNO覆盖签名行为。Derived data 默认落在 checkout 之外的稳定 asc 缓存路径当工作流需要特定产物目录时用--derived-data-path指定。--result-bundle-path用于在显式路径产出新的.xcresult包注意目标路径不得已存在asc 不会覆盖它。示例将目的地替换为主机上已安装的模拟器asc xcode build \ --project App.xcodeproj \ --scheme App \ --configuration Debug \ --destination platformiOS Simulator,nameiPhone 17 Pro Max,OS27.0 \ --no-code-signing \ --output json本地单元测试 / UI 测试xcode test本地单元或 UI 测试使用类型化测试命令。它把 Xcode 诊断信息保留在 stderr把结构化结果写到 stdout并保留.xcresult包供事后检查asc xcode test \ --project App.xcodeproj \ --scheme App \ --destination platformiOS Simulator,nameiPhone 17 Pro Max,OS27.0 \ --result-bundle-path .asc/artifacts/App-tests.xcresult \ --output json如需先产出测试产物再执行测试用--action build-for-testing生成测试产物再把安全发现的.xctestrun路径传给--action test-without-building。每个 action 都必须显式给出--destinationXcode 可能启动或唤醒所选模拟器/设备。该命令不会改动项目文件、上传产物或调用 App Store Connect。设备构建默认保留 Xcode 的签名行为除非显式传入--no-code-signing。归档后导出xcode export与export-options generateasc xcode export在省略--export-options时会自动生成归档专属的 App Store Connect 导出选项。它会选择一个唯一的、与归档相邻的路径且绝不覆盖已有文件命令结果会报告实际使用的路径。若希望在确定路径持久化一个 plist 供检查或复用应在归档后单独生成asc xcode export-options generate \ --archive-path .asc/artifacts/App.xcarchive \ --output-path .asc/export-options-app-store.plist \ --overwrite独立命令默认输出到.asc/export-options-app-store.plist若该文件已存在则必须加--overwrite。自动生成是跨平台的只读取归档元数据而手动签名解析仅在 DarwinmacOS上可用因为它要检查本机 Xcode 签名身份与 provisioning profile。向已注册设备导出 IPA 时使用现代 Xcode 方法名release-testing旧拼写ad-hoc已废弃asc xcode export \ --archive-path .asc/artifacts/App.xcarchive \ --ipa-path .asc/artifacts/App.ipa \ --method release-testing \ --signing-style manual \ --team-id TEAM_ID默认方法仍是app-store-connectrelease-testing永远产生本地导出且不能与--wait组合。对支持 PCCPasskey Credential/Carrier 等能力的多 target 应用或需要手动签名的应用把签名策略直接传给 export。asc 读取归档并匹配应用及其内嵌 target 已安装的 profile因此不需要profile UUID 标志或提交进仓库的 plistasc xcode export \ --archive-path .asc/artifacts/App.xcarchive \ --ipa-path .asc/artifacts/App.ipa \ --signing-style manual \ --team-id TEAM_ID同样的标志也适用于由本地发布流程拥有的归档与导出asc publish testflight \ --app APP_ID \ --workspace App.xcworkspace \ --scheme App \ --version 1.2.3 \ --group GROUP_ID \ --signing-style manual \ --team-id TEAM_ID注意约束显式--export-optionsplist 不能与--method、--signing-style或--team-id组合使用单独提供 plist 时它是权威配置。部署元数据注入.asc/deployment.jsonasc xcode inject的输入是一个注入清单manifest。创建.asc/deployment.json{ values: { bundle_id: com.example.app, app_name: Example, version: , build_number: }, outputs: [ { type: plist, path: ../Generated/Info.generated.plist, values: { CFBundleIdentifier: ${bundle_id}, CFBundleDisplayName: ${app_name}, CFBundleShortVersionString: ${version}, CFBundleVersion: ${build_number} } }, { type: text, path: ../Generated/Deployment.xcconfig, contents: PRODUCT_BUNDLE_IDENTIFIER ${bundle_id}\nMARKETING_VERSION ${version}\nCURRENT_PROJECT_VERSION ${build_number}\n }, { type: copy, source: ../Assets/AppIcon.appiconset/Contents.json, path: ../Generated/Assets.xcassets/AppIcon.appiconset/Contents.json } ] }让 Xcode 项目指向生成文件如Generated/Info.generated.plist或在构建配置中 includeGenerated/Deployment.xcconfig然后在归档前运行asc xcode inject填充发布专属值——这正是以前由 Fastlane 脚本负责的那部分工作。完整工作流示例testflight_beta在.asc/workflow.json中定义带环境变量、步间输出与 JSON 路径提取的完整流水线{ env: { APP_ID: 1234567890, PROJECT_PATH: App.xcodeproj, SCHEME: App, CONFIGURATION: Release, TESTFLIGHT_GROUP: Beta, VERSION: }, workflows: { testflight_beta: { description: Archive, export, upload, and distribute an app to a TestFlight group., steps: [ { name: validate_version, run: if [ -z \$VERSION\ ]; then echo \VERSION is required\ 2; exit 1; fi }, { name: resolve_next_build, run: asc builds next-build-number --app \$APP_ID\ --version \$VERSION\ --platform IOS --initial-build-number 1 --output json, outputs: { BUILD_NUMBER: $.nextBuildNumber } }, { name: inject_metadata, run: asc xcode inject --manifest .asc/deployment.json --set version\$VERSION\ --set build_number${steps.resolve_next_build.BUILD_NUMBER} --overwrite --output json, outputs: { GENERATED_FILES: $.outputs } }, { name: archive, run: asc xcode archive --project \$PROJECT_PATH\ --scheme \$SCHEME\ --configuration \$CONFIGURATION\ --archive-path \.asc/artifacts/App-$VERSION-${steps.resolve_next_build.BUILD_NUMBER}.xcarchive\ --clean --overwrite --xcodebuild-flag-destination --xcodebuild-flaggeneric/platformiOS --xcodebuild-flag-allowProvisioningUpdates --xcodebuild-flagMARKETING_VERSION$VERSION --xcodebuild-flagCURRENT_PROJECT_VERSION${steps.resolve_next_build.BUILD_NUMBER} --output json, outputs: { ARCHIVE_PATH: $.archive_path, VERSION: $.version, BUILD_NUMBER: $.build_number } }, { name: export, run: asc xcode export --archive-path ${steps.archive.ARCHIVE_PATH} --ipa-path \.asc/artifacts/App-$VERSION-${steps.archive.BUILD_NUMBER}.ipa\ --overwrite --timeout 10m --xcodebuild-flag-allowProvisioningUpdates --output json, outputs: { IPA_PATH: $.ipa_path, VERSION: $.version, BUILD_NUMBER: $.build_number } }, { name: publish, run: asc publish testflight --app \$APP_ID\ --ipa ${steps.export.IPA_PATH} --group \$TESTFLIGHT_GROUP\ --wait --poll-interval 10s --output json, outputs: { BUILD_ID: $.buildId, BUILD_NUMBER: $.buildNumber } } ] } } }运行方式分三步先校验再干跑预览最后真正执行asc workflow validate --output json asc workflow run --dry-run testflight_beta VERSION:1.2.3 asc workflow run testflight_beta VERSION:1.2.3KEY:VALUE形式的运行时参数会覆盖顶层env中同名变量internal/workflow/env.go的ParseParams同时支持KEY:VALUE与KEYVALUE取先出现的分隔符。使用要点与版本约束VERSION必须是该应用有效的下一个营销版本。若 App Store 上最新版本已处于READY_FOR_DISTRIBUTION复用同一版本可能导致 App Store Connect 拒绝上传TESTFLIGHT_GROUP接受 beta 组名称或组 ID若希望工作流从环境变量或配置解析凭据、而不是 macOS 钥匙串在顶层env块加入ASC_BYPASS_KEYCHAIN: 1产出输出的步名只需在同一运行图内可同时执行的工作流中保持唯一相互独立的工作流可以复用archive、publish这类名字声明输出会保留命令打印的精确 JSON 值数值型$.nextBuildNumber为42时存储与插值结果都是42可以直接传给CURRENT_PROJECT_VERSION等构建号参数。这得益于 internal/workflow/interpolate.go 中用json.Number解码 stdout、避免浮点改写42不会被写成42.000000。可恢复的上传与分发拆分--upload-only步骤当上传与外部分发需要各自独立的重试边界时用--upload-only让上传成为独立产出步骤。成功上传的步骤以BUILD_ID持久化若后续的处理等待或分发步骤失败--resume会跳过上传并复用那个确切的构建 ID{ env: { APP_ID: 1234567890, IPA_PATH: .asc/artifacts/App.ipa, TESTFLIGHT_GROUP: External Testers }, workflows: { testflight_external: { steps: [ { name: upload, run: asc publish testflight --app \$APP_ID\ --ipa \$IPA_PATH\ --upload-only --output json, outputs: { BUILD_ID: $.buildId, BUILD_VERSION: $.buildVersion, BUILD_NUMBER: $.buildNumber } }, { name: wait, run: asc builds wait --build-id ${steps.upload.BUILD_ID} --fail-on-invalid --output json }, { name: distribute, run: asc builds add-groups --build-id ${steps.upload.BUILD_ID} --group \$TESTFLIGHT_GROUP\ --submit --confirm --output json } ] } } }等待或分发步骤失败后使用asc workflow打印出的运行 ID 继续asc workflow run testflight_external --resume RUN_ID上传步骤不会再次执行因为其声明输出已持久化在 run-state 文件中。从 internal/workflow/execute.go 可以看到--resume时已成功status ok的持久化步骤直接以resumed状态回放且其输出被重新载入r.outputs后续步骤的steps.name.output插值照常可用。resume 的严格校验internal/workflow/execute.go的validateResumeState会拒绝以下情况的恢复运行已被标记为 terminal、运行 ID 不属于当前工作流、来自不同的 workflow 文件、工作流定义指纹definition hash不匹配、参数不一致、或没有成功检查点/重试启用失败步。运行状态文件含definition_hash、params、逐步steps与hooks由 internal/workflow/state.go 以0600权限、临时文件加原子 rename 的方式写入 workflow 文件同级的runs/目录。有界重试与超时替换 shell 重试循环长格式run步骤可以选用固定延迟重试策略与单次尝试超时用于替代针对 Apple 构建到 beta 组这种最终一致关系的 shell 重试循环{ name: add_build_to_group, run: asc builds add-groups --build-id $BUILD_ID --group $GROUP_ID, retry: { max_attempts: 6, delay: 10s }, timeout: 2m }参数规则与 internal/workflow/validate.go 中minRetryAttempts2、maxRetryAttempts100、maxPolicyDuration24h一致max_attempts包含首次执行取值必须介于 2 与 100 之间delay与timeout使用正的 Go duration 字符串最长24h合法示例包括250ms、10s、2mdelay固定且无 jitter无随机抖动timeout单独作用于每一次尝试。以上例计算总策略上界为6 次 2 分钟尝试 5 次 10 秒延迟。这是显式、有界的不会再无限循环。重试/超时的适用范围重试与超时仅支持run步骤。工作流调用步骤workflow字段以及before_all、after_all、error字符串钩子仍是单次执行。调用方取消会立即停止运行中的进程树或重试延迟error钩子只在最后一次尝试失败后运行after_all仍然只在成功后运行。终端terminal失败的语义重放安全只在明确判断命令可安全重复时才使用重试——runner不会推断shell 命令是只读、幂等还是变更操作成功但声明输出无效不重试因为副作用可能已经发生。该失败是终端的结构化结果置terminal: true运行状态记录 terminal 原因--resume拒绝该运行而不是再次执行命令仅有超时的失败同样是终端的重放安全原因本地终止无法证明远端变更没有被接受同时配置retry与timeout的步骤仍然可恢复因为retry是显式的可重复安全选择仅timeout造成的失败尝试诊断不会创建 resume 检查点恢复要求此前存在成功步骤或钩子或一个启用了重试的失败步骤省略retry或timeout即禁用显式null是无效的internal/workflow/workflow.go 的UnmarshalJSON会检测retry: null/timeout: null并报错。这些语义对应 internal/workflow/retry.go 中的executeRunStep失败原因timeout且无retry时调用setTerminalReason(terminalTimeoutReason(...))output_error成功命令但输出提取失败在terminalReasonForState/terminalReasonForResult中被识别为终端。尝试级输出与 stderr 约定尝试编号与重试延迟写入stderrstdout 始终是唯一机器可读的工作流结果。每次尝试使用全新的输出缓冲区声明输出只从成功尝试中提取并持久化。已持久化的尝试诊断在--resume后仍然可用已经成功的步骤永远不会重跑。独立工作流可复用输出步名以下配置是合法的因为testflight_beta与appstore_release相互独立{ workflows: { testflight_beta: { steps: [ { name: archive, run: printf {\buildId\:\beta\}, outputs: { BUILD_ID: $.buildId } } ] }, appstore_release: { steps: [ { name: archive, run: printf {\buildId\:\release\}, outputs: { BUILD_ID: $.buildId } } ] } } }但如果存在第三个工作流会在同一次运行中调用两者重复的archive产出步名仍需改名。校验器collectOutputProducerConflictsworkflowsReachableFrom会按可达性图检测这类冲突参见 internal/workflow/validate.go。工作流引擎的源码级工作原理加载与格式internal/workflow/load.go 默认读取.asc/workflow.jsonDefaultPath先用tidwall/jsonc把//与/* */注释转换为标准 JSON再用DisallowUnknownFields严格解码并拒绝尾随数据顶层结构internal/workflow/workflow.go 中的Definition支持env、before_all、after_all、error与workflowsWorkflow支持description、private、局部env与stepsStep支持裸字符串简写run: ...、if条件、with传参、outputs、retry、timeout。工作流名与输出名必须是^[a-zA-Z][a-zA-Z0-9_-]*$输出名不含连字符执行顺序与作用域internal/workflow/execute.go 中环境合并顺序为def.Env→wf.Env→ 运行时参数后者覆盖前者if条件按真值规则1/true/yes/y/on大小写不敏感决定步骤是否跳过子工作流调用深度上限MaxCallDepth 16并检测循环引用输出插值internal/workflow/interpolate.go 用${steps.stepName.outputName}模式做插值并智能识别单引号/双引号上下文做 shell 转义声明输出表达式必须是$.field形式的 JSON 路径支持点分多级Shell 执行internal/workflow/env.go 优先用bash -o pipefail -c保证false | cat这类管道失败在 CI 中正确传播找不到 bash 时回退sh -c会剔除BASH_ENV继承以防不可信输入被 shell 求值并在只读模式下强制注入只读环境变量只读模式联动forceReadOnlyEnv把只读策略传播进工作流子进程——因为根级--read-only是进程内的子进程需要环境变量继承该策略参见 concepts/read-only-mode.mdx命令入口internal/cli/workflow/workflow.go 中workflow run默认--file .asc/workflow.json、--dry-run只预览不执行、--resume RUN_ID恢复validate子命令输出{valid: true/false, errors: [...]}结构化结果运行状态目录为 workflow 文件同级的runs/。安全注意事项务必阅读工作流有意执行任意 shell 命令这是其设计特性也是风险所在。CLI 帮助文本明确警告只运行你信任的工作流文件尤其注意--file可指向任意路径不限于.asc/workflow.json把.asc/workflow.json当作代码对待运行前审查步骤继承你的进程环境小心机密声明的步输出会持久化到 run-state 文件不要把密钥映射进 outputsCI 中避免在不可信的 PR 上携带 token/机密运行工作流asc workflow validate只检查结构与接线不评估命令安全性。小结asc workflow把「选顶层路径 → 组合命令 → 注入元数据 → 归档导出 → 上传分发」沉淀为可校验、可干跑、可恢复、可重试的声明式流水线validate负责结构与环检测--dry-run负责预览run-state 持久化与--resume负责断点续跑retry/timeout负责替换手写 shell 重试。从 docs/WORKFLOWS.md 到 internal/workflow 的每个.go文件再到 internal/cli/workflow/workflow.go 的命令接线都遵循同一套「stdout 只输出机器可读 JSON、诊断走 stderr、输出只取成功尝试、失败区分可恢复与终端」的契约使流水线既可被 CI 可靠解析又能在真实发布事故中安全恢复。赞分享【免费下载链接】App-Store-Connect-CLIFast, scriptable CLI for the App Store Connect API. Automate TestFlight, builds, submissions, signing, analytics, screenshots, subscriptions, and more项目地址https://gitcode.com/gh_mirrors/ap/App-Store-Connect-CLI点击查看免费下载相关推荐App Store Connect CLI × CI/CD实战GitHub Actions、GitLab与Bitrise集成发布工作流App Store Connect CLI × CI/CD实战GitHub Actions、GitLab与Bitrise集成发布工作流 App Store COpenClaw iOS 应用工程指南Xcode 手动部署、Watch Rust 编译与 App Store 发布流水线OpenClaw iOS 应用工程指南Xcode 手动部署、Watch Rust 编译与 App Store 发布流水线 OpenClaw iOS 是 OpeAI 应用AI Agent交互助手后端即时通讯网关Easydict 发布与维护指南基于 asc workflow 的可恢复 macOS 发布流水线实战Easydict 发布与维护指南基于 asc workflow 的可恢复 macOS 发布流水线实战 本篇指南面向 Easydict 发布维护者完整讲解从本桌面应用AI 应用上一篇Chrome Regex Search告别CtrlF用正则表达式智能搜索网页内容下一篇PaddleNLP 天垓150Iluvatar BI-V150Llama-13B 四节点预训练实战指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表