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
#!/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"
EOFCe 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
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 prodMatchfile 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
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
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
endCe 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
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
endFastfile 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
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: 1200Ce 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 Matchpour 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=globalet cache Bundler. - Version bump race : Lock
increment_build_numberavecrescueet 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.