1817 字
9 分鐘

GitHub Actions workflow 被跳過後卡在 Pending?先處理 required check

GitHub Actions workflow 明明沒有失敗,Pull Request 卻一直顯示 Waiting for status to be reportedPending,最常見的原因不是 runner 壞掉,而是 workflow 根本沒有被觸發。當這個 workflow 又被分支保護規則列為 required check,GitHub 就沒有可供合併判斷的結果。

這篇聚焦在「被跳過的檢查」:怎麼分辨 workflow-level filter、job-level if 和 merge queue 的差異,以及如何把 required check 設計成每次都有可判讀的結果。若你的問題是同一個部署 workflow 被多次提交互相取消,則是另一個 GitHub Actions concurrency queue 設定問題。

先分清楚是 workflow 被跳過,還是 job 被跳過#

GitHub 官方把這幾種結果分得很清楚:

發生位置常見設定PR 上看到的結果對 required check 的影響
workflow 觸發前pathspaths-ignorebranchesbranches-ignoreworkflow 沒有 run相關 check 可能停在 Pending,阻擋合併
commit message[skip ci][skip actions]workflow 沒有 run相關 check 可能停在 Pending
workflow 裡的 jobjobs.<job_id>.ifjob 顯示 skipped官方文件把這類結果視為 Success
needs 相依 job前置 job 失敗後續 job skipped依賴鏈可能沒有產生你預期的 required 結果

這個差異很重要。把 if 從 job 移到 on.pull_request.paths,看起來只是把條件往上移一層,實際上卻會讓整個 workflow 消失。GitHub 的保護規則無法把「不存在的 workflow run」當成成功。

先查三個地方,不要重跑一個不存在的 run#

遇到 Pending 時,我會按照這個順序查:

  1. PR 的 Checks 頁面:確認缺少的是哪一個 check 名稱,以及它要套用在哪個 commit SHA。
  2. 分支保護或 ruleset:確認這個 check 是否被列為 required,是否還要求 branch 必須先更新到最新 base branch。
  3. workflow 的 on 區段:搜尋 pathspaths-ignorebranchesbranches-ignore,再看最近的 commit message 是否包含 skip 指令。

可以先在 repository 根目錄列出可能的觸發條件:

Terminal window
rg -n \
'(^|[[:space:]])(paths|paths-ignore|branches|branches-ignore|pull_request|push|merge_group):|skip (ci|actions)' \
.github/workflows .github 2>/dev/null

這個搜尋只協助定位,不會告訴你某個 PR 的檔案是否真的符合 glob。最後仍要把實際變更檔案和 workflow 的 pattern 對照;branchespaths 同時存在時,兩組條件都要通過才會觸發。

最容易重現 Pending 的 workflow 寫法#

下面的 workflow 只在 scripts/ 變更時觸發:

name: ci
on:
pull_request:
paths:
- 'scripts/**'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm test

如果分支保護把 build 列為 required,而 Pull Request 只修改 repository 根目錄的 README.md,這個 workflow 不會建立 run。GitHub 官方的排錯文件也直接以這種情境說明:required check 會留在等待狀態,PR 因此無法合併。

三種修法,依你的檢查邊界選一種#

修法一:不把可被 workflow filter 跳過的 check 設為 required#

如果 scripts/** 真的只是小範圍檢查,而且其他變更不需要它,可以從 branch protection 或 ruleset 的 required checks 移除 build。這是最直接的修法,但要確認沒有因此漏掉真正需要的安全或建置檢查。

修法二:讓 workflow 每次觸發,把條件放進 job#

如果 check 名稱必須穩定存在,讓 pull_request 不用 paths 過濾,再把選擇性條件放到 job。這樣 workflow 至少有機會產生一個可追蹤的 run;被 if 跳過的 job 也和整個 workflow 沒有 run 是不同結果。

name: ci
on:
pull_request:
merge_group:
jobs:
build:
if: >-
github.event_name == 'merge_group' ||
contains(github.event.pull_request.changed_files, 'scripts')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- run: npm ci
- run: npm test

上面的 changed_files 只作為結構示意,不能直接當作完整的路徑判斷實作。實務上可用 repository 已採用的變更偵測方式產生 output,再把 output 傳給後續 job。重點是:不要直接把 required workflow 在 on 層級整個過濾掉

修法三:把 required check 做成永遠回報的 gate#

若真正的 build job 有多個條件,建立一個最後的 gate job,使用 needs 收集結果,並以 if: ${{ always() }} 確保它會執行。Gate 再根據前置 job 的結果決定成功或失敗,讓分支保護只需要追蹤一個固定名稱。

jobs:
lint:
runs-on: ubuntu-latest
steps:
- run: ./scripts/run-lint.sh
build:
runs-on: ubuntu-latest
steps:
- run: ./scripts/run-build.sh
required:
if: ${{ always() }}
needs: [lint, build]
runs-on: ubuntu-latest
steps:
- name: Fail when a required job failed or was cancelled
if: >-
needs.lint.result == 'failure' ||
needs.lint.result == 'cancelled' ||
needs.build.result == 'failure' ||
needs.build.result == 'cancelled'
run: exit 1
- run: echo 'Required jobs completed'

這個範例沒有處理所有專案的變更範圍需求,但示範了 gate 的責任:把「哪些 job 要跑」和「required check 要不要通過」拆開。若前置 job 失敗或被取消,gate 仍會回報失敗,而不是讓 required check 消失。

Merge queue 還要加上 merge_group#

如果 repository 啟用了 merge queue,pull_requestpush 不一定能涵蓋被放進 queue 的測試事件。GitHub 官方要求需要在 workflow 觸發條件中加入 merge_group,否則 required check 可能不會在 queue 的測試合併提交上產生。

最小設定如下:

on:
pull_request:
merge_group:

加上事件後,仍要檢查 workflow 使用的 context。merge_group 的事件資料和 pull_request 不完全相同,不要把所有 github.event.pull_request.* 直接假設成兩種事件都存在。需要路徑判斷時,先為兩種事件分別設計 input,再在測試 PR 和 merge queue 中各跑一次。

提交前的最小驗證表#

修改 workflow 或 branch protection 後,至少驗證這四種 PR:

測試情境預期結果
修改會觸發檢查的檔案workflow run 建立,required check 完成
只修改不相關檔案若選擇性 job 被跳過,固定 gate 仍有結果
commit message 含 skip 指令確認這個行為不會和 required check 衝突
PR 加入 merge queuemerge_group workflow 產生對應 check

如果目前已經卡在 Pending,先把修法提交到新的 commit,再回到 PR 的最新 commit SHA 確認 check。GitHub 文件指出,較早 commit 的成功結果不能代替最新 SHA 上的 required check。

結論:required 的應該是結果,不是偶爾存在的 workflow#

GitHub Actions 的 pathsbranches 很適合減少不必要的執行,但它們放在 workflow 觸發層時,可能讓 required check 完全沒有結果。遇到 Pending,先判斷是 workflow 沒有 run、job 被條件跳過,還是 merge queue 少了 merge_group;再決定要移除 required、把條件下移,或建立永遠回報的 gate。

常見問題#

Q: Workflow 被 paths 跳過,為什麼不是 Success?#

A: paths 是 workflow-level filter。檔案不符合條件時,整個 workflow 不會建立 run,因此 GitHub 沒有一個可回報的 check 結果。若這個 check 被設定為 required,PR 可能停在 Pending。把條件放進 job,或不要把可被跳過的 workflow 列為 required,才能避免這個邊界。

Q: job 使用 if 被跳過,會不會也卡住?#

A: GitHub 文件把條件式跳過的 job 視為 Success,但仍要看它是否被 needs 依賴、是否有另一個 job 需要它的輸出。若 required 檢查由多個 job 組成,使用 always() 的固定 gate 會比直接要求某個選擇性 job 更容易維護。

Q: merge queue 已經有 pull_request,還需要 merge_group 嗎?#

A: 需要另外確認。GitHub 把 merge queue 的事件獨立成 merge_group;若 required check 必須在 queue 的測試合併提交上執行,workflow 應明確加入這個 trigger,並測試事件 context 是否符合你的條件判斷。

參考資料:

GitHub Docs:Skipping workflow runs

GitHub Docs:Troubleshooting required status checks

GitHub Docs:Triggering a workflow

GitHub Actions workflow 被跳過後卡在 Pending?先處理 required check
https://laplusda.com/posts/github-actions-skipped-required-check/
作者
Zero
發佈於
2026-08-05
許可協議
CC BY-NC-SA 4.0
這篇文章有幫助嗎?

回報錯字、失效連結,或告訴我你想看的延伸主題。