持續整合

<image>

持續整合（Continuous integration，縮寫CI），又譯為持續集成，是一種軟體工程流程，是將所有軟體工程師對於軟體的工作副本持續整合到共用主線（mainline）的一種舉措。該名稱最早由葛來迪·布區（Grady Booch）在他的布區方法中提出，在測試驅動開發（TDD）的作法中，通常還會搭配自動單元測試。持續整合的提出主要是為解決軟體進行系統整合時面臨的各項問題，極限編程稱這些問題為集成地獄（integration hell）。

## 解釋

持續整合的宗旨是避免整合問題，如同在極限編程(XP)方法學中描述的整合地獄。持續整合並非普遍接受是用來改善整合頻率的方法，因此重要的是區分兩者所帶來的效益。

在極限編程方法學，持續整合需要達到最佳成果，必須依靠著自動化整合單元測試並通過測試驅動開發。首先必須設想在上線運作之前，已在開發環境完成並通過所有的單元測試。這將幫助避免一個開發者的作業流程，導致其他開發者作業的中斷。如果有需要，可以在完整上線運作之前進用部分已完成的功能，例如使用功能切換。

接著進行CI伺服器建置概念的闡述、自動化執行單元測試的週期與每次測試需要提交給開發者的報告。建置CI伺服器的用途(不一定要執行單元測試) 已經開始在極限編程(XP)社群之外的團隊練習。如今，許多企業組織已經開始採用持續性整合，而非採用完整的極限編程(XP)。

除了自動化單元測試，組織在運用持續性整合(CI)一般會建置CI伺服器來維護持續性套用品質控制的程序-小部分的影響，並且經常性使用。除了執行單元與整合測試之外，還有額外的靜態與動態測試，量測與描述效能，從程式來源碼摘錄與文件格式與促成手動品質保證(QA)程序。持續性品質控制應用程式用意在提升軟體品質以及減少交付的時間，在完成所有開發後，取代傳統軟體上線品質控制機制。此非常相似進行頻繁整合的最初概念讓整合得以在QA程序上更容易地達成。

同樣的道理，持續性交付的最佳實踐進一步擴展了持續性整合(CI)，以確保軟體檢核在主要程序上並且能夠佈署到使用者以確保實際的佈署流程可以非常快速。

## 工作流程

當從事變更時，開發者會從目前基礎程式碼庫複製以進行作業，其他開發者提交程式碼的變更至來源程式碼庫，並透過副本的方式取代來源程式碼庫的程式碼。不只變更目前的程式碼庫，新的程式碼也可以新增成為程式庫、其它共用資源與潛在衝突。

當分支程式碼保持在取出狀態時間越長，當分支程式碼開發者進行主線重新整合時，就愈容易遭遇整合多重衝突的風險以及失敗。當開發者將程式碼提交到程式碼庫時，首先必須更新程式碼以反映他們在程式碼庫中的更改，因為他們拿到了副本。程式碼庫包含的更改越多，開發人員在提交自己的更改前必須執行的工作越多。

終於，該程式庫也許變成非常不同於開發者的目標程式碼，他們進入有時候被稱為合併地獄或整合地獄的階段，這時候開發者所花費的整合時間，將超過最初程式碼開發的時間。

持續性整合涉及預先整合與預先與經常性的整合，藉此來避免踩到整合地獄的陷阱，實踐的目標是減少重工、減少成本與時間。

持續性整合補充的實踐是在提交成果之前，每個開發人員必須執行一個完整的構建與執行及通過所有的單元測試、整合測試，這些都是當持續性整合伺服器偵測到程式碼有新的提交時，必須經常性與自動化的進行。

## 歷史

葛來迪·布區於1994年出版的《物件導向分析設計與應用》（Object-Oriented Analysis and Design with Applications）第二版中，首次提出持續整合這個名詞。

1997年，肯特·貝克與Ron Jeffries建立了極限編程方法，將持續整合作為極限編程的一部份。

## 最佳實踐

本節列出了各個作者就如何實現持續整合提出的最佳實踐，以及如何自動化的進行相關實踐。組建自動化本身就是最佳實踐。

持續性整合–經常將新的或改變的程式碼與現有的程式碼庫進行匯集，這作業應該頻繁地發生，在提交和構建之間不存在中間窗口，並且沒有錯誤可以在沒有開發人員注意到並立即糾正的情況下產生。最佳的做法是透過每次提交一個程式庫來觸發建構，而不是定期預定的版本才進行建構。在快速提交的多開發者環境實踐是這樣的：在每次提交之後的短時間內觸發，然後在時間到期後開始建構，或者在上次建構間隔一段時間之後。許多自動化工具都有提供相關的自動化排程。

另一個要因是建構一個支援原子提交的版本控制系統，此系統可以讓開發人員的每次變更都可成為單一提交操作，但如試圖從只有改變一半的檔案進行構建沒有意義。

為了實現上述目標，持續整合必須依靠以下原則。

### 維護一個代碼庫

這種做法意味著使用專案來源程式碼版本控制系統。所有專案相關的程式碼都需要儲存在該程式碼庫中，在這種做法的控制界中，依慣例該系統應該可以從新取出的進行構建並且不需要額外的作業。提倡極限編程法的馬丁·福勒也主張，必須有簡單的工具來支援程式開發的分支作業。相反的，最好將所有變更整合起來而不是同時維護多個版本的軟體。簡單的說，這是將軟體開發工作版本化的地方。

### 自動構建

透過一個單一指令來達成系統建構。許多的建構工具軟體如MAKE已存在許多年，其他較新的工具程式都頻繁的使用在持續性整合環境。建構的自動化應該包含自動化作業與整合作業，並且通常包含正式環境的佈署。在許多案例中，程式碼的建構不僅僅只是編譯二進位元，通常總是伴隨的文件的產生、網站的建構、狀態數據與佈署的封裝媒體。(如Debian的DEB、Red Hat的RPM與微軟的MSI檔案)。

### 讓構建時會自我測試

一旦代碼編輯好，下一個階段應該要進行所有的測試，以確保軟體開發的成果符合預期。

### 每人每天都應提交一次

透過定期的承諾，每個提交者都能夠減少變更衝突的數量。一次檢查一個星期的工作成果時，遭遇到整合衝突的風險相對交高，這時排解的困難性也相對升高。早期性系統的局部衝突，將可以讓系統團隊及時因應與進行調整建構的方向。

一天至少提交一次(每個功能構建一次)通常被定義為持續整合的一部分。此外，建議每晚在提交之後立即進行建構，以上都是下限值，實際上的頻率往往要高出許多。

### 每份提交都應進行建置

系統應該要在每次提交之後，針對當下的版本進行建構以確認程式可正確的整合。通常實務上會使用自動化進行持續整合，也許這個也可以手動進行。在許多時候，持續整合是使用自動化持續整合的代名詞，透過持續整合伺服器或應用程序監視版本控制系統的變化，然後自動進行建構的過程。

### 維持快速建置

每個建置必須要維持快速完成，如此一來便可以避免整合問題。

### 用線上環境的複本測試

擁有一個測試環境並不能保證一切順利，也會在佈署上線時產生錯誤，因為測試環境和正式環境或許存在很大的差距。然而，如果要建置一份與線上環境一模一樣的測試環境，還需要成本考量。相反的，測試環境或是獨立的預備環境應該要建置實際正式環境的擴展版本，以在可容許的成本內同時達到維護堆疊結構技術與細微化。在那些測試環境中，服務虛擬化是通常運來獲得，隨需求存取超出團隊控制的依賴關係(如API、第三方應用程式、第三方服務與大型主機系統)、持續演變或者因太複雜無法在虛擬實驗室中還原的情境。

### 讓取得最新發布版本更容易

使建置容易讓利益關係人與測試者使用，能夠減少許多因為建置的成果不合乎需求的重工狀況。此外，提早測是能夠在提早程式佈署前知道變更的缺陷。在某些情況下，也可以提前查出錯誤，從而減少解決問題所需的工作量。所有程式設計師都應該從儲存庫更新項目來開始新的一天。這樣，他們將可保持該程式儲存庫的最新狀態。

### 任何人都可以檢視最後建置的結果

系統應該要讓任何人都可以容易尋找出建構是否中斷，並且可以顯示何人正在變更相關程式。

### 自動部署

大部分的持續整合系統允許在建置完成後自動執行程式碼。因此能夠寫一段程式碼來佈署應用程式至任何人都可以觀察的測試伺服器。在持續性整合未來的思考發展成像持續性佈署邁進。持續性佈署將要求直接將軟體佈署至測試環境中，這通常需要額外的自動化機制來防止程式缺陷。

## 成本與效益

持續整合目的在產生以下效益：

- 及早發現整合錯誤，且由於修訂的內容較小所以易於追蹤，這可以節省專案的時間與成本。
- 避免發佈日期的前一分鐘發生混亂，當每個人都會嘗試為他們所造成的那一點點不相容的版本做檢查。
- 當單元測試失敗或發生錯誤，若開發人員需要在不除錯的情況下還原程式碼庫到一個沒有問題的狀態，只需要放棄一小部份的更改 (因為整合的次數頻繁)。
- 讓 "最新" 的程式可保持可用的狀態供測試、展示或發佈用。
- 頻繁的提交程式碼會促使開發人員建立模組化，低複雜性的程式碼。

關於持續性自動化測試的效益:

- 強制執行頻繁的自動化測試紀律
- 當改變對全系統造成影響時立即反饋
- 自動化測試和持續性整合產生的軟體度量(如程式碼覆蓋度量，程式碼複雜度和功能完整性等)標準將開發人員集中在開發功能性，高品質的程式碼上，並幫助開發團隊發展。

持續整合的缺點包含：

- 構建一個自動化測試套件需要大量的工作，包括不斷努力以覆蓋新功能，並依照特定情境進行程式碼修改。
- 無論是否採用持續性整合，測試被認為是軟體開發的最佳實踐，測試必須依循軟體佈署的最佳實務。自動化是諸如測試驅動開發之類項目方法的一個組成部分。
- 持續性整合可以在不需要測試套件下執行，但是如果必須手動和經常地完成，生產產品的品質保證成本將會提高。
- 建置系統需要一些工作，而且可能變得複雜，難以靈活修改。
- 但是，也有一些開放來源程式碼的持續整合的專案軟體可以使用。
- 如果範圍很小或包含無法測試的舊版程式碼，持續性整合不一定有價值。
- 增加的價值取決於測試的品質以及程式碼的真實可測性。
- 較大的團隊意味著不斷將程式碼添加到整合隊列中，因此追蹤交付（同時保持品質）很困難，而排隊可能會減慢所有人的進度。
- 通過一天的多次提交和合併，功能的部分程式碼可以輕鬆推送，如此一來整合測試將會失敗直到整個功能開發完成。

## 外部鏈結

- Bugayenko, Yegor. " Why Continuous Integration Doesn't Work"