Guide - Automatisation

Fastlane + serveur Mac dédié : guide complet d'automatisation

Automatisez l'intégralité de votre processus de release iOS -- builds, signature de code, captures d'écran et déploiement TestFlight -- en utilisant Fastlane sur un serveur Mac Mini M4 dédié.

35 min de lecture Mis à jour en janvier 2025

1. Pourquoi Fastlane sur un Mac dédié ?

Fastlane est l'outil d'automatisation standard pour le développement iOS et Android. Il gère tout, de la signature de code au déploiement TestFlight. Exécuter Fastlane sur un Mac Mini M4 dédié vous offre :

Trousseau persistant

Les certificats de signature de code persistent entre les exécutions. Pas besoin d'importer/exporter à chaque build.

Builds incrémentaux rapides

DerivedData persiste, donc fastlane build prend 2-4 minutes au lieu de 15+.

Captures d'écran sur simulateur

Exécutez fastlane snapshot avec de vrais simulateurs pour toutes les tailles d'appareil.

Déploiements fiables

Un environnement stable et dédié signifie moins de déploiements instables et de problèmes de signature de code.

2. Installer Fastlane

Connectez-vous en SSH à votre Mac Mini M4 et installez Fastlane. Nous recommandons Homebrew pour la configuration la plus simple :

Option A : Installation via Homebrew (recommandé)

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

Option B : Installation via RubyGems

# Use the system Ruby or install rbenv for version management
gem install fastlane -NV

# Or with Bundler (recommended for team consistency):
# Create a Gemfile in your project root
cat > Gemfile <<'EOF'
source "https://rubygems.org"

gem "fastlane"
gem "cocoapods"  # if using CocoaPods
EOF

bundle install

Initialiser Fastlane dans votre projet

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

# Initialize Fastlane
fastlane init

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

3. Configurer Match pour la signature de code

Fastlane Match stocke vos certificats de signature de code et profils de provisionnement dans un dépôt Git privé ou un stockage cloud. Cela garantit que toutes les machines (et les membres de l'équipe) utilisent la même identité de signature.

Initialiser Match

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

# This creates fastlane/Matchfile

Configurer le 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")

Générer les certificats

# 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

4. Créer un Fastfile (lanes de build, test, déploiement)

Voici un Fastfile complet avec des lanes pour compiler, tester et déployer votre application iOS :

# 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",
      derived_data_path: "DerivedData",
      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 16 Pro"],
      derived_data_path: "DerivedData",
      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
    increment_build_number(
      build_number: latest_testflight_build_number + 1
    )

    # Build the app
    build_app(
      workspace: "MyApp.xcworkspace",
      scheme: "MyApp",
      export_method: "app-store",
      derived_data_path: "DerivedData",
      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 + 1
    )

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

    upload_to_app_store(
      submit_for_review: true,
      automatic_release: false,
      api_key_path: "fastlane/AuthKey.json",
      precheck_include_in_app_purchases: false
    )

    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

5. Configurer les captures d'écran automatisées

Fastlane Snapshot capture automatiquement les captures d'écran App Store sur plusieurs appareils et langues. Sur un Mac dédié, cela s'exécute de manière fiable sans concurrence pour les ressources.

# Initialize snapshot
fastlane snapshot init

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

Configurer le Snapfile

# fastlane/Snapfile

devices([
  "iPhone 16 Pro Max",
  "iPhone 16 Pro",
  "iPhone SE (3rd generation)",
  "iPad Pro 13-inch (M4)"
])

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)

Ajouter Snapshot à vos tests UI

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

Ajouter une lane de captures d'écran

  # 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"
    )
  end

6. Déployer sur TestFlight

Pour les environnements CI, utilisez la clé API App Store Connect au lieu des identifiants Apple ID. Cela évite les invites 2FA sur les serveurs sans écran.

Créer une clé API

  1. Allez sur App Store Connect > Utilisateurs et accès > Clés
  2. Cliquez sur le bouton + pour générer une nouvelle clé API
  3. Sélectionnez le rôle App Manager
  4. Téléchargez le fichier .p8
  5. Notez le Key ID et l'Issuer ID

Créer le JSON de la clé 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

Exécuter le déploiement

# Deploy to TestFlight
fastlane beta

# Or run the full release pipeline
fastlane release

7. Intégrer avec le CI/CD

Fastlane s'intègre parfaitement avec n'importe quelle plateforme CI/CD. Voici un exemple GitHub Actions utilisant votre runner Mac Mini M4 auto-hébergé :

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

on:
  push:
    branches: [ main ]

jobs:
  deploy:
    runs-on: [self-hosted, macOS, ARM64, M4]

    env:
      MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
      MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_TOKEN }}

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

      - 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

8. Bonnes pratiques

Utiliser Bundler pour le verrouillage de version Fastlane

Ajoutez un Gemfile avec une version Fastlane fixée. Exécutez bundle exec fastlane pour garantir des versions cohérentes dans tous les environnements.

Utiliser les clés API App Store Connect, pas les identifiants Apple ID

Les clés API évitent les invites 2FA et sont plus sécurisées pour les environnements CI.

Utiliser match --readonly en CI

Empêche la création accidentelle de nouveaux certificats. Ne générez des certificats manuellement que lorsque c'est nécessaire.

Utiliser setup_ci dans les environnements CI

Cela crée un trousseau temporaire pour éviter de polluer le trousseau système et empêche les boîtes de dialogue de permission du trousseau.

Conserver DerivedData pour des builds plus rapides

Sur un Mac dédié, spécifiez derived_data_path pour réutiliser les artefacts de build entre les exécutions.

Guides connexes

Automatisez vos releases iOS dès aujourd'hui

Obtenez un Mac Mini M4 dédié et exécutez Fastlane avec des builds illimités. À partir de 75 $/mois avec un essai gratuit de 7 jours.