在開發(fā)中經(jīng)常需要打測(cè)試包冀惭,然后上傳至蒲公英等三方平臺(tái),這其中需要經(jīng)歷的操作為:
- 拉取代碼
- 設(shè)置項(xiàng)目的打包環(huán)境
- 利用xcode進(jìn)行打包
- 上傳至蒲公英等三方平臺(tái)
每一次打包上面的過程必不可少查排,而且都是手工的,本篇文章我們采用CD(Continuous Delivery)持續(xù)交付
和CI(Continuous Integration)持續(xù)集成
來進(jìn)行自動(dòng)化打包一鍵操作,解放雙手,拒絕手動(dòng)的重復(fù)低效率勞動(dòng)疏唾。
CD(Continuous Delivery)
工廠里的裝配線以快速、自動(dòng)化函似、可重復(fù)的方式從原材料生產(chǎn)出消費(fèi)品槐脏。同樣,軟件交付管道以快速撇寞、自動(dòng)化和可重復(fù)的方式從源代碼生成發(fā)布版本顿天,如何完成這項(xiàng)工作的總體設(shè)計(jì)稱為“持續(xù)交付”(CD)
,啟動(dòng)裝配線的過程稱為“持續(xù)集成”(CI)
蔑担。持續(xù)交付是將應(yīng)用程序推送到交付環(huán)境的自動(dòng)化牌废,大多數(shù)開發(fā)團(tuán)隊(duì)通常具有一個(gè)或多個(gè)開發(fā)和測(cè)試環(huán)境,在該環(huán)境中會(huì)進(jìn)行應(yīng)用程序更改以進(jìn)行測(cè)試和審查啤握,本文介紹的Jenkins
就可以完成上面打包過程必經(jīng)步驟的后三步鸟缕。
CI(Continuous Integration)
CD
往往和CI
是一起配合著使用的,CI
的概念很廣(可以自己百度)在本文主要用來完成上面打包過程必經(jīng)步驟的第一步,并執(zhí)行CD
的動(dòng)作懂从, 這樣就完成了上面所說的四個(gè)步驟授段,同時(shí)在指定時(shí)間內(nèi)執(zhí)行CI
動(dòng)作,本文使用jenkins
來完成CI
動(dòng)作番甩。
使用CI
與CD
后開發(fā)的具體流程將會(huì)是如下圖所示的:
fastlane和jenkins的安裝
fastlane的安裝
首先附上官網(wǎng)地址https://docs.fastlane.tools/里面會(huì)有詳細(xì)的使用教程侵贵。
-
安裝Xcode Command Line Tools
由于fastlane
需要使用Xcode Command Line Tools
所以使用此命令進(jìn)行安裝xcode-select --install
,安裝完成后需要在Xcode
->Preferences
->Locations
中選擇剛剛安裝的Command Line Tools
对室。
安裝bundle
由于fastlane
需要使用bundle
和gem
來管理fastlane
所使用的一些依賴模燥,使用gem install bundler
命名來進(jìn)行安裝。-
安裝xcbeautify
由于打包需要使用使用xcodebuild
來進(jìn)行打包掩宜,使用brew install xcbeautify
進(jìn)行安裝蔫骂,不然會(huì)報(bào)如下錯(cuò)誤:
安裝fastlane
使用brew install fastlane
命令進(jìn)行安裝,當(dāng)然先要安裝Homebrew
牺汤。
jenkins的安裝
直接使用brew install jenkins
命令進(jìn)行安裝辽旋,安裝完成后使用brew services start jenkins
來啟動(dòng)jenkins
(同樣brew services stop jenkins
是關(guān)閉jenkins
)在瀏覽器中輸入http://localhost:8080/進(jìn)入到jenkins
的GUI
管理頁面。接下來打開Jenkins
后會(huì)讓去一個(gè)填寫password
的頁面如下圖檐迟,存儲(chǔ)password
的地方就是圖片上那行紅色字體目錄下补胚,使用終端cat +
紅色字體路徑就看到了。
然后安裝推薦的插件:
進(jìn)入后首先第一件事就是安裝
Xcode integration
插件追迟,選擇系統(tǒng)管理’ -- ‘插件管理’
‘溶其,然后搜索進(jìn)行安裝。至此上面的二個(gè)算是初步安裝配置完成敦间。
項(xiàng)目的創(chuàng)建
為了更好的演示整個(gè)流程瓶逃,我們采用新建一個(gè)項(xiàng)目進(jìn)行講解,同時(shí)利用.xcconfig
文件來進(jìn)行環(huán)境的管理廓块,具體build configuration
文件的使用可見我另外一篇文章iOS開發(fā)中xconfig和script腳本的使用厢绝,項(xiàng)目新建了一個(gè)stage
環(huán)境:
同時(shí)利用
Custom Flags
來切換環(huán)境:
stage.xcconfig
中定了APP_NAME = AutoBuildStage
,debug.xcconfig
中定了APP_NAME = AutoBuildDebug
带猴,release.xcconfig
中定了APP_NAME = AutoBuildRelease
昔汉,同時(shí)并在info.plist
增加Bundle display name
這個(gè)key
并設(shè)置為$(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
則會(huì)envLabel
顯示不用的環(huán)境信息,同時(shí)安裝的APP
名字也會(huì)是.xcconfig
文件中所設(shè)置的值拴清。
利用fastlane初始化項(xiàng)目
cd
到項(xiàng)目所在的目錄利用fastlane init swift
進(jìn)行初始化靶病,會(huì)讓選擇構(gòu)建的方式,這里輸入4采用自定義的方式進(jìn)行構(gòu)建贷掖。
fastlane
現(xiàn)在是支持使用swift
的嫡秕,這里我們采用熟悉的swift
來編寫腳本,當(dāng)然swift
寫的腳本也是可以裝換成Ruby
格式的
構(gòu)建完成會(huì)自動(dòng)生成如下文件:
項(xiàng)目設(shè)置手動(dòng)簽名
在寫打包代碼前苹威,首先設(shè)置項(xiàng)目的簽名采用手動(dòng)簽名昆咽,項(xiàng)目較大團(tuán)隊(duì)較多時(shí)一定要采用手動(dòng)簽名的方式,不要使用Xcode
的自動(dòng)簽名方式,因?yàn)橐坏┠硞€(gè)人的簽名出問題就會(huì)導(dǎo)致其他人拉完代碼后也出問題掷酗,所以本項(xiàng)目采用手動(dòng)簽名的方式调违,在此先導(dǎo)出自己的.p12
證書和描述文件以備后面使用。
編寫自動(dòng)打包的代碼
打開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é)尾的稱之為一個(gè)lane
技肩,在終端中通過fastlane <laneName>
命令來執(zhí)行這個(gè)lane
,其實(shí)就等于執(zhí)行這個(gè)函數(shù)浮声。
-
首先新建一個(gè)Configuration協(xié)議
因?yàn)轫?xiàng)目配置了Debug
虚婿,Release
和stage
三個(gè)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枚舉
新建一個(gè)ProjectSetting
枚舉來保存一些項(xiàng)目打包過程中需要的常用配置信息然痊。
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"
// 蒲公英平臺(tái)的信息
static let pgyerApiKey = "xxxxxxxxxxxx";// 填入自己在平臺(tái)申請(qǐng)的
static let pgyerUserKey = "xxxxxxxxxxxxx";// 填入自己在平臺(tái)申請(qǐng)的
}
對(duì)于
keyChainDefaultPath
變量采用了environmentVariable(get:)
的方法來獲取系統(tǒng)的環(huán)境變量,系統(tǒng)的環(huán)境變量在終端中設(shè)置的命令為:export keyChainDefaultPath =”YOUR_ keyChainDefaultPath”
屉符,采用系統(tǒng)變量的好處是更加安全剧浸,不用在代碼中體現(xiàn)敏感信息。
- 定義一個(gè)Configuration
struct Staging: Configuration {
var certificate = "AppleDis"
var provisioningProfile = "AdHocMLife"
var buildConfiguration = "stage"
var appIdentifier = "com.mamba.iOSAutoBuild"
var exportMethod = "ad-hoc"
}
這樣想切換環(huán)境時(shí)只需要更改
buildConfiguration
變量即可矗钟。
- 打包時(shí)的簽名操作
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)")
}
// 新建一個(gè)以項(xiàng)目名命名的鑰匙串
createKeychain(
name: "\(keyChainName)",
password: stubKeyChainPassword,
defaultKeychain: false,
unlock: true,
timeout: 3600,
lockWhenSleeps: true
)
// 導(dǎo)入證書到自定義的鑰匙串
importCertificate(
certificatePath: "\(ProjectSetting.codeSigningPath)/\(config.certificate).p12",
certificatePassword: "\(ProjectSetting.certificatePassword)",
keychainName: keyChainName,
keychainPassword: "\(stubKeyChainPassword)"
)
// 更新項(xiàng)目的簽名設(shè)置
updateProjectProvisioning(
xcodeproj: "\(ProjectSetting.project)",
profile: "\(ProjectSetting.codeSigningPath)/\(config.provisioningProfile).mobileprovision",
targetFilter: "^\(ProjectSetting.target)$",
buildConfiguration: "\(config.buildConfiguration)",
certificate: "\(config.certificate).p12"
)
}
}
特別注意:
updateProjectProvisioning
是會(huì)更改項(xiàng)目的設(shè)置的唆香,所以最好是打包的機(jī)器是額外的一臺(tái)機(jī)器(熟稱打包機(jī)),當(dāng)然CD
是不會(huì)提交代碼到遠(yuǎn)端倉庫的吨艇,上面的一些action
可以查看fastlane的官網(wǎng)躬它,里面有詳細(xì)介紹。
- 打包構(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
是當(dāng)前時(shí)間變量东涡,代碼中沒有體現(xiàn)虑凛。
- 最后是打包
func developerStrageLane() {
desc("Mamba Create a developer stage")
package(config: Staging())
}
至此我們就可以利用fastlane
進(jìn)行打包了,在終端中輸入bundle exec fastlane developerStrage
即可软啼,最后會(huì)有打包完成的成功信息大致如下;
聯(lián)動(dòng)jenkins
上面的部分只是完成了打包延柠,現(xiàn)在繼續(xù)利用jenkins
來完成定時(shí)的遠(yuǎn)端代碼拉取祸挪,并打包同時(shí)長(zhǎng)傳蒲公英。fastlane
現(xiàn)在支持swift
贞间,同時(shí)也是支持第三方插件的,首先對(duì)上面的fastlane
過程中加入cocoapods
和蒲公英的插件。
加入cocoapods
由于前面說了fastlane
是利用gem
和bundle
來管理三方依賴的勺像,所以打開Gemfile
文件滔驾,增加gem "cocoapods"
保存,然后當(dāng)我們?cè)诮K端執(zhí)行bundle install — path vendor/bundler
時(shí)會(huì)安裝Gemfile
文件中的依賴峻仇,當(dāng)然一般我們的打包機(jī)是自己安裝了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
到項(xiàng)目所在的目錄執(zhí)行sudo fastlane add_plugin pgyer
凡蚜,會(huì)發(fā)現(xiàn)增加了一個(gè)Pluginfile
文件人断,具體的改變直接放官網(wǎng)說的吧:
-
執(zhí)行pgyer的action
直接使用下面代碼上傳至蒲公英
pgyer(apiKey:"\(ProjectSetting.pgyerApiKey)", userKey: "\(ProjectSetting.pgyerUserKey)")
新建jenkins任務(wù)
進(jìn)入jenkins
主頁,點(diǎn)擊新建任務(wù)朝蜘,輸入任務(wù)名后選擇構(gòu)建自由風(fēng)格的軟件項(xiàng)目恶迈。
在源碼管理處填入遠(yuǎn)端倉庫地址。
在構(gòu)建觸發(fā)器中輸入定時(shí)構(gòu)建的時(shí)間谱醇。
具體的時(shí)間語法百度即可暇仲,這里是每天每隔半小時(shí)自動(dòng)執(zhí)行任務(wù)。
在構(gòu)建處選擇執(zhí)行shell副渴,并填入需要執(zhí)行的命令奈附。
注意:這里可以填入上文提到的系統(tǒng)變量,例如在
cd
語句的后面加上export CERTIFICATE_PASSWORD=”xxx”
或者bundle install — path vendor/bundler
佳晶。
最后點(diǎn)擊保存即可桅狠,可在構(gòu)建歷史中查看歷史構(gòu)建記錄。
構(gòu)建任務(wù)的過程中可能會(huì)報(bào)
bundle :fastlane command not find
的錯(cuò)誤轿秧,解決辦法是在jenkins
的系統(tǒng)管理->系統(tǒng)設(shè)置->全局屬性->環(huán)境變量 增加鍵PATH
值:終端輸出值:(echo $PATH
)
總結(jié):
本文主要介紹了CD
和CI
在軟件開發(fā)中的運(yùn)用中跌,利用fastlane
和jenkins
,并配合swift
來實(shí)現(xiàn)iOS
項(xiàng)目的自動(dòng)化打包菇篡,并進(jìn)行了實(shí)際的Demo
演示漩符,實(shí)際操作可能因環(huán)境不同報(bào)錯(cuò),只需要仔細(xì)閱讀終端錯(cuò)誤提示并進(jìn)行相應(yīng)修復(fù)即可驱还,fastlane
的錯(cuò)誤提示機(jī)制還是相對(duì)友善的嗜暴。