解决CI+fastlane传包需要验证码的问题

fastlane 是 iOS 平台的一个自动传包工具,非常好用。

fastlane 使用过程中有个痛点是:在和 CI 工具集成的时候总是需要输入验证码,起因是苹果账号目前是强制开启二次验证的,系统会向信任的机器或者手机号发验证码,这时候就需要人手动输入,但是 CI 是没有交互环境的,所以就没办法顺利传包。

# 身份验证方式

官方提供了几种身份验证的方式如下,文档地址在这里 (opens new window)

# 两步身份验证。

传包的时候进行手动验证,即我们上面提到的情况,CI 是不支持的。

如果能在有交互的命令行上完成输入验证码这个过程的话,系统会将会话信息缓存在 ~/.fastlane/spaceship/[email]/cookie 这里。

不过 fastlane 还提供了一种直接生成 AppleID 登录会话的机制:

fastlane spaceauth -u user@email.com  //替换为自己的苹果账号

执行命令后会在命令行尝试登录操作,然后生成一段变量值,大致如下

--\n- !ruby/object:HTTP::Cookie\n name: myacinfo\n value: …. created_at: &2 2023-07-13 19:00:55.561831000 +08:00\n accessed_at: *2\n

生成的变量值必须存储在 CI 系统上的 FASTLANE_SESSION 环境变量中。每次 fastlane 与 Apple 的 API 通信时,该会话将被重用,而不是触发新的登录(下面会有设置 fastlane 环境变量的介绍)

理论上这种方式也能用于 CI 的自动化,避免每次输验证码。但其中是有一个严重缺陷的,即会话有效期,最长也就一个月。 即至少每个月你都得为 CI 生成一次新的会话。有可能一两天就得来一次,非常麻烦。Session 过期时间的长短取决于很多因素,并不是可控的。

一个小技巧是可以通过 -check_session 判断 session 是否有效。 (opens new window)

# 应用程序专用密码(Application-specific passwords)

如果您想将构建上传到 App Store Connect(upload_to_app_storedeliver )或 TestFlight(操作 upload_to_testflightpilottestflight )。我们可以生成应用程序特定密码:

  1. 访问 appleid.apple.com/account/manage (opens new window)
  2. 生成新的应用程序特定密码。
  3. 使用环境变量 FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD 提供应用程序特定密码

后续如果上传的话就直接用应用专用密码,就避免每次输验证码了。但是这种方式也有个限制,即如果做除了上传二进制文件之外的其他操作,则应用程序特定密码将不起作用,官方的这个限制其实说的很笼统,理论上我执行 upload_to_testflight 是不涉及到别的操作的。

其实不仅要设置 FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD 也要设置 FASTLANE_USER 的环境变量。

但是重点来了,我按照上面的说明,配置好 FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD 环境变量后,依然让我输入验证码,似乎这个配置并没有生效,有人遇到类似的问题 → Github ISSUE-2FA code still required for pilot even with FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD set) (opens new window),不过官方没有回应。

排查了半天之后发现一个解决方案 (opens new window),可能还是 Fastfile 里面的 upload_to_testflight 这个操作缺少一些参数,于是将 Fastfile 里面的 upload_to_testflight 加了俩参数

upload_to_testflight(skip_waiting_for_build_processing: true, apple_id: " 12345")

这里 apple_id 就是 AppleStore 后台为应用自动生成的 Apple ID,可以在 AppleStore 后台看。

skip_waiting_for_build_processing 如果设置为 true,则该 distribute_external 选项将不起作用,并且不会将任何生成分发给测试人员。而 distribute_external 选项又会用到 groups 选项,似乎 groups 选项对后台进行了二进制外的额外操作,导致没能使 fastlane 正确使用应用程序专用密码。

这时候再执行上传操作就没有问题了,基本上是完美解决。

# App Store Connect API 密钥

这个本文不涉及。

# 设置环境变量

有几种不同的方式可以设置 fastlane 的环境变量

  1. 通过 Fastfile 预定义的方式。**不推荐使用这种方式,主要是出于安全原因。**如果工程是 git 管理的话,你的苹果账号专用密码就会暴露。

    ENV["FASTLANE_USER"] = "user@email.com"
    ENV["FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD"] = "xxxyyy"
    
    lane :beta do
      upload_to_testflight
    end
    
  2. 通过在 bashfile 中定义。如果终端使用的 zsh,则使用 ~/.zshrc 文件进行定义。定义完记得 source 一下让改动生效。这种定义方式是在本地,相对安全,但有的 CI 的环境是不支持加载这些环境变量的。

    export FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD = "xxxyyy"
    export FASTLANE_USER = "user@email.com"
    
  3. 通过 dotenv (opens new window) 方式进行定义。 dotenv 用于将环境存储在特定于项目的文件中。这里你需要创建一个 .env.default 文件。内容如下

    FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD="xxxyyy"
    FASTLANE_USER="user@email.com"
    

大家可以根据自己的情况选择合适的设置变量的方式。

参考地址:

  1. fastlane » spaceship doc » Authentication.md (opens new window) #身份验证相关文档
  2. Docs » Best Practices » Continuous Integration (opens new window) #CI 集成相关的文档
  3. Docs » Best Practices » Keys (opens new window) #fastlane 设置环境变量方式

关注我的微信公众号,我在上面会分享我的日常所思所想。