【実務・中級編】GitHub Actionsの実行制限を回避!「Conditional Execution」と「Continue-on-error」を組み合わせた堅牢なワークフロー設計 – バージョン管理・CI/CD活用バイブル

GitHub Actionsの実行制限を回避!「Conditional Execution」と「Continue-on-error」を組み合わせた堅牢なワークフロー設計

こんにちは。テックリードの私だ。
日々、プルリクエストの山と格闘し、CI/CDの緑色のビルドアイコンに一喜一憂していることだろう。

さて、君たちのプロジェクトではこんな課題に直面していないか?

  • 「Flaky Test(不安定なテスト)」のせいで、本質的ではないエラーでデプロイが止まる。
  • Linterのわずかな警告でビルド全体が落ち、開発者のフローが完全に分断される。
  • GitHub Actionsの無料枠・同時実行数の制限に引っかかり、キューにジョブが溜まっていく。

GitHub Actionsは非常に強力だが、デフォルトの挙動のままでは「融通の利かない厳格すぎる検問所」になってしまう。開発スピードを極限まで高めつつ、プロダクトの品質を担保するためには、「失敗を許容する勇気」と「実行を制御する知性」が必要だ。

今回は、`continue-on-error` と条件分岐(`if`)を完璧に組み合わせ、CI/CDパイプラインを「止まらない、しかし緩まない」要塞へと進化させる実践テクニックを伝授しよう。

—

1. なぜ「`continue-on-error` 単体」では地獄を見るのか?

まず、GitHub Actionsの基本機能である `continue-on-error: true` についておさらいしておこう。
これは文字通り、そのジョブやステップが失敗(Exit Code != 0)しても、ワークフロー全体のステータスを「成功(または中立)」扱いにして後続の処理を続行させる機能だ。

悪い例:ただ continue-on-error を貼っただけ
jobs:
lint:
runs-on: ubuntu-latest
steps:

  • uses: actions/checkout@v4
  • name: Run strict linter

run: npm run lint:strict
continue-on-error: true # 失敗しても次に進むが…

このアプローチには致命的な欠陥がある。「失敗したという事実が握りつぶされ、後続のジョブが『すべて正常に完了した』という誤った前提で動き出す」点だ。

例えば、Linterがエラーを吐いたにもかかわらず、`continue-on-error: true` のおかげで後続のデプロイジョブが走り出し、結果として汚染されたコードが本番にデプロイされる……なんて大事故になりかねない。

私たちが目指すべきは、「失敗を検知する(Observability)」ことと、「処理を強制停止させない(Resilience)」ことの完全な分離だ。

—

2. 究極の解:「Conditional Execution(条件分岐)」との融合

この問題を解決するのが、`continue-on-error` と `steps..outcome`(または `conclusion`)を組み合わせた条件付き実行(Conditional Execution)だ。

  • `outcome`: `continue-on-error` の結果を反映したステータス(`success`, `failure`, `cancelled`, `skipped`)
  • `conclusion`: `continue-on-error` を考慮した最終的なステータス

これらを利用することで、「あえて失敗を許容するが、その後の振る舞いをコードで完全にコントロールする」という高度なパイプライン制御が可能になる。

実践:Flakyなテストと必須タスクのスマートな分離

以下のワークフロー設定を見てほしい。これは、外部APIに依存していてどうしても一定確率で失敗する「E2Eテスト」を安全にハンドリングしつつ、チームにアラートを飛ばすベストプラクティスだ。

name: Resilient CI Pipeline

on:
pull_request:
branches: [ main ]

jobs:
e2e-flaky-tests:
runs-on: ubuntu-latest
# ジョブ自体の失敗を許容しつつ、後続へステータスを渡すための出力定義を行う
outputs:
test_status: ${{ steps.e2e.outcome }}

steps:

  • name: Checkout Code

uses: actions/checkout@v4

  • name: Set up Node.js

uses: actions/setup-node@v4
with:
node-version: ’20’
cache: ‘npm’

  • name: Install Dependencies

run: npm ci

# 【核心】不安定なE2Eテスト。失敗してもパイプラインを即死させない

  • name: Run Flaky E2E Tests

id: e2e
run: npm run test:e2e
continue-on-error: true

# 【核心】テストが「失敗」した場合のみ、Slackやログへ警告を残す(処理は止めない)

  • name: Notify Test Flakiness (Non-Blocking)

if: steps.e2e.outcome == ‘failure’
run: |
echo “::warning::E2Eテストが失敗しましたが、continue-on-errorによりビルドを継続します。”
# ここにSlack Webhook通知スクリプトなどを置くことも可能

# 後続の必須ジョブ
build-and-pack:
needs: e2e-flaky-tests
runs-on: ubuntu-latest
steps:

  • uses: actions/checkout@v4
  • name: Build Application

run: |
echo “ビルド処理を実行…”
npm run build

  • name: Report Summary

if: always()
run: |
echo “E2E Test Outcome: ${{ needs.e2e-flaky-tests.outputs.test_status }}”
echo “ビルドプロセスが正常に完了しました。”

この構成の美しさは、「テストが落ちてもビルド・デプロイの検証までは進められるが、テストが失敗したという事実(`test_status`)は後続のジョブやダッシュボードに確実に伝播する」という点にある。

—

3. プロが実践するチーム開発のハック&設定共有ルール

ここからは、日々の開発スピードを爆発的に高めるための、現場で培った実践知(ハック)を共有しよう。

① 開発効率を上げる隠れたキーボードショートカット

ブラウザ版のGitHub Actions画面(特に `Actions` タブ)でイライラしたことはないか?

  • `f` キー または `/` キー: リポジトリ内やActionsの検索バーに一瞬でフォーマットを合わせる。
  • ワークフローのログ画面で `Shift + J` / `Shift + K` (※拡張機能やVimバインド環境によるが、ブラウザのコンソール操作等と合わせて)ログの展開・折りたたみをマスターすると、デバッグスピードが3倍になる。

② 絶対入れるべきVS Code神プラグイン

ローカルでGitHub Actionsを書く際、シンタックスエラーでプッシュ&プレビューを繰り返すのは時間の無駄だ。

  • 「GitHub Actions」 (Extension ID: `GitHub.vscode-github-actions`):

YAMLの補完はもちろん、ワークフローの構文チェックや、ローカルからのトリガー実行支援までやってくれる。これなしでActionsを書くのは、目隠しで高速道路を走るようなものだ。

  • 「YAML」 (Extension ID: `redhat.vscode-yaml`):

JSON Schemaを紐付けることで、GitHub Actionsのスキーマバリデーションをリアルタイムで行えるように設定しておこう。

③ チーム開発におけるワークフローのDRY原則(再利用の極意)

プロジェクトが増えてくると、似たようなCI設定が乱立する。これを防ぐために Reusable Workflows(再利用可能ワークフロー) を `.github/workflows` の共通リポジトリ(または同一リポジトリ内)に切り出し、今回紹介した `continue-on-error` のロジックをカプセル化せよ。

.github/workflows/_reusable-lint.yml (共通化されたLinterワークフロー)
name: Reusable Lint

on:
workflow_call:
inputs:
strict-mode:
required: false
type: boolean
default: false
outputs:
lint_outcome:
description: “Linterの実行結果”
value: ${{ jobs.lint.outputs.outcome }}

jobs:
lint:
runs-on: ubuntu-latest
outputs:
outcome: ${{ steps.run-lint.outcome }}
steps:

  • uses: actions/checkout@v4
  • name: Run Linter

id: run-lint
run: npm run lint
continue-on-error: ${{ !inputs.strict-mode }}

呼び出し側からは、引数一つで「厳格モード(エラーで即停止)」と「許容モード(警告のみで継続)」を切り替えられる。これにより、組織全体でCIのメンテナンスコストを劇的に削減できる。

—

4. まとめ:CI/CDは「開発者を縛る鎖」ではなく「加速させる翼」であるべきだ

ツールの仕様に振り回されるな。ツールをハックし、自分たちの開発フローに奉仕させろ。

今回紹介した `continue-on-error` と `if` 条件分岐を組み合わせたConditional Executionは、「機械的な厳格さ」と「人間的な柔軟性」を両立させるための最強の武器だ。

  • 不安定な外部要因による無駄なビルド失敗を排除し、GitHub Actionsの実行枠(Minutes)を節約する。
  • 失敗を隠蔽せず、コンテキストを持った状態で後続の処理や通知に繋げる。

この設計を取り入れた瞬間から、あなたのチームのCI/CDパイプラインは単なる「自動テスト装置」から、「チームの生産性を極限まで引き出す高速道路」へと生まれ変わるはずだ。

さあ、今すぐ `yaml` ファイルを開き、無駄に止まっているジョブたちを解放してこい。

タイトルとURLをコピーしました