Skip to content
Learni
View all tutorials
CI/CD

Comment automatiser les CI/CD mobiles avec Fastlane en 2026

Introduction

Fastlane est l'outil open-source incontournable pour automatiser les workflows mobiles en 2026, gérant builds, tests, screenshots et déploiements sans intervention manuelle. Pour un dev senior, il transcende les scripts basiques en offrant des lanes personnalisées, plugins modulaires et intégrations CI/CD natives, réduisant les cycles de release de jours à minutes. Imaginez : un commit push déclenche tests unitaires/UI, génération de screenshots localisés, build signé et upload sur TestFlight ou Google Play – tout cela avec gestion des secrets et rollbacks automatisés.

Ce tutoriel avancé cible les pros : configuration multi-plateformes (iOS/Android), optimisation des performances (caching certs/provisioning), et scaling en CI. Vous économiserez 80% du temps de release, éliminerez les erreurs humaines (comme les profils expirés) et scalerez vers 10+ builds/jour. Prêt à transformer vos déploiements en machine bien huilée ? Suivons un projet concret : une app 'MyMobileApp' avec beta hebdo et prod mensuel.

Prérequis

  • macOS Ventura+ avec Xcode 16+ (pour iOS) et Android Studio Iguana+ (pour Android).
  • Comptes : Apple Developer Program (99$/an), Google Play Console (25$ unique).
  • Ruby 3.2+ et Bundler 2.4+ installés (gem install bundler).
  • Projet existant : repo Git avec Xcodeproj iOS et Android app-level build.gradle.
  • GitHub repo avec secrets : MATCH_PASSWORD, APPSTORE_CONNECT_KEY_ID, GOOGLE_SERVICE_ACCOUNT_JSON.
  • Connaissances : Ruby basique, Fastlane actions (gym, fastlane/match).

Installation et initialisation Fastlane

terminal
#!/bin/bash

# Installer Fastlane via Bundler (recommandé pour isolation)
gem install bundler
bundle init

# Ajouter Fastlane et plugins avancés
cat > Gemfile << EOF
source "https://rubygems.org"
gem "fastlane"
plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
EOF

bundle install

# Initialiser pour iOS et Android
fastlane init --ios
fastlane init --android

# Configurer Appfile pour iOS
cat > fastlane/Appfile << EOF
app_identifier "com.example.mymobileapp"
apple_id "votre@apple.id"
team_id "ABC123DEF4"
EOF

# Configurer Appfile pour Android
cat > ./android/fastlane/Appfile << EOF
package_name "com.example.mymobileapp"
json_key_file "path/to/google-service-account.json"
EOF

Ce script initialise un environnement isolé avec Bundler, évitant les conflits de gems. L'init crée les dossiers fastlane/ pour iOS et ./android/fastlane/ pour Android, générant des Fastfiles squelettes. Les Appfiles centralisent les identifiants app, piégeant les erreurs de matching package/bundle ID dès le départ.

Configuration des certificats avec Match

Match centralise les profils et certs sur Git privé, rendant les clones CI reproductibles. Pour advanced, utilisez readonly: true en CI et generate en local. Associez-le à 1Password ou GitHub Secrets pour rotation auto.

Setup Match pour certificats partagés

fastlane/Matchfile
storage_mode("git")
git_url("git@github.com:yourorg/fastlane-certs.git")
app_identifier(["com.example.mymobileapp", "com.example.mymobileapp.Staging"])
username("votre@apple.id")
team_id("ABC123DEF4")
readonly(true)

# Avancé : types spécifiques
# Development certs auto-générés
# AdHoc pour beta/TestFlight
# AppStore pour prod

Matchfile configure un repo Git dédié aux certs, supportant multi-environnements (Staging/Prod). readonly(true) protège en CI contre les overwrites accidentels. Piège : toujours commiter/push après match init, sinon les clones échouent sur 'no profiles found'.

Gemfile avec plugins avancés

Gemfile
source "https://rubygems.org"

gem "fastlane", "~> 2.220"

# Plugins avancés pour screenshots, perf et analytics
plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
gem "fastlane-plugin-screenshot_timer"
gem "fastlane-plugin-match_custom"
gem "fastlane-plugin-appcenter_distribute"

# Optimisations CI
gem "cocoapods"
gem "jazzy" # Docs auto
group :test do
  gem "slather" # Coverage
end

# Lock versions pour reproductibilité CI
eval_gemfile "fastlane/Pluginfile"

Ce Gemfile locke les versions et ajoute plugins pour timers screenshots (optimise builds lents), Match custom et AppCenter (alternative TestFlight). Pluginfile auto-généré par bundle exec fastlane install_plugins. Évitez les gems globales : Bundler assure isolation et caching GitHub Actions.

Fastfile iOS : lanes avancées

Structure progressive : lanes modulaires pour test → build → screenshots → deploy. Utilisez before_all/after_all pour setup/teardown communs, comme caching CocoaPods.

Fastfile iOS complet avec screenshots et beta

fastlane/Fastfile
default_platform(:ios)

platform :ios do
  before_all do
    # Setup commun : certs, pods
    match(type: "appstore", readonly: true)
    cocoapods
  end

  desc "Tests unitaires et UI"
  lane :test do
    scan(
      scheme: "MyMobileApp",
      devices: ["iPhone 15 Pro"],
      clean: true
    )
    slather
  end

  desc "Générer screenshots localisés"
  lane :screenshots do
    snapshot(
      locale: ["en-US", "fr-FR"],
      dark_mode: true,
      ios_multiplier: 3
    )
    frameit
  end

  desc "Beta TestFlight"
  lane :beta do
    increment_build_number(build_number: latest_testflight_build_number + 1)
    gym(
      scheme: "MyMobileApp",
      export_method: "app-store",
      output_directory: "builds"
    )
    upload_to_testflight(
      skip_submission: true,
      notify_tester: true
    )
    appcenter_distribute
  end

  desc "Release App Store"
  lane :release do
    screenshots
    beta
    upload_to_app_store
  end

after_all do
  clean_build_artifacts
end
end

Ce Fastfile iOS gère tout : scan pour tests parallèles, snapshot/frameit pour assets marketing auto (13 tailles iOS), gym optimisé export AppStore. latest_testflight_build_number évite collisions. Piège : skip_waiting_for_build_processing en CI pour accélérer feedback.

Fastfile Android complet avec Play Store

android/fastlane/Fastfile
default_platform(:android)

platform :android do
  before_all do
    # Gradle clean
    gradle(task: "clean")
  end

  desc "Tests Android"
  lane :test do
    gradle(task: "test")
    gradle(task: "connectedAndroidTest")
  end

  desc "Build et deploy beta (Internal Testing)"
  lane :beta do
    gradle(
      task: "clean bundleRelease",
      properties: {
        "android.injected.signing.store.file": "keystore.jks",
        "android.injected.signing.store.password": ENV["KEYSTORE_PASSWORD"]
      }
    )
    upload_to_play_store(
      track: "internal",
      aab: "app/build/outputs/bundle/release/app-release.aab",
      skip_upload_metadata: true,
      skip_upload_images: true,
      skip_upload_screenshots: true
    )
  end

  desc "Release Production"
  lane :release do
    increment_version_code
    increment_version_name(append_version_code: false)
    beta
    upload_to_play_store(track: "production")
  end

after_all do |lane|
  sh("../gradlew clean")
end
end

Fastfile Android utilise gradle pour tasks custom avec signing injecté via ENV (sécurisé). upload_to_play_store avec tracks (internal/production) pour beta/GA. Avancé : increment_version_* auto-bump. Évitez hardcoded paths : utilisez vars pour scalabilité multi-modules.

Intégration CI/CD avec GitHub Actions

Exécutez fastlane beta sur push main. Cachez Bundler/Match pour <5min builds. Secrets GitHub : MATCH_GIT_URL private.

Workflow GitHub Actions multi-plateformes

.github/workflows/fastlane.yml
name: CI/CD Fastlane

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test-ios:
    runs-on: macos-14
    steps:
    - uses: actions/checkout@v4
    - uses: ruby/setup-ruby@v1
      with:
        ruby-version: '3.2'
        bundler-cache: true
    - name: Install dependencies
      run: bundle install
    - name: Run tests
      run: bundle exec fastlane test
      env:
        MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }}
        MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
        APPSTORE_CONNECT_KEY_ID: ${{ secrets.APPSTORE_CONNECT_KEY_ID }}

  beta-android:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: nttld/setup-ndk@v1
      id: setup-ndk
      with:
        ndk-version: "r26b"
    - name: Install Java
      uses: actions/setup-java@v4
      with:
        distribution: 'temurin'
        java-version: '17'
    - name: Bundle & Beta
      run: |
        bundle install
        bundle exec fastlane beta
      env:
        KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
        GOOGLE_SERVICE_ACCOUNT_JSON: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_JSON }}

  deploy-beta:
    needs: [test-ios, beta-android]
    runs-on: macos-14
    if: github.ref == 'refs/heads/main'
    steps:
    - uses: actions/checkout@v4
    - uses: ruby/setup-ruby@v1
      with: bundler-cache: true
    - run: bundle exec fastlane beta
      env:
        FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT: 1200

Ce workflow parallélise tests iOS/Android, déploie beta sur main. bundler-cache et setup-ndk optimisent (<10min total). Jobs conditionnels (needs/if) assurent gating. Piège : timeouts Xcode → FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT=1200.

Bonnes pratiques

  • Modularité : Une lane par responsabilité (test/screenshots/deploy), réutilisez via lanes(:sub_lane).
  • Secrets management : Jamais en dur ; utilisez ENV + GitHub/1Password CLI pour rotation auto.
  • Caching agressif : Pods/DerivedData en CI, readonly Match pour 90% speedup.
  • Observabilité : Slack/Teams notifications via notify, Slather pour coverage >80%.
  • Rollback : reset_git_repo + tag backup avant release.

Erreurs courantes à éviter

  • Cert expirat : match nuke distribution + regenerate ; surveillez avec cron GitHub.
  • Screenshots fail : Vérifiez simulators lancés (snapshot_launch_timer: 30) et locales installées.
  • CI timeouts : Augmentez FASTLANE_UPDATE_METHOD=global et cache Bundler.
  • Version bump race : Lock increment_build_number avec rescue et retry.

Pour aller plus loin

Plongez dans les formations Learni sur CI/CD mobile pour Fastlane + GitLab/Terraform. Lisez docs Fastlane, contribuez sur GitHub. Avancé : intégrez Firebase App Distribution et custom plugins Ruby.