iOS基于fastlane和jenkins的自動化打包

在開發(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動作。

使用CICD后開發(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需要使用bundlegem來管理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/進入到jenkinsGUI管理頁面。接下來打開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 = AutoBuildStagedebug.xcconfig中定了APP_NAME = AutoBuildDebugrelease.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é)議
    因為項目配置了DebugReleasestage三個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是利用gembundle來管理三方依賴的,所以打開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é):

本文主要介紹了CDCI在軟件開發(fā)中的運用,利用fastlanejenkins,并配合swift來實現(xiàn)iOS項目的自動化打包,并進行了實際的Demo演示,實際操作可能因環(huán)境不同報錯,只需要仔細閱讀終端錯誤提示并進行相應修復即可,fastlane的錯誤提示機制還是相對友善的。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,619評論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,155評論 3 425
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,635評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,539評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,255評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,646評論 1 326
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,655評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,838評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,399評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,146評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,338評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,893評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,565評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,983評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,257評論 1 292
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,059評論 3 397
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,296評論 2 376

推薦閱讀更多精彩內(nèi)容