[iOS] すぐできる!CIクレジット超ドケチ節約術
![Cover Image for [iOS] すぐできる!CIクレジット超ドケチ節約術](/assets/blog/authors/ryomm/2025-04-21/thumbnail.png)
KINTOテクノロジーズで my route(iOS) を開発しているRyommです。
CIクレジットの節約のため、 いくつか取り組んできたことを紹介します。
はじめに
弊プロジェクトにおいては、CIツールとしてBitriseを利用しています。
昨年は通常のユニットテストに加えて スナップショットテストを導入 したり、 SPMに移行 したりしました。
気付くとBitrise CIの1回あたりの実行時間が多いときは約25分ほどに膨れ上がり、多くの実装が行われた月は予算を超過してしまうこともしばしば発生するようになってしまいました。
Bitriseは契約分を超過すると高額になってしまうので、執筆時点の為替レートでは超過分のCI1回の実行に約400円ほど掛かっている計算です。たっっか!
そういうわけで、クレジットが超過しそうになるとCIを動かさないためにPRのマージも必要最低限に抑える動きが生まれてしまっていました。
この状況を打破すべく取り組んできた、弊プロジェクトにおけるクレジット節約術を紹介します。
CLIツールのセットアップを見直す
BitriseのBuild結果を見ると、どのステップにどのくらい時間が掛かったのか見ることができます。
BitriseのBuild結果
これを見ると、「Script Runner」にて12分も掛かっていることがわかります。
これは、swiftLintやLicensePlistのセットアップを行っているステップです。以前執筆した記事にて紹介しましたが、Build Phaseで実行するためにworkspaceとは独立して作成したパッケージにて、ライブラリを落として使えるようにしています。
ま〜たしかにこれは時間かかるよね〜というところなので、短縮していきます。
幸いここで使いたいライブラリはBuild Tool Pluginに対応しているため、そちらに移し替えていくことでこのステップを省くことができます。
元々 license_plist.yml や .swiftlint.yml などの設定は済んでいるため、ただプロジェクトの Package Dependencies にパッケージを追加し、ターゲットの Build Phase の Run Build Tool Plug-ins にプラグインを追加してあげればOKです。
Build Phaseの設定
LicensePlistはプラグインだと outputPath
の場所の指定が効かないため、READMEにあるように Settings.bundle の下にライセンスのファイルを移動するようBuildPhaseに含める必要があります。
また、パッケージはFrameworksでリンクしているパッケージではなく、アプリ本体に含める必要があります。
これで「Script Runner」のステップが丸々不要になったので、12分の短縮...そして消費クレジットも半分になりました!🎉
BitriseのBuild結果
さらにプロジェクト構成が単純化し、セットアップやバージョン更新の際に別途シェルを実行するような必要もなくなりました。
今回のケースでは全てBuild Tool Pluginに対応していたため構成ごと変更しましたが、別のアプローチとして nest も試しました。こちらは既存のCLIツールを別パッケージに分けて管理する構成のまま、CI時間を短縮させることができます。
tools ディレクトリ配下にあったCLIツールをインストールするためのパッケージをnestに置き換えます。
Project/
├── Hoge.xcworkspace
├── Hoge.xcodeproj
├── Test/
│ └── ...
├── ...
└── tools
└── nextfile.yaml // ここを置き換える
nest bootstrap nestfile.yaml
を実行すると tools/.nest/bin
内にバイナリがインストールされていることが確認できるので、これを Build Phase で実行されるように設定します。
Build Phaseでswiftlintを設定
Build Tool Plugin に対応していなかったりする場合には有用かもしれません。
テストを見直す
弊プロジェクトでは、1つのテストターゲット内に全てのテストが詰め込まれていたため、常に全てのテストが実行されていました。
またその中でもスナップショットテストは全て実行すると1時間ほど掛かる非常に重いテストのため、リファレンス画像と比較するメソッドはCI上ではコメントアウトして実行されないようにしていました。しかし、比較前の非同期の描画処理の待機などは実行されてしまうため、失敗時はタイムアウトが積もり積もって長い時間待つこととなり、これもクレジットを食い潰す要因の一つとなっていました。
そこで、CI上では動かしていないスナップショットテストを別のテストターゲットへ切り離し、TestPlanを使って実行するテストをコントロールするようにしてみました。
まずは、時間のかかるスナップショット用のテストターゲットを作成します。
テストターゲットを作成
既存のテストターゲットを参考にターゲットの設定を行ったら、 Build Phases の Compile Sources もしくは各テストファイルの File inspector より Target Membership から、スナップショットテストを新規作成したターゲットへ移していきます。
このとき、移動したテストファイルが元のテストターゲット内のテストファイルと依存関係があるとビルドできなくなるため、都度依存を切り離していきます。
ターゲットを変更していく
次に、TestPlanを作成します。
TestPlanとは、実行するテストとテストの設定をまとめたものです。その際、実行するテストを指定できる範囲はテストターゲット単位です。
このためにテストターゲットを分離させました。
TestPlanはスキーマに紐づけることができ、弊アプリではスキーマとTestPlanが1対1になるようにしています。
そしてCI上で使用するためのスキーマ用のTestPlanにおいては、スナップショットテストを実行しないようにします。
TestPlanの設定
実際に動かしてみると、CI上では失敗しない限り大きくは実行時間は変わりません。しかし、ローカルにおけるテスト体験は大きく改善しました。ロジックのみの変更でもスナップショットテストが実行されてしまっていたのが、チェックを外すだけで実行しないようにできるため、大幅な時間短縮が実現しました。
元々CI上では動かしていなかったテストをそもそも実行しないようにした形ですが、クレジット利用状況とのバランスを調整した上でスナップショットテストも実行できるようにしていきたいと考えています。
おわりに
ここに紹介したもの以外にも、型推論に時間が掛かっているコードを修正したり、使用していないアセットを削除することなどもビルド時間の短縮につながります。
これらの取り組みによってCI1回あたりの時間は平均約22分程度から12分程度まで短縮され、45%ほどのクレジット節約が実現しました。
今回はすぐにできる範囲としてビルド前後の時間短縮が主でしたが、次はビルド自体の時間ももっと短縮したいです。