3日目までで Monorepo の形が整い、docker compose up で全サービスが動くところまで来ました。
今日のテーマは 「高速で壊れないCIをつくる」 こと。
この基盤があることで、明日から始まる Auth0 + Go の認証フェーズ を安心して進められるようになります。
🧪 1. 各サービスに Linter を導入する
まず、プロダクトを開発する前にすべてのサービスに最低限の Linter を入れました。
docker-composeが動くテスト・単体テストとかも同様の理由なのですが、特にAIはすごく変な壊し方を時折するのでCommitレベルで「ここまで戻せばOK」の基準としてLinter/Test/結合テストを早い段階で入れています。
Linterはそれ抜きで最初に入れないと後から適用するコストが高過ぎて禿げます(N敗)
| サービス | 言語 | Linter |
|---|---|---|
| auth-service | Go | golangci-lint |
| frontend | TypeScript | eslint |
| publisher / upload | Ruby | rubocop |
| ai-gateway | Rust | cargo clippy |
| core-api | C# | dotnet format + analyzers |
最初から完璧にする必要はなく
「壊れたコードが入らない最低限のチェック」
が Day5 の目的です。
⚡ 2. Fast CI の設計方針:Lint → Build の2段構成
現時点(Phase0〜1)では、本格的なテストはまだ多くありません。
そのため、CI はできるだけ早く壊れたほうが良い。
そのため、共通で使う動作の確認テストとはじめに導入しておかないと詰むLinterだけを今日の記事の対象とし、単体テストはそれぞれのコンポーネントの設計時に必要に応じて紹介しようと思います。
正直なことを言うとテストフレームワークのベスプラを執筆時点で知らないので、一旦判断を保留にしています。
Linterはとりあえず一番強いものを入れればいいと思っていますstrictバンザイ。
🧼 3. 足りなかったポイントをまとめて改善
CI を組むにあたり、いくつかの不足も同時に修正しました。
✔ .env.example の統一
DB・Redis・S3 など、URL形式の環境変数をすべて追加し、
各サービスが同じ形式で接続できるように整理
これによって公開の時にどんな環境変数が共通で必要なんだと言うことを悩まずに済むかなと考えています。こう言うドキュメンテーションは作る時に入れるのが一番コンテキストスイッチが小さいかなと思いいつもドキュメントは作業のついでにを心がけてます。
できていないことも多いです。
✔ Dockerfile の最低限統一
Dockerfileを最初の時点でベース部分作っておき/pingくらいのエンドポイントを返せるように作っておくことでWebサーバーが使えなくなった!レベルのデバックをできるように実装を合わせておきました。これによりデプロイCIの導入という地味に長いタスクをサクッと終わらせています。(Day2でもお話ししている気がしますが...)
この時にDocker BuildKitを使うように変更されていてこれはなんだろ?と調べたところ、ビルドの並列実行とかができるものらしいですね。これは嬉しい。
https://docs.docker.com/build/buildkit/#getting-started
余談ですがこれを雑に日本語訳したところ、Low-Lebel BuildのLLBの見出しを「法学士」と誤翻訳していました。Law Bachelorで残りのLはなんだ...? とおもったのですが、Bachelor of Laws のLawが複数形である部分をLLで見出しているみたいですね。英語なんもわからん。
✔ Lint job を並列化
GitHub Actions の同時実行数を最大限に活用し、CI 全体を高速化。
LintJobだけではなく、それぞれで行われるdocker-buildジョブに関してもstrategy.matrix.includeの下にlistのように作ることで並列化できるみたいです。
並列化自体はしっていたのですが、AIが提案してくれて初めて書き方を知りました。こう言う学びがあるのも面白いですね。
jobs:
build-images:
name: Build ${{ matrix.service }} image
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: read
strategy:
fail-fast: false
matrix:
include:
- service: frontend
context: ./frontend
dockerfile: ./frontend/Dockerfile
- service: auth
context: ./auth
dockerfile: ./auth/Dockerfile
- service: core
context: ./core
dockerfile: ./core/Dockerfile
- service: publisher
context: ./publisher
dockerfile: ./publisher/Dockerfile
- service: upload
context: ./upload
dockerfile: ./upload/Dockerfile
- service: ai-gateway
context: .
dockerfile: ./ai-gateway/Dockerfile
- service: observability
context: ./observability
dockerfile: ./observability/Dockerfile
🍳分析:なぜこれほどPR数がかかったか
ここまで長かったですね。若干authの実装部分も混じっていますが、環境構築に20PRほど使っています。
失敗要因として冷静に考えるといくつかあります。
- ActionLintの導入が遅い(#14)かつそれまでにGithubActionsを結構作っていたためActionLintの修正だけでPRをいくつか費やしている
- モノレポ構成のためAIのコンテキストが大きくなりがちでそこを跨いだPRは失敗率が高くなりがち
- 逆にここを1PRずつにした上でdocker-build作っていくといった方針だったらもう少し1PRの粒度も総合的なPR数も減ったかも
- CodexがPR出した後修正失敗していた
- Codexの修正をするために別なCodexのセッションからCodexの既に出ているPRにPR'を作成するなど非効率な運用が行われていた
- これは既にClaudeを使うことで解決済み
🎅 明日は「Auth0ログイン画面を出すまで」
明日は、最初の成功体験となる
「Auth0のログイン画面を表示するところまで」 を扱う予定です!
私が実際にどこから手をつけてどこをAIに任せたのか苦労しているところを一緒に眺めて有識者の皆さんは「こうするとRTAできるぞ!」とかアドバイスいただけると嬉しいです。
このアドベントカレンダーではときよりGithubのリポジトリではなくコードブロックやGistのコードを使うことになると思います。開発中のリポジトリを「そのまま」見せたい気持ちもあるのですが、25日まではネタバレ防止のためにコードを公開しない方針にしました。25日には公開できるようにするのでちょっと待ってね!
