hitode909の日記

趣味はマリンスポーツですの日記です

CIからファイルを更新する、とは

「このファイルはCIから更新しているので、手で書き換えないこと」と言うことがあって、個人的にもよくやるのだけど、何をやっているのか整理しておきたい。

Docker以前

本番環境と手元開発環境が揃っていないときにCIから更新することは、結果を揃えるという用途が大きかったと思う。
CI環境と本番環境を見比べると、同じOSを選んだり、同じミドルウェアを入れたりと、なるべく同じ環境が再現されているけど、手元環境は近所の電気屋さんで買ってきたパソコンで、本番環境とは乖離している。
どのようなときに乖離が起きるかというと、

  • ファイルの生成に使うプログラムのバージョンを管理していないとき
    • 各開発者の環境によって実行結果がバラバラで、ビルドするたびにdiffが出てしまう、とか
    • CIサーバー間でのバージョンがバラバラだと同じ問題は起きる
  • ファイルの生成時にOSの種類に応じて分岐する処理が書かれているとき
  • インストールされているリソースの有無の影響を受けるとき
    • Puppeteerで撮ったスクリーンショットを比較する際に利用されるフォントが手元とCI環境で違う、とか

などなどある。というときに、このファイルはCIサーバーで更新してリポジトリにコミットする、と決めてしまえば、混乱を避けることができる。

Docker以後

手元の開発環境がDockerなどで仮想化されて、全員が同じ環境で同じプログラムを動かせるようになると、先程の問題はすべて消え去る。

  • ファイルの生成に使うプログラムのバージョンを管理していない→Dockerイメージをpullすれば全員が同じ環境で動かすことができる
  • ファイルの生成時にOSの種類に応じて分岐する処理が書かれているとき→全員のOSは同じ
  • インストールされているリソースの有無の影響を受けるとき→変なマウントをしていなければ、全員の環境は揃っている

このときに残るのは、以下のような用途。

  • ファイルの生成に時間がかかり、手元で待っていられないとき
  • 2ファイル間の状態がずれると問題があるファイルを更新したいとき
  • ファイルの生成過程を記録したいとき

これくらいだと思う。

ファイルの生成に時間がかかり、手元で待っていられないとき

docker runすれば同じファイルを手元で作ることができるのは分かっているけど、実行に数十分かかる処理を手元で動かすと、そのあいだ何もできなくなるので、ただリモートのサーバーで実行したい、というパターン。
自前でサーバーを管理していたら、ジョブが詰まってきたら並列度を調整したり、インスタンスサイズを変えたり、ということが必要になってくるけど、AWS CodeBuildなど使うと、実行に使うインスタンスサイズを決めるくらいで、全部でサーバーを何代用意するかといった制御は不要になり、気楽に重い処理を流していける。

2ファイル間の状態がずれると問題があるファイルを更新したいとき

package.jsonとpackage-lock.jsonはペアで動くもので、package-lock.jsonが古いと、npm installしたあとにpackage-lock.jsonにdiffが出てしまい、混乱することになる。
というときに、CIでnpm installして結果をコミットするタスクを用意しておくと、2ファイル間の内容を同期することができる。
CIからコミットしなくても、正しく管理されていることを確認するために、ビルド後にdiffが出ないことを確認する、というアプローチもある。開発者にやり直させるか、CIを正として上書きするかの違いで、コミットするかどうかはあまり問題ではなくて、CIでファイルを生成してみないと結果が出ないことには変わりがない。

ファイルの生成過程を記録したいとき

リリースに使うイメージを各開発者の手元からやっていると、うっかり誤ったバージョンをビルドしてしまうとか、中途半端に手元で書き換えたままビルドしてしまう、ということが起こり得る。
リリース用のイメージの生成をCI上で行っていれば、出来事のログを誰でも見れる形で残せるので、リポジトリが正しければリリース内容も正しい、とみなすことができる。
ライブラリのリリース時などに、手元からnpm publishなどしていると、利用者からすると、リリースされたライブラリを見ることはできても、どのような過程を経てライブラリがリリースされたかトレースすることができない。
SongmuさんはGoのプロジェクトのリリースをGitHub Actionsから実行できるようにされていて、良いなと思っていた。
songmu.jp

CIからファイルを更新する、とは

CIからファイルを更新するとき、CIがDockerで動いているということは、同じ工程を各開発者が手元で行えるものだが、あえてやっていない、ということを意味している。
そして、なぜやらないか、というときには、時間面へのアプローチと、結果の正しさへのアプローチがある。
時間面へのアプローチについては、1台のコンピュータで直列に行わず、実行環境のコンピュータを分散させている、とみなすことができる。
結果の正しさについては、リポジトリに何らかのコミットがある、という状態が事前条件で、CIのタスク実行の結果、エラーなくファイルが生成されたり、diffが出ないこと、を事後条件、としたテストをしている、とみなすことができる。