Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】Three holes in a green CI, and a lock for the free plan

2026年10月30日に「Dev.to」が公開したITニュース「Three holes in a green CI, and a lock for the free plan」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

CIがグリーンでも、実際は一部テストが実行されていなかったり、DBスキーマが本番と異なったり、CI結果を無視してデプロイできたりする問題があった。これらの隠れた欠陥を特定し、CI設定の修正、検証テストの追加、デプロイスクリプトによるCI結果確認を導入し、より信頼性の高いCI環境を構築した。

ITニュース解説

システムエンジニアを目指す皆さんにとって、ソフトウェア開発で耳にする「継続的インテグレーション(CI)」という言葉は非常に重要だ。CIは、開発者がコードを変更するたびに自動的にテストを実行し、問題がないかを確認する仕組みを指す。このCIの実行結果が「グリーン」、つまりすべてのテストが成功したと表示されれば、コードは問題なく、次の段階へ進める準備ができたと通常は判断される。しかし、この記事は、その「グリーン」が必ずしも安心を意味しない、という現実と、そこからどのようにして真の「グリーン」を手に入れるかについて詳しく解説している。

この記事の著者が最近関わったプロジェクトでは、CIが完全にグリーンと表示されていた。しかし、実際に何がテストされていたのか、何がテストされていなかったのかを詳しく調べてみると、三つの大きな「穴」が発見された。CIがグリーンでも、実際には重要な部分が全くテストされていなかったのだ。彼らはこれらの穴を一つずつ見つけ出し、修正していった経緯を説明している。

最初の落とし穴は、「CIが一度も実行しなかったテスト」だった。このプロジェクトのバックエンドは複数の小さなサービスで構成されており、CIはそれぞれのサービスのテストを別々のジョブとして実行していた。しかし、よく調べてみると、いくつかのサービスには多くのテストコードがあるにもかかわらず、CIのジョブリストにはそれらが全く追加されていなかった。誰も意図的に削除したわけではなく、単に追加されずに放置されていたのだ。

これらのテストをCIのワークフローファイルに一行ずつ追加してみると、隠されていた問題が露呈した。追加されたテストの多くがエラーで実行されなかったのだ。具体的には、データベースに接続するテストが、必要なPostgresデータベースのアドレスを環境変数から取得しようとしていたが、CI環境でその変数が設定されていなかったためにクラッシュしていた。また、非同期処理をテストするコードは、CI環境にpytest-asyncioという必要なライブラリがインストールされていなかったため、これまで一度も実行されていなかったことが判明した。これらのテストは壊れていたのではなく、単に実行されていなかったのだ。

この問題の解決策として、彼らはpytestの共通設定ファイルであるconftest.pyに一つのルールを追加した。それは、「テストデータベースが利用できない場合、ローカル環境ではそのデータベースが必要なテストをスキップするが、CI環境では必ず失敗させる」というものだ。特にCIで失敗させるという後半のルールが重要だ。なぜなら、CI環境でデータベース接続に問題があるなら、そのCI実行は「レッド(失敗)」になるべきであり、テストがひっそりとスキップされてしまうべきではないからだ。また、ログにデータベース接続情報(特にパスワード)が表示されないよう、機密情報をマスクする処理も忘れずに実装された。

二つ目の落とし穴は、「本番環境と異なるスキーマでデータベースを構築するテスト」だった。本番環境では、サービスが起動する際に「マイグレーション」と呼ばれる仕組みを使ってデータベースのテーブル構造を段階的に構築していた。これはデータベースの変更履歴を管理する標準的な方法だ。しかし、テスト環境では、マイグレーションを通さず、コードのモデル定義から直接テーブルを作成していた。マイグレーション忘れがなければ、この二つの方法で同じデータベース構造になるはずだが、もしマイグレーションファイルを一つでも忘れると、テストはパスするのに、本番環境でデータベースクエリが失敗するという事態が発生する可能性があった。

この問題を解決するため、彼らは新しいテストを追加した。このテストは、空のデータベースに対して実際のマイグレーションを全て実行し、その結果のデータベーススキーマが、現在のコードのモデル定義と一致するかどうかを比較するというものだ。このテストを最初に追加して実行したところ、すぐに小さな「ずれ」が発見された。幸い、そのずれは本番環境のデータベース自体には影響しないもので、大きな問題には至らなかったが、それでも、このテストが追加初日に問題を検出し、その価値を証明した。

さらに、この二つ目の落とし穴に関連して、別のサービスで別の問題も発覚した。そのサービスのバックグラウンドジョブが、テスト用のデータベースではなく、本番環境のデータベース接続文字列を使って独自のデータベース接続を開いていたのだ。CI環境では、この本番用のデータベースは空だったため、バックグラウンドジョブはエラーをログに残しつつも、静かに失敗し、全体のテスト自体はパスしていた。つまり、一部のテストはバックグラウンドジョブがサイレントに失敗していたためにパスしていたのだ。彼らはこの問題を修正し、バックグラウンドジョブがテスト環境ではテストデータベースを使用するようにするか、テストが明示的に要求しない限りバックグラウンドジョブが実行されないように設定した。

そして三つ目の落とし穴は、「誰もCIのグリーンを待っていなかった」というものだった。このリポジトリはプライベートであり、GitHubの無料プランを使用していたため、GitHubが提供する「ブランチ保護」機能、つまりCIが失敗したコミットがメインブランチにマージされるのを防ぐ機能が使えなかった。そのため、CIが「レッド(失敗)」になったコミットでも、その数分後にはデプロイされてしまう可能性があったのだ。これは、せっかくCIで問題を発見しても、それがデプロイを妨げないという、非常に危険な状態だった。

この致命的な問題を解決するため、彼らはデプロイを実行する唯一のコマンドに「ロック」をかけた。具体的には、デプロイコマンドの実行前に、小さなスクリプトを追加したのだ。このスクリプトは、デプロイしようとしているコミットがメインブランチの最新のコミットであることを確認し、そのコミットに対応するCIの実行を検索し、もしCIがまだ実行中であればそれが完了するまで待機する。そして、CIの実行結果が「成功」でなければ、デプロイを拒否するという仕組みだ。

ただし、ドキュメントのみの変更など、CIをトリガーしないコミットもあるため、それらに対する特別な処理も考慮された。もし、最後のテスト済みコミット以降の変更がすべてドキュメント関連であれば、その前のテスト済みコミットのCI結果を有効と見なす。それ以外の、CIが実行されていないコードの変更はデプロイを拒否する。緊急時には「ALLOW_UNTESTED=1」という環境変数を設定することで、警告を表示しつつもデプロイを続行できる「緊急脱出ハッチ」も用意された。これはCIが遅い時ではなく、本当の緊急事態にのみ使用されるべきだ。

このスクリプトは、最初は偽のGitHub CLIを使って様々なシナリオ(CIがグリーン、レッド、キャンセル、実行中、ドキュメントのみ、テストされていないコード、プッシュされていない、オーバーライド使用時など)で丁寧にテストされた。その後、実際にプッシュ後にリアルな環境でテストされ、「… waiting for Test Suite run on <commit>」「✓ CI green on <commit>」と表示され、期待通りに動作することが確認された。この変更は、デプロイコマンド内でのサブモジュールのプッシュ順序が間違っていた問題も同時に修正した。

今回のケースで彼らがとったデプロイをCIに待機させる方法は、デプロイコマンド内にチェックを組み込むというものだった。これは無料で、どのようなGitHubプランでも利用でき、唯一のデプロイ経路を保護するという利点があった。しかし、手動でのデプロイは防げないという限界もある。他にも、GitHubのブランチ保護機能を使う(有料プランが必要)、CI自体がテスト成功後にデプロイを実行する(強制力は高いがCIに認証情報が必要)、ローカルのgitフック(pre-pushフック)を使う(プッシュ前にテストするがスキップが容易)といった選択肢があることも紹介されている。

まとめとして、CIの「グリーン」を本当に信頼できるようになるためには、いくつかの重要なステップがある。まず、全てのサービスがCIのジョブリストに含まれているかを確認し、リストの乖離がないかを定期的にチェックすること。次に、「データベースなし」の状況では、ローカル環境ではスキップし、CI環境では必ず失敗させるという厳格なルールを設けること。そして、本番環境がマイグレーションでスキーマを構築し、テストがcreate_allを使う場合は、マイグレーションとモデル定義を比較するテストを必ず追加すること。さらに、独自のデータベース接続を開いているコードがないかをgrepなどで探し出し、全てのデータベース接続がテストによって管理されるようにすること。最後に、デプロイが実行される前にCIの成功を確認し、そのチェックをスキップすることが困難で、かつ大きな警告が発せられるようにすることだ。これらの対策を講じることで、CIの「グリーン」は、単なる色表示以上の、真に信頼できる品質保証の証となる。

関連コンテンツ

関連IT用語

関連ITニュース