Fastlane實戰(zhàn)(一):移動開發(fā)自動化之道

本人一直認為:在程序的世界里藻糖,一切重復性的江咳,流程化的工作都可以交給自動化去完成峭火。

在移動開發(fā)中也是如此:其實寫代碼只是我們開發(fā)過程中的一部分爱致,除此之外我們還需要進行編譯烤送,打包,上傳糠悯,部署帮坚,庫管理妻往,版本控制等等Coding之外的雜事,而正是這些乏味而重復的工作占用了我們寶貴的時間试和。

所以在“懶人”遍布的工程師世界中讯泣,總會有人想盡辦法做出改變,于是這些“懶人”們樂此不疲的造出許多美妙的輪子阅悍,既方便了自己好渠,又幫助了他人,讓這個世界變得更加美好节视。

今天就給大家介紹其中一個輪子:Fastlane拳锚,這個Github上的明星項目截止到目前共獲得1萬多個Star,并且還有1500多個Fork寻行。

Fastlane在我們團隊中的推廣和應用

怎么樣霍掺,聽起來是不是很牛?不過先別急拌蜘,在進入正題之前抗楔,我想跟大家簡單分享一下我們移動團隊在開展持續(xù)測試和持續(xù)交付工作中的一些心得體會。

大家都知道拦坠,最近幾年连躏,隨著智能手機的普及,移動端不僅要承載更多業(yè)務場景的實現(xiàn)贞滨,并且還要應對不斷變化業(yè)務需求入热。這就要求我們移動團隊能夠迅速響應變化,快速的迭代晓铆。那么隨之而來的問題就是如何保障在不犧牲質(zhì)量的前提下勺良,盡可能的提升速度,我認為這一切需要建立在高質(zhì)量的持續(xù)測試和持續(xù)交付體系之上骄噪。

但是移動端本身興起的時間就比較短尚困,各方面的成熟度也有所欠缺链蕊,能夠拿來用的工具更是少之又少事甜,隨著業(yè)務深度廣度的增加,迭代速度的加快,諸如證書管理,打包滋将,上傳邻悬,發(fā)布這類重復而毫無技術含量的工作逐漸占用了大家的時間,團隊內(nèi)部對此詬病不已随闽。

所以我們的架構團隊從去年初就一直在尋找這樣的一種工具父丰,一種解決方案,旨在徹底解放工程師的“雙手”橱脸。

剛開始我們嘗試使用Jenkins+Fir搭建了一套持續(xù)測試的環(huán)境,流程如下圖:

Jenkins+Fir

說實話分苇,效果還是可以的添诉,至少在一定的時期內(nèi)滿足了我們的要求,但是Jenkins本身只是一個通用的CI流程管理系統(tǒng)医寿,本身并不提供諸如ITC提包和Meta內(nèi)容管理栏赴,簽名,證書管理等等和移動端業(yè)務緊密結合的場景靖秩,而且配置的過程相當繁瑣须眷。

去年年底的時候,機緣巧合之下沟突,我們在Github上發(fā)現(xiàn)了Fastlane花颗,看了Readme后感覺有戲,于是決定嘗試一下惠拭。其實剛開始的時候扩劝,我們也只是用Fastlane來解決iOS團隊內(nèi)證書同步和上傳ITC的問題,但是隨著深入的研究职辅,發(fā)現(xiàn)其實Fastlane能做的更多棒呛,于是我們將其逐步應用到iOS端的更多的場景,比如:私有Pod的發(fā)布域携,代碼的靜態(tài)檢查簇秒,UIAutomation測試等等,接著又推廣到Andriod平臺秀鞭,完成諸如私有AAR的發(fā)布趋观,Monkey測試等等一系列任務。現(xiàn)在可以說Fastlane已經(jīng)變成了我們工作中密不可分的一部分锋边。

另外拆内,F(xiàn)astlane本身也可以和Jenkins,Circle等主流CI系統(tǒng)做很好的集成宠默,并且由于主要的CI流程都由Fastlane來管理和執(zhí)行麸恍,所以從根本上降低了這些系統(tǒng)配置的復雜度。

Fastlane簡介

說了這么多,我們回到今天的主角身上抹沪,首先先簡單介紹一下:Fastlane是用Ruby語言編寫的一套自動化工具集和框架刻肄,每一個工具實際都對應一個Ruby腳本,用來執(zhí)行某一個特定的任務融欧,而Fastlane核心框架則允許使用者通過類似配置文件的形式敏弃,將不同的工具有機而靈活的結合在一起,從而形成一個個完整的自動化流程噪馏。

到目前為止麦到,F(xiàn)astlane的工具集大約包含170多個小工具,基本上涵蓋了打包欠肾,簽名瓶颠,測試,部署刺桃,發(fā)布粹淋,庫管理等等移動開發(fā)中涉及到的內(nèi)容。

關于這些工具的描述和使用可以看這里:https://docs.fastlane.tools/actions/Actions/

如果這些工具仍然沒有符合你需求的瑟慈,沒有關系桃移,得益于Fastlane本身強大的Action和Plugin機制,如果你恰好懂一些Ruby開發(fā)的話葛碧,可以很輕易的編寫出自己想要的工具借杰。

其實真正官方出品的工具大約占一半左右,剩下的都是Github社區(qū)成員貢獻的进泼,本人有幸也貢獻過其中一個第步。(這里建議移動開發(fā)工程師還是需要學習一門腳本語言的,比如Ruby)

Fastlane的安裝非常簡單缘琅,和Cocoapods一樣粘都,F(xiàn)astlane也可以通過RubyGems來安裝,如果你的電腦上有Ruby環(huán)境的話刷袍,那么只需要執(zhí)行如下命令翩隧,即可完成:

gem install fastlane

移動客戶端持續(xù)測試和持續(xù)交付的常見場景及痛點

Fastlane本身能做的事情很多,但是其中一個最為重要的作用就是能夠無縫嵌入在持續(xù)測試和持續(xù)交付體系中呻纹。

下面堆生,為了便于大家理解,我拿iOS為例雷酪,舉幾個我們在移動開發(fā)過程中常常會遇到的場景:

場景一
當一個迭代開發(fā)測試結束淑仆,服務器端上線之后,我們會使用Testflight進行線上跟測哥力,一般會執(zhí)行如下流程:

  1. 執(zhí)行Git Pull命令蔗怠,拉最新的代碼到本地
  2. Pod Install安裝最新的依賴庫
  3. 在Xcode中將Build Version增加
  4. 在Xcode點擊Archive編譯并打包
  5. 選擇輸出一個iOS AppStore模式的ipa文件
  6. 通過Application Loader將IPA上傳到ITC(TestFlight)
  7. 然后等待ITC Process完成后墩弯,登錄上去選擇剛才的Build進行TestFlight測試
  8. 由于修改了版本號,所以需要將代碼Commit和Push一下

如果線上跟測發(fā)現(xiàn)有問題寞射,那么需要修復完畢后重復上面的8個步驟渔工。
其實做過這件事的同學應該都有體會,順利的話差不多一次得30分鐘吧桥温,如果某一次Build Version忘記增加了引矩,那么前面的工作就白做了。
在我們團隊早期的時候侵浸,由于自動化體系尚未建立旺韭,我們有一個同事專門負責此事,在線上跟測的這兩天掏觉,他有半天時間幾乎干不了別的区端,基本上都在打包上傳,說出來都是淚履腋。

場景二
隨著業(yè)務的發(fā)展珊燎,產(chǎn)品線的增加惭嚣,我們需要將APP拆分為若干個基礎組件和業(yè)務組件遵湖,以便跨APP使用,并且方便管理維護(這又是另外一個大的議題晚吞,就不在此贅述了)延旧。每個組件都由一個私有Pod來管理,Pod的發(fā)布和更新也成為了我們?nèi)粘9ぷ鞯囊徊糠植鄣兀瑢τ谶@些Pod迁沫,一般我們團隊內(nèi)部的原則是:誰制作,誰管理捌蚊,誰發(fā)布集畅,Pod的負責人我們內(nèi)部稱之為庫管,這件事也就分擔到了每個庫管身上缅糟,庫管發(fā)布一個Pod的流程大約如下:

  1. 增加Podspec中的版本號
  2. 執(zhí)行pod lib lint命令進行庫驗證
  3. Git Commit代碼
  4. Git Push代碼到遠端
  5. 打一個Git Tag
  6. 將Tag Push到遠端
  7. 執(zhí)行pod repo push命令發(fā)布庫到私有倉庫

如果只有兩三個庫的話挺智,并且?guī)斓母骂l率較低的時候,每次手動來處理還好窗宦。但是當庫逐漸增多的時候這件事就變得相當麻煩赦颇,尤其是當頂層的庫依賴底層庫的時候,那么升級一個庫赴涵,影響面將遠遠超過其本身媒怯,通過人工的方式處理的話,整個過程會變得相當痛苦扇苞。

說到這里,我相信但凡是操作過的同學杨拐,應該都對此深有感觸。

所以我們來看看針對以上這兩個場景哄陶,如何使用Fastlane來解決帆阳。

其實說起來也不難,首先在項目下執(zhí)行:

fastlane init

然后跟隨配置引導蜒谤,填寫App和ITC相關信息至扰,然后Fastlane會在項目目錄下創(chuàng)建一個fastlane目錄鳍徽,里面包含所有和此項目相關的配置,剩下要做的就是將以上的流程配置在fastlane目錄下的Fastfile中敢课,

場景一的Fastfile(可以忽略HipChat部分):

desc 'Deploy a new version to the App Store'
lane :do_deliver_app do |options|
  ENV["FASTLANE_PASSWORD"] = options[:itc_password]
  project          = options[:project]
  scheme           = options[:scheme]
  version          = options[:version] 
  build            = options[:build] || Time.now.strftime('%Y%m%d%H%M')
  output_directory = options[:output_directory]
  output_name      = options[:output_name]
    
  hipchat(message: "Start deilver app #{project} at version #{version}")
    
  hipchat(message: "Git pull")
  git_pull

  hipchat(message: "Pod install")
  cocoapods
   
  hipchat(message: "Update build number to #{build} and building ipa")
  update_build_number(version: build, plist: "#{project}/Info.plist")
  gym(scheme: options[:scheme], clean: true, output_directory: output_directory, output_name: output_name)

  hipchat(message: 'deliver to itunesconnect')
  deliver(force: false, skip_screenshots: true, skip_metadata: true)

  hipchat(message: "Upload #{project} to itunesconnect successfully!")
    
  git_add(path: '.')
  git_commit(path: '.', message: "update build number to #{build} and upload to itunesconnect")
  git_pull
  git_push(branch: "test")
end

場景二的Fastfile(可以忽略HipChat部分):

desc "Release new private pod version"
lane :do_release_lib do |options|
  target_version = options[:version]
  project        = options[:project]
  path           = "#{project}.podspec"
    
  hipchat(message: "Start release pod #{project} at version #{target_version}")
    
  git_pull
  ensure_git_branch # 確認 master 分支
  pod_install
  pod_lib_lint(verbose: true, allow_warnings: true, sources: SOURCES, use_bundle_exec: true, fail_fast: true)
  version_bump_podspec(path: path, version_number: target_version) # 更新 podspec
  git_commit_all(message: "Bump version to #{target_version}") # 提交版本號修改
  add_git_tag(tag: target_version) # 設置 tag
  push_to_git_remote # 推送到 git 倉庫
  pod_push(path: path, repo: "GMSpecs", allow_warnings: true, sources: SOURCES) # 提交到 CocoaPods
    
  hipchat(message: "Release pod #{project} Successfully!")
end

對于Fastlane的配置,官方提供的文檔中會有更詳細的描述:

https://docs.fastlane.tools/getting-started/ios/setup/

配置完這個腳本后巧骚,剩下的事就很輕松寫意了圾结。
針對場景一我們在項目目錄下,用終端執(zhí)行如下命令即可:

fastlane do_deliver_app 
project:Gengmei 
scheme:Gengmei-AppStore 
version:6.3.0 
build:201609011530 
...

同理筝野,針對場景二我們在項目目錄下,用終端執(zhí)行如下命令即可:

fastlane do_release_lib project:GMUtil version:0.1.4

任何復雜的流程挥唠,基本上一個命令全部搞定焕议,是不是很方便。

另外懊烤,為了能夠讓Fastlane的各種命令更加傻瓜話宽堆,可視化,我們基于Fastlane內(nèi)核畜隶,使用Ruby on Rails框架開發(fā)了一款適用于移動端持續(xù)測試和持續(xù)發(fā)布的系統(tǒng)Jaguar。Jaguar本身和Gitlab浸遗,Sentry,Jira弃秆,HipChat髓帽,Maven等等內(nèi)部系統(tǒng)進行打通,從而形成一個完整的自動化體系郑藏。

Jaguar

這樣對于大部分的自動化流程必盖,Jaguar會通過各種定時任務或WebHook自動觸發(fā);少部分需要人工操作的塌忽,工程師們也只需要點一個按鈕就能完成了阁吝。

這里附上一個Jaguar的截圖:


jaguar-screenshot

使用Fastlane的感受械拍,踩坑的簡單回顧

使用了Fastlane這么長的時間,我最深的感受就是:Fastlane真正的將工程師從各種無聊而又必須要做的重復性勞動和流程化工作中解放出來甲馋,專注于業(yè)務或架構本身迄损,使得整個開發(fā)效率,測試效率痊远,運維效率大大提升氏捞。

在使用Fastlane的過程中,有一些小的注意事項逞姿,也和大家分享一下:

由于Fastlane本身更新頻率比較高,大約1-2周一次滞造,那么如果最近的幾個版本都沒有升級的話,建議仔細閱讀一下這幾次更新的Release Notes挺狰,否則有可能會出現(xiàn)一些奇奇怪怪的Bug买窟,比如:
在1.87版本升級到1.88的時候,如果不在Fastfile中加入如下兩行:

ENV['FASTLANE_EXPERIMENTAL_TRANSPORTER_AVOID_SHELL_SCRIPT'] = '1'
ENV['SPACESHIP_LOGIN_ENCODING_IDENTITY'] = '1'

那么趁耗,上傳ITC的時候疆虚,就會報錯,而且從報錯中并不能看到具體原因罢屈,最后經(jīng)過各種折騰終于在Github上的issue中找到了原因:Spaceship這個工具增加了一個小的特性而導致的Bug篇亭。

另外,大家在使用Fastlane的過程中曼月,如果遇到了問題柔昼,建議直接在Github上提issue,F(xiàn)astlane的作者和社區(qū)工程師們會非常迅速的做出響應捕透,而且會非常熱心的幫你解決問題乙嘀,當然大家提問題的時候要盡量描述清楚,該貼代碼帖代碼虎谢,該截圖的截圖。

發(fā)散思維曹货,F(xiàn)astlane不只專屬于移動端

雖然Fastlane本身是為移動而生的,但是如果我們發(fā)揮一下想象力的話玩般,就會發(fā)現(xiàn)礼饱,其實也可以把后端,前端的持續(xù)集成和持續(xù)交付流程整理出來匀伏,然后也統(tǒng)一交給Fastlane來管理(當然這個過程中肯定需要自定義一些Plugin或Action的)

比如:我們團隊目前正計劃先把PC站的UI自動化測試流程集成進來:

  1. 執(zhí)行Git Pull命令蝴韭,拉最新的代碼
  2. 使用pip安裝Requirements
  3. 混淆壓縮前端Javascript和CSS
  4. 重啟Django服務
  5. 使用Selenium執(zhí)行UI自動化測試
  6. 收集測試結果,發(fā)郵件給QA團隊

對應的Fastfile如下(簡寫履磨,還未真正使用):

desc "Do automation test for pc web"
lane :automation_test_pc do |options|
  git_pull
  pip_install
  gulp_build
  restart_django
  selenium_test
end

這樣以來庆尘,一個團隊內(nèi)的客戶端,前端矛辕,后端的持續(xù)集成和持續(xù)交付就能夠統(tǒng)一部署付魔,統(tǒng)一管理,統(tǒng)一維護杨刨,從而在基礎設施上能夠盡可能滿足團隊對自動化的需求擦剑,何樂而不為呢芥颈。

結語

本次分享只是帶大家領略了一下Fastlane的風采,針對諸如:如何自定義Action纠屋,Plugin盾计,如何在Andriod平臺上使用的細節(jié)赁遗,如何應用在自動化測試場景族铆,以及一些Fastlane的高級用法等,我會在接下來的一段時間內(nèi)做出相應的整理剖煌,形成文章逝淹,以供大家參考。

由于本人的水平有限茉兰,難免會有錯誤和疏漏欣簇,也歡迎各位同學指正,如果大家在Fastlane的使用上燃辖,有更好的案例网棍,也歡迎交流和分享。

最后氏身,附上一個我們團隊正在使用到的Fastfile腳本和一些自定義Actions:
https://github.com/thierryxing/Fastfiles

另外惑畴,F(xiàn)astlane也提供了一些國外團隊的Example:
https://github.com/fastlane/examples

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末如贷,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子杠袱,更是在濱河造成了極大的恐慌楣富,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件庄萎,死亡現(xiàn)場離奇詭異,居然都是意外死亡秉馏,警方通過查閱死者的電腦和手機脱羡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來帆竹,“玉大人脓规,你說我怎么就攤上這事∶虢簦” “怎么了挨下?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵臭笆,是天一觀的道長。 經(jīng)常有香客問我鹰霍,道長茵乱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任督勺,我火速辦了婚禮在验,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己块饺,他們只是感情好,可當我...
    茶點故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布辨嗽。 她就那樣靜靜地躺著淮腾,像睡著了一般。 火紅的嫁衣襯著肌膚如雪洲押。 梳的紋絲不亂的頭發(fā)上圆凰,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天,我揣著相機與錄音挑童,去河邊找鬼跃须。 笑死,一個胖子當著我的面吹牛大年,可吹牛的內(nèi)容都是我干的玉雾。 我是一名探鬼主播,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼垦缅,長吁一口氣:“原來是場噩夢啊……” “哼驹碍!你這毒婦竟也來了志秃?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤竟坛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后担汤,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體崭歧,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年叔营,在試婚紗的時候發(fā)現(xiàn)自己被綠了绒尊。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片歧匈。...
    茶點故事閱讀 38,646評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖勘究,靈堂內(nèi)的尸體忽然破棺而出斟冕,到底是詐尸還是另有隱情,我是刑警寧澤景描,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布秀撇,位于F島的核電站,受9級特大地震影響棠绘,放射性物質(zhì)發(fā)生泄漏再扭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一让虐、第九天 我趴在偏房一處隱蔽的房頂上張望罢荡。 院中可真熱鬧对扶,春花似錦麸俘、人聲如沸从媚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽紧憾。三九已至昌渤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間膀息,已是汗流浹背潜支。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留埠对,地道東北人裁替。 一個月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓弱判,卻偏偏與公主長得像,于是被迫代替她去往敵國和親裕循。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,514評論 2 348

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