想了很久,還是寫這麼一篇文章來總結一下有關分支策略和DevOps的一些內容吧。其實,DevOps相關的內容並不是我的工作範圍,不過對於敏捷開發、DevOps、專案管理等等這一系列的與開發過程相關的內容,我還是有些經驗的,也就抽時間跟大家分享一下吧。
Git Flow應該是很多基於Git分散式版本控制系統的專案所實踐的一種開發流程,當然,很多人對於Github非常熟悉,甚至平時工作就是基於Github的。在此我也就不多介紹Git以及Github的概念了,只需要知道Github是基於Git的一個服務供應商,目前已被微軟收購。Git Flow就是由Vincent Driessen基於Git這一版本控制系統所總結出來的一套可行的、能夠滿足很多專案開發需求(註意:不是大多數專案)的敏捷開發流程,英語水平好的朋友可以參考閱讀Vincent的原文:《A successful Git branching model》。
Git Flow通常會用在有具體的release釋出的概念的專案上,簡言之就是類似於以前能夠給客戶提供安裝包的專案,對於SaaS專案,Git Flow還是太重了,而且不同環境、多租戶的部署策略也與Git Flow有一些差別。因此,團隊還是應該根據自己的實際情況來選擇合理的開發流程,這裡所介紹的內容也是僅供參考。下圖(來自《A successful Git branching model》一文)展示了Git Flow的整個流程,在這裡,我會對這個流程做一個詳細的介紹。
master分支
master分支始終保持了最近一次釋出(release)的程式碼,通常情況下,透過tag的方式來標記每一次release。在Github中,每建立一次release,就可以基於某個分支(branch)建立一個tag。在tag被建立後,分支是可以刪除的。因此,我覺得在上面的模型中,維護一個master分支並不是必需的。所以,有些團隊為了直觀,會將develop分支作為預設分支,甚至刪除master分支。但我覺得刪不刪除也都無所謂,直接將master分支作為開發分支(也就是合併develop和master的職責)也是可以的。
develop分支與feature分支
develop分支(或者master分支),是專案開發的主要分支,它包含了專案的最新程式碼,開發和測試團隊都會基於develop分支進行工作。每當有新的功能需要開發時,都會從develop分支分出一個feature分支。從實踐的角度:
- 上圖中分主體功能的feature分支(Major feature for next release)以及後續版本的feature分支(Feature for future release),實際上,出現後續版本的feature分支的可能性不會太大,由於採用敏捷開發,團隊通常不會做三個月以上的專案計劃,因為沒有意義,沒有人能夠確保三個月之內不會有功能需求的變更、backlog的調整以及市場對於專案本身的影響,因此,相對而言,近期的版本釋出標的應該是比較明確的,長遠的計劃反倒顯得意義不大。不過,也有可能會讓團隊的部分成員提前進入下一版本的研發,但由於敏捷開發和微服務理念的引入,一般敏捷團隊也不會特別龐大,所以,這樣的活動也不太會頻繁發生。當然,還是應該依據專案實際情況而定,如果真有部分成員提前進入下一版本開發的需要,那就按上圖中所描述的流程進行即可,不會有什麼大問題
- 多個feature並存的可能性還是很大的,團隊的不同成員會工作在不同的feature分支上,當每個feature完成之後,都會合併到develop分支。合併過程可能會出現程式碼衝突
- 每個成員是直接將程式碼提交到feature分支,還是自己再基於feature分支建立自己的工作分支?這取決於團隊的程式碼審核(Code Review)流程,如果團隊有嚴格的程式碼審核流程,開發人員應該在進行開發工作之前,基於feature分支建立自己的工作分支。開發完成後,將程式碼push到遠端,然後提交Pull Request(PR)並邀請團隊進行Code Review,Review透過,再將程式碼合併到feature分支,程式碼合併之後,將自己的工作分支刪掉即可。如果程式碼審核流程不嚴格,那麼直接將程式碼push到feature分支也是可以的,這需要團隊成員建立起互信機制,並且有高度自治(Self-organize)的能力。這裡有兩點可以多寫幾句,首先不要覺得重覆建立、刪除分支會帶來很大壓力,Git的分支是非常輕量的,需要的時候就建立,不需要就刪除,非常方便;其次,很多持續整合系統,對於多分支的開發流程以及Pull Request/Code Review流程都有完美的支援,Azure DevOps的這部分功能使用起來也是非常方便,之後我會詳細介紹
- 持續整合系統會從各feature分支上拉取程式碼進行編譯,然後自動化部署到開發環境進行冒煙測試(Smoke Test),便於開發人員儘早驗證已開發的功能。如果驗證透過,開發團隊可以將feature分支合併到develop分支,並刪除feature分支,然後進行下一個feature的開發
- 持續整合系統會從develop分支上拉取程式碼進行編譯,然後自動化部署到測試環境供測試團隊(QA)進行測試,最後給Product Owner或者其他Stakeholders做演示的環境,也是從develop分支出來的
release分支
當所有計劃好的feature都完成之後,團隊會基於develop分支建立release分支,此時:
- 持續整合系統會基於release分支產生新的構建,構建結果稱為Beta構建(Beta Builds)
- 測試團隊(QA)基於Beta構建進行回歸測試(Regression Testing)
- release分支不接受任何新的功能增加
- 開發團隊在release分支上進行Bug修複,同樣,修複Bug時是否需要建立自己的程式碼分支,取決於團隊自己的決定
- 當質量達標,團隊驗收之後,進入release流程,此時,基於release分支建立release tag,同時將release分支合併回develop分支,以將release分支中的bug修複帶入develop分支。在建完release tag並將程式碼合併回develop分支後,可將release分支刪除。上圖中沒有體現這一步
- 上圖中展示的是將release分支合併到master分支,然後在master分支上建release tag,這樣做是不妥的,因為合併到master分支的程式碼並沒有經過測試,所以release tag不能建立在master分支上
hotfix分支
在某個版本釋出之後,有可能會得到使用者的反饋,有些是功能性的,有些則是影響使用者使用的問題。對於功能性的反饋,可以與使用者商量,或者團隊自己決定,是準備在下一個版本中加入,還是基於使用者現在的版本為使用者專門定製。如果是後續版本再支援,則遵照上述feature分支的策略即可,但如果是基於使用者現在使用的版本進行定製,則需要走Hotfix的流程
- Hotfix分支從已經release的tag建立,找到已有的tag,然後基於tag新建Hotfix分支即可
- Hotfix分支的工作流程可以參考develop分支
- Hotfix開發完成並檢驗透過之後,基於Hotfix分支建立tag,標記為某一個Hotfix
- 根據當前所修複的問題是針對該特定使用者的(比如在介面上增加使用者公司的名稱與logo),還是針對所有使用者的(比如某個bug的修複),以此決定是否需要將改動合併到develop分支
- 刪除Hotfix分支
從上面的流程可以看出,除了develop分支(或者master分支)為長期存在的分支之外,其它的分支都為輔助分支,在工作完成之後可以立即刪除。為了滿足程式碼評審(Code Review)而建立的開發人員自己的分支,一般都是即用即刪。
以上就是Git Flow的整個流程,應該是已經介紹的比較細緻了。在平時的工作和自學中,我都是以Github作為程式碼託管平臺的,因此,本文的內容仍然是以Github為基礎,包括接下來有關Azure DevOps Pipeline的介紹,也是與Github進行整合的。
在上面的介紹中,不止一次提到持續整合系統,比如,當有程式碼簽入feature分支或者develop分支時,持續整合(Continuous Integration,CI)系統會自動進行編譯,然後觸發持續釋出(Continuous Delivery,CD)系統完成自動化部署。這些CI/CD系統所執行的自動化任務,需要人為地反覆定義並觸發嗎?其實並不需要。現在流行的持續整合系統基本都已成熟,比如Jenkins,它的Pipeline功能直接支援Multi-Branch,每當有新的分支建立,或者程式碼簽入,都會觸發Jenkins建立新的Job,並觸發相應的CI任務。Azure DevOps在這部分也做得非常好,而且它是雲端服務,簡單易用,對於開源專案是完全免費的。
首先,當你在Github中新建了一個repo,並且在Azure DevOps中配置了一個Project,使其從Github拉取程式碼時,你會發現,Github repo的Webhook裡已經自動為你加上Azure DevOps所需的Webhooks:
不用管它,我們設想兩個場景:
- 當開發人員準備向feature分支合併程式碼時
- 當feature分支出現程式碼簽入時
下麵看看,如何在Azure DevOps中進行配置,以實現上述兩個場景。在這個實驗中,我的Github Repo有三個分支:
- master:暫時沒用到
- dev:開發主線,保持了最新程式碼
- features/a1:功能代號為a1的開發分支
目前團隊都在集中開發features/a1功能,由於團隊選擇較為嚴格的Code Review流程,因此,開發人員在進行自己工作之前,都是從features/a1建立自己的工作分支,比如就叫features/a1-daxnet吧,以便在程式碼簽入features/a1分支的時候,能夠產生Pull Request並申請程式碼審查。工作分支的名稱可以自己決定,但團隊需要達成共識,因為這個名稱之後會被Azure DevOps用到。
現在,進入Github Repo的Settings頁,在Branches設定頁面中,在Branch protection rules設定下,新建一個規則(Rule),在Branch name pattern中,輸入features/a1(也可以使用規則來定義一組分支),然後,勾選Require status check to pass before merging:
註:如需Code Review,則還需要勾選Require pull request reviews before merging,在這裡就不演示了。然後,回到Azure DevOps,針對Github Repo建立CI,在CI的Trigger選項中,啟用Continuous integration,以保證每當有程式碼簽入指定的分支,就會觸發持續整合。在Branch filters中,新增features/*分支,表示以features作為開頭字元的所有分支,都需要進行持續整合,確保編譯透過:
啟用Pull request validation,在Branch filters中,新增features/a1,表示當有Pull Request需要合併到features/a1分支時,也需要進行持續整合,以確保編譯透過。註意,目前我們不涉及Repo被Fork的情況,所以我們不勾選Forks選項:
OK,基本設定就是這樣,非常簡單。接下來,我們開始開發新的功能。首先,在Github頁面基於features/a1,建立分支:features/a1-daxnet:
剛剛建立完新的分支,Azure DevOps就已經開始工作了,可以看到,Build Pipeline已經完成了新分支的編譯:
下麵,我要開始修改程式碼了,在程式碼修改完成本地編譯透過之後,我將程式碼提交到features/a1-daxnet分支:
再次檢視Azure DevOps的Build Pipeline,可以看到,CI已經開始幫我們編譯新的程式碼提交了:
OK,再次不管它,現在,我希望將程式碼合併到features/a1分支,於是,我到Github上建立Pull Request,此時Github會提示,程式碼校驗還在進行中,並等待Azure DevOps的編譯結果:
回到Azure DevOps,可以看到,Build Pipeline正在進行Pull Request編譯:
等待編譯成功後,開發者就可以將程式碼合併到features/a1分支了。當然,這裡我沒有強制要求必須要等程式碼編譯成功才能合併分支,因此,即使是編譯沒有完成,上圖中的Merge pull request的按鈕也是可用的。
本文首先介紹了Git Flow分支策略,並結合了實際應用介紹了Git Flow的詳細步驟,然後基於Azure DevOps介紹了在實際應用中,Azure DevOps Build Pipeline對Git Flow的支援。本文所介紹的內容並不能完全適用於所有的軟體專案,尤其是SaaS專案,所以在開發過程中,團隊還是應該根據自己的實際需求來定製自己的開發流程,在保證軟體質量的前提下,提高開發體驗和團隊生產力,探索尋求適合自己的實踐流程。
原文地址:http://sunnycoding.cn/2019/03/23/git-flow-and-azure-devops/