Skip to content

Instantly share code, notes, and snippets.

@y-hirakaw
Created July 17, 2025 14:50
Show Gist options
  • Select an option

  • Save y-hirakaw/67cdd5f0b321f346a5501dc6936bd8f3 to your computer and use it in GitHub Desktop.

Select an option

Save y-hirakaw/67cdd5f0b321f346a5501dc6936bd8f3 to your computer and use it in GitHub Desktop.
Bitrise CI最適化案
# iOS CI/CD ビルド時間短縮ガイド - Bitrise & Fastlane編
## 概要
iOSアプリのCI/CDにおいて、ビルド時間の短縮は開発生産性に直結する重要な課題です。本ガイドでは、BitriseとFastlaneを使用した環境でのビルド時間短縮のベストプラクティスをまとめています。
## 1. Fastlane最適化
### 1.1 scanアクションの最適化
#### 並列テストの活用
```ruby
lane :test do
scan(
scheme: "MyApp",
parallel_testing: true, # 並列テストを有効化
concurrent_workers: 2, # 並列実行数を指定
max_concurrent_simulators: 2, # 同時シミュレータ数
devices: ["iPhone 15"], # デバイスは最小限に
)
end
```
#### 不要な処理の省略
```ruby
lane :test do
scan(
scheme: "MyApp",
output_types: "", # レポート生成を省略
suppress_xcode_output: true, # ログ出力を抑制
disable_slide_to_type: true, # "Slide to type"プロンプトを無効化
prelaunch_simulator: true, # シミュレータを事前起動
skip_slack: true, # Slack通知を省略
)
end
```
### 1.2 条件分岐による最適化
#### 変更ファイルに応じたテスト実行
```ruby
lane :pr_test do |options|
# PR更新時の変更ファイルを検出
changed_files = `git diff --name-only HEAD~1`.split("\n")
# Podfileが変更された場合のみpod install実行
if changed_files.any? { |f| f.include?("Podfile") }
cocoapods
end
# 変更箇所に応じてテストを限定
if changed_files.any? { |f| f.include?("Login") }
scan(only_testing: ["MyAppTests/LoginTests"])
elsif changed_files.any? { |f| f.include?("Network") }
scan(only_testing: ["MyAppTests/NetworkTests"])
else
scan # フルテスト
end
end
```
### 1.3 ビルド設定の最適化
```ruby
lane :optimized_test do
scan(
scheme: "MyApp",
configuration: "Debug", # Releaseより高速
xcargs: [
"-parallelizeTargets",
"-maximum-concurrent-test-simulator-destinations 2",
"COMPILER_INDEX_STORE_ENABLE=NO", # インデックス作成を無効化
"SWIFT_COMPILATION_MODE=wholemodule" # 最適化
].join(" ")
)
end
```
### 1.4 環境準備の最適化
```ruby
before_all do
# Fastlane自体の高速化
ENV["FASTLANE_SKIP_UPDATE_CHECK"] = "1"
ENV["FASTLANE_HIDE_TIMESTAMP"] = "1"
# シミュレータの事前起動
sh "xcrun simctl boot 'iPhone 15' || true"
# キーボード設定の最適化
sh "defaults write com.apple.iphonesimulator ConnectHardwareKeyboard -bool NO"
end
```
## 2. Bitrise設定の最適化
### 2.1 効果的なキャッシュ設定
```yaml
workflows:
primary:
steps:
# キャッシュを最初に取得
- cache-pull@2:
# Bundlerインストール(キャッシュ活用)
- bundle-install@1:
inputs:
- gemfile_path: "./Gemfile"
- bundle_path: "./vendor/bundle"
# CocoaPodsインストール(キャッシュ活用)
- cocoapods-install@2:
inputs:
- install_cocoapods_version: "false"
- fastlane@3:
inputs:
- lane: test
# キャッシュを保存
- cache-push@2:
inputs:
- compress_archive: "true"
- cache_paths: |
# Bundler(ほぼ不変)
./vendor/bundle -> ./Gemfile.lock
# CocoaPods(ライブラリ更新時のみ変更)
./Pods -> ./Podfile.lock
# SPM(比較的安定)
.build -> ./Package.resolved
~/Library/Caches/org.swift.swiftpm
```
### 2.2 キャッシュ効果の実測値
```
初回ビルド(キャッシュなし):
- bundle install: 30-60秒
- pod install: 60-120秒
- SPM resolve: 30-90秒
合計: 2-4分
2回目以降(キャッシュあり):
- bundle install: 2-5秒 ✨
- pod install: 5-10秒 ✨
- SPM resolve: 3-8秒 ✨
合計: 10-20秒
```
## 3. SPMモジュール化による高速化
### 3.1 モジュール構成例
```swift
// Package.swift
let package = Package(
name: "MyAppModules",
platforms: [.iOS(.v15)],
products: [
.library(name: "Core", targets: ["Core"]),
.library(name: "Networking", targets: ["Networking"]),
.library(name: "UI", targets: ["UI"]),
.library(name: "LoginFeature", targets: ["LoginFeature"])
],
dependencies: [
.package(url: "https://github.com/Alamofire/Alamofire", from: "5.0.0")
],
targets: [
.target(name: "Core"),
.target(name: "Networking", dependencies: ["Core", "Alamofire"]),
.target(name: "LoginFeature", dependencies: ["Core", "Networking", "UI"]),
.testTarget(name: "LoginFeatureTests", dependencies: ["LoginFeature"])
]
)
```
### 3.2 モジュール別テスト実行
```ruby
lane :modular_test do
# 変更されたモジュールを検出
changed_modules = sh("git diff HEAD~1 --name-only | grep 'Sources/' | cut -d'/' -f2 | sort -u").split("\n")
if changed_modules.empty?
UI.message "No module changes detected, skipping tests"
else
changed_modules.each do |module|
UI.message "Testing module: #{module}"
sh "swift test --filter #{module}Tests"
end
end
end
```
## 4. 実践的な最適化Fastfile
```ruby
# Fastfile
default_platform(:ios)
platform :ios do
before_all do
# 環境最適化
ENV["FASTLANE_SKIP_UPDATE_CHECK"] = "1"
ENV["FASTLANE_HIDE_TIMESTAMP"] = "1"
# キャッシュ状態の確認
if File.exist?("./vendor/bundle")
UI.success "✅ Bundler cache found!"
end
if File.exist?("./Pods")
UI.success "✅ CocoaPods cache found!"
ENV["SKIP_POD_INSTALL"] = "true"
end
end
desc "PR用の高速テスト"
lane :pr_test do
# 依存関係のインストール(必要時のみ)
unless ENV["SKIP_POD_INSTALL"]
cocoapods(
clean_install: false,
verbose: false
)
end
# 高速テスト実行
scan(
scheme: "MyApp",
devices: ["iPhone 15"],
parallel_testing: true,
concurrent_workers: 2,
output_types: "",
suppress_xcode_output: true,
disable_slide_to_type: true,
prelaunch_simulator: true,
xcargs: "-parallelizeTargets COMPILER_INDEX_STORE_ENABLE=NO"
)
end
desc "ベンチマーク用レーン"
lane :benchmark do
start_time = Time.now
UI.message "Starting tests..."
test_time = benchmark_action { pr_test }
UI.important "Total time: #{test_time}s"
end
def benchmark_action
start = Time.now
yield
Time.now - start
end
end
```
## 5. コスト vs 効果の判断基準
### Bitriseプラン変更 vs GitHub Actions移行
**現状維持 + 最適化を選ぶべきケース:**
- 待ち時間が許容範囲内(10-15分程度)
- 予算制約が厳しい
- 最適化の余地がまだある
**GitHub Actions移行を選ぶべきケース:**
- 待ち時間による機会損失が大きい
- エンジニアが今後も増える予定
- CI/CD改善が経営課題
### 段階的アプローチ
1. **Phase 1**: 本ガイドの最適化を実施(1-2週間)
2. **Phase 2**: 効果測定と追加最適化(2-4週間)
3. **Phase 3**: それでも不足なら移行検討
## 6. 期待される効果
本ガイドの施策を実装することで:
- **初回ビルド**: 8-10分 → 6-8分(20-30%短縮)
- **2回目以降**: 8-10分 → 4-6分(40-50%短縮)
- **部分テスト**: 8-10分 → 2-3分(70-80%短縮)
特に依存関係のキャッシュとテストの並列化の組み合わせが効果的です。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment