GitHub Actions concurrency 怎麼設:部署工作流避免互相取消的 queue 選擇
部署 workflow 同時被多次 push 觸發時,問題往往不是 runner 不夠,而是兩個 job 在同一個環境做不同版本的操作。GitHub Actions 的 concurrency 能把它們放進同一個群組,但若沒有讀懂 pending run 的預設行為,新的提交可能會取代尚未開始的部署。
實務上先回答一題:**這個環境要「只保留最新版本」,還是「每個版本都必須依序部署」?**前者用預設 single queue;後者才選 queue: max。不要把這兩種意圖混在同一份 workflow。
只保留最新提交:預覽環境的設定
同一 concurrency group 預設最多保留一個 pending run;再來一個 run 時,原本等待中的 run 會被取消並由新的取代。對每次 push 都重建的 preview,這正好避免舊 commit 排隊後才發布。
name: Preview
on: push: branches: [main]
concurrency: group: preview-${{ github.ref }} cancel-in-progress: true
jobs: deploy: runs-on: ubuntu-latest steps: - run: ./scripts/deploy-preview.shcancel-in-progress: true 不只替換等待中的 run,也會取消同組正在執行的 run。這適合可安全重跑、且部署程序能處理中斷的 preview;若腳本會做不可逆的 migration,就不要照抄這個設定。
每個版本都要跑:production 用 queue: max
需要保留每次 release 的順序時,使用 queue: max 讓同組最多排隊 100 個 run。它不能與 cancel-in-progress: true 同時使用,GitHub 會把這個組合視為 workflow 驗證錯誤。
concurrency: group: production-deploy queue: max這不會替你的部署做版本鎖定或 rollback。它只是保證同組不會同時執行;資料庫 migration、人工核准與部署健康檢查仍要在 job 裡處理。
group 名稱要包含工作流與環境
group 名稱不區分大小寫,而且不同 workflow 若用了相同名稱,也會互相影響。建議將 workflow 與目標環境都放進 key:
concurrency: group: ${{ github.workflow }}-production queue: max若 workflow 同時處理 pull_request 與 push,像 github.head_ref 這種只在 pull request 存在的欄位必須提供 fallback,否則 group expression 可能無法組成有效名稱。
提交前的檢查清單
- 寫下被取消的 run 是否可以安全重跑或中斷。
- 確認 group 不會跨 workflow、跨 staging/production 意外共用。
- production 若不能遺失發布,改用
queue: max,並移除cancel-in-progress。 - 以連續兩次 push 在非正式環境測試 Actions 頁面的 pending 與 cancel 結果。
如果 workflow 還涉及不可信的 fork PR,先完成 GitHub Actions pull_request_target 的 checkout 保護;concurrency 只能序列化執行,不能降低權限風險。
參考資料:
回報錯字、失效連結,或告訴我你想看的延伸主題。