iOS基于fastlane和jenkins的自動(dòng)化打包

在開發(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)作番甩。

使用CICD后開發(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需要使用bundlegem來管理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)入到jenkinsGUI管理頁面。接下來打開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 = AutoBuildStagedebug.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虚婿,Releasestage三個(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是利用gembundle來管理三方依賴的勺像,所以打開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é):

本文主要介紹了CDCI在軟件開發(fā)中的運(yùn)用中跌,利用fastlanejenkins,并配合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ì)友善的嗜暴。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市议蟆,隨后出現(xiàn)的幾起案子闷沥,更是在濱河造成了極大的恐慌,老刑警劉巖咐容,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件舆逃,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡戳粒,警方通過查閱死者的電腦和手機(jī)路狮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蔚约,“玉大人奄妨,你說我怎么就攤上這事∑凰睿” “怎么了砸抛?”我有些...
    開封第一講書人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵评雌,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我锰悼,道長(zhǎng)柳骄,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任箕般,我火速辦了婚禮耐薯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘丝里。我一直安慰自己曲初,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開白布杯聚。 她就那樣靜靜地躺著臼婆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪幌绍。 梳的紋絲不亂的頭發(fā)上颁褂,一...
    開封第一講書人閱讀 52,255評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音傀广,去河邊找鬼颁独。 笑死,一個(gè)胖子當(dāng)著我的面吹牛伪冰,可吹牛的內(nèi)容都是我干的誓酒。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼贮聂,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼靠柑!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起吓懈,我...
    開封第一講書人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤歼冰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后耻警,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體停巷,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年榕栏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蕾各。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡扒磁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出式曲,到底是詐尸還是另有隱情妨托,我是刑警寧澤缸榛,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站兰伤,受9級(jí)特大地震影響内颗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜敦腔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一均澳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧符衔,春花似錦找前、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至形帮,卻和暖如春槽惫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背辩撑。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來泰國打工界斜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人槐臀。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓锄蹂,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親水慨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子得糜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359

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