本篇主要介绍 ios 如何通过 fastlane 打包发布到所需平台 Android 打包将会在第二篇 Jenkins 集成将会在第三篇
在之前开发 APP 的时候,公司的技术总监一直让我研究 app 自动化打包持续集成部署这种工程化的任务,对于这种技术我肯定是非常感兴趣去折腾的,鉴于当时 app 业务开发任务繁重,和优先级相对较低的原因,等到不怎么忙了才有时间来研究,最近看了一些相关文档,总结如下:
持续集成:
大多是采用 jenkins,我们后端的服务也是集成了 jenkins,所以 CI 平台无需考虑;
ios 打包:
大多数文章是基于在 jenkins安装 xcode 相关插件,以构建出 xcode 打包环境,以及配置证书、认证文件、钥匙串的相关插件,整体流程比较繁琐,稍有流程顺序和配置出错都会导致打包失败,而且听说 jenkins 的 xcode 插件更新频率极慢,可能不能满足最新版 xcode 的使用,我在参考一些文章的配置之后,打包出了测试版本的 ipa,并且推送到蒲公英,但是发布到 appstore 的构建一直未果;
折腾了两到三天,有点心累了,直到我发现了 fastlane!
关于 fastlane 的介绍我这里就不罗列了,网上介绍它的文章太多了,可以参考中文文档快速了解。
看了文档之后,感觉简直打开了新世界的大门,随便几个功能都足够吸引力。
比如:
就这几个功能就已经省去相当多的时间了,身为一个 app 开发者,相信每个人都有过跟 xcode 的证书、认证文件各种折腾的爱恨情仇!
借用一张图总结 fastlane 的流程和功能:
fastlane 管理许多工具集合,这些工具也都是通过 ruby 开发,每个工具只负责一项任务,可以通过 fastlane 的配置文件灵活的构建这些工具的使用和流程,从而实现
打包 => 自动化测试 => 发布测试包 => 发布正式包 复制代码
一条龙服务!
就问你妙不妙?
开始折腾.....
经过两天的折腾,顺利完成了
所以,如果看文章的你想要实现以上需求,可以参考以下我分享的内容。
xcode-select --install 复制代码
若提示如下,说明已经安装了Xcode命令行工具;否则会弹出对话框,选择安装即可。
$ xcode-select --install xcode-select: error: command line tools are already installed, use "Software Update" to install updates 复制代码
安装界面:
推荐使用RubyGems
来安装,fastlane就是用 ruby 写的一个工具,所以这种安装方式比较友好,后续不易出错。
如果你是 Mac 用户,其实 Mac 自带 ruby 环境,但是仅仅这样不够,需要2.0及以上版本,检查你的版本:
ruby -v 复制代码
目前我的版本:
$ ruby -v ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin19] 复制代码
ruby 管理推荐使用 rvm,类似 node 的 nvm 的工具,可以提供一个便捷的多版本 Ruby 环境的管理和切换。
检查你当前正在使用系统Ruby
which ruby 复制代码
如果结果是:
/usr/bin/ruby 复制代码
说明目前是 Mac 系统自带的 ruby 环境,所以首先安装 rvm,而安装 rvm 又需要 gpg
,还要安装mpapis
公钥
(觉得麻烦不?)
没事,坑都趟完了,跟着走就对了....
brew install gnupg 复制代码
查看最新公钥: rvm.io/rvm/install
拿过来安装
gpg --keyserver hkp://pgp.mit.edu --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB 复制代码
\curl -sSL https://get.rvm.io | bash -s stable --ruby 复制代码
$ rvm list =* ruby-2.6.3 [ x86_64 ] # => - current # =* - current && default # * - default 复制代码
which ruby
查看当前Ruby版本返回 /Users/xxx/.rvm/rubies/ruby-2.6.3/bin/ruby
则说明当前使用了 rvm 安装的 ruby,ruby 环境至此告一段落!
通过RubyGems
安装:
sudo gem install fastlane -NV 复制代码
当然你也可以通过homebrew
安装(费了这么大劲你肯定不会选这个):
brew cask install fastlane 复制代码
在项目目录下执行fastlane init
安装的时候出现了下面的选项
1. 📸 Automate screenshots (自动截屏功能配置) 2. 👩✈️ Automate beta distribution to TestFlight (发布到Testfilght配置) 3. 🚀 Automate App Store distribution (自动发布到 appstore配置) 4. 🛠 Manual setup - manually setup your project to automate your (选择手动配置) 复制代码
新手在这里肯定会纠结,根据情况,自动截屏功能肯定不是首选的刚需,发布到 appstore 也不是一上来就要搞定的,手动配置刚上来你会配置吗?
所以这里选择 2 ,然后一路回车,会让你输入apple 开发者ID,就是你的邮箱,以及密码,通过命令行登录苹果开发者来下载你的证书等配置,登录 ITC 和从 ITC 拉取项目已存在的信息,如果开启了苹果的双重认证,会提示输入验证码登录
Two-factor Authentication (6 digits code) is enabled for account 'xxx@xxx.com' 复制代码
为避免每次登录输验证码,官方给出解决方案。
命令中也有相关 issue 以供查阅解决方案,界面和提示非常入门化和友好,这真是我用过最贴心的的命令行工具了!
执行完毕后,会生成fastlane
文件夹、Gemfile
文件,Appfile
、Fastfile
文件。
Appfile: 存储有关开发者账号相关信息
Fastfile: 核心文件,用于命令行调用和处理具体的流程,lane相对于一个action方法或函数
Gemfile 类似于cocopods 的Podfile文件
.env 配置环境变量(在fastlane init进行初始化后并不会自动生成,如果需要可以自己创建
打开项目,看看刚生成的文件,你可能发现都没有代码高亮.... 因为 fastlane 使用 ruby 写的,所以要支持 ruby 啊 我这里用的是 VS Code,搜索 ruby 插件安装。
(webstorm 没有发现 ruby 插件)
Fastlane的插件是一个或者一组action的打包,单独发布在fastlane之外。
你可以通过命令行搜索插件
#查看所有插件 fastlane search_plugins 复制代码
# 安装方法 fastlane add_plugin [name] #常用插件 fastlane add_plugin versioning fastlane add_plugin firim fastlane add_plugin pgyer 复制代码
首次安装插件会生成Pluginfile
文件
# Autogenerated by fastlane # # Ensure this file is checked in to source control! gem 'fastlane-plugin-firim' gem 'fastlane-plugin-pgyer' gem 'fastlane-plugin-versioning' 复制代码
Gemfile
文件会自动添加一下内容
plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') eval_gemfile(plugins_path) if File.exist?(plugins_path) 复制代码
插件说明:
fastlane-plugin-versioning
用来修改 build 版本号和 version 版本号,fastlane 内嵌的actionincrement_build_number
使用的是苹果提供的agvtool
, 在更改Build的时候会改变所有target的版本号。如果你在一个工程里有多个target,每次编译,所有的Build都要加1。
有了fastlane-plugin-versioning
不仅可以指定target增加Build,当然也可以直接设定Version, 并且可以指定版本号的版本(major/miner/patch),这一点非常重要,而且这个插件也可以非常方便的修改 android 的版本号,插件排行榜第一位。
fastlane-plugin-pgyer
上传到蒲公英分发平台。
fastlane-plugin-firim
上传到 firim。
新建.env
文件,这里可以配置所有的账号、秘钥、路径等自定义变量,定义之后,然后Fastlane的三个配置文件(Appfile、Deliverfile、Fastfile
)分别从.env
文件中读取配置信息。
你可能又发现 env 文件中没有语法高亮,强迫症又表示不舒服,可以在 VS Code 安装 dotEnv
插件。
给出.env
文件配置做参考:
App_Identifier = "com.xx.xxx" # Apple email address Apple_Id = "xxx@sina.com" # TeamId Team_Id = "xxx" # target scheme Scheme = "xxx" # xcodeproj Xcodeproj ="xxx.xcodeproj" # xcworkspace Workspace="xxx.xcworkspace" # ipa输出路径-- Appstore Appstore_Output_Path = "builds/appstore" # ipa输出路径-- 蒲公英 Pgy_Output_Path = "builds/pgy" # ipa输出路径-- TestFlight TF_Output_Path = "./builds/testflight" # 蒲公英 的api_key Pgy_Api_Key = "xxx" # 蒲公英 的user_key Pgy_User_Key = "xxx" 复制代码
使用方式:ENV['Apple_Id']
apple_id("xxx@xxx.com") ===> apple_id ENV['Apple_Id'] 复制代码
Fastfile 配置:
default_platform(:ios) platform :ios do # 所有lane执行之前 # 使用环境变量提供这个密码给fastlane,解决双重认证生成的特殊密码 before_all do ENV["FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD"] = "xxx" end desc "发布 测试版本到 TestFlight" lane :beta_tf do increment_build_number_in_plist(target: [target_name]) increment_version_number_in_plist( target: [target_name], ) get_certificates( # Create or get certificate, and install it output_path: "./builds" # Download certificate in the build folder (you don't need to create the folder) ) get_provisioning_profile( # Create or get provisioning profile output_path: "./builds", # Download provisioning profile in the build folder filename: "provisioning.mobileprovision" # Rename the local provisioning profile ) update_project_provisioning( # Set the project provisioning profile (related in Xcode to the General > Signing Release section) xcodeproj: ENV['Xcodeproj'], target_filter: ENV['Scheme'], # Name of your project profile: "./builds/provisioning.mobileprovision", build_configuration: "Release" ) update_project_team( # Set the right team on your project teamid: CredentialsManager::AppfileConfig.try_fetch_value(:team_id) ) gym( workspace: ENV['Workspace'], scheme: ENV['Scheme'], clean: true, export_method: "ad-hoc", # Valid values are: app-store, ad-hoc, package, enterprise, development export_options: { provisioningProfiles: { "com.xxx.xxx" => "xxx", } }, build_path: "./builds/testflight", output_directory: "./builds/testflight", output_name: logDirectory ) upload_to_testflight end # 发布到蒲公英 lane :beta_pgy do desc "发布 测试版本 到 蒲公英" # 这里是打的 Debug 版本 gym( scheme: ENV['Scheme'], clean:true, #打包前clean项目 export_method: "development", #导出方式 Defaults to 'Release' configuration: "Debug",#环境 output_directory: ENV['Pgy_Output_Path'], output_name: logDirectory, export_options: { provisioningProfiles: { "com.xxx.xxx" => "xxx", } } ) # 上传版本到蒲公英平台 pgyer( api_key: ENV["Pgy_Api_Key"], user_key: ENV["Pgy_User_Key"], update_description: "update by fastlane" ) # 在macOS 通知栏发送通知 notification(subtitle: "上传完成", message: "最新测试包已经上传至蒲公英平台") end lane :release do desc "发布正式版到 appstore" # 下载证书 get_certificates( output_path: "./builds" ); # 下载认证文件 get_provisioning_profile( output_path: "./builds" ); #get_push_certificate # 获取正式推送证书 # 拉取最新代码 git_pull # 增加 version number # 使用文档 https://github.com/SiarheiFedartsou/fastlane-plugin-versioning # eg: version_number: '2.1.1' # Set a specific version number # eg: bump_type: 'minor' # Automatically increment minor version number increment_version_number_in_plist( bump_type: 'patch', # Automatically increment patch version number(patch/minor/major) target: ENV['Scheme'] ); # 从最新 release 分支名中获取 version. # `pattern` is pattern by which version number will be found, `#` is place where action must find version number. # Default value is 'release-#'(for instance for branch name 'releases/release-1.5.0' will extract '1.5.0') # version = get_version_number_from_git_branch(pattern: 'release-#') # 增加 build number latest_build_number = lane_context[SharedValues::LATEST_BUILD_NUMBER]; # Automatically increments the last part of the build number. increment_build_number_in_plist( target: ENV['Scheme'] ); # see code signing guide for more information #sync_code_signing(type: "appstore"); # 打包 gym( scheme: ENV['Scheme'], configuration: "Release", clean: true, export_method:"app-store", export_options: { provisioningProfiles: { "com.xxx.lph" => "xxx", } } ); # 上传到 ITC deliver # 在macOS 通知栏发送通知 notification(subtitle: "上传完成", message: "最新正式包已经上传至 appstore") end end 复制代码
执行以下命令初始化 metadata,几分钟过后,会将 ITC 的所有元数据,截图等信息下载到本地
fastlane deliver init 复制代码
metadata 文件夹中的文件比较多且杂,仅一个属性的配置却使用一个单独的.txt 文件来存储,官方文档表示可以创Deliverfile
文件来配置,且优先级高于 matadata 中.txt 文件中的配置,所以我们要创建Deliverfile
。
下边给出我的配置:
# The Deliverfile allows you to store various App Store Connect metadata # For more information, check out the docs # https://docs.fastlane.tools/actions/deliver/ ############################# 基本信息 #################################### # 1 bundle identifier app_identifier "xxx" # 2 用户名,Apple ID电子邮件地址 username "xx@xx.com" # 3 版权声明 copyright "xxx" # 4 支持的语言 supportedLanguages = { "cmn-Hans" => "zh-Hans" } # 5 App应用名称 name( 'zh-Hans' => "xxx" ) # 6 副标题 subtitle( 'zh-Hans' => "xxx" ) # 应用说明 description({ 'zh-Hans' => " xxx " }) ################################### 类别配置 ################################### # 类别列表设置参见 https://github.com/fastlane/fastlane/blob/master/deliver/Reference.md # 设置 App 的类别.这里可以设置一个主要类别,一个次要类别. # 主要类别 primary_category "xxx" # 主要类别第一个子类别 primary_first_sub_category # 主要类别第二个子类别 primary_second_sub_category # 要设置的次要类别 无 secondary_category # 设置的次要第一个子类别 无 secondary_first_sub_category # 设置的次要第二个子类别 无 secondary_second_sub_category ################################## 关键字\描述等信息 ################################### # 关键字 keywords( 'zh-Hans' => "xxx,xxx,xxx" ) # 营销地址 marketing_url({ 'zh-Hans' => "http://xxx.cn" }) # 隐私地址 privacy_url({ 'zh-Hans' => "http://xxx.cn" }) # 支持网址 support_url({ 'zh-Hans' => "http://xxx.cn" }) # 发行说明,版本更新内容 release_notes({ 'zh-Hans' => "修复了一些 bug,提高使用体验,推荐更新!" }) ################################# 提交信息等 ######################################### # 1 提交审核信息:加密, idfa 等 submission_information({ export_compliance_encryption_updated: false, export_compliance_uses_encryption: false, content_rights_contains_third_party_content: false, add_id_info_uses_idfa: false }) # 2 应用审核小组的联系信息 app 审核信息 app_review_information( first_name: "x", last_name: "x", phone_number: "+86xxx", email_address: "xxx@sina.com", demo_user: "xxx", demo_password: "xxx", notes: "" ) # 出口合规证明文件 trade_representative_contact_information( first_name: "", last_name: "", address_line1: "xxx", address_line2: "", address_line3: "", city_name: "Beijing", state: "", country: "China", postal_code: "xxx", phone_number: "", email_address: "", trad_name: "xxx", is_displayed_on_app_store: false ) ####################################### 其他信息 ################################### # 1 App价格 price_tier 0 # 2 自动发布 app: false,则需要手动发布 automatic_release false # 3 图标路径 app_icon './fastlane/metadata/app_icon.jpg' # 跳过HTML报告文件验证 force true # 上传完成后提交以供审核 submit_for_review false 复制代码
配置好Deliverfile
后,可以删除 metadata 文件夹中的文本配置。