fastlane precheck(check_app_store_metadata)详解:在提交 App Store 审核前自动检查元数据,避免被拒
Apple 会因各种可避免的元数据问题拒审应用:包含脏话、提到其他公司的商标、甚至提及 Apple 自家产品的 Bug。precheck 是 fastlane 中的元数据"预审员":它通过 spaceship 从 App Store Connect 下载应用元数据,再逐一运行一组社区驱动的审核规则,把拒审风险在提交之前暴露出来。本文基于文档 check_app_store_metadata.md 展开,结合 precheck 组件源码,讲清楚它检查什么、有哪些规则与参数、如何用 Precheckfile 和 deliver 做长期配置,以及底层 RuleProcessor 的判定机制。
一、precheck 的定位与工作机制
precheck 是一个独立的 fastlane 组件(位于 precheck),同时也以 action 形式注册在主包中。入口实现见 check_app_store_metadata.rb:
class CheckAppStoreMetadataAction < Action
def self.run(config)
# 仅当未设置 :api_key_path 时,才从 SharedValues 中取 :api_key(两者是冲突选项)
unless config[:api_key_path]
config[:api_key] ||= Actions.lane_context[SharedValues::APP_STORE_CONNECT_API_KEY]
end
require 'precheck'
Precheck.config = config
return Precheck::Runner.new.run
end
def self.return_value
return "true if precheck passes, else, false"
end
def self.return_type
:bool
end
def self.is_supported?(platform)
platform == :ios
end
end
从源码可以看到三个关键事实:
precheck只是别名:precheck.rb 中PrecheckAction < CheckAppStoreMetadataAction,description直接写着 "Alias for thecheck_app_store_metadataaction",因此两个名字可以互换使用;- 返回布尔值:所有规则跑完后,action 返回
true/false(存在:error级别失败时返回false并抛出 user error),方便在 Fastfile 中做流程控制; - 仅支持 iOS:
is_supported?限定平台为:ios。
底层执行流程由 Precheck::Runner 完成,整体调用链为:
Precheck.config.load_configuration_file(Precheck.precheckfile_name)—— 加载Precheckfile中保存的默认规则配置;- 认证 App Store Connect:优先使用 API Key(
api_key/api_key_path),否则回退到 Apple ID 登录(Spaceship::ConnectAPI.login),多团队时通过team_id/team_name(环境变量FASTLANE_ITC_TEAM_ID/FASTLANE_ITC_TEAM_NAME)消歧; ensure_app_exists!—— 通过Spaceship::ConnectAPI::App.find(Precheck.config[:app_identifier])确认应用存在,找不到直接报错Could not find app with App Identifier '...';- 取版本:
use_live为真时取 App Store 上的线上版本(get_live_app_store_version),否则取最新(未提交)版本(get_latest_app_store_version); - 调用 RuleProcessor.process_app_and_version 运行全部规则;
- 打印结果表格:有失败时输出 "Potential problems" 表(字段名 + 失败原因,警告黄色、错误红色);若存在
:error级别失败,执行UI.user_error!终止 fastlane,否则仅提示 "found one or more potential metadata problems, but this won't prevent fastlane from completing"。
二、内置规则清单:precheck 到底检查什么
文档 Features 一节列出的能力,对应 Options.rules 中注册的 10 条规则(rules/all.rb 会加载 rules/ 目录下所有规则文件):
| 规则 key | 检查目标 | 对应 Features 描述 |
|---|---|---|
negative_apple_sentiment |
元数据中对 Apple 产品的负面表述(暗示产品有 Bug) | Apple 产品 bug 提及 |
curse_words |
可能引起反感的脏话/冒犯性词汇 | 脏话检查器 |
other_platforms |
提到其他平台(如 Android、Chrome 等) | 提及其他平台 |
unreachable_urls |
元数据中 URL 是否可达 | URL 可达性检查 |
placeholder_words |
占位符/测试性质的词 | 占位符/测试词 |
future_functionality |
宣称尚未实现的未来功能 | 提及未来功能 |
test_words |
"test"、"demo" 等测试词 | 占位符/测试词 |
free_stuff_iap |
免费内容却在 IAP 中收费 | 文档未单列,源码新增 |
custom_text |
用户自定义的词表(需传 data:) |
可自定义词表检查 |
copyright_date |
版权年份缺失或在未来 | 版权年份检查 |
每条规则都是 Rule 的子类,并区分两种"检查项"类型:TextRule(处理 TextItemToCheck 文本字段)和 URLRule(处理 URLItemToCheck 链接字段)。Rule#check_item 会先做两层过滤——handle_item?(类型匹配)与 item_field_supported?(规则只支持特定字段,例如 copyright_date 只检查 :copyright 字段)——不匹配的项直接跳过(返回 nil),保证每条规则只作用于自己关心的字段。
几个有代表性的规则实现:
unreachable_urls(unreachable_urls_rule.rb):用Addressable::URI解析 URL(并去掉 fragment),通过 Faraday 发 HEAD 请求(follow_redirects中间件),只有状态码为 200 才算通过,否则记录HTTP <status>或unreachable: <url>;copyright_date(copyright_date_rule.rb):用正则/\b(?:19|20)\d{2}\b/提取年份,缺失年份报missing copyright year,年份大于当前年份报copyright year is in the future;curse_words(curse_words_rule.rb):把元数据分词(含去标点变体)后做 SHA256 哈希,与词库哈希集合 curse_word_hashes/en_us.txt 比对,命中即报出具体词;custom_text(custom_text_rule.rb):这是唯一的"需定制"规则(needs_customization?返回true),必须传入data: ["word1", "word2"],词表会被strip.downcase归一化;若没传data,该规则会被整体跳过并打印提示#{rule.key} excluded because no data was passed to it。
三、precheck 检查哪些元数据字段
RuleProcessor.generate_app_items_to_check / generate_version_items_to_check 把 App Store Connect 返回的数据扁平化为"待检查项",每项带有语言标签,便于定位问题出在哪个地区:
应用级(App Info)
| 字段 | item_name | 备注 |
|---|---|---|
| 应用名称 | :app_name |
|
| 应用副标题 | :app_subtitle |
可选字段(空值自动通过) |
| 隐私政策文本(tvOS) | :privacy_policy_text |
|
| 隐私政策 URL | :privacy_policy_url |
可选字段 |
| 内购名称/描述 | :in_app_purchase |
需开启 include_in_app_purchases(默认开启) |
版本级(Version)
| 字段 | item_name |
|---|---|
| 版权信息 | :copyright |
| 关键词 | :keywords |
| 描述 | :description |
| 新功能说明(Release Notes) | :release_notes |
| 支持 URL | :support_url |
| 营销 URL | :marketing_url(可选) |
值得注意的实现细节:
- 每个字段都按语言(locale)生成独立检查项,失败输出会带
(locale)后缀,例如description: (fr_FR); - 标记为
is_optional的字段(副标题、营销 URL 等)如果值为空,Rule#perform_check会直接判为passed,不会误报; - 未覆盖字段会被显式提示:
RuleProcessResult.items_not_checked收集没被任何规则处理过的项,Runner 会打印Metadata fields not checked by any rule: ...,提醒你有字段处于"检查盲区"; - IAP 检查目前仍走旧的 iTunes Connect 接口(源码注释注明 "As of 2020-09-04, this is the only non App Store Connect call in prechecks"),因此 使用 App Store Connect API Key 登录时无法检查 IAP——Runner 会直接
UI.user_error!要求关闭include_in_app_purchases或改用 Apple ID 登录。
四、使用方法与完整参数
基本用法(文档 Usage 节原样保留):
# 检查 App Store Connect 中的应用元数据
fastlane precheck
# 查看所有可用选项
fastlane action precheck
在 Fastfile 中以 action 方式调用时,可内联配置规则级别(action 中的 example_code):
check_app_store_metadata(
negative_apple_sentiment: [level: :skip], # 跳过 negative_apple_sentiment 规则
curse_words: [level: :warn] # 脏话检查失败时仅警告
)
# 或者直接用别名
precheck
完整参数定义见 precheck/options.rb:
| 参数 | 短选项 / 环境变量 | 默认值 | 说明 |
|---|---|---|---|
app_identifier |
-a / PRECHECK_APP_IDENTIFIER |
取 Appfile 中的 app_identifier |
应用的 bundle ID(必填) |
username |
-u / PRECHECK_USERNAME |
取 Appfile 中的 apple_id/itunes_connect_id |
Apple ID 用户名,使用 API Key 时可不填 |
api_key |
PRECHECK_API_KEY / APP_STORE_CONNECT_API_KEY |
— | App Store Connect API Key(Hash 形式,敏感项),与 username、api_key_path 互斥 |
api_key_path |
PRECHECK_API_KEY_PATH / APP_STORE_CONNECT_API_KEY_PATH |
— | API Key JSON 文件路径,与 username 互斥 |
team_id |
-b / PRECHECK_TEAM_ID |
取 Appfile 的 itc_team_id |
多团队时指定团队 ID |
team_name |
-l / PRECHECK_TEAM_NAME |
取 Appfile 的 itc_team_name |
多团队时指定团队名 |
platform |
-j / PRECHECK_PLATFORM |
ios |
取值仅限 ios、appletvos/tvos、osx(源码有 verify 校验) |
default_rule_level |
-r / PRECHECK_DEFAULT_RULE_LEVEL |
:error |
未单独配置的规则使用的默认级别 |
include_in_app_purchases |
-i / PRECHECK_INCLUDE_IN_APP_PURCHASES |
true |
是否检查内购 |
use_live |
PRECHECK_USE_LIVE |
false |
是否改为检查 App Store 上线版本(而非最新待审版本) |
五、规则级别:warn / error / skip
每条规则有三档级别,定义在 rule.rb 的 RULE_LEVELS:
:warn—— 命中时打印警告,不中断 fastlane;:error—— 命中时所有扫描结束后UI.user_error!,阻止后续命令执行;:skip—— 该规则整体跳过,Runner 会打印Skipped: <规则> -> <描述>。
RuleProcessor.process_rules 中的判定逻辑(对应文档 "You can decide if you want to warn about potential problems and continue or have fastlane show an error and stop" 这一 Feature):
rule_config = Precheck.config[rule.key]
rule_level = rule_config[:level].to_sym unless rule_config.nil?
rule_level ||= Precheck.config[:default_rule_level].to_sym # 未配置则回落到默认级别
if rule_level == RULE_LEVELS[:skip]
skipped_rules << rule
next
end
# ...
error_results = add_new_result_to_rule_hash(...) if rule_level == RULE_LEVELS[:error]
warning_results = add_new_result_to_rule_hash(...) if rule_level == RULE_LEVELS[:warn]
最终由 RuleProcessResult 汇总:只要 error_results 非空(should_trigger_user_error?),Runner 就报错退出;只有警告时则打印 UI.important 提示后继续;全部通过且无盲区字段时输出绿色成功消息。测试用例 rule_processor_spec.rb 覆盖了各级别的组合行为。
六、Precheckfile:持久化默认规则配置
由于你可能想手动触发 precheck 而不希望每次都写全所有参数,文档推荐把默认配置存入 Precheckfile。执行 fastlane precheck init 可生成配置文件,典型内容(文档 Example 节原文):
# 表示该规则不会检查你的元数据
negative_apple_sentiment(level: :skip)
# 命中时警告潜在的元数据问题
curse_words(level: :warn)
# 报错,precheck 结束后阻止后续命令执行
unreachable_urls(level: :error)
# 传入任意你想检查的词
custom_text(data: ["chrome", "webos"],
level: :warn)
Precheckfile 会在 Runner 启动第一行就被 load_configuration_file 加载,之后命令行参数可覆盖其中的设置。规则默认值还可从 Appfile 读取(Rule.default_value 委托给 CredentialsManager::AppfileConfig.try_fetch_value),因此团队可以把词表配置集中管理。
七、与 deliver 集成:提交审核前自动预审
文档强调 precheck 与 deliver 完全集成。在 deliver/runner.rb 的 precheck_app 方法中可以看到完整衔接:
# 确保通过 precheck 后再上传
def precheck_app
return true unless options[:run_precheck_before_submit]
# ...
precheck_options = {
default_rule_level: options[:precheck_default_rule_level],
include_in_app_purchases: options[:precheck_include_in_app_purchases],
# api_key / api_key_path / username / platform 等透传
}
# ...
precheck_success = Precheck::Runner.new.run
# precheck 内部异常时捕获并提示可用 verbose 模式排查
end
随后 submit_for_review if options[:submit_for_review] && precheck_success —— 只有 precheck 通过,才会真正提交审核。相关 deliver 参数见 deliver/options.rb:
run_precheck_before_submit(默认开启,可设为false关闭,注意 deliver init 时会强制置为false);precheck_default_rule_level—— 透传给 precheck 的默认规则级别;precheck_include_in_app_purchases—— 透传 IAP 检查开关。
因此 Fastfile 写法即文档 Example 节所示:
lane :production do
# ...
# 默认 deliver 会调用 precheck 并警告任何问题;
# 若希望 precheck 失败时中止提交审核,可传 precheck_default_rule_level: :error
deliver(precheck_default_rule_level: :error)
# ...
end
# 或者单独运行 precheck
lane :check_metadata do
precheck
end
八、给 precheck 补充规则
文档最后一条建议:如果发现新的常见拒审模式,请到项目仓库提交 issue 并附上 App Store 拒审邮件内容(注意脱敏,因为 issue 是公开的)。从源码结构看,新增规则的成本很低:继承 TextRule 或 URLRule,实现 key、env_name、friendly_name、description、rule_block(返回 RuleReturn.new(validation_state:, failure_data:)),必要时覆写 supported_fields_symbol_set 限定作用字段,再把类加入 Options.rules 数组即可被 Runner 自动拾取。
小结
precheck(别名check_app_store_metadata)通过 spaceship 拉取 App Store Connect 元数据,按 10 条内置规则逐字段检查,返回值供 Fastfile 分支控制;- 规则支持
:warn/:error/:skip三档级别,未配置时回落到default_rule_level(默认:error); - 用
Precheckfile持久化规则级别与custom_text词表,用deliver(run_precheck_before_submit:, precheck_default_rule_level:)把它接进发布流水线; - 使用 App Store Connect API Key 时无法检查 IAP,
use_live可切换为检查已上线版本,platform仅支持ios/appletvos(tvos)/osx三类取值。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
