iOS之深入解析CocoaPods的GitLab CI与组件自动化构建与发布
生活随笔
收集整理的這篇文章主要介紹了
iOS之深入解析CocoaPods的GitLab CI与组件自动化构建与发布
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一、Gitlab CI/CD 簡介
① GitLab
- GitLab 是一個利用 Ruby on Rails 開發的開源應用程序,實現一個自托管的 Git 項目倉庫,可通過 Web 界面進行訪問公開的或者私有的項目。
- GitLab 擁有與 GitHub 類似的功能,能夠瀏覽源代碼,管理缺陷和注釋。
- GitLab 可以管理團隊對倉庫的訪問,它非常易于瀏覽提交過的版本并提供一個文件歷史庫。
② GitLab CI/CD
- Gitlab CI/CD 是一個內置在 GitLab 中的工具,用于通過持續方法進行軟件開發:
-
- 持續集成(Continuous Integration):頻繁地(一天多次)將代碼集成到主干,讓產品可以快速迭代,同時還能保持高質量,它的核心措施是,代碼集成到主干之前,必須通過自動化測試;
-
- 持續交付(Continuous Delivery):頻繁地將軟件的新版本,交付給質量團隊或者用戶,以供評審,如果評審通過,代碼就進入生產階段,持續交付可以看作持續集成的下一步,它強調的是,不管怎么更新,軟件是隨時隨地可以交付的;
-
- 持續部署(continuous Deployment):代碼通過評審以后,自動部署到生產環境,是持續部署是持續交付的下一步,持續部署的目標是,代碼在任何時刻都是可部署的,可以進入生產階段。
③ GitLab Runner
- GitLab Runner 用于執行 Gitlab CI/CD 觸發的一系列作業,并將結果發送回 Gitlab。
- GitLab Runner 可以在 Docker 容器內運行或部署到 Kubernetes 集群中。
④ Pipeline
- Pipeline 中文稱為流水線,是分階段執行的構建任務。如:安裝依賴、運行測試、打包、部署開發服務器、部署生產服務器等流程,合起來稱為 Pipeline。
- Stage表示構建階段,可以理解為上面所說安裝依賴、運行測試等環節的流程。我們可以在一次 Pipeline 中定義多個 Stage。
- Job 表示構建的作業(或稱之為任務),表示某個 Stage 里面執行的具體任務,可以在 Stages 里面定義多個 Jobs。
- Pipeline,Stage 和 Job 的關系如下所示:
⑤ 整體流程
- CI/CD 是一種通過在應用開發階段引入自動化來頻繁向客戶交付應用的方法,CI/CD 的核心概念是持續集成、持續交付和持續部署。
- 整個流程將分為幾個部分:
-
- 首先開發人員在本地完成項目的開發之后,將代碼推送到 Gitlab 倉庫中;
-
- 當代碼提交到 Gitlab 倉庫時,會觸發 Pipeline,Gitlab Runner 會根據 .gitlab-ci.yml 配置文件運行 Pipeline 中各階段的任務,總共定義 3 個階段:compile,build,deploy;
-
- 在 compile 階段,Gitlab Runner 將項目編譯成 jar 包,使用 MinIO 作為緩存,首次編譯項目時會從 Maven 官網拉取依賴,之后會將依賴壓縮后上傳至 MinIo,在下一次編譯時就可以直接從 MinIO 下載依賴文件;
-
- 在 build 階段,Gitlab Runner 使用在 compile 階段編譯生成的 jar 包構建 Docker 鏡像,并將鏡像推送至鏡像倉庫;
-
- 在 deploy 階段,Gitlab Runner 使用構建好 Docker 鏡像在 Kubernetes 集群中部署應用。
二、背景分析
- 在實施業務組件化后,大部分沒有組件化工具鏈支撐的團隊一般都會遇到組件發布效率問題,如果遇到多個特性一起上線時,發布的組件數量可能達到幾十個,手動發布這些組件的話,費時費力,非常影響開發體驗。雖然可以通過 CI 簡化單個組件的發布,只需要根據 Podfile 中的版本提交相應 tag 即可觸發發布動作,但是 CI 并沒有解決多個關聯組件發布的前后順序問題。如果下層組件還未發布就發布上層組件,此組件的 CI 很可能會因為缺少下層組件的某些接口而執行失敗。
- 基于 GitFlow 工作流進行日常項目的開發,項目在進入預發階段時,關聯的組件都需要拉取 release 分支,當某次發版的所有關聯項目都預發測試完畢時,此次發版的負責人(通常是其中某個項目的負責人)會通知團隊內部成員對組件進行封板,然后組件的負責人會去合并 release 分支到 master & develop,并且發布一個新版本,等所有組件都發布完成后,發版負責人再去更新主工程 Podfile。
- 整個發版過程,組件負責人除了需要重復若干次以下操作,還需要知悉是否有下層組件還未發布:
- 可以看到如果需要發布多個組件,其過程還是非常繁瑣的。再單獨說下發布順序的問題,假設當前有需要發布的組件 A、B、C、D,其依賴關系如下:
- 在遵守 CocoaPods 發布規則的前提下,發布先后順序應依次為 A、B-C、D ,其中 B 和 C 組件可同時發布,D 則需要等 B、C 都發布完成后才可以發布,也就是說只有當前組件的依賴沒有包含未發布組件,此組件才可發布。
- 我們以前的發布情況常常是這樣的:下層組件 A 由于 lint 不通過,導致依賴 A 的 B、C 都 lint 失敗,由于沒有限制開發者對私有源倉庫的 push 權限,B、C 組件的負責人這時候可能就會選擇向私有源倉庫強推 podspec,導致出現 lint 失敗的連鎖反應,越來越多的組件本身代碼沒問題,卻因為下層組件而 lint 失敗,只能選擇強推 podspec。遇到這種情況,除了強調發布規則,從根本上還是要減少發版操作給組件負責人帶來的工作量。
- labor 就是為了能在一定程度上解決以上問題而創建的,在 labor 上執行發版操作時,組件負責人只需要關注 lint 的錯誤信息即可,剩余發布操作,包括上下層組件的發布順序都由 labor 進行管理。
三、效果演示
- 以上述的 A、B、C、D 組件為例,在 labor 上添加發布并分析依賴后,可以看到組件發布頁:
- 在發布頁中,使用者可以查看依賴發布的組件,也可以修改發布組件的版本,labor 會在組件發布時同步到倉庫的 podspec 文件中。執行自動發布后,labor 會和 GitLab 進行一系列交互。
- 以組件 A 為例,labor 會先創建所有發布組件目標分支的 MR:
- 然后觸發對應 release 分支的 pipeline:
- 這里省去的 code review 這一步驟,如果需要的話,可以在 web 上設置入口,組件負責人設置為 review 完成后,才觸發 pipeline。如果 pipeline 執行成功,那么 GitLab 會自動合并 MR,如果沖突的話,需要負責人在此 MR 下解決:
- MR 合并成功后,組件會更新狀態為發布中(假如組件還有依賴未發布,那么這里的狀態為已合并,等待依賴發布完成,狀態才為發布中):
- 然后 labor 會給組件打 tag,并且觸發 tag 的 pipeline:
- 在 tag 的 pipeline 執行成功后,就視 A 組件發布成功。A 發布成功后,labor 會查看 B、C 對應 MR 狀態執行后續操作:
-
- MR pipeline 已經執行成功,并且對應的分支已經合并到 master (組件狀態:已合并);
-
- MR pipeline 由于 A 沒發布,lint 失敗,分支沒有合并到 master(組件狀態:等待中);
- 如果是 1 狀態,則直接創建 tag 發布,如果是 2 狀態,則觸發 MR 對應分支的 pipeline,假如此 MR 是因為 A 組件未發布導致合并失敗的,那么在 A 發布后,重新觸發的 MR pipeline 一般都能執行成功,當 MR 合并成功后,后續步驟與 1 一致。受益于 GitLab 分布式的 runner,可以通過 CI 同時發布多個組件。
- 當所有組件發布完成后,labor 會根據使用者輸入的組件版本,更新發版工程的 Podfile:
- 以上就是 labor 的主要工作步驟,可以看到,使用 labor 發版后,組件負責人只需要確保 MR 能順利合并即可,不需要等待下層組件負責人發布完成的通知,也省去了繁瑣的發布操作。
- 截止到目前為止,labor 已經幫助團隊執行了近 30 次自動發布,每次發布的組件個數平均在 15 個左右,節省了很多組員溝通與操作時間。
四、labor 結構與發布類型
- labor 由如下幾個服務構成:
- 其中 web 端主要專注于發版交互與組件發布數據的展示,后端負責發版信息采集、組件發布任務調度以及和 GitLab 進行通信,websocket server 主要負責實時更新 web 中組件的發布狀態。server 中很多操作都是與 GitLab 進行交互,耗時較多,所以 server 中的大部分 service 都是交給 sidekiq(后臺任務處理系統)執行的。
- 依據發布性質,labor 把發布分為兩種:
-
- 主發布 (main deploy);
-
- 子發布 (pod deploy)。
- 這里主發布的主體是發版工程,子發布的主體是組件,其中主發布主要負責發版工程信息的獲取和更新,比如依賴的分析、最后目標分支 Podfile 的更新等。主發布經過分析后,會創建若干子發布,子發布則負責組件發布的所有流程,包括組件 MR 的創建,組件 tag 的創建,發布 CI 的觸發等。
- 主發布和子發布涉及的所有狀態如下:
- 使用 state_machines-activerecord 以狀態機的形式對發布狀態進行管理,狀態發生變更之后,都會通過 websocket 同步到 web 端。
- 分析目標工程的組件依賴是發版的第一步,對應主發布的 analyzing 狀態,如下是 labor 分析步驟的序列圖:
- 首先,web 向 server 端發起分析依賴請求,server 接收到請求后,使用 gitlab 向 GitLab 請求 Podfile 文件內容。由于 Podfile 可能在倉庫的根目錄或者 Example 文件夾下,使用 5 層深度的遞歸查詢來獲取文件路徑。
- 獲取到 Podfile 路徑后,再根據文件內容創建 Podfile 對象:
- 生成 Podfile 對象后就可以分析發版信息,可以先過濾出需要發版的依賴:
- 然后通過同樣的方式循環獲取這些依賴的 podspec 文件,并且構建 Specification 對象:
- 這里可以利用多線程加快執行效率,不過線程數不要過多,不然容易造成 GitLab 返回數據失敗。獲取到所有需要發布的 spec 后,可以結合組件與其依賴、間接依賴創建發布結構,然后保存至數據庫。
- 同樣以 A、B、C、D 組件為例,它們的 podspec 依賴如下:
- 最終生成的發布結構如下:
- 當 A 發布完成后,發布結構轉變為:
- B、C 組件會在需要發布的依賴清空之后,繼續執行組件發布的后續操作。
五、發布組件
① 組件發布核心過程
- 組件的發布是整個服務的核心功能,順利發布單個組件時的序列圖如下:
- 上圖中省略了部分和 websocket server 相關的邏輯,實際上其右邊的狀態發生變更時,都會進行 12、13 步驟的消息流動。
- 如果是自動發布所有組件,而不是發布單個組件,labor 會對主發布分析出的所有組件執行發布操作,這樣相關負責人就可以選擇提前去 review MR 上需要合并的代碼,而不是等依賴的組件發布完成。
- 對于已知 lint 不通過,短時間無法解決錯誤的組件,labor 提供了手動標志組件發布成功的功能,使用者需要手動發布組件,再設置發布成功。添加這個功能主要是考慮到發版工程會接入其他業務線的組件,而我們并不想讓這些組件影響發版進程。
- 組件發布過程中,根據處理對象的不同,又可分為以下兩個階段:
-
- 準備階段(preparing ~ pending);
-
- 正式發布階段(merged ~ success)。
② 準備階段
- 首先,server 會給還未配置過的組件工程添加 webhook ,GitLab 很多任務都是放到 sidekiq 的,要想獲取任務的執行信息,只能通過 webhook 讓 GitLab 主動發送,所以這一步是組件能自動發布的重要前提。
- 接著,server 會校驗創建 MR 的必要條件:
-
- 組件倉庫必須要有 CI 配置文件,并且文件中包含發布 stage;
-
- 組件倉庫的 default 分支必須為 master。
- 如果滿足以上條件,我們會繼續處理目標組件的 podspec 版本。labor 在分析依賴時,默認會使用 release 分支名中或者 podspec 中較高的版本號作為發布版本,如果實際發布時,組件發布版本比倉庫中的 podspec 高,就需要更新 GitLab 倉庫中 podspec 的 version 字段。
- 更新 podspec 版本的代碼:
- 更新字符串后,同步至 GitLab:
- 這里 commit 信息以 [ci skip] 作為前綴,以減少觸發不必要的 pipeline。最后,會校驗目標分支是否為 master 或者已經合并到 master ,如果是的話 server 會直接標記此發布為 merged ,等待正式發布,否則 server 會給組件倉庫創建 MR ,發布進入 pending 狀態,等依賴的組件都發布完成后,如果此 MR 還未合并,server 會重新觸發 MR 關聯的 pipeline,pipeline 運行成功,目標分支合入 master 后,再執行正式發布。
③ 正式發布階段
- 由于 tag 觸發的 pipeline 包含二進制打包、源碼及二進制版本發布的 stage,因此在這個階段只需要管理 tag 及關聯的 pipeline 就可以實現發布功能。這時候如果 tag 版本已經存在,GitLab 會返回創建失敗,雖然 GitLab 提供了刪除 tag 的功能,但還是不建議這么做的,我們會在 web 端提示發布失敗,并且注明 tag 已存在,讓組件負責人修改版本后重試發布此組件。
- GitLab 創建 tag 操作是異步的,因此我們會在 webhook 中監聽 tag 創建結果,創建成功之后,就可以處理 pipeline。上文提到過 以 [ci skip] 開頭 commit 不會觸發 pipeline,所以需要在這里做下判斷,如果已經有 pipeline,那么只更新對應數據庫條目,否則就需要創建新的 pipeline。
六、更新依賴
- 在所有組件發布完成后,我們會把變更的組件版本,同步到發版工程對應分支的 Podfile 中。受限于目前的工作流程和有限的工具鏈,在開發時,還是避免不了手動修改 Podfile,所以依舊使用 ruby 來編寫 Podfile,沒有使用 YAML 或者 JSON 格式 (pod ipc podfile/podfile-json 可查看)。
- 過于靈活的編寫方式,使得對其執行正則匹配需要以先推行編寫規范為基礎,因此不使用正則,而是讓開發者提供 Podfile 模版,比如正常的 Podfile 為:
- 那么開發者可以在同級目錄下,添加 PodfileTemplate :
- labor 會先獲取原 Podfile,解析其依賴之后,再根據數據庫中的組件及其發布版本號,更新這些依賴,然后將 PodfileTemplate 文件的 :TRIPLECCREPLACEME 替換成更新后的依賴,以生成新的 Podfile:
- 當然,在工具鏈成熟的情況下,使用 JSON 或者 YAML 格式編寫的 Podfile,更易于自動化處理,所以個人推薦前期最好不要在 Podfile 中添加過多 ruby 自定義代碼,如果需要的話,可以以 cocoapods 插件的形式集成。
七、總結
- 由于 labor 只是個人在開發維護,所以制定流程和細節處理上可能不是非常合理,但就目前成果來看,labor 還是初步實現了發版時的組件自動化發布。本文更多的是展示了正常發布流程,實際上整個發布過程中,還是要處理挺多異常流程的。
- 不過在發版本時才統一對需要發布的組件進行驗證,容易出現解決組件 lint 錯誤時間過長影響發版問題,所以可能還是需要添加組件準入規則,將這部分驗證往前移,比如約束主工程的分支權限,在預發提測前讓開發者預先打 rc 版本,確保 rc 版本驗證成功之后,再以 web 操作的方式接入主工程,預發階段修復組件 bug 后,重新走接入流程,這樣應該就能保證發版時,需要發布組件的正確性(關于 rc 版本的 lint ,可能還需要變更下 CocoaPods 查找版本的默認方式,畢竟 Semantic Versioning 優先采用正式版本)。
總結
以上是生活随笔為你收集整理的iOS之深入解析CocoaPods的GitLab CI与组件自动化构建与发布的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【数据结构与算法】之深入解析“24点游戏
- 下一篇: iOS之深入解析CocoaPods的插件