安裝 fastlane
fastlane 官方給出了3種安裝方式,根據(jù)個人喜好自由選擇。
Homebrew | Installer Script | Rubygems |
---|---|---|
macOS | macOS | macOS or </br>Linux with Ruby 2.0.0</br> or above |
brew cask install fastlane | Download the zip file. </br> Then double click on theinstall script (or run it in a terminal window). | sudo gem install fastlane -NV |
一、fastlane 使用入門
1、cd 到項(xiàng)目根目錄
$ cd ./[工程目錄]
2、配置fastlane至項(xiàng)目
$ fastlane install
這里會要求你輸入appstore賬戶/密碼,以及各種詢問,不要盲目選擇y。
這里有個坑:Would you like to create your app on iTunes Connect and the Developer Portal? (y/n)
如果你是測試工程的話,一定要選n,否則iTunes Connect生成應(yīng)用之后沒法刪除。。
后面會輸出一大堆log,這里略過
install 結(jié)束之后,會看到你的工程目錄下會新增一個fastlane文件夾。
下面會有2個文件:
Appfile
# 這個文件里記錄了剛才安裝時的一些輸入,里面內(nèi)容可以按需更改
app_identifier "com.xxxxx" # The bundle identifier of your app
apple_id "" # Your Apple email address
team_id "[[DEV_PORTAL_TEAM_ID]]" # Developer Portal Team ID
# you can even provide different app identifiers, Apple IDs and team names per lane:
# More information: https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Appfile.md
Fastfile
platform :ios do
desc "Runs all the tests"
#此處 ↑ 省略
lane :test do
scan
end
#此處 ↓ 省略
end
為了更好閱讀,無關(guān)代碼已省略
在此之前,先介紹一下fastlane actions 可以理解為fastlane提供的一些api,一個action可以理解為一個方法,包括參數(shù)和返回值等等。
查詢所有的actions
$ fastlane actions
幾個常用的action
# gym 自動構(gòu)建最核心的action
| gym | Easily build and sign | KrauseFx |
| | your app using _gym_ | |
# match 自動管理證書 配合github作為證書倉庫使用
| match | Easily sync your | KrauseFx |
| | certificates and | |
| | profiles across your | |
| | team using git | |
# pem 自動管理推送證書
| pem | Makes sure a valid | KrauseFx |
| | push profile is | |
| | active and creates a | |
| | new one if needed | |
查詢某個action的詳情,以及需要傳入的參數(shù),例:查詢gym的詳情
$ fastalne action gym
Loading documentation for gym:
+----------------------------------------------+
| gym |
+----------------------------------------------+
| Easily build and sign your app using _gym_ |
| |
| More information: https://fastlane.tools/gym |
| |
| Created by KrauseFx |
+----------------------------------------------+
+------------------+------------------+------------------+------------------+
| gym Options |
+------------------+------------------+------------------+------------------+
| Key | Description | Env Var | Default |
+------------------+------------------+------------------+------------------+
| workspace | Path the | GYM_WORKSPACE | |
| | workspace file | | |
| project | Path the | GYM_PROJECT | |
| | project file | | |
| scheme | The project's | GYM_SCHEME | |
| | scheme. Make | | |
| | sure it's | | |
| | marked as | | |
| | `Shared` | | |
3、fastlane 自定義 lane
這里講一下 fastlane 配置文件 使用的是ruby語言。Fastfile作為fastlane的核心配置文件,我們可以自由修改
lane :test do
#可以認(rèn)為它是fastlane的一個方法,內(nèi)部可按需更改
end
我們可以更改Fastfile,添加自定義方法,以便完成相應(yīng)功能
推薦大家使用Sublime 選擇ruby語言進(jìn)行編輯
構(gòu)建SIT環(huán)境ipa包
lane :sit do
# snapshot
currentTime = Time.new.strftime("%Y-%m-%d-%H-%M")
ipaName = "SIT-#{currentTime}.ipa"
gym(
scheme: "xxxSIT",
export_method:"ad-hoc",
silent:true,
archive_path:"./build/sit/",
output_directory:"./build/sit/",
output_name:ipaName
) # Build your app - more options available
# You can also use other beta testing services here (run `fastlane actions`)
end
然后命令行執(zhí)行它:
$ fastlane sit
執(zhí)行順利的話,會在你的 項(xiàng)目目錄/build/sit/ 下新增一個xxx.ipa文件
到此,我們已經(jīng)通過fastlane實(shí)現(xiàn)了自動打包功能,基本功能已實(shí)現(xiàn)
二、fastlane 進(jìn)階
第一部分配置成功之后,或許會有一點(diǎn)成就感,也是一件值得高興的事,但不要松懈,好戲才剛剛開始。
有一種說法:懶人是推動社會進(jìn)步的最重要力量
好吧,我承認(rèn)我很懶,多一行代碼都懶得敲
那么問題來了:自動打包完成了,那之后呢,得想辦法讓它自己跑到測試手里啊,反正我是懶得手動發(fā)給測試人員。
1、上傳ipa至服務(wù)端
去fastlane官網(wǎng)逛了一遍,發(fā)現(xiàn)自定義action可以實(shí)現(xiàn)
$ fastlane new_action
緊接著輸入action名稱,然后會生成一個.rb文件
#部分注釋已被省略
module Fastlane
module Actions
module SharedValues
MY_ACTION_CUSTOM_VALUE = :MY_ACTION_CUSTOM_VALUE
end
class MyActionAction < Action
def self.run(params)
end
def self.available_options
[
FastlaneCore::ConfigItem.new(key: :api_token,
env_name: "FL_MY_ACTION_API_TOKEN",
description: "API Token for MyActionAction",
end),
]
end
def self.output
[
['MY_ACTION_CUSTOM_VALUE', 'A description of what this value contains']
]
end
def self.is_supported?(platform)
platform == :ios
end
end
end
end
作為一個iOS開發(fā)人員,第一眼看到這個文件是懵逼的!
趕緊百度ruby 惡補(bǔ)一下 然后開始我的修改action之路
1、ruby的變量不需要聲明
2、輸出語句:UI.message("xxx")
3、FastlaneCore::ConfigItem 聲明變量,作用為傳入?yún)?shù),每個參數(shù)對應(yīng)一個聲明
4、字典取值方式:params[:upload_url]
5、字典賦值方式:paramsDict = { 'envType' => "envType"}
6、ruby網(wǎng)絡(luò)請求:require 'faraday'
準(zhǔn)備工作差不多了,下面開始修改:
require 'faraday'
require 'faraday_middleware'
module Fastlane
module Actions
module SharedValues
UPLOAD_TO_TTEL_CUSTOM_VALUE = :UPLOAD_TO_TTEL_CUSTOM_VALUE
end
class UploadToTtelAction < Action
def self.run(params)
# 執(zhí)行到這里,會讀傳入?yún)?shù),這里會使用變量接收
# host, envType, prodType, changeLog
upload_url = params[:upload_url]
if upload_url.nil?
UI.user_error!("You have to provide a upload url")
end
envType = params[:envType]
if envType.nil?
UI.user_error!("You have to provide the envType")
end
prodType = params[:prodType]
if prodType.nil?
UI.user_error!("You have to provide the prodType")
end
changeLog = params[:changeLog]
if changeLog.nil?
changeLog = ''
end
# 這里 IPA_OUTPUT_PATH 是一個全局變量 需在fastfile里進(jìn)行賦值
build_file = ENV["IPA_OUTPUT_PATH"]
if build_file.nil?
UI.user_error!("IPA_OUTPUT_PATH is nil.")
end
UI.message("upload_url: #{upload_url}")
UI.message("envType: #{envType}")
UI.message("prodType: #{prodType}")
UI.message("changeLog: #{changeLog}")
UI.message("build_file: #{build_file}")
# start upload
conn_options = {
request: {
timeout: 1000,
open_timeout: 300
}
}
# 網(wǎng)絡(luò)請求客戶端 初始化
ttel_client = Faraday.new(nil, conn_options) do |c|
c.request :multipart
c.request :url_encoded
c.response :json, content_type: /\bjson$/
c.adapter :httpclient
c.ssl.verify = false
end
# 網(wǎng)絡(luò)請求參數(shù)
paramsDict = {
'envType' => "#{envType}",
'prodType' => "#{prodType}",
'changeLog' => "#{changeLog}",
'package' => Faraday::UploadIO.new(build_file, 'application/octet-stream')
}
UI.message "Start upload #{build_file} to ttel server..."
response = ttel_client.post upload_url, paramsDict
info = response.body
# 請求結(jié)束 驗(yàn)證返回值
if info["code"] == 1
UI.success "Upload success. "
else
UI.user_error!("Upload failed. error info : #{info}")
end
end
#####################################################
# @!group Documentation
#####################################################
def self.description
"uploading ipa or apk file to ttel Server, Only use for Ttel project"
end
def self.details
# Optional:
# this is your chance to provide a more detailed description of this action
"uploading ipa or apk file to ttel Server, Only use for Ttel project"
end
def self.available_options
# Define all options your action supports.
# Below a few examples
[
FastlaneCore::ConfigItem.new(key: :upload_url,
env_name: "UPLOAD_URL",
description: "The url of your upload archive file api, like https://172.16.32.23/app/upload",
is_string: true, # true: verifies the input is a string, false: every kind of value
default_value: ''), # the default value if the user didn't provide one
FastlaneCore::ConfigItem.new(key: :envType,
env_name: "ENV_TYPE",
description: "envType",
is_string: true, # true: verifies the input is a string, false: every kind of value
default_value: ''), # the default value if the user didn't provide one
# host, envType, prodType, changeLog
FastlaneCore::ConfigItem.new(key: :prodType,
env_name: "PROD_TYPE",
description: "prodType",
is_string: true, # true: verifies the input is a string, false: every kind of value
default_value: ''), # the default value if the user didn't provide one
FastlaneCore::ConfigItem.new(key: :changeLog,
env_name: "CHANGE_LOG",
description: "changeLog",
is_string: true, # true: verifies the input is a string, false: every kind of value
default_value: '') # the default value if the user didn't provide one
]
end
def self.output
[
['UPLOAD_TO_TTEL_CUSTOM_VALUE', 'A description of what this value contains']
]
end
def self.return_value
end
def self.authors
["https://github.com/xxx"]
end
def self.is_supported?(platform)
platform == :ios
end
end
end
end
大功告成,action寫好之后,需要在fastfile調(diào)用:
my_action(upload_url:"https://xxx:8081/apiv1/app/upload", envType:envType, prodType: "1001", changeLog:"")
這樣,我們就可以在打包完成之后,執(zhí)行此action,將ipa包上傳至我們自己的文件服務(wù)器。
又取得了一個階段性進(jìn)展,先小小的慶祝一下!
然而,懶是沒有止境的···
問題又來了:我其它項(xiàng)目也需要自動打包怎么辦,總不能每次都copy文件吧
2、制作插件
由于篇幅原因,請參考fastlane官方文檔,親測不難,半小時左右即可搞定,先準(zhǔn)備好梯子。
https://docs.fastlane.tools/plugins/create-plugin/
3、版本號自增
既然我們的目的是打造全自動持續(xù)集成,那么版本號還要手動修改也太low了吧
def auto_add_build_num
# fastlane build 號 處理代碼
# => https://developer.apple.com/library/content/qa/qa1827/_index.html
# => currentTime 當(dāng)前時間 20170401 string
# => build build 號 string
# => lastStr 小數(shù)點(diǎn)后2位 string
# => lastNum 小數(shù)點(diǎn)后2位(用于計算) int
#Build版本文件名
buildVerFileName = "buildVersion.txt"
buildNumber = ""
currentTime = Time.new.strftime("%Y%m%d")
#文件是否存在
if File.file?(buildVerFileName)
#存在
#讀取
buildNumber = File.read(buildVerFileName)
if buildNumber.include?"#{currentTime}."
# => 為當(dāng)天版本 計算迭代版本號
lastStr = buildNumber[buildNumber.length-2..buildNumber.length-1]
lastNum = lastStr.to_i
lastNum = lastNum + 1
lastStr = lastNum.to_s
if lastNum < 10
lastStr = lastStr.insert(0,"0")
end
buildNumber = "#{currentTime}.#{lastStr}"
else
buildNumber = "#{currentTime}.01"
end
else
#不存在
# => 非當(dāng)天版本 build 號重置
buildNumber = "#{currentTime}.01"
end
puts("*************| #{buildNumber} |*************")
# => 更改項(xiàng)目 build 號
increment_build_number(
build_number: buildNumber
)
#讀取文件
buildNumberFile = File.new(buildVerFileName, "w+")
#將新版本號寫入文件
buildNumberFile.syswrite(buildNumber)
#關(guān)閉
buildNumberFile.close
end
自增版本的時候需要注意一點(diǎn),首次配置一定要修改buildsettings里面的version配置
current project version 隨便填
versionsystem 選擇apple generic
如果不更改,則可能會出現(xiàn)自增失敗
小結(jié)
fastlane提供了一些非常方便的action來幫助我們完成持續(xù)集成的任務(wù),并且擴(kuò)展性很強(qiáng),支持插件,本文只是粗略的引導(dǎo)各位入門,還有很多action沒有一一介紹,如果大家有時間的話可以參考官方文檔:https://docs.fastlane.tools
持續(xù)集成 fastlane 介紹到此結(jié)束,接下來我會更新 gitlab 搭建并集成 fastlane,敬請期待。