在開發(fā)中經(jīng)常需要打測試包,然后上傳至蒲公英等三方平臺,這其中需要經(jīng)歷的操作為:
- 拉取代碼
- 設置項目的打包環(huán)境
- 利用xcode進行打包
- 上傳至蒲公英等三方平臺
每一次打包上面的過程必不可少,而且都是手工的,本篇文章我們采用CD(Continuous Delivery)持續(xù)交付
和CI(Continuous Integration)持續(xù)集成
來進行自動化打包一鍵操作,解放雙手,拒絕手動的重復低效率勞動。
CD(Continuous Delivery)
工廠里的裝配線以快速、自動化、可重復的方式從原材料生產(chǎn)出消費品。同樣,軟件交付管道以快速、自動化和可重復的方式從源代碼生成發(fā)布版本,如何完成這項工作的總體設計稱為“持續(xù)交付”(CD)
,啟動裝配線的過程稱為“持續(xù)集成”(CI)
。持續(xù)交付是將應用程序推送到交付環(huán)境的自動化,大多數(shù)開發(fā)團隊通常具有一個或多個開發(fā)和測試環(huán)境,在該環(huán)境中會進行應用程序更改以進行測試和審查,本文介紹的Jenkins
就可以完成上面打包過程必經(jīng)步驟的后三步。
CI(Continuous Integration)
CD
往往和CI
是一起配合著使用的,CI
的概念很廣(可以自己百度)在本文主要用來完成上面打包過程必經(jīng)步驟的第一步,并執(zhí)行CD
的動作, 這樣就完成了上面所說的四個步驟,同時在指定時間內(nèi)執(zhí)行CI
動作,本文使用jenkins
來完成CI
動作。
使用CI
與CD
后開發(fā)的具體流程將會是如下圖所示的:
fastlane和jenkins的安裝
fastlane的安裝
首先附上官網(wǎng)地址https://docs.fastlane.tools/里面會有詳細的使用教程。
-
安裝Xcode Command Line Tools
由于fastlane
需要使用Xcode Command Line Tools
所以使用此命令進行安裝xcode-select --install
,安裝完成后需要在Xcode
->Preferences
->Locations
中選擇剛剛安裝的Command Line Tools
。
安裝bundle
由于fastlane
需要使用bundle
和gem
來管理fastlane
所使用的一些依賴,使用gem install bundler
命名來進行安裝。-
安裝xcbeautify
由于打包需要使用使用xcodebuild
來進行打包,使用brew install xcbeautify
進行安裝,不然會報如下錯誤:
安裝fastlane
使用brew install fastlane
命令進行安裝,當然先要安裝Homebrew
。
jenkins的安裝
直接使用brew install jenkins
命令進行安裝,安裝完成后使用brew services start jenkins
來啟動jenkins
(同樣brew services stop jenkins
是關(guān)閉jenkins
)在瀏覽器中輸入http://localhost:8080/進入到jenkins
的GUI
管理頁面。接下來打開Jenkins
后會讓去一個填寫password
的頁面如下圖,存儲password
的地方就是圖片上那行紅色字體目錄下,使用終端cat +
紅色字體路徑就看到了。
然后安裝推薦的插件:
進入后首先第一件事就是安裝
Xcode integration
插件,選擇系統(tǒng)管理’ -- ‘插件管理’
‘,然后搜索進行安裝。至此上面的二個算是初步安裝配置完成。
項目的創(chuàng)建
為了更好的演示整個流程,我們采用新建一個項目進行講解,同時利用.xcconfig
文件來進行環(huán)境的管理,具體build configuration
文件的使用可見我另外一篇文章iOS開發(fā)中xconfig和script腳本的使用,項目新建了一個stage
環(huán)境:
同時利用
Custom Flags
來切換環(huán)境:
stage.xcconfig
中定了APP_NAME = AutoBuildStage
,debug.xcconfig
中定了APP_NAME = AutoBuildDebug
,release.xcconfig
中定了APP_NAME = AutoBuildRelease
,同時并在info.plist
增加Bundle display name
這個key
并設置為$(APP_NAME)
:
ViewController
中的代碼如下:
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var envLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
#if Debug
envLabel.text = "環(huán)境是Debug"
#elseif STG
envLabel.text = "環(huán)境是Stage"
#elseif Release
envLabel.text = "環(huán)境是Release"
#endif
}
}
這樣配置后在Edit Scheme
中選擇不同的build configuration
則會envLabel
顯示不用的環(huán)境信息,同時安裝的APP
名字也會是.xcconfig
文件中所設置的值。
利用fastlane初始化項目
cd
到項目所在的目錄利用fastlane init swift
進行初始化,會讓選擇構(gòu)建的方式,這里輸入4采用自定義的方式進行構(gòu)建。
fastlane
現(xiàn)在是支持使用swift
的,這里我們采用熟悉的swift
來編寫腳本,當然swift
寫的腳本也是可以裝換成Ruby
格式的
構(gòu)建完成會自動生成如下文件:
項目設置手動簽名
在寫打包代碼前,首先設置項目的簽名采用手動簽名,項目較大團隊較多時一定要采用手動簽名的方式,不要使用Xcode
的自動簽名方式,因為一旦某個人的簽名出問題就會導致其他人拉完代碼后也出問題,所以本項目采用手動簽名的方式,在此先導出自己的.p12
證書和描述文件以備后面使用。
編寫自動打包的代碼
打開fastlane/swift/FastlaneSwiftRunner/FastlaneSwiftRunner.xcodeproj
所在的工程文件,并打開Fastfile.swift
文件里面代碼如下所示:
import Foundation
class Fastfile: LaneFile {
func customLane() {
desc("Description of what the lane does")
// add actions here: https://docs.fastlane.tools/actions
}
}
在
Fastfile
類中函數(shù)以lane
結(jié)尾的稱之為一個lane
,在終端中通過fastlane <laneName>
命令來執(zhí)行這個lane
,其實就等于執(zhí)行這個函數(shù)。
-
首先新建一個Configuration協(xié)議
因為項目配置了Debug
,Release
和stage
三個Configuration
,所以利用協(xié)議能更好的改變Configuration
。
protocol Configuration {
/// file name of the certificate
var certificate: String { get }
/// file name of the provisioning profile
var provisioningProfile: String { get }
/// configuration name in xcode project
var buildConfiguration: String { get }
/// the app id for this configuration
var appIdentifier: String { get }
/// export methods, such as "ad-doc" or "appstore"
var exportMethod: String { get }
}
-
新建ProjectSetting枚舉
新建一個ProjectSetting
枚舉來保存一些項目打包過程中需要的常用配置信息。
enum ProjectSetting {
static var workspace = " iOSAutoBuild.xcworkspace"
static var project = "iOSAutoBuild.xcodeproj"
static var scheme = "iOSAutoBuild"
static var target = "iOSAutoBuild"
static var productName = "iOSAutoBuild"
static let devices: [String] = ["iPhone 8", "iPad Air"]
static let codeSigningPath = "certs"
// 采用環(huán)境變量的方式更加安全
static let keyChainDefaultPath = environmentVariable(get: "KEYCHAIN_DEFAULT_PATH").replacingOccurrences(of: "\"", with: "")
static let certificatePassword = ""
static let sdk = "iphoneos15.2"
// 蒲公英平臺的信息
static let pgyerApiKey = "xxxxxxxxxxxx";// 填入自己在平臺申請的
static let pgyerUserKey = "xxxxxxxxxxxxx";// 填入自己在平臺申請的
}
對于
keyChainDefaultPath
變量采用了environmentVariable(get:)
的方法來獲取系統(tǒng)的環(huán)境變量,系統(tǒng)的環(huán)境變量在終端中設置的命令為:export keyChainDefaultPath =”YOUR_ keyChainDefaultPath”
,采用系統(tǒng)變量的好處是更加安全,不用在代碼中體現(xiàn)敏感信息。
- 定義一個Configuration
struct Staging: Configuration {
var certificate = "AppleDis"
var provisioningProfile = "AdHocMLife"
var buildConfiguration = "stage"
var appIdentifier = "com.mamba.iOSAutoBuild"
var exportMethod = "ad-hoc"
}
這樣想切換環(huán)境時只需要更改
buildConfiguration
變量即可。
- 打包時的簽名操作
class Fastfile: LaneFile {
var stubKeyChainPassword: String = environmentVariable(get: "KEYCHAIN_PASSWORD")
var keyChainName: String {
return "\(ProjectSetting.productName).keychain"
}
var keyChainDefaultFilePath: String {
return "\(ProjectSetting.keyChainDefaultPath)/\(keyChainName)-db"
}
func package(config: Configuration) {
if FileManager.default.fileExists(atPath: keyChainDefaultFilePath) {
deleteKeychain(name: "\(keyChainName)")
}
// 新建一個以項目名命名的鑰匙串
createKeychain(
name: "\(keyChainName)",
password: stubKeyChainPassword,
defaultKeychain: false,
unlock: true,
timeout: 3600,
lockWhenSleeps: true
)
// 導入證書到自定義的鑰匙串
importCertificate(
certificatePath: "\(ProjectSetting.codeSigningPath)/\(config.certificate).p12",
certificatePassword: "\(ProjectSetting.certificatePassword)",
keychainName: keyChainName,
keychainPassword: "\(stubKeyChainPassword)"
)
// 更新項目的簽名設置
updateProjectProvisioning(
xcodeproj: "\(ProjectSetting.project)",
profile: "\(ProjectSetting.codeSigningPath)/\(config.provisioningProfile).mobileprovision",
targetFilter: "^\(ProjectSetting.target)$",
buildConfiguration: "\(config.buildConfiguration)",
certificate: "\(config.certificate).p12"
)
}
}
特別注意:
updateProjectProvisioning
是會更改項目的設置的,所以最好是打包的機器是額外的一臺機器(熟稱打包機),當然CD
是不會提交代碼到遠端倉庫的,上面的一些action
可以查看fastlane的官網(wǎng),里面有詳細介紹。
- 打包構(gòu)建buildApp
buildApp(
scheme: "\(ProjectSetting.scheme)",
clean: true,
outputDirectory: "./打包目錄",
outputName: "\(ProjectSetting.productName)_\(dateStr).ipa",
configuration: "\(config.buildConfiguration)",
silent: true,
exportMethod: "\(config.exportMethod)",
exportOptions: optionsArr,
sdk: "\(ProjectSetting.sdk)"
)
dateStr
是當前時間變量,代碼中沒有體現(xiàn)。
- 最后是打包
func developerStrageLane() {
desc("Mamba Create a developer stage")
package(config: Staging())
}
至此我們就可以利用fastlane
進行打包了,在終端中輸入bundle exec fastlane developerStrage
即可,最后會有打包完成的成功信息大致如下;
聯(lián)動jenkins
上面的部分只是完成了打包,現(xiàn)在繼續(xù)利用jenkins
來完成定時的遠端代碼拉取,并打包同時長傳蒲公英。fastlane
現(xiàn)在支持swift
,同時也是支持第三方插件的,首先對上面的fastlane
過程中加入cocoapods
和蒲公英的插件。
加入cocoapods
由于前面說了fastlane
是利用gem
和bundle
來管理三方依賴的,所以打開Gemfile
文件,增加gem "cocoapods"
保存,然后當我們在終端執(zhí)行bundle install — path vendor/bundler
時會安裝Gemfile
文件中的依賴,當然一般我們的打包機是自己安裝了cocoapods
的。
- 執(zhí)行
cocoapods
在上面的package
方法上面增加如下代碼:
func beforeAll() {
cocoapods()
}
加入蒲公英插件
-
pgyer插件的安裝
插件安裝前修改gemfile
文件,增加如下信息:
plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
eval_gemfile(plugins_path) if File.exist?(plugins_path)
按照官網(wǎng)的說法是需要鏈接gemfile
和后面生成的Pluginfile
文件,cd
到項目所在的目錄執(zhí)行sudo fastlane add_plugin pgyer
,會發(fā)現(xiàn)增加了一個Pluginfile
文件,具體的改變直接放官網(wǎng)說的吧:
-
執(zhí)行pgyer的action
直接使用下面代碼上傳至蒲公英
pgyer(apiKey:"\(ProjectSetting.pgyerApiKey)", userKey: "\(ProjectSetting.pgyerUserKey)")
新建jenkins任務
進入jenkins
主頁,點擊新建任務,輸入任務名后選擇構(gòu)建自由風格的軟件項目。
在源碼管理處填入遠端倉庫地址。
在構(gòu)建觸發(fā)器中輸入定時構(gòu)建的時間。
具體的時間語法百度即可,這里是每天每隔半小時自動執(zhí)行任務。
在構(gòu)建處選擇執(zhí)行shell,并填入需要執(zhí)行的命令。
注意:這里可以填入上文提到的系統(tǒng)變量,例如在
cd
語句的后面加上export CERTIFICATE_PASSWORD=”xxx”
或者bundle install — path vendor/bundler
。
最后點擊保存即可,可在構(gòu)建歷史中查看歷史構(gòu)建記錄。
構(gòu)建任務的過程中可能會報
bundle :fastlane command not find
的錯誤,解決辦法是在jenkins
的系統(tǒng)管理->系統(tǒng)設置->全局屬性->環(huán)境變量 增加鍵PATH
值:終端輸出值:(echo $PATH
)
總結(jié):
本文主要介紹了CD
和CI
在軟件開發(fā)中的運用,利用fastlane
和jenkins
,并配合swift
來實現(xiàn)iOS
項目的自動化打包,并進行了實際的Demo
演示,實際操作可能因環(huán)境不同報錯,只需要仔細閱讀終端錯誤提示并進行相應修復即可,fastlane
的錯誤提示機制還是相對友善的。