學習目標
- 用
workflow:rules決定整條 pipeline 要不要建立。 - 用
rules:if、rules:changes、rules:exists決定 job 要不要跑。 - 會用
when: manual、when: delayed、when: never。 - 知道
only/except已不建議使用,一律改寫成rules。
觀念
rules 是一串條件,由上往下比對,第一條符合就停:
| 結果 | 意思 |
|---|---|
符合,沒寫 when | 照常執行(on_success) |
符合,when: never | 不執行 |
符合,when: manual | 出現按鈕,要有人按 |
符合,when: delayed | 等 start_in 後執行 |
| 全部不符合 | job 不會出現在 pipeline 裡 |
本關每個 job 什麼時候會跑
| job | push 到 main | push 到 feature/x | MR |
|---|---|---|---|
always | 跑 | 跑 | 跑 |
default-branch-only | 跑 | — | — |
feature-branch-only | — | 跑 | — |
mr-only | — | — | 跑 |
docs-changed | 文件有改才跑 | 文件有改才跑 | 文件有改才跑 |
notes-exist | 檔案存在就跑 | 同左 | 同左 |
manual-release | 手動按鈕 | — | — |
重點說明
- 重複 pipeline:同一個分支開了 MR,push 一次會同時產生分支 pipeline 與 MR pipeline。
workflow的前兩條規則讓「有 MR 時只跑 MR pipeline」。 - **
parent_pipeline**:本課程根目錄的.gitlab-ci.yml把每一關當成子 pipeline 觸發,來源是parent_pipeline,所以要在workflow明確放行,否則子 pipeline 會因為沒有任何 job 而建立失敗。 - **
changes**:比對這次 push 改了哪些檔案。手動執行、排程這類沒有「前一個 commit」的來源,changes一律當成成立;要穩定比對可以加compare_to: refs/heads/main。 - manual 的兩種寫法:job 層的
when: manual預設不擋 pipeline;寫在rules裡的when: manual預設會擋(pipeline 停在「blocked」),要不擋就加allow_failure: true。正式環境的發布通常就是要擋,Lv.9 會再談。 - 正規表示式:
=~ /^feature\//,斜線要跳脫。
動手做
- 開一個
feature/try-rules分支 push,feature-branch-only出現、default-branch-only消失。 - 對 main 開 MR,看 pipeline 只剩一條(MR pipeline),而且出現
mr-only。 - 「Run pipeline」時加變數
SKIP_LESSON6=yes,skip-switch不會出現。 - 在 main 的 pipeline 按下
manual-release的播放鍵。
常見錯誤
- 在同一個 job 混用
rules和only/except:GitLab 會報錯。 when: never放在最後一條:前面已經符合就輪不到它了,排除條件要放最前面。changes的路徑忘了加**:lv06-rules/*.md不包含子資料夾。
下一關
Lv.7 用 needs 打破階段限制,讓 job 一準備好就開始。
範例檔案
資料夾 lv06-rules/ 的全部檔案。.gitlab-ci.yml 可以直接複製到自己的專案根目錄執行。
lv06-rules/.gitlab-ci.yml
lv06-rules/.gitlab-ci.yml
# Lv.6 Rules 與 workflow:決定「整條 pipeline」與「每個 job」要不要跑、怎麼跑
# rules 由上往下比對,第一條符合的就決定結果;都不符合=這個 job 不會出現在 pipeline 裡。
# workflow:rules 決定整條 pipeline 要不要建立
workflow:
rules:
# 當成子 pipeline 被觸發時(本課程的根目錄 pipeline 就是這樣跑每一關)
- if: $CI_PIPELINE_SOURCE == "parent_pipeline"
# 有 MR 時只跑 MR pipeline,避免同一個 commit 跑兩條(分支+MR)
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
when: never
- if: $CI_COMMIT_BRANCH
- if: $CI_COMMIT_TAG
stages:
- check
- release
default:
image: alpine:3.22
always:
stage: check
script:
- echo "沒有 rules 的 job 永遠會跑(來源:$CI_PIPELINE_SOURCE)"
default-branch-only:
stage: check
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
script:
- echo "只在 $CI_DEFAULT_BRANCH 執行"
feature-branch-only:
stage: check
rules:
- if: $CI_COMMIT_BRANCH =~ /^feature\//
script:
- echo "只在 feature/ 開頭的分支執行"
mr-only:
stage: check
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
script:
- echo "只在 MR pipeline 執行,標題:$CI_MERGE_REQUEST_TITLE"
docs-changed:
stage: check
rules:
# 這一關的說明文件有改才跑;手動、排程等沒有「改了什麼」的來源一律視為有改
- changes:
- lv06-rules/**/*.md
script:
- echo "說明文件有變更,執行文件檢查"
- grep -q '^# Lv.6' lv06-rules/README.md
notes-exist:
stage: check
rules:
- exists:
- lv06-rules/release-notes.md
script:
- echo "找到 release-notes.md"
- cat lv06-rules/release-notes.md
skip-switch:
stage: check
rules:
# 設變數 SKIP_LESSON6=yes 就不跑;when: never 要放在前面
- if: $SKIP_LESSON6 == "yes"
when: never
- when: on_success
script:
- echo "沒有設定 SKIP_LESSON6=yes,所以執行"
delayed:
stage: release
rules:
- when: delayed
start_in: 10 seconds
script:
- echo "等了 10 秒才開始"
manual-release:
stage: release
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
when: manual
# rules 裡的 manual 預設會「卡住」pipeline,直到有人按下;allow_failure: true 表示不擋
allow_failure: true
script:
- echo "有人按下了發布按鈕:$GITLAB_USER_LOGIN"lv06-rules/release-notes.md
lv06-rules/release-notes.md
# Lv.6 示範用的發布說明
`notes-exist` 這個 job 用 `rules:exists` 偵測到本檔案存在才會執行。把這個檔案刪掉,下一條 pipeline 就看不到那個 job。