継続的インテグレーション(CI)パイプラインは、具体的な問いに答えるものでなければなりません。この変更を、既知の不具合を持ち込んだり、合意済みの要件を損なったりせずに、共有コードベースへ取り込めるでしょうか。その問いに答えるには、繰り返し実行できるチェックを定め、実行順序を整理し、各失敗から有用な情報が得られるようにする必要があります。
CIは、すべての変更を自動的にデプロイすることではありません。変更を頻繁に統合し、自動的に検証する取り組みです。継続的デリバリーは、デプロイ可能なリリースを継続的に準備します。継続的デプロイは、定められた条件を満たした場合に、本番環境へのリリースまで自動化します。CIを導入していてもデリバリーを自動化しない構成もあれば、テスト環境までのデリバリーを自動化し、本番環境への反映前に承認を求める構成もあります。
パイプラインの契約を定義する

ツールを選ぶ前に、実行時に何を受け取り、何を生成し、どの条件で失敗とするのかを明確にします。有用な設定では、少なくとも次の事項を文書化します。
- 入力:検証対象の変更、関連する設定、プロジェクトが宣言する依存関係。
- 環境:システムと実行要件、PHPの設定、テストに必要なサービス。実行ごとの結果を比較できるよう、環境は十分にそろえる必要があります。
- 結果:最終ステータス、テストと分析のレポート、該当する場合は後から検証できる識別可能な成果物。
- 失敗条件:統合をブロックするエラー、警告にとどめるエラー、一時的な例外を承認できる担当者。
依存関係のインストールには、バージョン管理された設定を使い、実行のたびに新しいバージョンを暗黙に解決するのではなく、ロックファイルに従います。これにより、開発者の環境とCIの差異の一因を減らせます。また、アプリケーションが想定するプラットフォーム要件と拡張機能を宣言し、実行環境がそれらを満たすことを確認します。
ローカルファイル、個人用サービス、文書化されていない手作業に依存するパイプラインは避けてください。チェックにデータベース、キャッシュなどのサービスが必要なら、起動方法、必要なデータ、後片付けの方法を定義します。契約で本番環境を完全に再現する必要はありませんが、結果に影響し得る違いは明示してください。
コストと検出能力を踏まえてチェックを並べる
実践的な順序は、短時間で済む検証から始め、時間やインフラをより多く必要とする検証で終わります。タスクを積み上げることが目的ではなく、重要なカバレッジを損なわず、問題をできるだけ早く検出することが重要です。
- 再現可能なインストール:プロジェクトの定義とロックファイルに基づいて依存関係を解決します。ここで失敗した場合、それ以降の結果は信頼できません。
- フォーマットと規約:合意済みのフォーマットやスタイルのルールを確認します。こうしたチェックは高速で、よりコストの高いレビューに進む前に表記上の差異を防げます。
- 静的解析:アプリケーションのすべての処理を実行せずに検出できる非互換性やエラーを探します。ルールは、実際のコードとプロジェクト設定に合わせて調整してください。
- テスト:まずユニットテストを実行し、対象とするリスクや必要なサービスに応じて、統合テストやエンドツーエンドテストを追加します。
- 成果物の検証:生成されたパッケージやイメージに実行に必要なものが含まれ、開発用ファイル、ローカルデータ、秘密情報が除外されていることを確認します。
すべてのアプリケーションで同じテストや順序が必要なわけではありません。静的解析が小規模なユニットテストより大幅に時間を要する場合は、依存関係のインストール後に両方を並行実行するとよいでしょう。独立したタスクも、再現可能な基盤を共有し、同じ変更に対する結果として関連付けられるなら、待ち時間を減らすために並行実行できます。
一方で、同じディレクトリを変更する手順や、先行する結果に依存する手順をむやみに並行化するのは避けてください。共通の準備と後続のタスクを分け、共有サービスの同時実行数を制限し、ステージ間の依存関係を明確にします。目的は、パイプラインを予測不能にせず、フィードバックを速めることです。
ブロックする条件と後続の処理を決める
まずは、変更の安全性に関わる重要なチェックが失敗した場合に統合をブロックします。対象は、インストール、合意済みの分析、必須テスト、パッケージの検証などです。評価中のチェックなど、情報提供を目的とするタスクは、期間を定めてブロックせずに結果だけを報告してもかまいません。その場合は担当者、見直し日、必須化する基準を設定してください。そうしなければ、警告が恒久化し、価値を失います。
高速なチェックは、変更のたびに早い段階でフィードバックを返すべきです。時間やインフラ上の理由があれば、コストの高いテストを並行実行したり、後続ステージで実行したり、頻度を変えたりできます。しかし、重要な検証をすべて統合後に回すと、未検証の変更が残る時間帯が生じます。遅れて失敗した場合の影響を考慮し、統合前に必須とするものと、追加検証とするものを決めてください。
実行が成功しても、デプロイが承認されたことにはなりません。CIは変更を検証し、成果物を生成することがありますが、それをどのように、どの環境へ、どのような制御のもとで昇格させるかはデリバリープロセスが決めます。この境界を明確に保つことで、検証タスクが誤って本番環境へ公開する事態を防げます。自動デプロイを行う場合は、その条件、承認、段階的な公開戦略、ロールバックの仕組みを個別に定義してください。
設定と秘密情報を保護する
秘密情報をリポジトリ、設定ファイルのサンプル、実行レポートに含めてはいけません。CI環境のシークレット管理機能を使い、必要なタスクだけが利用できるように制限してください。また、隔離されたサービスだけを必要とする検証に、本番環境の認証情報を与えないでください。
ログも確認してください。例外、失敗したテスト、診断コマンドによって、機密性の高い変数が出力されることがあります。値のマスキングは役立ちますが、出力自体を避けることの代わりにはなりません。実データを露出させないテストデータを使い、ログや成果物に認証情報が含まれた場合に失効させる手順を定めてください。
失敗を診断できるようにする
背景情報のない失敗ステータスは、作業のやり直しを招き、CIをブラックボックスにしてしまいます。テストと分析のレポート、失敗した手順の特定に必要な出力、依存関係や成果物のバージョン識別子を保持してください。一方で、個人情報、秘密情報、環境の無差別なダンプは記録しないでください。
テストが不安定に失敗する場合、無制限に再試行した後で成功扱いにしてはいけません。変動するテスト、その頻度、発生条件を記録し、並行実行、外部依存、共有状態、タイムアウトなどの原因を調査してください。不安定なテストを一時的に隔離する場合は、リスクを文書化し、担当者を割り当て、ブロック条件を復元する日付を設定します。
コードの失敗とインフラの問題を区別することも重要です。サービスを起動できなかった、依存関係を取得できなかった、リソース不足でタスクを完了できなかった場合は、その旨を報告してください。一時的な障害に対する再試行は妥当な場合がありますが、回数を制限し、見える形にする必要があります。最初の失敗を隠すと、繰り返し発生する問題を検出しにくくなります。
レガシーアプリケーションにCIを適応させる

古いシステムで厳格なルールを一度に有効にすると、有益な変更をブロックし、無秩序な例外を増やすおそれがあります。まず現状の基準線を作り、現在成功するテスト、解析で報告される既存エラー、各ステージの所要時間を把握します。すでに存在する問題をリグレッションとして扱わない一方、基準線をいつまでも言い訳にしてはなりません。
- インストールや信頼できるテスト群など、すでに安定していて再現可能なチェックから必須化します。
- 既存の検出事項を記録し、ツールとプロジェクトで比較が可能なら、新たな変更で問題を拡大させないことを求めます。
- 変更リスクの高い領域を対象にテストを追加し、カバレッジを段階的に広げます。
- 小さくレビューしやすい変更の単位で例外を減らし、一時的な例外ごとに担当者と期限を設定します。
- どのリスクをカバーしていたのか分からないまま検証を削除するのではなく、具体的なステージを最適化するため、所要時間と失敗原因を測定します。
優れたPHPの継続的インテグレーションは、ステージの数ではなく、再現可能で理解しやすい結果によって定義されます。各手順で何を検証し、何が統合をブロックし、失敗をどう調査するのかをチームが説明できれば、証拠に基づいて変更を次の段階へ進める準備ができているか判断する助けになります。



