Langsung ke konten

Fastlane + Server Mac Dedikasi: Panduan Otomatisasi Lengkap

Otomatiskan seluruh proses rilis iOS Anda (build, code signing, tangkapan layar, dan deployment TestFlight) menggunakan Fastlane di server Mac Mini M4 dedikasi.

CI/CD Baca 35 menit Diperbarui Oktober 2026

Mengapa Fastlane di Mac Dedikasi?

Fastlane adalah alat otomatisasi standar untuk pengembangan iOS dan Android. Ia menangani segalanya mulai dari code signing hingga deployment TestFlight. Menjalankan Fastlane di Mac Mini M4 dedikasi memberi Anda:

Keychain Persisten

Sertifikat code signing bertahan di antara eksekusi. Tidak perlu impor/ekspor pada setiap build.

Build Inkremental Cepat

DerivedData persisten, jadi fastlane build memakan waktu 2-4 menit alih-alih 15+.

Tangkapan Layar Simulator

Jalankan fastlane snapshot dengan simulator asli untuk semua ukuran perangkat.

Deploy yang Andal

Server Mac dedikasi yang stabil berarti lebih sedikit deployment yang tidak stabil dan masalah code signing.

Instal Fastlane

SSH ke server build Mac mini M4 khusus Anda dan instal Fastlane. Untuk CI, kami merekomendasikan Bundler: Gemfile mengunci versi Fastlane sehingga setiap run memakai versi yang sama, sedangkan Homebrew cocok untuk penggunaan lokal. Hindari Ruby sistem macOS: rilis Fastlane saat ini memerlukan Ruby 3.1 atau yang lebih baru.

Opsi A: Bundler dan Gemfile (Direkomendasikan untuk 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

Opsi B: Instal melalui Homebrew (Penggunaan Lokal)

# 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

Inisialisasi Fastlane di Proyek Anda

# 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

Konfigurasikan Match untuk Code Signing

Fastlane Match menyimpan sertifikat code signing dan provisioning profile Anda di repositori Git privat atau penyimpanan cloud. Ini memastikan semua mesin (dan anggota tim) menggunakan identitas penandatanganan yang sama.

Inisialisasi Match

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

# This creates fastlane/Matchfile

Konfigurasikan 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")

Hasilkan Sertifikat

# 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

Buat Fastfile (Lane Build, Test, Deploy)

Berikut Fastfile lengkap dengan lane untuk mem-build, menguji, dan men-deploy aplikasi iOS Anda:

# 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

Siapkan Tangkapan Layar Otomatis

Fastlane Snapshot menangkap tangkapan layar App Store di berbagai perangkat dan bahasa secara otomatis. Di Mac dedikasi, ini berjalan andal tanpa bersaing untuk sumber daya.

# Initialize snapshot
fastlane snapshot init

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

Konfigurasikan 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)

Tambahkan Snapshot ke UI Test Anda

// 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")
    }
}

Tambahkan Lane Tangkapan Layar

  # 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

Deploy ke TestFlight

Untuk lingkungan CI, gunakan kunci API App Store Connect alih-alih kredensial Apple ID Anda. Ini menghindari prompt 2FA di server headless.

Buat Kunci API

  1. Buka App Store Connect > Users and Access > Integrations > App Store Connect API
  2. Klik tombol + untuk menghasilkan kunci API baru
  3. Pilih peran App Manager
  4. Unduh file .p8
  5. Catat Key ID dan Issuer ID

Buat JSON Kunci API

# 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

Jalankan Deployment

# Deploy to TestFlight
fastlane beta

# Or run the full release pipeline
fastlane release

Integrasikan dengan CI/CD

Fastlane berintegrasi mulus dengan platform CI/CD apa pun. Berikut contoh GitHub Actions menggunakan self-hosted runner Mac Mini M4 Anda:

# .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

Praktik Terbaik

Gunakan Bundler untuk menyematkan versi Fastlane

Tambahkan Gemfile dengan versi Fastlane yang disematkan. Jalankan bundle exec fastlane untuk memastikan versi yang konsisten di semua lingkungan.

Gunakan kunci API App Store Connect, bukan kredensial Apple ID

Kunci API menghindari prompt 2FA dan lebih aman untuk lingkungan CI.

Gunakan match --readonly di CI

Mencegah pembuatan sertifikat baru secara tidak sengaja. Hanya hasilkan sertifikat secara manual saat diperlukan.

Gunakan setup_ci di lingkungan CI

Ini membuat keychain sementara untuk menghindari pengotoran keychain sistem dan mencegah dialog izin keychain.

Simpan DerivedData untuk build lebih cepat

Di Mac dedikasi, tentukan derived_data_path untuk menggunakan kembali artifact build di antara eksekusi.

Otomatiskan Rilis iOS Anda Hari Ini

Dapatkan Mac Mini M4 dedikasi dan jalankan Fastlane dengan build tanpa batas. Mulai dari $85/bulan (USD, belum termasuk pajak).

Harga My Remote Mac dalam USD, belum termasuk pajak.

Butuh detail lebih lanjut?

Jelajahi dokumentasi lengkap untuk panduan langkah demi langkah, referensi konfigurasi, dan pemecahan masalah.

Buka dokumentasi →