本文へスキップ

Fastlane + 専用 Mac サーバー: 完全自動化ガイド

専用 Mac Mini M4 サーバー上の Fastlane を使って、iOS のリリースプロセス全体(ビルド、コード署名、スクリーンショット、TestFlight へのデプロイ)を自動化します。

CI/CD 読了時間 35 分 2026年10月更新

なぜ専用 Mac で Fastlane なのか?

Fastlane は、iOS と Android の開発における標準的な自動化ツールです。コード署名から TestFlight へのデプロイまで、あらゆる処理を扱います。専用 Mac Mini M4 で Fastlane を実行すると、次のメリットが得られます。

永続的なキーチェーン

コード署名証明書が実行間で保持されます。ビルドのたびにインポート/エクスポートする必要がありません。

高速なインクリメンタルビルド

DerivedData が保持されるため、fastlane build が 15 分以上ではなく 2〜4 分で完了します。

シミュレーターのスクリーンショット

fastlane snapshot を実際のシミュレーターで実行し、すべてのデバイスサイズに対応します。

信頼性の高いデプロイ

安定した専用Macサーバーなら、不安定なデプロイやコード署名の問題が減ります。

Fastlane をインストールする

専用 Mac mini M4 ビルドサーバーに SSH で接続し、Fastlane をインストールします。CI には Bundler をおすすめします。Gemfile で Fastlane のバージョンを固定すれば、毎回同じバージョンで実行できます。Homebrew はローカルでの利用に向いています。macOS のシステム Ruby は使わないでください。現在の Fastlane には Ruby 3.1 以降が必要です。

選択肢 A: Bundler と Gemfile(CI に推奨)

# Ruby 3.1 or later (current Fastlane releases need it; the macOS system Ruby is older)
# Homebrew not installed yet? Install it first, see Option B.
brew install ruby
echo 'export PATH="/opt/homebrew/opt/ruby/bin:$PATH"' >> ~/.zprofile
export PATH="/opt/homebrew/opt/ruby/bin:$PATH"

# In your project, a Gemfile that pins Fastlane
cd /path/to/your/ios-project
cat > Gemfile <<'EOF'
source "https://rubygems.org"

gem "fastlane", "~> 2.240"
gem "cocoapods"  # if using CocoaPods
EOF

bundle install

# Commit Gemfile and Gemfile.lock, then always run Fastlane through Bundler
bundle exec fastlane --version

選択肢 B: Homebrew でインストールする(ローカル用)

# Install Homebrew (if not already installed)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"

# Install Fastlane
brew install fastlane

# Verify installation
fastlane --version

プロジェクトで Fastlane を初期化する

# Navigate to your project directory
cd /path/to/your/ios-project

# Initialize Fastlane (with the Homebrew install, run: fastlane init)
bundle exec fastlane init

# Choose option 4: "Manual setup"
# This creates the fastlane/ directory with Appfile and Fastfile

コード署名のために Match を設定する

Fastlane Match は、コード署名証明書とプロビジョニングプロファイルを、プライベートな Git リポジトリまたはクラウドストレージに保存します。これにより、すべてのマシン(およびチームメンバー)が同じ署名 ID を使えるようになります。

Match を初期化する

# Initialize match (choose "git" for storage)
fastlane match init

# This creates fastlane/Matchfile

Matchfile を設定する

# fastlane/Matchfile

git_url("https://github.com/your-org/ios-certificates.git")

storage_mode("git")

type("appstore")  # default type, can be overridden per lane

app_identifier(["com.yourcompany.myapp"])
username("your-apple-id@example.com")

# For CI environments, use App Store Connect API key instead of username/password
# api_key_path("fastlane/AuthKey.json")

証明書を生成する

# Generate development certificates and profiles
fastlane match development

# Generate App Store distribution certificates and profiles
fastlane match appstore

# For ad-hoc distribution
fastlane match adhoc

# On CI, use readonly mode to avoid accidentally creating new certs
fastlane match appstore --readonly

Fastfile を作成する(ビルド、テスト、デプロイのレーン)

iOS アプリのビルド、テスト、デプロイのためのレーンを含む、完全な Fastfile を示します。

# fastlane/Fastfile

default_platform(:ios)

platform :ios do

  # ---- SHARED ----

  before_all do
    setup_ci if ENV['CI']  # Configures keychain for CI environments
  end

  # ---- BUILD ----

  desc "Build the app for testing"
  lane :build do
    match(type: "development", readonly: true)

    build_app(
      workspace: "MyApp.xcworkspace",
      scheme: "MyApp",
      configuration: "Debug",
      destination: "generic/platform=iOS Simulator",
      # Outside the checkout, so it survives the next git clean
      derived_data_path: File.expand_path("~/DerivedData/MyApp"),
      skip_archive: true,
      skip_codesigning: true
    )
  end

  # ---- TEST ----

  desc "Run all unit and UI tests"
  lane :test do
    run_tests(
      workspace: "MyApp.xcworkspace",
      scheme: "MyApp",
      devices: ["iPhone 17"],  # any device from: xcrun simctl list devices available
      derived_data_path: File.expand_path("~/DerivedData/MyApp"),
      result_bundle: true,
      output_directory: "fastlane/test_results",
      parallel_testing: true,
      concurrent_workers: 4
    )
  end

  # ---- BETA ----

  desc "Build and push a new beta to TestFlight"
  lane :beta do
    # Ensure we are on a clean git state
    ensure_git_status_clean

    # Fetch App Store certificates
    match(type: "appstore", readonly: true)

    # Increment build number (the API key avoids an Apple ID login and 2FA)
    increment_build_number(
      build_number: latest_testflight_build_number(
        api_key_path: "fastlane/AuthKey.json"
      ) + 1
    )

    # Build the app
    build_app(
      workspace: "MyApp.xcworkspace",
      scheme: "MyApp",
      export_method: "app-store",
      derived_data_path: File.expand_path("~/DerivedData/MyApp"),
      output_directory: "fastlane/builds"
    )

    # Upload to TestFlight
    upload_to_testflight(
      skip_waiting_for_build_processing: true,
      api_key_path: "fastlane/AuthKey.json"
    )

    # Commit the version bump
    commit_version_bump(
      message: "chore: bump build number [skip ci]",
      force: true
    )

    # Tag the release
    add_git_tag(
      tag: "beta/#{lane_context[SharedValues::BUILD_NUMBER]}"
    )

    push_to_git_remote
  end

  # ---- RELEASE ----

  desc "Build and submit to App Store Review"
  lane :release do
    match(type: "appstore", readonly: true)

    # Increment version number (patch)
    increment_version_number(bump_type: "patch")
    increment_build_number(
      build_number: latest_testflight_build_number(
        api_key_path: "fastlane/AuthKey.json"
      ) + 1
    )

    build_app(
      workspace: "MyApp.xcworkspace",
      scheme: "MyApp",
      export_method: "app-store",
      derived_data_path: File.expand_path("~/DerivedData/MyApp")
    )

    upload_to_app_store(
      submit_for_review: true,
      automatic_release: false,
      api_key_path: "fastlane/AuthKey.json",
      precheck_include_in_app_purchases: false,
      force: true  # skip the HTML preview prompt, which fails in non-interactive CI
    )

    commit_version_bump(message: "chore: release #{lane_context[SharedValues::VERSION_NUMBER]}")
    add_git_tag
    push_to_git_remote
  end

  # ---- ERROR HANDLING ----

  error do |lane, exception|
    # Send notification on failure (Slack, email, etc.)
    # slack(
    #   message: "Lane #{lane} failed: #{exception.message}",
    #   success: false
    # )
  end
end

スクリーンショットの自動化を設定する

Fastlane Snapshot は、複数のデバイスと言語にわたる App Store 用スクリーンショットを自動的に撮影します。専用 Mac では、リソースを奪い合うことなく安定して実行できます。

# Initialize snapshot
fastlane snapshot init

# This creates:
# - fastlane/Snapfile
# - fastlane/SnapshotHelper.swift (add to UI test target)

Snapfile を設定する

# fastlane/Snapfile

# Simulator names from: xcrun simctl list devices available
devices([
  "iPhone 17 Pro Max",
  "iPhone 17",
  "iPad Pro 13-inch (M5)"
])

languages([
  "en-US",
  "fr-FR",
  "de-DE",
  "ja"
])

scheme("MyAppUITests")
output_directory("./fastlane/screenshots")
clear_previous_screenshots(true)

# Speed up by running in parallel
concurrent_simulators(true)

UI テストに Snapshot を追加する

// In your XCUITest file:
import XCTest

class ScreenshotTests: XCTestCase {

    override func setUp() {
        continueAfterFailure = false
        let app = XCUIApplication()
        setupSnapshot(app)
        app.launch()
    }

    func testHomeScreen() {
        snapshot("01_HomeScreen")
    }

    func testDetailScreen() {
        let app = XCUIApplication()
        app.cells.firstMatch.tap()
        snapshot("02_DetailScreen")
    }

    func testSettings() {
        let app = XCUIApplication()
        app.tabBars.buttons["Settings"].tap()
        snapshot("03_Settings")
    }
}

スクリーンショット用のレーンを追加する

  # Add to your Fastfile:
  desc "Capture App Store screenshots"
  lane :screenshots do
    capture_screenshots
    frame_screenshots(white: true)  # Add device frames
    upload_to_app_store(
      skip_binary_upload: true,
      skip_metadata: true,
      api_key_path: "fastlane/AuthKey.json",
      force: true  # skip the HTML preview prompt when run in CI
    )
  end

TestFlight にデプロイする

CI 環境では、Apple ID の認証情報の代わりに App Store Connect API キーを使います。これにより、ヘッドレスサーバーでの 2FA プロンプトを回避できます。

API キーを作成する

  1. App Store Connect > Users and Access > Integrations > App Store Connect API に移動します
  2. + ボタンをクリックして新しい API キーを生成します
  3. App Manager ロールを選択します
  4. .p8 ファイルをダウンロードします
  5. Key ID と Issuer ID を控えておきます

API キーの JSON を作成する

# fastlane/AuthKey.json
{
  "key_id": "YOUR_KEY_ID",
  "issuer_id": "YOUR_ISSUER_ID",
  "key": "-----BEGIN PRIVATE KEY-----\nYOUR_P8_KEY_CONTENT\n-----END PRIVATE KEY-----",
  "in_house": false
}

# IMPORTANT: Add this to .gitignore!
echo "fastlane/AuthKey.json" >> .gitignore

デプロイを実行する

# Deploy to TestFlight
fastlane beta

# Or run the full release pipeline
fastlane release

CI/CD と連携する

Fastlane はあらゆる CI/CD プラットフォームとシームレスに連携します。ここでは、セルフホストの Mac Mini M4 ランナーを使った GitHub Actions の例を示します。

# .github/workflows/deploy.yml
name: Deploy to TestFlight

on:
  push:
    branches: [ main ]

jobs:
  deploy:
    runs-on: [self-hosted, macOS, ARM64, M4]
    # push_to_git_remote needs write access: the token is read-only by default
    permissions:
      contents: write

    env:
      # Xcode for this job, without sudo (path from `xcodes installed`)
      DEVELOPER_DIR: /Applications/Xcode-27.0.0.app/Contents/Developer
      MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
      # base64 of "github_username:personal_access_token", for example:
      #   echo -n your_github_username:your_personal_access_token | base64
      MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }}

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Use the Homebrew Ruby (3.1 or later)
        run: echo "/opt/homebrew/opt/ruby/bin" >> "$GITHUB_PATH"

      - name: Set up App Store Connect API Key
        run: |
          mkdir -p fastlane
          echo '${{ secrets.APP_STORE_CONNECT_API_KEY }}' > fastlane/AuthKey.json

      - name: Install dependencies
        run: |
          bundle install
          pod install  # if using CocoaPods

      - name: Deploy to TestFlight
        run: bundle exec fastlane beta

      - name: Clean up API key
        if: always()
        run: rm -f fastlane/AuthKey.json

ベストプラクティス

Fastlane のバージョン固定に Bundler を使う

バージョンを固定した Fastlane を含む Gemfile を追加します。bundle exec fastlane を実行して、すべての環境で一貫したバージョンを保証します。

Apple ID の認証情報ではなく App Store Connect API キーを使う

API キーは 2FA プロンプトを回避でき、CI 環境ではより安全です。

CI では match --readonly を使う

新しい証明書が誤って作成されるのを防ぎます。証明書は必要なときにだけ手動で生成します。

CI 環境では setup_ci を使う

これにより一時的なキーチェーンが作成され、システムキーチェーンを汚さずに済み、キーチェーンの権限ダイアログを防げます。

ビルドを速くするために DerivedData を保持する

専用 Mac では、derived_data_path を指定して、実行間でビルド成果物を再利用します。

今日から iOS のリリースを自動化しましょう

専用 Mac Mini M4 を手に入れ、無制限のビルドで Fastlane を実行しましょう。月額$85(USD・税抜)から。

My Remote Mac の料金は USD・税抜です。

さらに詳しい情報をお探しですか?

ステップバイステップのセットアップ手順、設定リファレンス、トラブルシューティングについては、完全なドキュメントをご覧ください。

ドキュメントを開く →