iOS使用fastlane實現持續整合

aron1992發表於2019-04-04

fastlane
fastlane

前言

最近公司有打渠道包的需求,領導說使用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
複製程式碼
  • 下載安裝包安裝

fastlane下載,下載完了之後開啟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在專案中的實戰演練,需要能夠幫助到需要得人

相關文章