4 月份參加 2017@Swift 大會的時候有幸聽到了 @zesming 大佬關於美團組件化的 Topic,有一張圖印象特別深入。html
後來跟 @zesming 大佬溝通怎麼去整理組件自動構建發佈思路的時候他也跟我提到了這張圖。因此我準備圍繞這張圖來整理一下 iOS 工程自動化的思路。ios
首先,咱們須要掌握一些自動構建發佈的基礎知識,主要包含以下幾個方面。git
GitFlow 是由 Vincent Driessen 提出的一個 Git 操做流程標準。包含以下幾個關鍵分支:github
名稱 | 說明 |
---|---|
master | 主分支 |
develop | 主開發分支,包含肯定即將發佈的代碼 |
feature | 新功能分支,通常一個新功能對應一個分支,對於功能的拆分須要比較合理,以免一些後面沒必要要的代碼衝突 |
release | 發佈分支,發佈時候用的分支,通常測試時候發現的 bug 在這個分支進行修復 |
hotfix | 熱修復分支,緊急修 bug 的時候用 |
GitFlow 的優點有以下幾點:數據庫
feature
分支,從而和已經完成的功能隔離開來,並且只有在新功能完成開發的狀況下,其對應的 feature
分支纔會合併到主開發分支上(也就是咱們常常說的 develop
分支)。另外,若是你正在開發某個功能,同時又有一個新的功能須要開發,你只須要提交當前 feature
的代碼,而後建立另一個 feature
分支並完成新功能開發。而後再切回以前的 feature
分支便可繼續完成以前功能的開發。feature
分支上改動的代碼都只是爲了讓某個新的 feature
能夠獨立運行。同時咱們也很容易知道每一個人都在幹啥。feature
開發完成的時候,它會被合併到 develop
分支,這個分支主要用來暫時保存那些尚未發佈的內容,因此若是須要再開發新的 feature
,咱們只須要從 develop
分支建立新分支,便可包含全部已經完成的 feature
。hotfix
分支。這種類型的分支是從某個已經發布的 tag 上建立出來並作一個緊急的修復,並且這個緊急修復隻影響這個已經發布的 tag,而不會影響到你正在開發的新 feature
。新功能都是在 feature
分支上進行開發swift
feature
分支都是從 develop
分支建立,完成後再合併到 develop
分支上,等待發布。xcode
當須要發佈時,咱們從 develop
分支建立一個 release
分支bash
而後這個 release
分支會發布到測試環境進行測試,若是發現問題就在這個分支直接進行修復。在全部問題修復以前,咱們會不停的重複發佈->測試->修復->從新發布->從新測試這個流程。服務器
發佈結束後,這個 release
分支會合併到 develop
和 master
分支,從而保證不會有代碼丟失。併發
master
分支只跟蹤已經發布的代碼,合併到 master
上的 commit 只能來自 release
分支和 hotfix
分支。
hotfix
分支的做用是緊急修復一些 Bug。
它們都是從 master
分支上的某個 tag 創建,修復結束後再合併到 develop
和 master
分支上。
若是要在項目中引入 GitFlow,推薦使用 SourceTree 來作客戶端工具,它包含了全部 GitFlow 的流程,可視化操做,很方便。
爲何提到 gitignore,審過 PR(Pull request) 或者 MR(Merge request)的同窗應該深有體會,當一個帶着一大堆 Pods 庫代碼的 PR/MR 襲來的時候,你的心裏應該是絕望的……因此咱們要了解下怎麼把一些非模塊相關的代碼 ignore 掉。
若是你在項目倉庫內建立一個 .gitignore
文件,Git 會根據這個文件來決定哪些文件和目錄是須要忽略的。
注意:若是你想把一個已經被跟蹤的文件 ignore 掉,這是時候新增的規則並不會對這個文件產生做用,你須要先用下面的指令把這個文件設置爲不跟蹤:
git rm --cached FILENAME複製代碼
這個其實沒啥意思,不過仍是看一下怎麼設置吧:
git config --global core.excludesfile ~/.gitignore_global複製代碼
Github 官方模版給出的建議以下:
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## Build generated
build/
DerivedData/
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/
## Other
*.moved-aside
*.xccheckout
*.xcscmblueprint
## Obj-C/Swift specific
*.hmap
*.ipa
*.dSYM.zip
*.dSYM
# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/
# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts
Carthage/Build
# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/#source-control
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
# Code Injection
#
# After new code Injection tools there's a generated folder /iOSInjectionProject
# https://github.com/johnno1962/injectionforxcode
iOSInjectionProject/複製代碼
經過這個文件咱們能夠看到,忽略的內容主要包含下面幾種:
咱們能夠根據自身項目的狀況來對這些配置進行相應的調整和定製。
注:本段主要翻譯自 githooks 官方文檔。
githooks 是指 git 在執行某些特定操做時會觸發的一系列程序,有點相似數據庫中的觸發器,由用戶編寫。默認狀況下,這些程序都被放置在 $GIT_DIR/hooks
目錄下,固然,咱們也能夠經過 git 的環境配置變量 core.hooksPath
來改變這個目錄。
githooks 的類型有不少中,這些 hook 會在各個特定操做時幫你自動完成一些你想要作的操做,好比:在提交代碼到 develop 分支的時候自動打包。
git commit
流程由 git commit
觸發,能夠經過 --no-verify
選項來跳過,這個 hook 不須要參數,在獲得提交消息並開始提交(commit)前被調用,若是返回非 0,則會致使 git commit
失敗。
當默認的 pre-commit 鉤子被啓用時,若是它發現文件尾部有空白行,這次提交就會被終止。
若是進行 git commit
的命令沒有指定一個編輯器來修改提交信息(commit message),任何的 git commit
hook 被調用時都會帶上環境變量 GIT_EDITOR=:
執行 git commit
命令後,在默認提交消息準備好後但編輯器啓動前,這個 hook 就會被調用。
它接受 1 到 3 個參數。第 1 個是包含了提交消息的文件的名字。第 2 個是提交消息的來源,它能夠是:
message
(若是指定了 -m
或者 -F
選項) template
(若是指定了 -t
選項,或者在 git config
中開啓了 commit.template
選項) merge
(若是本次提交是一次合併,或者存在文件 .git/MERGE_MSG
) squash
(若是存在文件 .git/SQUASH_MSG
)commit
而且第 3 個參數是一個提交的 SHA1 值(若是指定了 -c
,-C
或者 --amend
選項)若是返回值不是 0,那麼 git commit
命令就會被停止。
這個 hook 的目的是用來在工做時編輯信息文件,而且不會被 --no-verify
選項跳過。一個非 0 值意味着 hook 工做失敗,會終止提交。它不該該用來做爲 pre-commit
鉤子的替代。
由 git commit
觸發,能夠經過 --no-verify
選項來跳過,接受 1 個參數,這個參數包含了提交消息的文件的名字,若是返回非 0,則會停止 git commit
命令。
這個 hook 能夠用來規範提交信息,好比把信息格式化成項目定製的標準格式,或者發現提交信息不符合格式時拒絕此次提交。
由 git commit
觸發,在提交後被調用,不能影響 git commit
的結果。
git push
流程在 git push
運行期間,更新了遠程引用但還沒有傳送對象時執行。若是返回非 0,將停止推送過程。它接受遠程分支的名字和位置做爲參數,同時從標準輸入中讀取一系列待更新的引用。 你能夠在推送開始以前,用它驗證對引用的更新操做。
服務端處理來自客戶端的推送操做時,最早被調用的 hook。它從標準輸入獲取一系列被推送的引用。若是它以非 0 值退出,全部的推送內容都不會被接受。你能夠用這個 hook 阻止對 non-fast-forward
的更新,或者對該推送所修改的全部引用和文件進行訪問控制。
和 pre-receive
hook 十分相似,不一樣之處在於它會爲每個準備更新的分支各運行一次。假如推送者同時向多個分支推送內容,pre-receive
只運行一次,相比之下 update
則會爲每個被推送的分支各運行一次。它不會從標準輸入讀取內容,而是接受三個參數:引用的分支名;推送前引用所指向內容的 SHA-1 值;以及用戶準備推送內容的 SHA-1 值。若是這個 hook 以非 0 值退出,只有相應的那一個引用會被拒絕;其他的依然會被更新。
在整個過程完結之後運行,能夠用來更新其餘系統服務或者通知用戶。它接受與 pre-receive
相同的標準輸入數據。它的用途包括給某個郵件列表發信,通知持續集成服務器,或者更新缺陷追蹤系統 —— 甚至能夠經過分析提交信息來決定某個問題是否應該被開啓、修改或者關閉。該腳本沒法停止 push 進程,不過客戶端在它結束運行以前將保持鏈接狀態,因此若是你想作其餘操做需謹慎使用它,由於它將耗費你很長的一段時間。
由遠程倉庫的 git-receive-pack
調用,也是就是本地倉庫完成 git push
時。這個指令只能用來作通知,不能改變 git-receive-pack
的結果。
由遠程倉庫的 git-receive-pack
調用,也是就是本地倉庫完成 git push
時。若是這個 hook 返回非 0 值,則會中斷 git push
操做。
由 git am
觸發,主要用於引入第三方 patch 的時候用。由於項目中暫時沒有這樣的使用場景,因此不作具體研究。
由 git rebase
觸發,能夠用來防止某個分支被 rebase。這個 hook 接受 1 到 2 個參數。第 1 個參數是上游分支,第 2 個參數是將要執行 rebase 的分支。
在 git checkout
成功運行後執行。你能夠根據你的項目環境用它調整你的工做目錄。包括放入大的二進制文件、自動生成文檔或進行其餘相似這樣的操做。
在 git merge
成功運行後執行。你能夠用它恢復 Git 沒法跟蹤的工做區數據,好比權限數據。這個 hook 也能夠用來驗證某些在 Git 控制以外的文件是否存在,這樣你就能在工做區改變時,把這些文件複製進來。
這個 hook 被那些會替換提交記錄的命令調用,好比 git commit --amend
和 git rebase
(不過不包括 git filter-branch
)。 它惟一的參數是觸發重寫的命令名,同時從標準輸入中接受一系列重寫的提交記錄。 這個 hook 的用途很大程度上跟 post-checkout
和 post-merge
差很少。
這個 hook 由 git send-email
觸發,它接受一個參數:包含 e-mail 接受者郵箱的文件名。若是這個 hook 返回非 0 值,git send-email
就會被停止。
Git 的一些平常操做在運行時,偶爾會調用 git gc --auto
進行垃圾回收。這個 hook 會在垃圾回收開始以前被調用,能夠用它來提醒你如今要回收垃圾了,或者依情形判斷是否要中斷回收。
作爲一個第三方庫依賴管理工具,Cocoapods 在模塊化開發中扮演了很是核心的角色。關於 Cocoapods 的功能和介紹這裏就不一一陳述了,主要看一下和模塊化開發過程當中相關的一些東西。
咱們通常用 pod lib create
這個指令來建立一個模塊,其實這個指令還有一個選項:--template-url=URL
,用來指定生成庫的模板,官方模板地址是:github.com/CocoaPods/p…,根據這個模板生成的工程目錄結構以下:
MyLib
├── .travis.yml
├── _Pods.xcproject
├── Example
│ ├── MyLib
│ ├── MyLib.xcodeproj
│ ├── MyLib.xcworkspace
│ ├── Podfile
│ ├── Podfile.lock
│ ├── Pods
│ └── Tests
├── LICENSE
├── MyLib.podspec
├── Pod
│ ├── Assets
│ └── Classes
│ └── RemoveMe.[swift/m]
└── README.md複製代碼
也就是說,咱們能夠 fork 這份官方模版地址,而後定製咱們本身的模板(包括主工程和業務模塊),增長自定義的功能。好比:
和主工程同樣,模塊也是須要作版本管理的,目前來看比較好的方式就是經過 Cocoapods 來發布版本。因此咱們須要準備一個本身的私有 Cocoapods 倉庫,其實很簡單,首先在內網 git 上創建一個空倉庫,而後在本地執行一下下面的指令便可:
pod repo add [Spec name] [Git url]複製代碼
而後就是在發佈庫的時候注意一下,用以下指令便可發佈到私有倉庫內:
pod repo push [Spec name] [Lib name].podspec複製代碼
使用過 Cocoapods 的童鞋應該都知道,Cocoapods 的引用方式有三種:
方式 | 例子 | 說明 |
---|---|---|
版本號引用 | pod 'Alamofire', '~> 3.0' | 這種方式引用的是已經發布的版本,包含了 >``>=``<``<=``~> 幾種版本限制符號,其中~> 符號表明只更新最新的小版本號,好比 ~> 1.0.0 則只會更新到 1.0.x 的最新版本,而不會更新 1.x.0 以上的版本 |
本地路徑引用 | pod 'Alamofire', :path => '~/Documents/Alamofire' | 這種方式直接引用本地的代碼,這種方式下對引用庫的修改仍然會提交到引用庫的 git 上,而不會提交到主工程。 |
遠程 git 路徑引用 | pod 'Alamofire', :git => 'github.com/Alamofire/A…' | 這種方式直接引用遠程 git 代碼,不須要引用的庫進行發佈,並且還支持 :branch => 、:tag => 和 :commit => 三種選項 |
下面就是我對 @zesming 大佬分享的流程圖中各個過程的一些思考。
根據 GitFlow 的規範,新的需求走的是 feature
的流程。這裏咱們應該能夠開發一些輔助開發的腳本。
feature
分支組件化到了一個完整階段的時候,主工程應該是沒有代碼的,只是一個殼。可是在發展階段的時候,主工程還會包含一些業務代碼,因此咱們在開發某個 feature
的時候,每每是模塊內有一些具體業務代碼,主工程還有一些調用代碼,這個時候就須要在主工程和業務模塊都新建一樣的 feature
分支,因此咱們在主工程增長一個腳本。這個腳本的參數包含:
feature
分支名。執行過程以下:
feature
分支;feature
分支;一鍵切換主工程開發狀態是指某些業務模塊須要依賴主工程來進行調試的時候(PS:固然,比較理想的狀態是業務模塊能夠獨立運行,不過通常狀況下,理想很美好,現實很殘酷😂),須要將 Podfile 中這個模塊的引用方式修改成本地路徑的引用方式。這樣主工程代碼的修改和業務模塊代碼的修改會分別提交到各自的 git 倉庫,從而實現邊調試邊開發邊提交代碼。
這個功能考慮用腳本的方式來實現,放置在主工程的經常使用腳本目錄下。參數應該包含:
feature
分支名 [可選]。執行的操做應該包含:
feature
分支名,則須要先給業務模塊新建分支,不然直接執行第 2 步;pod install
;業務模塊開發調試完成以後,須要將主工程恢復到正常的狀態並提交。
這個功能仍是用腳本的方式實現,放置在主工程的經常使用腳本目錄下。參數應該包含:
feature
分支名。執行的操做應該包含:
pod install
;這個過程咱們主要經過 githooks 來作一些一些自動檢測。包括:
這個階段作的事情主要是檢測當前須要發佈的全部分支是否都已經提交 PR / MR 併合併到了 develop
分支。
注:這裏對分支的命名會有一些規則上的要求比。如在分支名內須要帶上當前須要發佈的版本號,從而能夠經過這個版本號匹配到當前須要發佈的全部分支。
這裏須要把 GitFlow 和 Cocoapods 的發佈流程結合起來。
考慮到通常須要依賴主工程來進行測試,咱們須要在主工程增長一個本地腳原本輔助發佈,包含的參數以下:
feature
分支名[可選]。實現的功能以下:
feature
分支名,須要先切換主工程分支;不然跳過這一步;develop
分支創建 release
分支。若是已經存在,則跳過這一步;release
分支;pod update [模塊名]
更新代碼;測試完成後就能夠發佈業務模塊到內網 Pods 了。這裏咱們在業務模塊工程內準備一個腳本,參數以下:
實現的功能以下:
release
分支。release
流程,合併 release
分支到 develop
分支和 master
分支,而後在 master
分支創建對應版本號的 tag 並 push 到遠程 git 倉庫。由於以前發佈組件的時候已經將主工程對各業務模塊的引用方式修改成版本號引用。因此這個階段咱們只須要驗證一下當前主工程引用的是不是各業務模塊的最新發布版本便可。參數以下:
完成的功能以下:
檢測主工程依賴模塊的全部業務模塊的最新版本是否和 Podfile 中指定的一致,若是不一致,報錯。
這個階段作的事情有兩點:
develop
分支。這個階段能夠理解爲集成測試階段。一樣是主工程中的一個腳本。參數以下:
develop
分支創建 release
分支。若是已經存在,則跳過這一步;release
流程,合併 release
分支到 develop
分支和 master
分支,而後在 master
分支創建對應版本號的 tag 並 push 到遠程 git 倉庫;本文只是一個不成熟的思考,後續實施的過程當中可能會對一些細節進行改善,也歡迎你們對我思路中不合理的地方進行指正,我會很是感激。
下一篇的內容應該是業務方需求到開發這個過程當中的一些具體的實踐記錄。敬請期待!
Introducing GitFlow:datasift.github.io/gitflow/Int…
Using Git / Ignoring files:
help.github.com/articles/ig…
githooks:git-scm.com/docs/githoo…
自定義 Git - Git 鉤子:git-scm.com/book/zh/v2/…