Section 9/181 menit

9. Fastlane: Automasi Deployment

9. Fastlane: Automasi Deployment

Fastlane mengotomasi sertifikat, build, test, screenshot, dan upload ke App Store.

swift
# Install
gem install fastlane

# Setup
cd MyApp
fastlane init
# → Pilih "Automate App Store distribution"

Fastfile: Definisi Lanes

swift
# fastlane/Fastfile

default_platform(:ios)

platform :ios do
  
  # Shared setup — dijalankan sebelum setiap lane
  before_all do
    ensure_git_status_clean  # gagal jika ada uncommitted changes
    cocoapods if File.exist?("Podfile")
  end

  # Lane: run tests
  desc "Run unit dan UI tests"
  lane :test do
    run_tests(
      project: "MyApp.xcodeproj",
      scheme: "MyApp",
      devices: ["iPhone 15 Pro"],
      clean: true,
      output_directory: "fastlane/test_output",
      output_types: "html,junit"
    )
  end

  # Lane: build untuk internal testing
  desc "Build dan upload ke TestFlight (beta)"
  lane :beta do
    # Sync certificates dari git repo privat
    match(
      type: "appstore",
      app_identifier: "com.mycompany.myapp",
      readonly: is_ci  # di CI: hanya baca, tidak buat baru
    )
    
    # Bump build number otomatis
    increment_build_number(
      build_number: latest_testflight_build_number + 1
    )
    
    # Build
    gym(
      scheme: "MyApp",
      configuration: "Release",
      export_method: "app-store",
      output_directory: "./build",
      include_symbols: true,
      include_bitcode: false
    )
    
    # Upload ke TestFlight
    pilot(
      skip_waiting_for_build_processing: true,
      changelog: last_git_commit[:message]
    )
    
    # Commit build number change
    commit_version_bump(
      message: "Bump build number [skip ci]",
      force: true
    )
    
    push_to_git_remote
  end

  # Lane: full release ke App Store
  desc "Submit ke App Store untuk review"
  lane :release do
    match(type: "appstore", readonly: true)
    
    # Pastikan version number sudah diupdate
    version = get_version_number(target: "MyApp")
    UI.confirm("Release version #{version}?")
    
    gym(scheme: "MyApp", configuration: "Release")
    
    deliver(
      submit_for_review: true,
      automatic_release: false,
      force: true,
      metadata_path: "./fastlane/metadata",
      screenshots_path: "./fastlane/screenshots",
      skip_binary_upload: false
    )
  end

  # Lane: generate screenshots
  desc "Generate App Store screenshots otomatis"
  lane :screenshots do
    capture_screenshots(
      scheme: "MyAppUITests",
      devices: [
        "iPhone 15 Pro",
        "iPhone SE (3rd generation)",
        "iPad Pro (12.9-inch) (6th generation)"
      ],
      languages: ["id-ID", "en-US"],
      output_directory: "./fastlane/screenshots",
      clear_previous_screenshots: true
    )
    
    frame_screenshots(
      white: false,
      path: "./fastlane/screenshots"
    )
  end

  after_all do |lane|
    slack(
      message: "Lane #{lane} berhasil!",
      channel: "#ios-builds"
    ) if is_ci
  end

  error do |lane, exception|
    slack(
      message: "Lane #{lane} gagal: #{exception.message}",
      channel: "#ios-builds",
      success: false
    ) if is_ci
  end
end

Match: Manage Certificates & Profiles

swift
# fastlane/Matchfile

git_url("https://github.com/myorg/ios-certificates.git")
git_branch("main")
storage_mode("git")
type("appstore")   # development, adhoc, appstore, enterprise

app_identifier([
  "com.mycompany.myapp",
  "com.mycompany.myapp.widget",
  "com.mycompany.myapp.notification-service"
])

team_id("ABCDEF1234")
swift
# Pertama kali: setup certificates (developer yang punya Apple Developer access)
fastlane match appstore --generate_apple_certs

# CI/CD: read-only
fastlane match appstore --readonly

# Nuke & regenerate semua (jika ada masalah)
fastlane match nuke development
fastlane match development