最近、生成AIを活用した開発が進む中で「レビュー」がボトルネックになることが増えています。
レビューワーがまとめてレビューを行い、複数のPRを一気にマージするケースも珍しくありません。
弊社でも同様に、PRのマージをトリガーに GitHub Actions が走り、CI/CD パイプラインが実行される構成を採用しています。
構成としては以下のようになっています:
- CI(テスト系): Lint、Prettier、DTO更新チェック、型チェック
- CD(デプロイ系): AWS CDK による自動デプロイ
ただし、ここである課題に直面しました。
課題:PRを連続マージするとデプロイが壊れる
弊社の環境では、
1つのデプロイジョブが動いている間に別のジョブが走ると競合が発生し、CIが失敗する
という問題がありました。
結果として:
- 後からマージしたPRのデプロイが失敗する
- 最新のコミットが test 環境に反映されない
- CDKのCloudFormationスタックが中途半端な状態で残る
という状況が頻発していました。
解決の鍵:concurrency オプション
GitHub Actions には concurrency という便利なオプションがあります。
同じ「group」に属するワークフローを制御し、同時実行やキャンセルの挙動を制御できます。
通常の定義例
concurrency:
group: deploy-${{ github.ref }}
この場合、同じブランチ上のワークフローは 同時に実行されません。
1つが動いている間、次のジョブは「待機(queued)」状態になります。
つまり「安全」ですが、「遅い」です。
最新重視の設定:cancel-in-progress: true
concurrency:
group: deploy-${{ github.ref }}
cancel-in-progress: true
この設定では、進行中のジョブをキャンセルして新しいものを優先します。
常に最新のコミットをデプロイできる点で非常に便利です。
ただし、ここにも落とし穴があります。
CDKとの相性問題
AWS CDK は内部的に CloudFormation を使ってスタックを更新します。
つまり、途中でキャンセルすると CloudFormation の更新が中途半端になり、
ロールバック中のスタックと新しいデプロイが競合して失敗します。
これにより、
- 「ROLLBACK_IN_PROGRESS」状態で止まる
- 次のデプロイが
UPDATE_ROLLBACK_FAILEDで止まる
などの不安定な状態が発生することがあります。
弊社で採用している運用方針
そのため、弊社ではあえて cancel-in-progress を false(デフォルト)にしています。
すなわち:
- 複数PRをまとめてマージ
- CDKデプロイは1つずつ順番に実行
- 競合を完全に回避
結果として、test環境の安定性が大きく向上しました。
まとめ
GitHub Actions の concurrency は非常に強力ですが、
「どんなツールと組み合わせるか」で最適解が変わります。
| 目的 | 推奨設定 |
|---|---|
| 最新の状態を常に反映させたい(例:静的サイト) | cancel-in-progress: true |
| CDK のように CloudFormation を使う(状態を持つ) | cancel-in-progress: false |
状況に応じてこのオプションを調整することで、CI/CD の安定性と開発者体験を両立できます。
Actionsは結構細かい設定ができるので、状況に応じてオプションを設定し、
CI/CDをよりよくし、開発者体験を上げましょう。
参考書籍
『GitHub CI/CD 実践ガイド ――持続可能なソフトウェア開発を支える GitHub Actions の設計と運用』
https://gihyo.jp/book/2024/978-4-297-14173-8