Fastlane-移動開發自動化之道

本人一直認爲:在程序的世界裏,一切重複性的,流程化的工作都可以交給自動化去完成。

在移動開發中也是如此:其實寫代碼只是我們開發過程中的一部分,除此之外我們還需要進行編譯,打包,上傳,部署,庫管理,版本控制等等Coding之外的雜事,而正是這些乏味而重複的工作佔用了我們寶貴的時間。

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

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

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

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

大家都知道,最近幾年,隨着智能手機的普及,移動端不僅要承載更多業務場景的實現,並且還要應對不斷變化業務需求。這就要求我們移動團隊能夠迅速響應變化,快速的迭代。那麼隨之而來的問題就是如何保障在不犧牲質量的前提下,儘可能的提升速度,我認爲這一切需要建立在高質量的持續測試和持續交付體系之上。

但是移動端本身興起的時間就比較短,各方面的成熟度也有所欠缺,能夠拿來用的工具更是少之又少,隨着業務深度廣度的增加,迭代速度的加快,諸如證書管理,打包,上傳,發佈這類重複而毫無技術含量的工作逐漸佔用了大家的時間,團隊內部對此詬病不已。

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

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

這裏寫圖片描述

說實話,效果還是可以的,至少在一定的時期內滿足了我們的要求,但是Jenkins本身只是一個通用的CI流程管理系統,本身並不提供諸如ITC提包和Meta內容管理,簽名,證書管理等等和移動端業務緊密結合的場景,而且配置的過程相當繁瑣。

去年年底的時候,機緣巧合之下,我們在Github上發現了Fastlane,看了Readme後感覺有戲,於是決定嘗試一下。其實剛開始的時候,我們也只是用Fastlane來解決iOS團隊內證書同步和上傳ITC的問題,但是隨着深入的研究,發現其實Fastlane能做的更多,於是我們將其逐步應用到iOS端的更多的場景,比如:私有Pod的發佈,代碼的靜態檢查,UIAutomation測試等等,接着又推廣到Andriod平臺,完成諸如私有AAR的發佈,Monkey測試等等一系列任務。現在可以說Fastlane已經變成了我們工作中密不可分的一部分。

另外,Fastlane本身也可以和Jenkins,Circle等主流CI系統做很好的集成,並且由於主要的CI流程都由Fastlane來管理和執行,所以從根本上降低了這些系統配置的複雜度。

Fastlane簡介

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

到目前爲止,Fastlane的工具集大約包含170多個小工具,基本上涵蓋了打包,簽名,測試,部署,發佈,庫管理等等移動開發中涉及到的內容。

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

如果這些工具仍然沒有符合你需求的,沒有關係,得益於Fastlane本身強大的Action和Plugin機制,如果你恰好懂一些Ruby開發的話,可以很輕易的編寫出自己想要的工具。

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

Fastlane的安裝非常簡單,和Cocoapods一樣,Fastlane也可以通過RubyGems來安裝,如果你的電腦上有Ruby環境的話,那麼只需要執行如下命令,即可完成:

gem install fastlane
移動客戶端持續測試和持續交付的常見場景及痛點
Fastlane本身能做的事情很多,但是其中一個最爲重要的作用就是能夠無縫嵌入在持續測試和持續交付體系中。

下面,爲了便於大家理解,我拿iOS爲例,舉幾個我們在移動開發過程中常常會遇到的場景:

場景一

當一個迭代開發測試結束,服務器端上線之後,我們會使用Testflight進行線上跟測,一般會執行如下流程:

執行Git Pull命令,拉最新的代碼到本地
Pod Install安裝最新的依賴庫
在Xcode中將Build Version增加
在Xcode點擊Archive編譯並打包
選擇輸出一個iOS AppStore模式的ipa文件
通過Application Loader將IPA上傳到ITC(TestFlight)
然後等待ITC Process完成後,登錄上去選擇剛纔的Build進行TestFlight測試
由於修改了版本號,所以需要將代碼Commit和Push一下

如果線上跟測發現有問題,那麼需要修復完畢後重覆上面的8個步驟。

其實做過這件事的同學應該都有體會,順利的話差不多一次得30分鐘吧,如果某一次Build Version忘記增加了,那麼前面的工作就白做了。

在我們團隊早期的時候,由於自動化體系尚未建立,我們有一個同事專門負責此事,在線上跟測的這兩天,他有半天時間幾乎幹不了別的,基本上都在打包上傳,說出來都是淚。

場景二

隨着業務的發展,產品線的增加,我們需要將APP拆分爲若干個基礎組件和業務組件,以便跨APP使用,並且方便管理維護(這又是另外一個大的議題,就不在此贅述了)。每個組件都由一個私有Pod來管理,Pod的發佈和更新也成爲了我們日常工作的一部分,對於這些Pod,一般我們團隊內部的原則是:誰製作,誰管理,誰發佈,Pod的負責人我們內部稱之爲庫管,這件事也就分擔到了每個庫管身上,庫管發佈一個Pod的流程大約如下:

增加Podspec中的版本號
執行pod lib lint命令進行庫驗證
Git Commit代碼
Git Push代碼到遠端
打一個Git Tag
將Tag Push到遠端
執行pod repo push命令發佈庫到私有倉庫

如果只有兩三個庫的話,並且庫的更新頻率較低的時候,每次手動來處理還好。但是當庫逐漸增多的時候這件事就變得相當麻煩,尤其是當頂層的庫依賴底層庫的時候,那麼升級一個庫,影響面將遠遠超過其本身,通過人工的方式處理的話,整個過程會變得相當痛苦。

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

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

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

fastlane init

然後跟隨配置引導,填寫App和ITC相關信息,然後Fastlane會在項目目錄下創建一個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/

配置完這個腳本後,剩下的事就很輕鬆寫意了。

針對場景一我們在項目目錄下,用終端執行如下命令即可:

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

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

fastlane do_release_lib project:GMUtil version:0.1.4

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

另外,爲了能夠讓Fastlane的各種命令更加傻瓜話,可視化,我們基於Fastlane內核,使用Ruby on Rails框架開發了一款適用於移動端持續測試和持續發佈的系統Jaguar。Jaguar本身和Gitlab,Sentry,Jira,HipChat,Maven等等內部系統進行打通,從而形成一個完整的自動化體系。

這裏寫圖片描述

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

這裏附上一個Jaguar的截圖:

這裏寫圖片描述

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

使用了Fastlane這麼長的時間,我最深的感受就是:Fastlane真正的將工程師從各種無聊而又必須要做的重複性勞動和流程化工作中解放出來,專注於業務或架構本身,使得整個開發效率,測試效率,運維效率大大提升。

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

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

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

那麼,上傳ITC的時候,就會報錯,而且從報錯中並不能看到具體原因,最後經過各種折騰終於在Github上的issue中找到了原因:Spaceship這個工具增加了一個小的特性而導致的Bug。

另外,大家在使用Fastlane的過程中,如果遇到了問題,建議直接在Github上提issue,Fastlane的作者和社區工程師們會非常迅速的做出響應,而且會非常熱心的幫你解決問題,當然大家提問題的時候要儘量描述清楚,該貼代碼帖代碼,該截圖的截圖。

發散思維,Fastlane不只專屬於移動端

雖然Fastlane本身是爲移動而生的,但是如果我們發揮一下想象力的話,就會發現,其實也可以把後端,前端的持續集成和持續交付流程整理出來,然後也統一交給Fastlane來管理(當然這個過程中肯定需要自定義一些Plugin或Action的)

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

1.執行Git Pull命令,拉最新的代碼
2.使用pip安裝Requirements
3.混淆壓縮前端Javascript和CSS
4.重啓Django服務
5.使用Selenium執行UI自動化測試
6.收集測試結果,發郵件給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

這樣以來,一個團隊內的客戶端,前端,後端的持續集成和持續交付就能夠統一部署,統一管理,統一維護,從而在基礎設施上能夠儘可能滿足團隊對自動化的需求,何樂而不爲呢。

結語

本次分享只是帶大家領略了一下Fastlane的風采,針對諸如:如何自定義Action,Plugin,如何在Andriod平臺上使用的細節,如何應用在自動化測試場景,以及一些Fastlane的高級用法等,我會在接下來的一段時間內做出相應的整理,形成文章,以供大家參考。

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

最後,附上一個我們團隊正在使用到的Fastfile腳本和一些自定義Actions:

https://github.com/GengmeiRD/Fastfiles

另外,Fastlane也提供了一些國外團隊的Example:

https://github.com/fastlane/examples


好了。祝大家生活愉快。多多收穫友誼和愛情。如果想獲取更多的訊息,請掃描下方二維碼關注我的微信公衆號:

這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章