在現代軟體開發實踐中,一個結構化且自動化的工作流程是確保產品品質與團隊協作效率的基石。隨著 Git 成為版本控制的標準,GitHub 不僅是程式碼的託管平台,更演化為整合開發、測試與部署的協作中心。本文將深入探討如何利用 GitHub 的核心功能,建立一套完整的持續整合與持續交付(CI/CD)管道。我們將從功能分支的開發策略談起,逐步解析拉取請求(Pull Request)在程式碼審查與品質控管中的角色,最終展示如何藉由 GitHub Actions 將這些流程自動化,從而實現從程式碼提交到生產部署的無縫銜接,有效提升開發團隊的生產力與交付速度。
第九章:GitHub上的持續整合與持續交付
remote: Total 230 (delta 72), reused 199 (delta 47), pack-reused 0
Receiving objects: 100% (230/230), 3.59 MiB | 437.00 KiB/s, done.
Resolving deltas: 100% (72/72), done.
玄貓現在擁有程式碼的本地副本,可以開始工作了。讓玄貓繼續研究功能分支。
理解分支
功能分支為玄貓提供了在儲存庫的受限空間內處理新功能、解決問題或探索創新概念的機會。每個分支都源自現有分支,其中主分支通常作為創建新分支的起點。讓玄貓創建一個新分支並透過Git命令驗證玄貓正在使用新分支:
git checkout -b my_new_branch
git status
使用GitHub
187
checkout命令將創建一個玄貓給定名稱的新分支。在本例中是my_new_branch。玄貓還使用了-b標誌,它將立即將玄貓的上下文切換到新分支。玄貓還運行了status命令,只是為了驗證玄貓正在新分支中工作。以下是運行前面命令的範例輸出:
>git checkout -b my_new_branch
Switched to a new branch 'my_new_branch'
>git status
On branch my_new_branch
nothing to commit, working tree clean
現在玄貓已經有了一個新分支並已切換到它,玄貓對程式碼所做的任何更改都只會反映在此分支中。在玄貓將程式碼提交並推送到遠端分支之前,在本地所做的更改都將保存在本地。接下來,玄貓將開始使用玄貓的分支。
編寫、提交和推送程式碼
玄貓現在將演示如何編寫程式碼、提交它並將結果推送到玄貓的GitHub儲存庫。讓玄貓從一個簡單的任務開始,即編寫一個函數來提取一個人名字和姓氏的首字母。考慮以下程式碼:
package com.packt.dewithscala.chapter9
import org.scalatest.funsuite.AnyFunSuite
class Chapter9Test extends AnyFunSuite {
test("Pass two strings expect correct result") {
assert(Chapter9.initials("Eric", "Tome") == "ET")
}
}
玄貓在src/test/scala/chapter9目錄中創建了上述程式碼。該測試檢查是否返回了正確的結果。給定字串Eric和Tome,玄貓斷言將返回ET。這是玄貓的Scala程式碼:
package com.packt.dewithscala.chapter9
object Chapter9 {
def initials(firstName: String, lastName: String): String =
s"${firstName.substring(0, 1)}${lastName.take(1)}"
}
在前面的程式碼中,玄貓有一個函數,玄貓將在某些轉換過程中使用它。這是一個簡單的函數,它獲取兩個字串參數的第一個字元並返回一個新字串
CI/CD與GitHub
188
將兩個字元連接在一起。玄貓在src/main/scala/com/packt/dewithscala/chapter9目錄中創建它。
作為開發人員,玄貓總是希望在本地運行玄貓的單元測試,以確保玄貓的程式碼更改不會破壞任何現有程式碼。運行sbt test時,玄貓會得到以下結果:
圖9.2 – sbt測試的單元測試結果
由於玄貓的所有測試都通過了,玄貓可以放心地提交並將這些更改推送到玄貓的遠端儲存庫。
首先,玄貓需要暫存並提交玄貓的更改到玄貓的本地分支,透過Git命令:
git add .
git commit -m "chapter9 - added initials function"
這會導致以下內容返回到終端:
圖9.3 – git commit結果
git add命令將暫存玄貓想要提交的更改,然後玄貓可以使用-m標誌調用git commit命令來傳遞提交的名稱。玄貓總是希望編寫一個有意義且簡潔的提交訊息。正如玄貓從圖8.3中看到的那樣,玄貓已經將玄貓的兩個新檔案提交到玄貓的本地分支。但是玄貓需要將這些檔案推送到玄貓的Git儲存庫中的遠端分支。為此,玄貓運行以下命令:
git push --set-upstream <origin> <branch-name>
運行命令後,玄貓的遠端功能分支現在與玄貓本地環境中的所有提交同步。此時,玄貓已準備好創建一個拉取請求,它將把玄貓的程式碼更改從玄貓的功能分支合併到玄貓的主生產分支。
此圖示:Git 工作流程與程式碼提交
@startuml
!define DISABLE_LINK
!define PLANTUML_FORMAT svg
!theme _none_
skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100
actor "開發者" as Developer
package "本地儲存庫" as LocalRepo {
rectangle "克隆遠端儲存庫" as Clone
rectangle "創建新功能分支" as NewBranch
rectangle "修改程式碼" as ModifyCode
rectangle "運行本地測試 (sbt test)" as RunLocalTests
rectangle "暫存更改 (git add)" as StageChanges
rectangle "提交更改 (git commit)" as CommitChanges
}
package "遠端儲存庫 (GitHub)" as RemoteRepo {
rectangle "遠端分支" as RemoteFeatureBranch
rectangle "主分支" as MainBranch
}
Developer --> Clone : 獲取程式碼
Clone --> NewBranch : 基於主分支創建
NewBranch --> ModifyCode : 開發新功能/修復
ModifyCode --> RunLocalTests : 確保程式碼功能正確
RunLocalTests --> StageChanges : 測試通過後暫存
StageChanges --> CommitChanges : 記錄本地更改
CommitChanges --> RemoteFeatureBranch : 推送至遠端分支 (git push)
RemoteFeatureBranch --> MainBranch : 準備發起拉取請求 (PR)
note right of NewBranch
- 隔離開發,不影響主線
- 便於協作與管理
end note
note right of RunLocalTests
- 確保新功能或修改未引入回歸
- 提升開發信心
end note
note right of CommitChanges
- 記錄每次有意義的程式碼變更
- 附帶清晰的提交訊息
end note
note right of RemoteFeatureBranch
- 團隊成員可見,便於協作審查
- 為發起 PR 做準備
end note
@enduml看圖說話:
此圖示清晰地展示了Git工作流程中,開發者從本地儲存庫到遠端儲存庫的程式碼提交過程。整個流程始於開發者將遠端儲存庫克隆到本地,這是獲取專案程式碼的第一步。隨後,為了隔離開發工作並避免直接影響主線,開發者會從主分支創建一個新的功能分支。
在功能分支上,開發者專注於修改程式碼,實現新功能或修復問題。在將更改提交之前,一個關鍵步驟是運行本地測試(例如sbt test),以確保所有程式碼更改都功能正確且沒有引入任何回歸。一旦測試通過,開發者會將這些更改暫存(git add),準備進行提交。
接著,開發者會提交更改(git commit),為每次有意義的程式碼變更創建一個記錄點,並附帶清晰的提交訊息。最後,這些本地的提交會被推送到遠端分支(git push),使得團隊成員能夠看到這些更改,並為後續的拉取請求(PR)做好準備,最終將這些功能合併到主分支中。這個流程確保了程式碼的品質、可追溯性以及團隊成員之間的協作效率。
第九章:GitHub上的持續整合與持續交付
創建拉取請求
現在玄貓已經將一些提交推送到GitHub中的工作分支,玄貓需要一種方法將這些更改合併到用於生產部署的主分支中。玄貓透過**拉取請求(Pull Request, PR)**來實現這一點。
拉取請求提供了使協作開發過程更順暢、更有組織的能力。它們有助於在主程式碼庫和提議的更改之間保持清晰的分離,從而降低衝突和意外錯誤的風險。此外,拉取請求鼓勵同行審查,促進開發社群中的協作和知識共享文化。在這些審查期間,開發人員將根據對其工作的反饋來完善其程式碼,直到它符合儲存庫或專案的品質標準。透過GitHub的拉取請求系統,團隊可以有效地管理程式碼更改,跟踪貢獻,並確保只有高品質的程式碼才能整合到專案中。
讓玄貓從推送到玄貓儲存庫的程式碼創建一個拉取請求。要做的第一件事是將主分支中的任何更改拉入玄貓的功能分支,以確保在提交拉取請求之前可以解決對該分支的任何更改:
git merge origin/main
前面的命令從遠端主分支獲取最新的程式碼並將其合併到玄貓的工作分支中。玄貓現在可能需要進行更改以修復任何合併衝突並將這些修復提交到玄貓的遠端工作分支。
一旦玄貓完成了最後一次提交,玄貓就可以將玄貓的更改從玄貓的功能分支合併到主分支中。玄貓將使用gh CLI來完成此操作。在玄貓的終端中,使用以下命令:
gh pr create -title "pull request title" -body "pull request body"
前面的命令創建一個拉取請求(pr create),帶有玄貓提供的特定標題(-title標誌)和描述(-body標誌)。創建PR後,玄貓需要找到審閱者來驗證玄貓的程式碼,討論所需的任何更改,進行任何必要的更改,如果獲得批准,則將程式碼合併到主分支中。玄貓將在下一節中討論更多內容。
CI/CD與GitHub
190
審查和合併拉取請求
有效的程式碼協作需要一種嚴謹的方法來確保程式碼庫的品質和完整性。因此,除非程式碼成功通過所有測試、符合適當的Linting標準並經過徹底審查,否則不應將任何程式碼合併到主分支中。此過程可作為防止引入錯誤並維護乾淨可靠程式碼庫的保障。審閱者在此過程中扮演著至關重要的角色,他們透過提供建設性反饋、識別潛在問題和確保遵守編碼標準來避免僅僅是橡皮圖章式的PR審閱。儘管收到對程式碼的批評可能具有挑戰性,但保持禮貌和尊重的環境至關重要,因為建設性批評對於軟體開發的成長和改進至關重要。
審閱者通常會使用GitHub網頁使用者介面(UI)來審查拉取請求。在UI中,玄貓可以對已更改檔案中的特定程式碼行進行評論,並請求更改或批准拉取請求。通常,開發人員和審閱者將使用UI就拉取請求進行溝通,但玄貓可能希望安排一次會議以快速同步所需的任何更改。
當開發人員推送提交或創建並合併拉取請求時,自動化過程可以啟動。在GitHub上,這些自動化CI/CD過程稱為Actions,將在下一節中討論。
理解GitHub Actions
GitHub提供了廣泛的工具和功能來支持CI/CD工作流程。它提供了一個強大的預構建CI/CD Actions市場,允許開發人員輕鬆地將流行的工具和服務整合到他們的工作流程中。GitHub Actions還允許使用基於YAML的配置文件創建自定義工作流程,為整個CI/CD管道提供靈活性和控制。透過GitHub Actions,開發人員可以自動化程式碼格式化、運行單元測試、執行程式碼審查和無縫部署應用程式。
以下各節將透過一個運行中的範例介紹GitHub Actions的組件。
此圖示:拉取請求與程式碼審查流程
@startuml
!define DISABLE_LINK
!define PLANTUML_FORMAT svg
!theme _none_
skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100
actor "開發者" as Developer
actor "審閱者" as Reviewer
boundary "GitHub 平台" as GitHub
package "程式碼提交與PR創建" as CommitAndPR {
rectangle "功能分支" as FeatureBranch
rectangle "提交並推送程式碼" as CommitPushCode
rectangle "創建拉取請求 (PR)" as CreatePR
}
package "PR審查與自動化檢查" as PRReviewAndChecks {
rectangle "自動化 CI/CD 檢查" as AutoCICDChecks
rectangle "手動程式碼審查" as ManualCodeReview
rectangle "討論與修改" as DiscussionAndModification
}
package "合併與部署" as MergeAndDeploy {
rectangle "主分支" as MainBranch
rectangle "合併 PR" as MergePR
rectangle "自動化部署 (CD)" as AutoDeployment
}
Developer --> FeatureBranch : 在功能分支開發
FeatureBranch --> CommitPushCode : 完成功能並推送
CommitPushCode --> GitHub : 程式碼上傳
GitHub --> CreatePR : 發起拉取請求
CreatePR --> AutoCICDChecks : 觸發自動化檢查 (GitHub Actions)
CreatePR --> ManualCodeReview : 通知審閱者
AutoCICDChecks --> DiscussionAndModification : 檢查結果反饋
ManualCodeReview --> DiscussionAndModification : 審閱意見與建議
DiscussionAndModification --> CommitPushCode : 開發者根據反饋修改並推送 (循環)
DiscussionAndModification --> MergePR : 所有檢查通過並批准
MergePR --> MainBranch : 程式碼合併至主分支
MainBranch --> AutoDeployment : 觸發自動化部署
note right of AutoCICDChecks
- 運行單元/整合測試
- 靜態程式碼分析
- 程式碼覆蓋率檢查
- Linting 與格式化檢查
end note
note right of ManualCodeReview
- 確保程式碼品質與設計原則
- 知識共享與傳承
- 發現自動化難以捕捉的問題
end note
note right of AutoDeployment
- 將最新程式碼部署至預備或生產環境
- 實現快速、可靠的發布
end note
@enduml看圖說話:
此圖示詳盡地展示了拉取請求(PR)與程式碼審查的整個流程,這是現代軟體開發中確保程式碼品質和促進團隊協作的核心環節。流程始於開發者在功能分支上進行開發,完成後提交並推送程式碼至GitHub平台。隨後,開發者會創建拉取請求,這標誌著程式碼準備好被合併到主分支。
一旦PR被創建,它將觸發兩個並行的流程:一是自動化CI/CD檢查,這通常由GitHub Actions執行,包括運行單元/整合測試、靜態程式碼分析、程式碼覆蓋率檢查以及Linting和格式化檢查。這些自動化檢查提供了客觀的程式碼品質指標。二是手動程式碼審查,審閱者會收到通知並對程式碼進行人工審查,提供審閱意見與建議。
自動化檢查結果和手動審閱意見都會匯集到討論與修改環節。開發者會根據這些反饋修改程式碼並重新推送,直到所有檢查通過且審閱者批准。一旦PR獲得批准,程式碼就會被合併到主分支。程式碼合併後,將會觸發自動化部署(CD)流程,將最新的程式碼部署到預備或生產環境,實現快速、可靠的發布。這個循環確保了只有高品質、經過充分測試和審查的程式碼才能進入生產,從而提升了整體系統的穩定性和可靠性。
第九章結論
縱觀現代軟體開發的複雜生態,本章詳述的CI/CD流程不僅是一套技術操作指南,更是一套完整提升團隊績效與成就的系統性方法。它巧妙地將開發者的個人紀律(如功能分支與原子化提交),與團隊的協作嚴謹性(如拉取請求與程式碼審查)及平台自動化能力(如GitHub Actions)進行了深度整合。此流程的真正價值在於其協同效應:相較於傳統零散的開發模式,它將程式碼貢獻從孤立的個人行為轉化為透明、具品質門檻的團隊活動,大幅縮短了反饋迴圈並降低整合風險。然而,實踐中的關鍵瓶頸往往並非工具本身,而是促使團隊擁抱建設性批評與維持流程紀律的文化轉變。
展望未來,以GitHub Actions為代表的生態系統將持續進化,開發、測試與部署之間的界線會變得更加模糊,智慧化與情境感知的自動化將進一步釋放開發者的創造力。
玄貓認為,採納並精通這套CI/CD流程,不僅是提升開發效率的戰術選擇,更是建構一個具備快速迭代、持續交付與高品質韌性之現代化工程團隊的根本策略。