Test, Packages and Releases

想像一下,現在我們已經有個派遣工,會在 master 分支有異動時來幫忙打包,但是,既然已經有派遣工可用,能不能讓派遣工幫更多忙呢?

答案當然是肯定的!

使用 GitLab CI/CD 的精神就是 自由發揮,端看開發人員怎麼設計!(重責大任在自己身上,只能苦笑~

回歸到研發流程上,我習慣的研發流程是以 issue 為基礎:

  1. 不管是功能新增、錯誤修正... 等,只要跟變動程式碼有關,都必須基於 issue
  2. 一個 issue 通常會對應一個 issue 專用的分支
  3. 在 issue 完成之後,提交 merge request(以下簡稱 MR),審核通過後,issue 分支的內容才能被合併回 master 上

以上,就是我的日常~(現在明明就在休息,逃~

在這個研發流程中,最累人的就是 MR,總是一來一回異動無數次,每天都是 MR 地獄,不誇張!(遠目

這時,如果有派遣工,能幫上些什麼忙呢?

聰明的你想到了!是 unit test!但是我必須很誠懇的說,unit test 真的不是第一步。
第一步是要能 通過編譯
無論是不會寫、沒有人手寫或者沒辦法寫 test case,至少也要能被無誤的編譯!(GraphQLet 就是一個實例,由於沒有架設自己的伺服器,所以目前並沒有撰寫 test case 實際去呼叫、取得結果來進行測試。)

人難免都會犯錯,這時就要讓派遣工來幫忙 ❤️~

回歸到研發流程,若多了一個測試的階段,就算不包含 unit test,無論是對 issue 的苦主或者 MR 的苦主來說,都是件很大的幫助。

  • issue 的苦主不用再擔心自己 push 錯或漏 push 了什麼,因為派遣工會幫忙編譯,如果沒通過,也來得及調整!
  • MR 的苦主這時多了一層保障,因為派遣工已確保了編譯正確!此時,透過人腦編譯來 review,寫下建議的你也能更心安理得!
  • 假如你的團隊已經要求 unit test,那更棒了!CI/CD 界面中可以整合 JUnit 的報告!

CI/CD 流程調整|新增 Test Stage

不多說,先直接調整一版設定!

image: maven:3.6.3-jdk-11

stages:
  # 新增了一個階段 test 在 build 之前
  - test
  - build

variables:
  MAVEN_OPTS: "-Dhttps.protocols=TLSv1.2 -Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN -Dorg.slf4j.simpleLogger.showDateTime=true -Djava.awt.headless=true"
  MAVEN_CLI_OPTS: "--batch-mode --errors --fail-at-end --show-version -DinstallAtEnd=true -DdeployAtEnd=true"

# test 中執行 mvn 的 test 指令
test:
  stage: test
  script:
    - mvn $MAVEN_CLI_OPTS test

build:
  stage: build
  only:
    - master
  script: 
    - mvn $MAVEN_CLI_OPTS package
  artifacts:
    name: "$CI_PROJECT_TITLE"
    paths:
      - target/*.jar

好的,這樣我們就加上了測試的階段。但實際運行起來的效果是如何呢?(自己測試看看會更有感覺)你會發現:

  • MR 時會驅動跑 test
  • 合併回 master 時會驅動跑 test -> build

但我真正想要的是什麼呢?我想要的其實是:

  • MR|test
  • master|build

原因是,這兩個步驟的行為有點重複,mvn 執行 package 時已經會包含 test 的效果。調整後如下:

image: maven:3.6.3-jdk-11

stages:
  - test
  - build

variables:
  MAVEN_OPTS: "-Dhttps.protocols=TLSv1.2 -Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN -Dorg.slf4j.simpleLogger.showDateTime=true -Djava.awt.headless=true"
  MAVEN_CLI_OPTS: "--batch-mode --errors --fail-at-end --show-version -DinstallAtEnd=true -DdeployAtEnd=true"

test:
  stage: test
  # 限定在只有 MR 時會驅動
  only:
    - merge_requests
  script:
    - mvn $MAVEN_CLI_OPTS test
  # 以下是 GitLab 和 JUnit 整合的方法,如果有採用 JUnit + surefile 來進行 unit test,可以將 report 整合到 MR 及 pipeline 的 UI 上
  # 若不需要請自行註解掉或拿掉
  artifacts:
    reports:
      junit:
        - target/surefire-reports/TEST-*.xml

build:
  stage: build
  only:
    - master
  script: 
    - mvn $MAVEN_CLI_OPTS package
  artifacts:
    name: "$CI_PROJECT_TITLE"
    paths:
      - target/*.jar

這樣調整之後,就可以達成前述的效果,派遣工不重工也更省時。

有派遣工真好 ❤️~
就曾經發生過小夥伴 push 時漏了檔案,負責 MR 的我人腦編譯沒發現,MR 通過後 master 掛掉 Orz......


以前總會希望,能輕鬆的完成兩件事情:

  • 希望有個服務能代管打包完的成果,但是,又不想自己架,唉~
  • 希望有個方便記錄版本異動資料的機制,好快速查詢每個版本的差別到底在哪

不曉得萬能的派遣工,能不能幫上這個忙呢 😘~

答案是,辦!不!到!!! 呃...... 我的意思是~
只要用上了 GitLab PackagesGitLab Releases 這兩個功能就能實現喔喔喔喔喔!(偉哉 GitLab

CI/CD 流程調整|新增 Deploy Stage

在設定 CI/CD 之前,要先來對 Packages 進行設定。
為了能讓 maven 能順利將打包的產出部署到 Packages 服務,我們需要在 pom.xml 中加入以下設定:

<!-- 請放在 pom 中的第一級 -->
<repositories>
    <repository>
        <id>gitlab-maven</id>
        <url>https://gitlab.com/api/v4/projects/${env.CI_PROJECT_ID}/packages/maven</url>
    </repository>
</repositories>
<distributionManagement>
    <repository>
        <id>gitlab-maven</id>
        <url>https://gitlab.com/api/v4/projects/${env.CI_PROJECT_ID}/packages/maven</url>
    </repository>
    <snapshotRepository>
        <id>gitlab-maven</id>
        <url>https://gitlab.com/api/v4/projects/${env.CI_PROJECT_ID}/packages/maven</url>
    </snapshotRepository>
</distributionManagement>

同時,需要在 project 的新增一個 xml,GitLab 官方建議放置在 project 根目錄,命名為 ci_settings.xml,內容全文如下:

<settings xmlns="http://maven.apache.org/SETTINGS/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0 http://maven.apache.org/xsd/settings-1.1.0.xsd">
    <servers>
        <server>
            <id>gitlab-maven</id>
            <configuration>
                <httpHeaders>
                    <!-- GitLab Packages 服務認證用 -->
                    <property>
                        <name>Job-Token</name>
                        <value>${env.CI_JOB_TOKEN}</value>
                    </property>
                </httpHeaders>
            </configuration>
        </server>
    </servers>
</settings>

這些設定的目的是讓 GitLab 的派遣工,在執行 maven 的部署工作時,

  1. 取得正確的資源庫路徑,
  2. 提供認證資訊給 GitLab,以供識別是否為合法的服務使用者。

有了以上前置作業,就做好使用 Packages 的準備了!

接下來,來思考一下我們期望的流程,這個部署的步驟,會是怎麼樣被驅動的呢?同時,得問問自己,怎麼樣的產出標準得以被部署呢?

在這和大家分享一下我的經驗,以往,版本的迭代是採用了 milestone 的機制:

  • 一個 milestone 中有數個 issue,issue 之間可能有相依性
  • milestone 是否完成,是由團隊共同檢驗
  • milestone 確定完成時,會在被當成 production 環境的分支(整合複雜的情況會開不同於 master 的分支)上以 版本號 標記 tag

換言之,是由 來決定是否完成了某個版本。

這麼一想,期望的流程就變得非常清楚:

  • MR|test
  • master|build
  • tag|deploy

調整後如下:

image: maven:3.6.3-jdk-11

stages:
  - test
  - build
  - deploy

variables:
  MAVEN_OPTS: "-Dhttps.protocols=TLSv1.2 -Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN -Dorg.slf4j.simpleLogger.showDateTime=true -Djava.awt.headless=true"
  MAVEN_CLI_OPTS: "--batch-mode --errors --fail-at-end --show-version -DinstallAtEnd=true -DdeployAtEnd=true"

test:
  stage: test
  only:
    - merge_requests
  script:
    - mvn $MAVEN_CLI_OPTS test
  artifacts:
    reports:
      junit:
        - target/surefire-reports/TEST-*.xml

build:
  stage: build
  only:
    - master
  script: 
    - mvn $MAVEN_CLI_OPTS package
  artifacts:
    name: "$CI_PROJECT_TITLE"
    paths:
      - target/*.jar

# 新增一個 job 叫做 maven 
maven:
  stage: deploy
  # 在標記了 tag 時驅動
  only:
    - tags
  # 先前設定的 ci_settings.xml 是部署 deploy 指令的重要參數
  script:
    - mvn $MAVEN_CLI_OPTS deploy -s ci_settings.xml

完成!
這時,只要 git 版本庫中有標記任何的 tag,派遣工就會趕來把打包成果部署到 Packages 服務中。

等等!部署都做完了,那前面提到的 Releases 又是什麼呢?

Releases 是 GitLab 提供一個附加在 tag 上的機制,在使用 GitLab UI 添加 tag 時,有一個額外的區塊叫做 Release notes,只要填入內容,發佈 tag 時就等於釋出了一個版本。


匯總一下研發流程:

  1. 制定 milestone,這個 milestone 規範了某個版本要開發哪些功能
  • 小型專案是否要採用 milestone 可以仔細考慮,目前 GraphQLet 是沒有使用的
  1. 制定要完成的 issues,進入 issue 開發循環
  2. 逐個開發 issue,完成 issue 後提交 MR - 派遣工幫忙 test
  3. 逐個審核 MR,若未通過則退回繼續調整,若通過則 merge 回 master - 派遣工幫忙 build
  4. 檢核 milestone,確認 milestone 目標且所有 issue 都已經完成
  • 若完成,關閉 milestone
  • 若未完成,回步驟 2
  • 如未採用 milestone 請確保要釋出新版本的 issue 都開發完成
  1. 新增 tag 標記版本及版本資訊
  • 派遣工幫忙 deploy

哇~這個流程感覺好棒啊!
把不需要人為邏輯判斷的例行性事務交給派遣工就對了,除了可以讓研發人更專注在研發上,更可以避免人工出錯提升整體品質!以前要是也有派遣工就好了......(遠目

Packages 的效果請參考 => GraphQLet Packages
Releases 的效果請參考 => GraphQLet Releases