yuhanle / blogbag

我会不断更新这个仓库中的文章
https://latehorse.github.io/
14 stars 1 forks source link

使用 fastlane 进行 iOS 打包 #8

Open yuhanle opened 6 years ago

yuhanle commented 6 years ago

使用 fastlane 进行 iOS 打包

fastlane 是一个通过简单命令来完成诸如截图、获取证书、编译、导出安装包、提交 iTunesConnect 等一系列操作的工具,它同时支持 iOS 和 Android。

fastlane
🚀 Save hours every time you push a new release to the store or beta testing service
Integrates with all your existing tools and services (170 actions currently)
📖 100% open source under the MIT license
🎩 Easy setup assistant to get started in a few minutes
Runs on your machine, it's your app and your data
👻 Integrates with all major CI systems
🖥 Supports iOS, Mac, and Android apps
🔧 Extend and customise fastlane to fit your needs, you're not dependent on anyone
💭 Never remember any commands any more, just fastlane
🚢 Deploy from any computer, including a CI server

你能够通过简单的方式配置流程进行的顺序,并通过非常简单的命令执行其中的一个流程。当然它的简单并不代表功能也简陋,有开源社区的支持,更新迅速且有很多功能能够满足你的需求。

为什么使用它

作为公司的 iOS 程序员,少不了在发布应用的时候各种等待。标准的手动发布流程是:编译->打包上传->填写应用更新数据->等待 iTunesConnect 编译->选择版本发布,整个过程大概需要 30 分钟左右。关键是这个过程就像windows 装系统一样,虽然手动参与的不多,但是要一直守在电脑前等着。

刚开始用 fastlane,发现其使用和配置还算简单,有开源社区支持,更新迅速功能强大。且相比 jenkins 里的插件,fastlane 可以单独使用,也可以被多种CI 接入。

程序员这么懒,一定会想办法让他自动化的。后来发现特别懒的 Felix Krause 写的 Fastlane,Fastlane可以非常快速简单的搭建一个自动化发布服务,并且支持 AndroidiOSMacOS。他可以实现一条命令从编译选版发布全程不用干预。作为程序员的你只要一条命令,看集美剧,发布就完成了。截止刚刚 Fastlane 官网上宣称已经为程序员节省了13,746,550小时+

使用场景

系统要求

工具集

fastlane 将如下的工具套件有机地结合起来,从管理证书到单元测试,从编译打包到上传发布,都能通过命令行轻松完成.该套件支持与 JenkinsCocoaPodsxctools 等其他第三方工具的集成,并且能够定义多个通道(lanes)以支持不同的部署目标。

测试

证书,配置文件

截图

发布

TestFlight 管理

辅助工具

Android

简例

fastlane 命令是一个流程控制的命令行工具(CLI),通过内部集成 action 和第三方的 action 完成一系列控制流程。运行 fastlane 命令行工具,会读取当前目录或者 ./fastlane 目录下的 Fastfile 配置文件。

Fastfile 中:

一个简单的发布流程:

lane :deploy do
  # 执行 pod instasll
  cocoapods
  # 执行 carthage bootstrap
  carthage
  # 增加build版本号
  increment_build_number
  # 编译代码
  gym
  # 发布到Apple Store
  deliver(force: true)
end

安装

安装

如果 ruby 版本满足要求,可以直接在命令行执行以下命令安装 fastlane

#安装fastlane
sudo gem install -n /usr/local/bin fastlane

使用的环境搭建好了,就可以进行下一步来配置 fastlane

初始化

执行默认的初始化

cd 项目目录
fastlane init

如果你的项目只有一个 bundle id 也只有一个开发者账号的话,可以直接遵循官方的步骤配置。否则需要按照官方文档,配置不同参数和环境变量。

Last login: Tue Aug  7 13:19:59 on ttys000
yuhanle:360qws-G100 yuhanle$ fastlane init
[✔] 🚀 
[✔] Looking for iOS and Android projects in current directory...
[13:33:35]: Created new folder './fastlane'.
[13:33:35]: Detected an iOS/macOS project in the current directory: 'G100.xcworkspace'
[13:33:35]: -----------------------------
[13:33:35]: --- Welcome to fastlane 🚀 ---
[13:33:35]: -----------------------------
[13:33:35]: fastlane can help you with all kinds of automation for your mobile app
[13:33:35]: We recommend automating one task first, and then gradually automating more over time
[13:33:35]: What would you like to use fastlane for?
1. 📸  Automate screenshots
2. 👩‍✈️  Automate beta distribution to TestFlight
3. 🚀  Automate App Store distribution
4. 🛠  Manual setup - manually setup your project to automate your tasks
?  

在提问了你的 Apple IDTeam 的问题之后,fastlane 会自动检测当前目录下项目的 App NameApp Identifier。如果检测的不对,选择 n 自行输入。

接下来会问你这个 app 是否需要在 iTC 和 ADC 中创建(上一步中如果选择 y 会自动检测是否需要创建),fastlane 会调用 produce 进行初始化,如果现在还不想创建,也可以之后再运行 produce init 进行这个流程。如果不执行 produce 的流程,deliver 的流程也会被掠过,当然之后也可以 deliver init 运行完全一样的流程。

在执行 deliver init 的过程中,会同步 iTC 中的所有语言的元数据和截图,并按照目录结构组织好。目录结构应该类似下面:

fastlane
├── Appfile
├── Deliverfile
├── Fastfile
├── metadata
│   ├── copyright.txt
│   ├── en-US
│   │   ├── description.txt
│   │   ├── keywords.txt
│   │   ├── marketing_url.txt
│   │   ├── name.txt
│   │   ├── privacy_url.txt
│   │   ├── release_notes.txt
│   │   └── support_url.txt
│   ├── primary_category.txt
│   ├── primary_first_sub_category.txt
│   ├── primary_second_sub_category.txt
│   ├── secondary_category.txt
│   ├── secondary_first_sub_category.txt
│   ├── secondary_second_sub_category.txt
│   └── zh-Hans
│       ├── description.txt
│       ├── keywords.txt
│       ├── marketing_url.txt
│       ├── name.txt
│       ├── privacy_url.txt
│       ├── release_notes.txt
│       └── support_url.txt
└── screenshots
    ├── README.txt
    ├── en-US
    │   ├── 一堆png图片

这里肯定会被创建的是 AppfileFastfile。如果 Deliverfilescreenshotsmetadata 目录没被创建,可以运行 deliver init 来创建。

PS:

  1. 这里有个小问题,iTC 和 ADC 中的 Team ID 是不一样的,在 fastlane init 中只会自动在 Appfile 里写入 ADC 的 team_id,所以在这个过程中会不停的问你 iTC 的 Team ID,所以在创建完 Appfile 后,手动在里面添加 itc_team_id
  2. 这个问答对不同的项目可能有各种各样的分支。我已经用不同的项目试过很多次了,但是可能还不是全部,所以你还需要见招拆招。
  3. 在这里可以安心的输入密码,所有的密码都加密保存在系统的 Keychain 里。

配置

fastlane 可以通过配置 .env 文件、Appfile、Deliverfile、Fastfile 来完成各种工作。

其中 Fastfile 是最核心的用来控制流程走向的配置文件,.env 和 Appfile 可以辅助 Fastfile 来设置一些参数,Deliverfile 可用于配置提交 iTunesConnect 的一些参数。

需要查看,样例配置可直接前往下载样例配置

Appfile

Appfile是用来配置一些类似于 AppleID、BundleID 参数(参数是 fastlane 已经定义好的,新增的并没有用,如果想新增变量需要使用 .env 方式),可以在 Fastfile 中使用,AppleID、BundleID 等其实会被一些 actions 直接调用,并不需要写出来传递。

普通配置方式

直接在 Appfile 里填写 app_identifier、apple_id、team_id 等,然后根据 lane 的不同可以设置成不同。

# 默认配置
app_identifier "com.xxxxxxxxxx.test"
apple_id "xxxxxxxxxx@xxxx.com"
team_id "xxxxxxxxxx"

# 如果lane是ent换成Dev的配置
for_lane :ent do
  app_identifier "com.xxxxxxxxxx.testDev"
  apple_id "xxxxxxxxxx@xxxx.com"
  TEAM_ID "xxxxxxxxxx"
end
使用 .env 配置方式

.env 这个文件的作用是作为环境变量的配置文件,在 fastlane init 进行初始化后并不会自动生成,如果需要可以自己创建。

执行时默认会读取 .env.env.default 文件里的配置。通过执行 fastlane [lane-name] --env [envName] 来指定使用配置文件 .env.[envName],读取顺序是 .env -> .env.default -> .env.<envName>,相同的变量名会被后面的覆盖。

如我建了文件 .env.myDev,里面写了一些参数,那在执行的时候使用 fastlane [lane-name] --env myDev 即可,想在 Appfile、Deliverfile、Fastfile 等调用,直接使用 ENV['keyName'] 即可

# .env.myDev文件
# bundle id
App_Identifier = "com.xxxxxxxxxx.testDev"
# 开发者账号
Apple_Id = "xxxxxxxxxx@xxxx.com"
# 开发者TeamId
Team_Id  = "xxxxxxxxxx"
# project的target scheme名称
Scheme   = "xxxxxxxxxx"
# Appfile使用.env方式直接读取变量即可
app_identifier   ENV['App_Identifier']
apple_id         ENV['Apple_Id']
team_id          ENV['Team_Id']

注意:因为是 .env 文件是.开头文件,默认是在 finder 中隐藏的,需要通过执行一下命令来显示

# 设置隐藏文件可见
defaults write com.apple.finder AppleShowAllFiles TRUE
# 重启finder服务以生效
killall Finder
配置方式对比

Fastfile

Fastfile 是对流程进行控制的核心文件,需要设定支持的平台和在一些环节里需要做的事情,是我们脚本的入口,所有的事件驱动都是在这个文件来调度的。

Lane

有了 Fastfile,就可以添加自己的发布流程了。打开Fastfile文件(这里我用Sublime 设定语法为Ruby),如果不出意外的话你生成的Fastfile和我应该差不多。这里我就不贴出来了。

最开始定义了

platform 中就是需要修改的重点。先忽略 before_allafter_allerror 这些方法,这里的 lane 就是一组任务,上传到 Firim 的任务如下

lane :to_firim do
  # 如果你用 pod install 注意参数 -> 否则会出错
  cocoapods(use_bundle_exec: false)
  # 如果你没有申请adhoc证书,sigh会自动帮你申请,并且添加到Xcode里
  sigh(adhoc: true)
  # 以下两个action来自fastlane-plugin-versioning,
  # 第一个递增 Build,第二个设定Version。
  # 如果你有多个target,就必须指定target的值,否则它会直接找找到的第一个plist修改
  # 在这里我建议每一个打的包的Build都要不一样,这样crash了拿到日志,可以对应到ipa上
  increment_build_number_in_plist(target: [target_name])
  increment_version_number_in_plist(
    target: [target_name],
    version_number: '7.1.3'
    )
  # gym用来编译ipa
  gym(
    output_directory: './firim',
    export_options: {
      method: "ad-hoc", # 这可以不指定
      thinning: "<none>"
    }
    )
  # 上传ipa到fir.im服务器,在fir.im获取firim_api_token
  firim(firim_api_token: [firim_api_token])
end
Sigh

如果你不确定证书目前是否可用,可以用 Sigh 自动生成获取证书。Sigh 会自动根据 Appfile 里设置的 app_identifier从 ADC (苹果开发者中心)生成证书,并下载到项目根目录下(不是 fastlane 目录),下载后自动安装。你可以通过指定 output_path 指定证书下载位置。

PS:建议不要把这个文件夹同步到项目的 git 中( Fastlane 提供了 match 专门管理所有证书)。可以在 .gitignore 中可以忽略这个文件夹。

Sigh 常用的配置项:

Name Type Description Default
adhoc bool 获取adhoc证书 fasle
development bool 更新开发证书,不更新production证书 false
force bool 强制更新证书,不管证书是否在ADC中存在 false

iOS 里 code 打包证书有 4 种,adhoc,inhouse,appstore,development 证书。

价格 AppStore 证书 In-House 证书 AdHoc 证书 Development 证书
企业帐户 $299
公司账号 $99
个人账户 $99

其中 In-House 的方式打包的ipa安装没有设备的限制。AdHoc 打包的 ipa 必须提前把设备的 UDID 添加到证书中,并且有 100 台设备限制。

所以如果你不指定 adhoctrue,Sigh 会识别帐户类型,企业帐户默认生成 In-House 证书,公司账号和个人帐户默认生成 AppStore 证书。

Gym

Gym 常用配置项:

Name Type Description Default
scheme string 指定需要编译的scheme
clean bool 是否在编译前clean false
output_directory string 导出目录 ./
output_name string 导出ipa名字 [app_name].ipa
export_options hash/string 这里指定Xcode API的外部配置文件地址,或者配置hash,见下文
export_method string 打包方式,可选项app-store ad-hoc``package enterprise``development``developer-id 如果在fastlane中使用了sigh,这个值会从上下文获取
include_bitcode bool 是否开启bitcode Xcode API 默认值为true
include_symbols bool 是否生成符号表 Xcode API 默认值为true

Xcode7 之后,Xcode API 允许我们指定一个 plist 文件作为额外的配置文件。gym 默认会帮你创建这个文件,你可以直接指定配置。更多关于 plist 可配置项,执行 xcodebuild -help 查看 Available keys for -exportOptionsPlist

export_methodinclude_symbols,和 include_bitcode 这些参数都是 exportOptionsPlist 的配置,对应 methoduploadSymbolsuploadBitcode

Gym 可以指定配置文件 Gymfile

初始化:

gym init 

Multi-Target

如果我们需要配置多个 target 进行打包的话,我们可以使用 环境变量,来进行配置。假如我们现在有两个 targettargetAtargetB,则我们需要创建两个 .env 文件,例如 .env.targetA.env.targetB,放在 Fastfile 文件同级目录下

.env 文件里面我们可以配置一些不同的内容(非公共),比如 app_identifierrelease_notes 等等。截图如下:

AppfileDeliverfileFastfile 等文件,我们都可以直接使用 .env 文件里面的内容。

Appfile

# Appfile

#The bundle identifier of your app
app_identifier ENV['APP_IDENTIFIER']

# Your Apple email address
apple_id ENV['APPLE_ID'] 

# Developer Portal Team ID
team_id ENV['TEAM_ID']

复制代码

Deliverfile,请在设置 release_nores、support_url、private_url 等配置的时候,采用 hash 的方式写。

# app_identifier
app_identifier ENV['APP_IDENTIFIER']

# 用户名,Apple ID电子邮件地址
username ENV['APPLE_ID']

# 团队ID
team_id ENV['TEAM_ID']

# 团队name
team_name ENV['TEAM_NAME']

# copyright
copyright ENV['COPYRIGHT']

# 关键字
keywords(
    'zh-Hans' => ENV['KEYWORDS'],
)

# 新版本修改记录
release_notes(
    # 中国
    'zh-Hans' => ENV['RELEASE_NOTES'],
    # 澳大利亚
    'en-au' => ENV['RELEASE_NOTES_AU'],
    # 美国
    'en-us' => ENV['RELEASE_NOTES_US']
)

# 支持网址
support_url(
    # 中国
    'zh-Hans' => ENV['SUPPORT_URL'],
    # 澳大利亚
    'en-au' => ENV['SUPPORT_URL_AU'],
    # 美国
    'en-us' => ENV['SUPPORT_URL_US']
)

# 隐私政策网址 国家代码 https://www.cnblogs.com/Mien/archive/2008/08/22/1273950.html
privacy_url(
    # 中国
    'zh-Hans' => ENV['PRIVACY_URL'],
    # 澳大利亚
    'en-au' => ENV['PRIVACY_URL_AU'],
    # 美国
    'en-us' => ENV['PRIVACY_URL_US']
)

# 上传完成后提交新版本进行审查
submit_for_review false

# 跳过HTML报告文件验证
force true

# 启用iTC的分阶段发布功能 灰度发布
phased_release true

# 应用审核小组的联系信息 app 审核信息
app_review_information(
  first_name: "xx",
  last_name: "xx",
  phone_number: "+86 18888888888",
  email_address: "xxxx",
  demo_user: "test1@test.com",
  demo_password: "test123"
)

... 

Deliver

Deliver 可以完全管理与 iTC 的交互。其中包括:

还记得上面初始化的时候初始化的 metadatascreenshots 目录么?iTC 中的所有的元数据信息都被保存在 metadata 中,所有的截图信息都被保存在 screenshots 中。

metadata:

screenshots:

如果要通过 deliver 修改元数据或截图,你必须提供所有 iTC 中有的语言。比如后台中有「简体中文」和「英文」,你也必须提供对应的 zh-Hansen-US 文件,否则 deliver 会报缺少语言的错误。可以在 iTC 后台提交的版本中删除语言。

Deliver 常用配置项:

Name Type Description Default
ipa string ipa地址 如果使用gym,可以通过上下文获取
metadata_path string 指定metadata目录地址 如果在fastlane./fastlane/metadata,如果作为独立的命令行应用./metadata
screenshots_path string 指定screenshots目录地址 如果在fastlane./fastlane/screenshots,如果作为独立的命令行应用./screenshots
skip_binary_upload bool 跳过二进制文件上传,适用于只想改metadata false
skip_screenshots bool 跳过截图上传,如果截图没有变化,开启这项节约时间 false
skip_metadata bool 跳过元数据上传 false
force bool deliver会在上传时汇总信息生成HTML也,等待你审核。跳过这项审核此项设为true false
submit_for_review bool 上传完成是否自动提交审核 false
automatic_release bool 审核通过是否自动释放 false
price_tier int App价格级别。注意:这项提交当时就会生效,所以更改价格还是在后天手动操作
submission_information hash 这是在iTC上点击提交之后的问答表格,可选项
app_review_information hash 提供审核时的信息,详情
app_icon string 指定icon图片地址,必须为png格式

submission_information({
  export_compliance_available_on_french_store: "false",
  export_compliance_contains_proprietary_cryptography: "false",
  export_compliance_contains_third_party_cryptography: "false",
  export_compliance_is_exempt: "false",
  export_compliance_uses_encryption: "false",
  export_compliance_app_type: nil,
  export_compliance_encryption_updated: "false",
  export_compliance_compliance_required: "false",
  export_compliance_platform: "ios",
  content_rights_contains_third_party_content: "false",
  content_rights_has_rights: "false",
  add_id_info_limits_tracking: "true",
  add_id_info_serves_ads: "true",
  add_id_info_tracks_action: "false",
  add_id_info_tracks_install: "false",
  add_id_info_uses_idfa: "true"
});

deliverCLI 工具:

fastlane ios deploy

系统级 lane

fastlane 默认有 lane。

before_all do
  # 检出到 Developer 分支
  sh 'git checkout Developer'
  git_pull
  cocoapods(repo_update: true)
end

执行顺序

执行顺序 方法名 说明
1 before_all 在执行 lane 之前只执行一次。
2 before_each 每次执行 lane 之前都会执行一次。
3 lane 自定义的任务。
4 after_each 每次执行 lane 之后都会执行一次。
5 after_all 在执行 lane 成功结束之后执行一次。
6 error 在执行上述情况任意环境报错都会中止并执行一次。

Error

ENV["FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT"] = "180"
ENV["FASTLANE_XCODE_LIST_TIMEOUT"] = "180" 

插件

Fastlane 的插件是一个或者一组 action 的打包,单独发布在 fastlane 之外。Fastlane Plugin 指南

自从 16 年 5 月份推出插件系统以来,现在已经有很多第三方的插件可以使用。查看所有插件:

fastlane search_plugins

这里介绍两个下文会用到的插件:

  1. fastlane-plugin-versioning => 用来修改 build 版本号和 version 版本号。

    Fastlane 内嵌的 action increment_build_number使用的是苹果提供的 agvtoolagvtool 在更改 Build 的时候会改变所有target的版本号。这时如果你在一个工程里有多个产品的话,每次编译,所有的Build都要加1,最后就不知道高到哪里去了。

    有了 fastlane-plugin-versioning 不仅可以指定 target 增加 Build,而且可以按照「语义化版本」规范增加 Version,当然也可以直接设定 Version。

    PS:最开始写 iOS 时不知道怎么定义 Build。现在我一般都直接定义成纯数字,比如 100 起,每次编译的时候让他自动加一。

  2. fastlane-plugin-firim => 直接把 AdHoc 或者 InHouse 打包的 ipa 上传到 fir.im,供测试下载。

安装上面的插件

fastlane add_plugin [name] # 安装方法
fastlane add_plugin versioning
fastlane add_plugin firim

参考链接

iOS 持续交付之 Fastlane

和重复劳动说再见-使用fastlane进行iOS打包

小团队的自动化发布-Fastlane带来的全自动化发布