欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

iOS使用fastlane实现持续集成的方法教程

程序员文章站 2023-01-11 10:11:15
前言 最近公司有打渠道包的需求,领导说使用fastlane来做持续集成,发了点时间研究了下,所有有了这篇文章 本文主要涉及到以下几个主题: fastl...

前言

最近公司有打渠道包的需求,领导说使用fastlane来做持续集成,发了点时间研究了下,所有有了这篇文章

本文主要涉及到以下几个主题:

  • fastlane是什么和为什么使用fastlane
  • fastlane安装和设置
  • 在项目中集成fastlane

是什么和为什么

fastlane是一款使用ruby实现的跨平台的持续集成工具,支持安卓和ios平台项目的持续集成实践,fastlane处理提供基本的但是很强大的包含了:初始设置、屏幕截图、打包、上传到测试平台、部署等功能。此外还有大量的第三方插件可以使用,比如fir插件支持上传beta版本到fir测试平台,appicon插件支持自定义某个子版本的应用图标,还有很多不胜枚举。

因为项目的配置、打包、上传等一系列操作是耗时且没有技术含量的工作,所以有了fastlane可以为我们节省大量的没有什么价值的时间花费,以提高我们的工作效率。

fastlane安装和设置

安装

支持三种方式安装

使用brew安装

brew cask install fastlane

使用gem安装

sudo gem install fastlane -nv

下载安装包安装

,下载完了之后打开install脚本即可安装

安装完成之后命令行输入fastlane -v,可以看到fastlane的版本信息说明安装成功了,接下来可以继续设置步骤

➜ plushgame fastlane -v
fastlane installation at path:
/users/aron/.rvm/gems/ruby-2.3.0@global/gems/fastlane-2.62.0/bin/fastlane
-----------------------------
fastlane 2.62.0

设置

进入到项目目录执行fastlane init命令,会有一系列的设置,包含了登录apple账号,确认fastlane检测到的项目信息确认,登录itunesconnect和从itunesconnect拉取项目已存在的信息

➜ plushgame fastlane init
[20:37:45]: get started using a gemfile for fastlane https://docs.fastlane.tools/getting-started/ios/setup/#use-a-gemfile
[20:37:48]: detected ios/mac project in current directory...
...
# 根据提示输入项目的apple id和对应的密码,会自动登录到苹果开发者后台,拉取开发者账号对应的信息完成设置
[20:37:52]: your apple id (e.g. fastlane@krausefx.com): xxxxxx@126.com
[20:37:55]: verifying that app is available on the apple developer portal and itunes connect...
[20:37:55]: starting login with user 'xxxxxx@126.com'
-------------------------------------------------------------------------------------
please provide your apple developer program account credentials
the login information you enter will be stored in your macos keychain
you can also pass the password using the `fastlane_password` environment variable
more information about it on github: https://github.com/fastlane/fastlane/tree/master/credentials_manager
-------------------------------------------------------------------------------------

# 输入apple id 对应的密码
password (for xxxxxx@126.com): **************

+----------------+------------------------------------------------------+
|       detected values       |
+----------------+------------------------------------------------------+
| apple id  | xxxxxx@126.com         |
| app name  | plushgame           |
| app identifier | com.xxxx.xxxx        |
| workspace  | /users/aron/putaoworkspace/project/plushgame/plushg |
|    | ame.xcworkspace          |
+----------------+------------------------------------------------------+


# 检测到的项目信息要求确认,按y即可
[20:38:50]: please confirm the above values (y/n)
y
[20:39:03]: created new file './fastlane/appfile'. edit it to manage your preferred app metadata information.
[20:39:03]: loading up 'deliver', this might take a few seconds
[20:39:03]: login to itunes connect (xxxxxxx@126.com)
[20:39:16]: login successful

+--------------------------------------+------------------------+
|     deliver 2.62.0 summary      |
+--------------------------------------+------------------------+
| run_precheck_before_submit   | false     |
| screenshots_path      | ./fastlane/screenshots |
| metadata_path      | ./fastlane/metadata |
| username        | xxxxxx@126.com |
| app_identifier      | com.xxxx.xxxxx |
| edit_live       | false     |
| platform        | ios     |
| skip_binary_upload     | false     |
| skip_screenshots      | false     |
| skip_metadata      | false     |
| skip_app_version_update    | false     |
| force        | false     |
| submit_for_review     | false     |
| automatic_release     | false     |
| dev_portal_team_id     | jn55ysq744    |
| overwrite_screenshots    | false     |
| precheck_default_rule_level   | warn     |
| ignore_language_directory_validation | false     |
+--------------------------------------+------------------------+


# 从ituneconnect拉取已存在的元数据,这里只有截屏的信息
[20:39:34]: downloading all existing screenshots...
[20:39:37]: downloading existing screenshot '1_iphone58_1.x1_1.jpg' for language 'zh-hans'
[20:39:42]: downloading existing screenshot '2_iphone58_2.x2_1.jpg' for language 'zh-hans'
[20:40:22]: downloading existing screenshot '3_iphone58_3.x3_1.jpg' for language 'zh-hans'
[20:40:49]: downloading existing screenshot '4_iphone58_4.x5_1.jpg' for language 'zh-hans'
....

初始化设置之后再项目文件夹下多出了一个fastlane文件夹,主要有以下的一些的信息

➜ fastlane tree
.
├── appfile
├── deliverfile
├── fastfile
├── metadata
│ ├── app_icon.jpg
│ ├── copyright.txt
│ └── zh-hans
│  ├── description.txt
│  ├── keywords.txt
│  ├── marketing_url.txt
│  ├── name.txt
│  ├── privacy_url.txt
│  ├── promotional_text.txt
│  ├── release_notes.txt
│  ├── subtitle.txt
│  └── support_url.txt
└── screenshots
 ├── readme.txt
 └── zh-hans
  ├── 1_iphone58_1.x1_1.jpg
  ├── 1_iphone6plus_1.1.png
  ├── 2_iphone58_2.x2_1.jpg
  ├── 2_iphone6plus_2.2.png
  ├── 3_iphone58_3.x3_1.jpg
  ├── 3_iphone6plus_3.3.png
  ├── 4_iphone58_4.x5_1.jpg
  ├── 4_iphone6plus_4.4.png
  └── 5_iphone6plus_5.5.png

8 directories, 62 files

最重要的是appfile文件、fastfile文件和deliverfile文件

appfile文件保存了项目信息,包含项目使用的apple账号信息、项目的team_id信息、项目identifier信息

app_identifier "com.xxxx.xxxx" # the bundle identifier of your app
apple_id "xxxxxx@126.com" # your apple email address

team_id "xxxxxsq744" # developer portal team id

fastfile文件保存了apple账号信息和teamid信息,该文件的信息用于上传appstore使用

app_identifier "com.sanshuai.funcatch" # the bundle identifier of your app
username "sanshuai2017@126.com" # your apple id user

fastfile文件定义了一些列的action,包含了项目打包设置、项目打包、项目截图、项目上传测试平台、项目发布、项目打包完成后期的一些操作

初始的fastfile大致如下,可以看到 lane :release 包含了

snapshot action用于截屏

gym action用于打包

deliver action用于上传到app store

frameit action用于给截屏添加边框

after_all 块中的slack action 则是负责打包成功之后发送信息到slack

fastlane_version "2.62.0"

default_platform :ios

platform :ios do

 desc "deploy a new version to the app store"
 lane :release do
 # match(type: "appstore")
 # snapshot
 gym(scheme: "plushgame") # build your app - more options available
 deliver(force: true)
 # frameit
 end

 # you can define as many lanes as you want

 after_all do |lane|
 # this block is called, only if the executed lane was successful

 # slack(
 # message: "successfully deployed new app update."
 # )
 end
end

接下来是为我们自己的项目编写符合业务需求的fastfile了

fastlane项目集成

fastlane 详细介绍

该部分会详细的介绍fastlane中的块操作和lane的用途,以及fastlane中预定义的常用的action

fastlane 中的块和lane

fastlane 包含了预定义块有:

before_all 块用于项目打包之前的操作,比如可以使用cocopods命令更新pod库(如果使用的是pod项目)

after_all 块用于项目打包完成之后的操作,比如通知用户打包完成

error 块用于项目打包失败之后的操作,比如通知用户打包失败

lane 定义一个打包操作的具体流程,预设的有 lane :release定义了打包并且发布到app store,lane :beta 打包并且发布到testflight测试平台。

fastlane 常用的action以及参数

fastlane中最主要的操作就是调用action,fastlane预定义了很多很有用的action,如果不够还可以使用第三发提供的插件调用第三发的action。下面介绍几个fastlane中预定的action

gym 是打包action,是 build_ios_app action的别名,他有以下常用的参数:

key 描述
workspace 如果项目是一个workspace,则是该项目的workspace路径
project 项目的project路径
scheme 项目的scheme,注意指定的scheme需要被指定为shared,/->manage schemes->shared复选框需要打钩
output_directory 打包保存路径
output_name 打包保存的文件名
export_method 相当于配置 archives->export->mehtod 可选:[app-store, ad-hoc, package, enterprise, development(默认), developer-id]
configuration 相当于配置scheme->build configuration:[release, debug],release会生成dsym文件,而debug不会

set_info_plist_value是设置修改info.plist文件的 action,一次调用该action只能设置一个值,可以多次调用该命令来设置多个值,他有以下常用的参数:

key 描述
path info.plist文件路径
key info.plist文件中的key
value key对应的值

pilot 是上传到testflight的 action,是 upload_to_testflight action的别名,没有特殊需求可以不指定参数

deliver 是上传到app store的 action,是 upload_to_app_store action的别名,没有特殊需求可以不指定参数,会从deliverfile文件读取账号信息登录到itunesconnect从./fastlane/metadata ./fastlane/screenshots 文件夹中读取配置的app信息元数据然后上传

fastlane其他操作

fastlane是一个ruby的脚本,可以在里面执行自定义的脚本文件,比如添加 sh "your_script.sh" 语句可以执行自定义的脚本命令,也可以添加 'pwd' (这里使用的是反引号)执行一个shell命令或者添加 exec("pwd") 执行一个shell命令

fastfile文件基本的编写

下面以一个真实的业务场景为例,该业务场景要求多渠道打包,并且不同渠道对应的配置有差异(info.plist文件配置不同和图标不同),打包支持上传到fir测试平台,不同渠道打包的命名方式是【时间+渠道+名称.ipa】格式,最后还需要把对应的dsym文件和ipa文件压缩归档为一个zip包文件。

# customise this file, documentation can be found here:
# https://docs.fastlane.tools/actions/
# all available actions: https://docs.fastlane.tools/actions
# can also be listed using the `fastlane actions` command

# change the syntax highlighting to ruby
# all lines starting with a # are ignored when running `fastlane`

# if you want to automatically update fastlane if a new version is available:
# update_fastlane

# this is the minimum version number required.
# update this, if you use features of a newer version
fastlane_version "2.62.0"

default_platform :ios

platform :ios do
 before_all do
 # env["slack_url"] = "https://hooks.slack.com/services/..."
 # cocoapods
 # carthage
 end

 desc "runs all the tests"
 lane :test do
 scan
 end

 # 定义全局参数:项目名称
 projname = "plush"
 # 定义全局参数:assets路径
 projassetspath = "../#{projname}/resources/assets.xcassets"
 # 定义全局参数:info.plist路径
 plistpath = "./#{projname}/resources/info.plist"
 # 定义全局参数:包名(接收之后压缩使用)
 pkgname = ""
 # 定义全局参数:基本包名
 basepkgname = "plush"
 # 定义全局参数:输出目录,对应output_directory配置
 outputbasedir = "output" 

 desc "自定义的渠道打包"
 desc "使用方式:苹果助手:`bundle exec fastlane customchannelbeta ch:pgzs shouldup:0`"
 desc "使用方式:91助手:`bundle exec fastlane customchannelbeta ch:91zs shouldup:0`"
 desc "op 支持参数: ch:渠道类型[pgzs, 91zs, appstore(默认)]"
 desc "op 支持参数: shouldup:是否需要上传[0, 1]"
 desc "op 支持参数: up:上传类型[testflight, fir]"
 desc "op 支持参数: exportmethod: export_method 配置[app-store, ad-hoc, package, enterprise, development(默认), developer-id] ** up为testflight需要制定为app-store **"
 desc "op 支持参数: version:版本(可选)"
 desc "op 支持参数: build:构建版本(可选,testflight和appstore这个参数需要制定一个不重复的)"
 lane :customchannelbeta do |op|
 
 # 渠道
 channel = op[:ch]
 if channel.nil? || channel.empty?
  channel = "appstore"
 end

 # 打包名字
 time = time.new
 datetimestr = "#{time.year}-#{time.month}-#{time.day}_#{time.hour}-#{time.min}"
 pkgname = "#{datetimestr}_#{channel}_#{basepkgname}.ipa"

 # match(type: "appstore") # more information: https://codesigning.guide
 #设置plist,此处传入plist路径,可以用来做环境配置
 set_info_plist_value(path: plistpath, key: "pt_channel", value: channel)


 # # 设置appicon
 # iconpath = op[:iconpath]
 # if iconpath.nil? || iconpath.empty?
 # iconpath = "channel_config/#{channel}/icon1024.png"
 # iconpath = "channel_config/91zs/icon1024.png"
 # end
 # puts "iconpath = #{iconpath}"
 # appicon(appicon_image_file: iconpath,
 # appicon_devices: [:ipad, :iphone, :ios_marketing, :watch, :watch_marketing])

 # 拷贝appicon.appiconset替换图标
 iconassetpath = op[:iconpath]
 if iconassetpath.nil? || iconassetpath.empty?
  iconassetpath = "../channel_config/#{channel}/appicon.appiconset"
 end
 cpcmd = "cp -frv #{iconassetpath} #{projassetspath}"
 `#{cpcmd}`
 puts "cpcmd = #{cpcmd}"

 # 设置显示版本和构建版本
 version = op[:version]
 build = op[:build]
 if version.nil? || version.empty?
 else
  increment_version_number(version_number: version)
 end
 if build.nil? || build.empty?
 else
  increment_build_number(build_number: build)
 end

 exportmethod = op[:exportmethod]
 if exportmethod.nil? || exportmethod.empty?
  exportmethod = "development"
 end

 puts "exportmethod = #{exportmethod} pkgname = #{pkgname} outputbasedir = #{outputbasedir}"

 # build_ios_app 参数配置
 gym(
  export_method: exportmethod, # 相当于配置 archives->export->mehtod:[app-store, ad-hoc, package, enterprise, development(默认), developer-id]
  output_name: pkgname,
  configuration: "release", # 相当于配置scheme->build configuration:[release, debug],release会生成dsym文件,而debug不会
  output_directory: outputbasedir,
  scheme: "plush",
 )


 # 是否需要上传配置,默认不上传
 shouldup = op[:shouldup]
 if shouldup == "1"
  # 上传测试包到测试平台类型,默认上传到fir,可以支持多种测试平台使用逗号分隔
  # testflight, fir
  uptype = op[:up]
  if uptype.nil? || uptype.empty?
  uptype = "fir"
  end
  if uptype.include? "testflight"
  pilot # 上传到testflight
  elsif uptype.include? "fir"
  firim(firim_api_token: "xxxxxxxx")#上传fir,如果是新项目,fir会自动创建
  end
 end
 end

 desc "上传到 app store"
 lane :release do
 # match(type: "appstore")
 # snapshot
 gym(
  export_method: "app-store", # 相当于配置 archives->export->mehtod:[app-store, ad-hoc, package, enterprise, development, developer-id]
  output_name: pkgname,
  configuration: "release", # 相当于配置scheme->build configuration:[release, debug],release会生成dsym文件,而debug不会
  output_directory: outputbasedir,
  scheme: "plush",
 )
 deliver(force: true)
 # frameit
 end

 # you can define as many lanes as you want

 after_all do |lane, op|
 # this block is called, only if the executed lane was successful

 # slack(
 # message: "successfully deployed new app update."
 # )

 if !pkgname.empty?
  filename = pkgname.gsub(".ipa", "")
  dsymname = "#{filename}.app.dsym.zip"
  zipfilename = "#{filename}.zip"

  # 使用zip命令压缩
  zipcmd = "zip ../#{outputbasedir}/#{zipfilename} ../#{outputbasedir}/#{dsymname} ../#{outputbasedir}/#{pkgname}"
  puts "begin zip #{zipcmd}"
  `#{zipcmd}`
 end

 end

 error do |lane, exception|
 # slack(
 # message: exception.message,
 # success: false
 # )
 end
end

使用方式:

#打91助手渠道的包不上传
bundle exec fastlane customchannelbeta ch:91zs shouldup:0

# 91助手渠道打包并且上传到fir
bundle exec fastlane customchannelbeta ch:91zs shouldup:1 up:fir

# testflight 打包上传
bundle exec fastlane customchannelbeta shouldup:1 up:testflight exportmethod:app-store

# appstore 打包上传
bundle exec fastlane release

执行打包的命令会在项目目录下生成一个output目录,内容如下

➜ output tree
.
├── 2018-1-17_11-53_pgzs_plush.app.dsym.zip
├── 2018-1-17_11-53_pgzs_plush.ipa
└── 2018-1-17_11-53_pgzs_plush.zip

添加第三方插件

fastlane有很多的第三方扩展插件,下面以添加firim插件为例

查找插件

使用fastlane search_plugins查找插件

➜ output fastlane search_plugins fir
...
+----------+-------------------------------------+-----------+
|     fastlane plugins 'fir'     |
+----------+-------------------------------------+-----------+
| name  | description       | downloads |
+----------+-------------------------------------+-----------+
| firebase | unofficial tool to access firebase | 1393  |
|   | project settings     |   |
| firim | firim        | 1142  |
| fir  | upload a new build to fir.im  | 807  |
+----------+-------------------------------------+-----------+

安装插件

使用fastlane add_plugin安装插件

# fastlane 安装firim插件
➜ plushgame fastlane add_plugin firim

# 添加 `bundle exec` 使用本地的插件,加快速度
➜ plushgame bundle exec fastlane add_plugin firim
[15:08:48]: plugin 'fastlane-plugin-firim' was added to './fastlane/pluginfile'
[15:08:48]: make sure to commit your gemfile, gemfile.lock and pluginfile to version control
installing plugin dependencies...
successfully installed plugins

添加成功之后再 fastlane 目录下多出了一个 pluginfile 文件,描述添加的插件的信息

# autogenerated by fastlane
#
# ensure this file is checked in to source control!

gem 'fastlane-plugin-firim'

插件使用方法

使用fastlane action查找某个action的使用方法和参数,下面是查找firim action的使用方法和参数的例子

➜ plush.app fastlane action firim
[23:50:31]: fastlane detected a gemfile in the current directory
[23:50:31]: however it seems like you don't use `bundle exec`
[23:50:31]: to launch fastlane faster, please use
[23:50:31]: 
[23:50:31]: $ bundle exec fastlane action firim
[23:50:31]: 
[23:50:31]: get started using a gemfile for fastlane https://docs.fastlane.tools/getting-started/ios/setup/#use-a-gemfile
+-------------------------+---------+-------------------------+
|      used plugins       |
+-------------------------+---------+-------------------------+
| plugin     | version | action     |
+-------------------------+---------+-------------------------+
| fastlane-plugin-firim | 0.1.0 | firim     |
| fastlane-plugin-appicon | 0.11.0 | android_appicon appicon |
+-------------------------+---------+-------------------------+

loading documentation for firim:

+------------------------------------+
|    firim    |
+------------------------------------+
| uses firim to upload ipa to fir.im |
|         |
| created by whlsxl     |
+------------------------------------+

+------------------------+--------------------------+------------------+---------+
|         firim options         |
+------------------------+--------------------------+------------------+---------+
| key     | description    | env var   | default |
+------------------------+--------------------------+------------------+---------+
| firim_api_token  | fir.im user api token |     |   |
| firim_username   | fir.im username, a sign |     |   |
|      | for identify different |     |   |
|      | token     |     |   |
| ipa     | path to your ipa file | deliver_ipa_path |   |
| icon     | path to the app icon, |     |   |
|      | must be jpg    |     |   |
| app_identifier   | the app's identifier  |     |   |
| app_name    | the app's name   |     |   |
| app_desc    | the app's desc   |     |   |
| app_short    | the app's short url  |     |   |
| app_is_opened   | app's download link  |     |   |
|      | whether opened   |     |   |
| app_is_show_plaza  | whether the app show in |     |   |
|      | plaza     |     |   |
| app_passwd    | the app's download page |     |   |
|      | password     |     |   |
| app_store_link_visible | whether show store link |     |   |
|      | in download page   |     |   |
| app_version   | the app's version  |     |   |
| app_build_version  | the app's build version |     |   |
| app_release_type  | the app's release type |     |   |
|      | (adhoc, inhouse)   |     |   |
| app_changelog   | the app's changelog  |     |   |
| app_info_to_file_path | append all [app's name] |     |   |
|      | : [url] to this file  |     |   |
+------------------------+--------------------------+------------------+---------+

more information can be found on https://docs.fastlane.tools/actions

在fastfile中添加firim action,把包上传到fir,token可以到fir后台点击右上角的设置获取即可

firim(firim_api_token: "xxxxxx")#上传fir,如果是新项目,fir会自动创建

结束

以上就是fastlane在项目中的实战演练,需要能够帮助到需要得人

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对的支持。