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 :
Les certificats de signature de code persistent entre les exécutions. Pas besoin d'importer/exporter à chaque build.
DerivedData persiste, donc fastlane build prend 2-4 minutes au lieu de 15+.
Exécutez fastlane snapshot avec de vrais simulateurs pour toutes les tailles d'appareil.
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
- Allez sur App Store Connect > Utilisateurs et accès > Clés
- Cliquez sur le bouton + pour générer une nouvelle clé API
- Sélectionnez le rôle App Manager
- Téléchargez le fichier
.p8 - 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
Ajoutez un Gemfile avec une version Fastlane fixée. Exécutez bundle exec fastlane pour garantir des versions cohérentes dans tous les environnements.
Les clés API évitent les invites 2FA et sont plus sécurisées pour les environnements CI.
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.
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.
Sur un Mac dédié, spécifiez derived_data_path pour réutiliser les artefacts de build entre les exécutions.
Guides connexes
Runner GitHub Actions auto-hébergé
Configurez un runner GitHub Actions sur votre Mac Mini M4.
Agent de build Jenkins Mac
Configurez des agents de build Jenkins sur un Mac Mini dédié pour le CI/CD iOS.
Runner GitLab CI Mac
Enregistrez un runner GitLab sur un Mac Mini dédié pour les pipelines CI iOS/macOS.