iOS 自动化部署

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

「文章标题虽然说是小团队的自动发布工具,但是这篇文章适合只要你们公司还没有自动化发布流程。」

​From: Hailong's Blog (格式比专栏的好!)

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

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

这是一篇中速入门,看完这篇文章,自己搭建个「iOS自动发布服务」肯定是没问题了。这篇文章很长,有很多细节,可以当文档查。

文章难度:★☆☆☆☆

系统要求

  • 会编程
  • Xcode7+
  • Mac OS 10.11+

Fastlane组件

Fastlane是一套工具集,包括:

  • 测试
    • scan => 自动运行测试工具,并且可以生成漂亮的HTML报告
  • 证书,配置文件
    • cert => 自动创建管理iOS代码签名证书
    • sigh => 一声叹息啊,这么多年和Provisioning Profile战斗过无数次。总是有这样那样的问题导致配置文件过期或者失效。sigh是用来创建、更新、下载、修复Provisioning Profile的工具。
    • pem => 自动生成、更新推送配置文件
    • match => 一个新的证书和配置文件管理工具。我会另写一篇文章专门介绍这个工具。他会所有需要用到的证书传到git私有库上,任何需要配置的机器直接用match同步回来就不用管证书问题了,小团队福音啊!
  • 截图
    • snapshot => 用Xcode7推出的UI test功能实现自动化截图
    • frameit => 可以把截的图片自动套上一层外边框
  • 编译
    • shenzhen => 当年大名鼎鼎的自动编译工具,现在已经被弃用
    • gym => Fastlane家族的自动化编译工具,和其他工具配合的非常默契
  • 发布
    • produce => 如果你的产品还没在iTunes Connect(iTC)或者Apple Developer Center(ADC)建立,produce可以自动帮你完成这些工作
    • deliver => 自动上传截图,APP的元数据,二进制(ipa)文件到iTunes Connect
  • TestFlight管理
    • pilot => 管理TestFlight的测试用户,上传二进制文件
    • boarding => 建立一个添加测试用户界面,发给测试者,可自行添加邮件地址,并同步到iTC
  • 辅助工具
    • spaceship => 为pilot,boarding和deliver等工具提供和 iTC 和 ADC 的交互API。spaceship本来是个独立的项目,后来被Fastlane收编进来
    • WatchBuild => 是一个独立的iTC监控工具,开启WatchBuild可以监控iTC上的文件状态,弹出MacOS自带的Notification
  • Android
    • supply => 自动上传到Google Play工具(如果有时间,我想把国内提供API的Android Store都写个插件自动上传,这个问题从10年我刚开始工作就觉得是个痛点)
    • screengrab => Android的自动截图工具

Fastlane中的概念

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

在Fastfile中:

  • action => Fastlane中的每一条命令都是一个扩展(action),上面提到的deliver,sigh之类的工具本身是CLI,但是在Fastlane中内嵌了对他们支持的action
  • lane => Fastlane中流程的合集,每一个动作即可以是action,也可以是其他的lane。语法和ruby中的rake非常像

一个简单的发布流程:

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

安装

苹果的系统升级率非常高,所以现在绝大部分开发者电脑应该是满足依赖要求的。

确保Xcode Command Line Tools 安装了最新版

xcode-select --install

如果你单独安装过ruby(如果你能看得懂这句),去掉sudo。如果使用系统自带的ruby,需要sudo权限

[sudo] gem install fastlane

初始化

在项目根目录下,初始化Fastlane:

fastlane init

提问了你的Apple ID,Team的问题之后,fastlane会自动检测当前目录下项目的App Name和App 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图片

这里肯定会被创建的是Appfile和Fastfile。如果Deliverfile,screenshots和metadata目录没被创建,可以运行deliver init来创建。

  • Fastfile => 用来定义所有的lane任务Fastfile帮助
  • Appfile => 是用来存储一些公共信息的,比如app_identifier,apple_id,team_id,itc_team_id等。Appfile帮助
  • Deliverfile => deliver的配置文件Deliverfile帮助

PS:

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

插件

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

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

fastlane search_plugins

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

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

    Fastlane内嵌的actionincrement_build_number使用的是苹果提供的agvtool,agvtool在更改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

配置发布流程

可以把二进制发布到三个地方:

发布到http://Fir.im

Lane

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

最开始定义了

  • fastlane_version => 指定fastlane最小版本
  • default_platform => 指定当前平台,可选ios,android,mac

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

lane :to_firim do
  # 如果你用 pod install
  cocoapods
  # 如果你没有申请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台设备限制。

所以如果你不指定adhoc为true,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_method, include_symbols,和include_bitcode 这些参数都是exportOptionsPlist的配置,对应method,uploadSymbols和uploadBitcode。

Gym可以指定配置文件Gymfile。 初始化:

gym init

Firim

http://Fir.im是一个ipa托管网站。你可以用AdHoc或者In-House的方式打包,上传到http://fir.im发送给测试人员测试。需要先注册Fir.im帐户,并生成Token。这种方式适合:

  • 小团队 => 测试的机器数量很少并且非常可控。直接连上开发机,真机调试一次,就可以添加
  • 土豪 => 有企业帐户可以打In-House的包

Firim常用配置项:

Name Type Description Default firim_api_token string 指定http://fir.im的token ipa string ipa地址 如果使用gym,可以通过上下文获取 icon string icon的path,注意这里有个非常坑的地方,http://fir.im只支持jpg格式的图片

还有项如app_name等等,是用来配置http://fir.im页面属性的。firim --help

Firim的配置文件是Firimfile。初始化:

firim init

Firim是我完全仿照fastlane组件的方式写的,所以也可以单独作为CLI使用。

如果有任何Firim的问题请到Firim的issues提问。

发布

配置完以上项,就可以一条命令发布到http://fir.im了:

fastlane to_firim

发布到App Store

Lane

先看下lane:

lane :deploy do
  # 如果你用 pod install
  cocoapods
  # 不带adhoc参数,sigh会自动生成App Store证书(公司或个人帐户)
  sigh
  increment_build_number_in_plist(target: [target_name])
  increment_version_number_in_plist(
    target: [target_name],
    version_number: '7.1.3'
    )
  # 指定输出目录
  gym(
    output_directory: './build',
    )
  # 上传所有信息到App Store
  deliver(force: true)
end

Deliver

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

  • 上传和下载多语言截图
  • 上传和下载多语言元数据
  • 上传二进制文件

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

metadata:

  • 可以很容易的管理对应目录下的文件和iTC后台的表单项,在执行deliver时会自动被传到iTC。
  • 在metadata目录下的文件,如copyright.txt,是没有本地化的,在二层目录中的文件都是需要对应不同语言的表单项。
  • 如果你不想修改某些项的信息,直接把对应的文件删除即可。
  • 所有这些表单项也可以在Deliverfile中指定,Deliverfile中指定的项优先级比文件高

screenshots:

  • 如果不想更改截图,可以把整个截图目录删除
  • 如果不使用snapshot(自动化截图),也可以自己截图放到对应目录下,比一张一张上传iTC快的多。截图在iTC中的排列顺序就是本地文件名的「字母表顺序」(在目录中右击,按文件名排序)。deliver会识别图片分辨率,上传到对应设备中。

如果要通过deliver修改元数据或截图,你必须提供所有iTC后台中有的语言。比如后台中有「简体中文」和「英文」,你也必须提供对应的zh-Hans和en-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 => 对应「出口合规信息」,没有特殊情况都选false就可以。

    • 前缀content_rights => 问你是否包含,显示,访问第三方内容(这项我没在我提交过程中找到),没有特殊情况也都选false就可以。

    • 前缀add_id_info => 可就关键了,对应「广告标识符」,如果你在App中使用了IDFA。你必须在这给个理由,而不能直接选false。

      下图等价下表,App中投放了广告。

      image
      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"
      });
      
      

deliver的CLI工具:

  • 下载iTC上的截图deliver download_screenshots
  • 下载iTC上的元数据 deliver download_metadata

发布

配置后,还是一条命令:

fastlane deploy

后记

终于写了第二篇分享,第一篇还是3年前在知乎上的问答。这篇文章前后用业余时间改了一个星期,主要时间都放在验证各种情况,核实很多细节,尽量能把提到的说的详细。以前读一些入门文章,经常我的环境稍微不对,可能就跑不通了(当然,这篇也不可能覆盖所有情况)。所以最后也导致写的太长,而且有很多废话,希望大家谅解。

写文章真没有想象中容易。以前看别人写的文章,读可能只要10分钟,作者可能要写几个小时。感谢所有人的分享!

写文章是个好习惯。程序员的工作虽然辛苦,但是太单调安逸。有时感到很绝望,感觉生命就这样一直走到底,再也不会改变。我想逼自己一次,做点自己不喜欢或者不擅长的事。

这篇文章还没写完,因为太长,只能分成两部分。另一部分会讲下fastlane一些进阶用法,还有自动上传到TestFlight => pilot,还有证书管理工具match

最后,谢谢大家的观看!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容