Communitygithub.com

berkayturk/appstore-precheck

只读的 iOS App Store 预提交检查:扫描 20 种被拒场景,封装 Apple 的 fastlane precheck,监控指南漂移,并在提交前运行对抗性审核。

appstore-precheck 是什么?

这是一个专为 iOS 开发者设计的 AI 代理技能,用于在每次向 App Store 提交应用前进行全面的预检查,最大程度降低被拒风险。它支持只读模式,通过 5 个阶段(0-4)工作:第一阶段静态扫描 20 种常见被拒场景(如隐私清单不符、元数据限制、试用订阅披露等);第二阶段集成 Apple 官方的 `fastlane precheck` 进行元数据检查;第三阶段通过扮演名为 Pierre 的法国风格审核员进行对抗性模拟审核;最后汇总结果并写入或删除预检查通行令牌(`.precheck-pass`)。该技能适用于 Claude Code、Cursor 等支持脚本执行的 AI 代理环境,也可独立用于 CI 或 git pre-commit 钩子。其核心价值在于提供确定性、机器可验证的门控机制,在提交前发现潜在问题,节省开发者反复提交被拒的时间。

兼容平台✓Claude Code~Codex CLI~Cursor
npx skills add berkayturk/appstore-precheck

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

App Store Precheck

一个命令即可在每次 iOS App Store 提交前运行的门控。它通过静态扫描最常见的被拒场景、运行 Apple 自己的元数据检查器、监控指南漂移以及模拟对抗性审核,最大限度地降低被拒风险。

此技能是只读的。 它从不编辑代码、元数据或资源。它仅报告并写入一个通行令牌。详细方法(每个被拒向量、漂移检查机制)位于 references/methodology.md 中;当您需要了解某个检查的具体细节时,请阅读该文件。

何时运行

  • 在 为 TestFlight 归档之前。
  • 在 App Store Connect 中点击“提交以供审核”之前。
  • 在 任何 fastlane deliver / release / pilot(可选的提交保护钩子会拦截此操作)之前。
  • 每个小版本发布时。

请有目的地运行它。这是一个人工触发的门控,而不是自动的后台步骤。

配置(可选)

扫描器会自动检测标准的 fastlane + Xcode 项目布局,因此大多数项目无需任何配置。要覆盖检测,请将 config.example.json 复制到仓库根目录下的 .appstore-precheck.json。键值:bundleId(阶段 2 必需)、iosSourceDir、metadataDir、screenshotsDir、xcstringsPath、paywallGlobs、locales、disclosureKeys.{subscription,trial}、optionalChecks.familyControls、reviewPrepNotes。请参阅 config.example.json 获取完整的带注释列表。

输出契约

该技能会达到以下三种终态之一:

状态含义.precheck-pass 令牌保护行为
GREEN无 FAIL,≤2 个 WARN已写入(有效 60 分钟)允许上传
YELLOW无 FAIL 但 3+ 个 WARN未写入保护阻止;要求明确确认
RED至少 1 个 FAIL已移除保护阻止;显示 FAIL 列表

当您向用户呈现判决时,首先以 一句 来自 Pierre(法国 App Review 评论家,见阶段 3)的符合角色台词开头,然后直接进入简洁、精确的分解。角色声音只是一个薄层。其下的数据——FAIL/WARN 列表、file:line 引用和修复建议——保持干净且忠于机器输出。切勿重写或改写 scan.sh 的输出,并将 Pierre 限定在一行内。

流程(5 个阶段:0–4)

阶段 0:实时指南漂移检查

将实时的 App Store 审核指南 部分编号与 guidelines-baseline.json 进行差异比较,以检测自上次核对后 Apple 新增或删除 的任何部分。始终非阻塞(最多 WARN)。 漂移是 我们 覆盖范围的缺口,绝非构建的错。页面在获取时会被截断,因此需要两次抓取技术;具体提示和核对程序见 references/methodology.md。基线绝不会自动更新。核对是需要人工介入的步骤。

阶段 1:静态扫描

bash skills/appstore-precheck/scripts/scan.sh

输出 FAIL: / WARN: / PASS: 行,覆盖 20 个被拒向量:隐私清单一致性(5.1.1(v))、用途字符串(5.1.1)、ATT(5.1.2)、其他平台提及(2.3.10)、元数据限制(2.3.1)、本地化一致性(2.3.7)、屏幕截图(2.3.3)、试用和自动续订披露(3.1.2)、恢复/条款/隐私链接(3.1.2)、私有 API(2.5.1)、最低功能要求(4.0)、Apple 登录一致性(4.8)、外部购买链接(3.1.1(a))、可选的 Screen Time / FamilyControls 证明(5.1.5)、不带 ATT 提示的跟踪/IDFA SDK(5.1.2)、出口合规密钥(ITSAppUsesNonExemptEncryption)、fastlane 元数据中的支持/隐私 URL(2.3)、分析 SDK 与 PrivacyInfo 数据类型一致(5.1.1),以及占位符/虚假元数据副本(2.1)。当没有应用内购买信号时,IAP 检查(8-10)会自动跳过。完整的检查表见 references/methodology.md。

扫描器是便携式 Bash 脚本,因此您也可以直接在任何代理之外运行它,用于快速 CI 或 pre-commit 检查。

阶段 2:Apple 官方的 fastlane precheck

需要在配置中提供 bundleId(或直接传递 app_identifier)以及 App Store Connect API 凭据。切勿提交密钥。 封装的脚本从您的环境变量中构建 ASC API 密钥 JSON,运行 precheck,然后在退出时删除密钥(使用 --dry-run 预览命令,无需凭据且不进行网络调用):

ASC_KEY_ID=… ASC_ISSUER_ID=… ASC_P8_PATH=/path/AuthKey.p8 \
  bash skills/appstore-precheck/scripts/phase2-precheck.sh com.example.app

或者手动运行:从环境变量生成密钥 JSON,运行 precheck,然后删除它。

fastlane run precheck \
  app_identifier:"<YOUR_BUNDLE_ID>" \
  api_key_path:"/tmp/asc-key.json" \
  include_in_app_purchases:false \
  default_rule_level:":error"
rm -f /tmp/asc-key.json   # 立即删除密钥

Apple 自己的规则引擎会检查 URL、GitHub 提及、脏话、Apple 商标、定价语言和 Beta 关键词。Result: true → PASS;任何违规行 → FAIL。IAP 已在阶段 1 中覆盖,因此 include_in_app_purchases:false 可以避免 API 密钥的 IAP 限制。

阶段 3:对抗性审核(最重要)

分派一个子代理,扮演 Pierre,一位持怀疑态度的 Apple App Review 审核员,具有法国评论家挑剔的品味。逐字使用以下提示,并填写应用的具体信息:

你是 Pierre,一位经验丰富的 Apple App Review 审核员,你的评论风格像法国评论家。你看过一万次被拒,对什么都不屑一顾。一份新的提交刚刚落在你的桌上。你的任务是 真实地尝试拒绝它,没有批准偏向。挑选 5 个随机的指南条目,在不同章节间分布(一个 2.x,两个 3.x 偏向付费墙,一个 4.x,一个 5.x);每次运行选择不同的组合(包含一个种子行,使重新运行结果不同)。对于每个条目:(A 轮)在相关文件中 grep 至少 2 条具体证据:2.3.x 的元数据;3.1.x 的付费墙视图 + 字符串目录;4.x 的核心/导航;5.1.x 的 Info.plist + PrivacyInfo。(B 轮)问自己“作为审核员,我有什么理由标记这个?”(C 轮)用 Apple 的真实口吻撰写一份拒绝草稿(Guideline X.Y.Z – Category / We noticed… / Specifically… / Next Steps… / Resources…)。为每个条目分配风险等级:REJECT-CERTAIN / REJECT-RISK / WARN / PASS。最后给出提交建议(HOLD / SUBMIT WITH WARNINGS / GO)和最关键的一个修复点。只读:绝不修改文件;如果找不到证据,就如实说,不要编造。保持在 500 字以内。包含至少一个 PASS 和至少一个高风险条目,以模拟真实分布。

阶段 4:汇总 + 令牌

GREEN/YELLOW/RED 判定和令牌操作是 确定性的,纯粹根据 FAIL/WARN 计数得出。scripts/verdict.sh 计算它们,使得判决结果可通过机器测试,而不仅仅是代理的判断;将扫描结果通过管道传入:

bash skills/appstore-precheck/scripts/verdict.sh < scan-output.txt   # 输出 VERDICT / COUNTS / TOKEN

退出码:0 GREEN / 1 RED / 2 YELLOW,使用 --apply 时相应写入或删除 .precheck-pass(YELLOW 状态保留令牌以待用户明确确认)。阶段 0-3 仍生成叙述;verdict.sh 只固定阈值计算。

  1. 收集阶段 0-3 的输出;将 FAIL + WARN + PASS 统计到输出契约表格中。
  2. 对于每个 FAIL,提供 file:line 引用和建议的修复方法。
  3. 用 一句 Pierre 的台词打开判决(每次运行变换措辞;保持简短,符合他冷漠的法国评论家风格),然后进入简洁的分解。决定:
    • GREEN: Pierre, 勉强地: “哼。我没发现什么。可接受。别让我后悔。” 然后 date +%s > .precheck-pass && echo "token written"(有效 60 分钟)。
    • YELLOW: Pierre:“一些小的瑕疵。我不会拒绝,但我注意到了。” 直接列出 WARN,询问用户“确认并仍然提交?”,仅在确认后写入令牌。
    • RED: Pierre:“不。{n} 个问题。Apple 会发现更少。下一个。” 不写令牌;然后直接给出包含 file:line + 修复的 FAIL 列标,并明确说明提交已被阻止。 Pierre 的行仅用于润色。其下的 FAIL/WARN 列表、file:line 和修复建议保持简洁和精确,绝不改写。
  4. 打印最终手动检查清单(见 references/methodology.md)。

规则

  • 只读: 绝不更改代码或资源。仅报告和写入令牌。
  • 速度 > 穷尽: scan.sh 使用并行 grep/jq,在几秒内完成。
  • 不吞没错误: 如果任何扫描命令失败,该行报告为 FAIL 并继续扫描。
  • 令牌位置: 仓库根目录下的 .precheck-pass;保护机制使用 mmin -60 过滤器测试它。
  • 仅本地: 设计用于手动本地运行;避免在 CI 中使用,以免产生误报信号。

已知限制

  • 无运行时崩溃测试;那是 TestFlight 和崩溃报告工具的工作。仅静态分析。
  • 多项检查是基于检测到的信号给出的咨询性 WARN(Apple 登录 4.8、外部购买 3.1.1(a)、不带 ATT 的跟踪/IDFA、分析与隐私清单一致性、元数据 URL 和占位符副本)。出口合规密钥在缺失时会被标记,但实际的加密答案仍应在 App Store Connect 中设置。
  • 对抗性审核是一种启发式模拟,不能保证 Apple 的决定。
  • 对原生 Swift / SwiftUI 最准确。元数据、隐私清单、屏幕截图和出口合规检查适用于任何 iOS 应用,但代码级检查读取 Swift 源代码,因此在 React Native(JavaScript)或 Flutter(Dart)上会检测不足,但不会误报。
  • 仅限 iOS。
  • 阶段 0 仅检测结构性漂移(添加/删除的章节编号);原因请参阅参考文档。

可选:提交保护钩子

hooks/fastlane-guard.sh 会阻止 fastlane deliver/pilot/release,除非存在新鲜的 .precheck-pass 令牌。在 Claude Code 中,它通过 hooks/hooks.json 自动连接。在其他环境中,请自行将其连接为前置命令检查,或将令牌视为手动通行/禁止信号。

相关技能