1. Pourquoi utiliser un runner Mac auto-hébergé ?
Les runners macOS hébergés par GitHub sont pratiques mais coûteux. À 0,08 $ par minute, une équipe effectuant 75 heures de builds par mois paie environ 360 $. Un Mac Mini M4 dédié de My Remote Mac coûte 75 $/mois avec des minutes de build illimitées, vous offrant le même matériel (voire meilleur) pour une fraction du coût.
| Caractéristique | Runner hébergé par GitHub | My Remote Mac auto-hébergé |
|---|---|---|
| Coût | 0,08 $/min (~350 $/mois pour 75h) | 75 $/mois (minutes illimitées) |
| Architecture | Intel x86 (quelques M1) | Apple M4 (dernière génération) |
| Vitesse de build | ~12 min (projet moyen) | ~4 min (même projet) |
| Cache persistant | Non (éphémère) | Oui (disque persistant) |
| Logiciels personnalisés | Limité | Accès root complet |
| Jobs simultanés | 5 (gratuit) / 20 (payant) | Illimités (votre matériel) |
Avantage clé : Comme le runner est persistant, DerivedData, les caches SPM et CocoaPods sont préservés entre les builds. Cela seul peut réduire les temps de build de 50 à 70 % par rapport aux runners GitHub hébergés éphémères qui repartent de zéro à chaque fois.
2. Prérequis
Avant de commencer, assurez-vous de disposer des éléments suivants :
- Un serveur Mac Mini M4 chez My Remote Mac (à partir de 75 $/mois)
- Un compte GitHub avec un accès admin à votre dépôt ou organisation
- Un accès SSH à votre Mac Mini (fourni avec votre abonnement My Remote Mac)
- Un compte Apple Developer (pour la signature de code et les profils de provisionnement)
- Une connaissance de base de YAML et des commandes terminal
3. Étape 1 : Se connecter en SSH à votre Mac Mini et installer Xcode
Commencez par vous connecter à votre Mac Mini M4 via SSH. Vous aurez reçu vos identifiants lors de la mise en place de votre serveur My Remote Mac.
Connexion via SSH
# Connect to your Mac Mini M4
ssh admin@your-server-ip
# Verify you're on Apple Silicon
uname -m
# Expected output: arm64
# Check macOS version
sw_vers
# ProductName: macOS
# ProductVersion: 15.2
# BuildVersion: 24C101
Installer les outils en ligne de commande Xcode
# Install Command Line Tools
xcode-select --install
# Accept the license agreement
sudo xcodebuild -license accept
# Verify installation
xcode-select -p
# /Library/Developer/CommandLineTools
Installer Xcode (version complète)
Pour les builds iOS, vous avez besoin de l'application Xcode complète. Le moyen le plus rapide de l'installer sur un serveur sans écran est d'utiliser xcodes :
# Install Homebrew (if not already installed)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Add Homebrew to PATH
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"
# Install xcodes CLI tool
brew install xcodes
# List available Xcode versions
xcodes list
# Install the latest stable Xcode
xcodes install 16.2
# Set it as the active Xcode
sudo xcode-select -s /Applications/Xcode-16.2.app/Contents/Developer
# Verify
xcodebuild -version
# Xcode 16.2
# Build version 16C5032a
Installer les simulateurs iOS
# Install the iOS 18 simulator runtime
xcodebuild -downloadPlatform iOS
# Verify simulator availability
xcrun simctl list runtimes
# == Runtimes ==
# iOS 18.2 (18.2 - 22C150) - com.apple.CoreSimulator.SimRuntime.iOS-18-2
4. Étape 2 : Installer le runner GitHub Actions
Téléchargeons et configurons maintenant l'agent runner GitHub Actions. Accédez à votre dépôt sur GitHub, allez dans Settings > Actions > Runners > New self-hosted runner, et sélectionnez macOS + ARM64.
Télécharger et configurer
# Create a directory for the runner
mkdir -p ~/actions-runner && cd ~/actions-runner
# Download the latest runner package (ARM64)
curl -o actions-runner-osx-arm64-2.321.0.tar.gz -L \
https://github.com/actions/runner/releases/download/v2.321.0/actions-runner-osx-arm64-2.321.0.tar.gz
# Extract the package
tar xzf actions-runner-osx-arm64-2.321.0.tar.gz
# Configure the runner
# Replace YOUR_TOKEN with the token from GitHub Settings
./config.sh --url https://github.com/YOUR_ORG/YOUR_REPO \
--token YOUR_TOKEN \
--name "mac-mini-m4-runner" \
--labels "self-hosted,macOS,ARM64,M4" \
--work "_work"
# Test the runner interactively first
./run.sh
Installer en tant que service launchd persistant
Exécuter l'agent de manière interactive convient pour les tests, mais pour la production, il faut qu'il démarre automatiquement au démarrage et redémarre en cas de plantage. GitHub fournit un script d'installation de service intégré pour macOS :
# Install as a launchd service
cd ~/actions-runner
sudo ./svc.sh install
# Start the service
sudo ./svc.sh start
# Check the service status
sudo ./svc.sh status
# Expected: "active (running)"
# View the launchd plist (for reference)
cat /Library/LaunchDaemons/actions.runner.*.plist
Le service démarrera désormais automatiquement au démarrage et redémarrera si le processus s'arrête. Vous pouvez vérifier que le runner apparaît comme "Idle" dans la page Settings > Actions > Runners de votre dépôt GitHub.
Configurer le runner pour plusieurs dépôts (niveau organisation)
# For an organization-level runner, use the organization URL:
./config.sh --url https://github.com/YOUR_ORG \
--token YOUR_ORG_TOKEN \
--name "mac-mini-m4-org-runner" \
--labels "self-hosted,macOS,ARM64,M4" \
--runnergroup "Default" \
--work "_work"
# This allows ALL repositories in your organization to use this runner
5. Étape 3 : Créer votre workflow de build iOS
Créez un fichier workflow dans votre dépôt à l'emplacement .github/workflows/ios-build.yml. Ce workflow s'exécutera sur votre runner Mac Mini M4 auto-hébergé.
name: iOS Build & Test
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: [self-hosted, macOS, ARM64, M4]
env:
SCHEME: "MyApp"
DESTINATION: "platform=iOS Simulator,name=iPhone 16 Pro,OS=18.2"
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Select Xcode version
run: |
sudo xcode-select -s /Applications/Xcode-16.2.app/Contents/Developer
xcodebuild -version
- name: Resolve Swift Package Dependencies
run: |
xcodebuild -resolvePackageDependencies \
-scheme "$SCHEME" \
-clonedSourcePackagesDirPath .spm-cache
- name: Build the app
run: |
xcodebuild build \
-scheme "$SCHEME" \
-destination "$DESTINATION" \
-clonedSourcePackagesDirPath .spm-cache \
-derivedDataPath DerivedData \
| xcbeautify
- name: Run unit tests
run: |
xcodebuild test \
-scheme "$SCHEME" \
-destination "$DESTINATION" \
-clonedSourcePackagesDirPath .spm-cache \
-derivedDataPath DerivedData \
-resultBundlePath TestResults.xcresult \
| xcbeautify
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results
path: TestResults.xcresult
Ajouter la signature de code pour les builds de release
Pour les déploiements TestFlight ou App Store, ajoutez des étapes de signature de code. Stockez vos certificats et profils de provisionnement en tant que GitHub Secrets :
deploy:
needs: build
runs-on: [self-hosted, macOS, ARM64, M4]
if: github.ref == 'refs/heads/main'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install certificate and provisioning profile
env:
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
run: |
# Create a temporary keychain
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
# Import certificate
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH
security import $CERTIFICATE_PATH -P "$P12_PASSWORD" \
-A -t cert -f pkcs12 -k $KEYCHAIN_PATH
security list-keychain -d user -s $KEYCHAIN_PATH
# Install provisioning profile
PP_PATH=$RUNNER_TEMP/build_pp.mobileprovision
echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode -o $PP_PATH
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles
- name: Build for distribution
run: |
xcodebuild archive \
-scheme "MyApp" \
-archivePath DerivedData/MyApp.xcarchive \
-destination "generic/platform=iOS" \
CODE_SIGN_STYLE=Manual
- name: Export IPA
run: |
xcodebuild -exportArchive \
-archivePath DerivedData/MyApp.xcarchive \
-exportOptionsPlist ExportOptions.plist \
-exportPath DerivedData/Export
- name: Upload to TestFlight
env:
APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }}
run: |
xcrun altool --upload-app \
-f DerivedData/Export/MyApp.ipa \
-t ios \
--apiKey $APP_STORE_CONNECT_API_KEY
6. Étape 4 : Optimiser les performances
L'un des plus grands avantages d'un runner auto-hébergé est le cache persistant. Voici les optimisations clés pour tirer le meilleur parti de votre Mac Mini M4.
Activer le cache DerivedData
Puisque le runner est persistant, DerivedData est préservé entre les builds. Utilisez un chemin DerivedData cohérent :
# In your workflow, always use:
-derivedDataPath DerivedData
# On the runner, periodically clean old DerivedData to save space:
# Add a cron job to clean builds older than 7 days
echo "0 3 * * 0 find ~/actions-runner/_work/*/DerivedData -maxdepth 0 -mtime +7 -exec rm -rf {} +" \
| crontab -
Mettre en cache les paquets SPM
# Use clonedSourcePackagesDirPath to keep SPM packages on disk
xcodebuild build \
-scheme "MyApp" \
-clonedSourcePackagesDirPath ~/spm-cache \
-derivedDataPath DerivedData
# This avoids re-downloading packages on every build
Exécution parallèle des tests
# Run tests in parallel across multiple simulators
xcodebuild test \
-scheme "MyApp" \
-destination "platform=iOS Simulator,name=iPhone 16 Pro,OS=18.2" \
-destination "platform=iOS Simulator,name=iPhone 15,OS=17.5" \
-parallel-testing-enabled YES \
-maximum-parallel-testing-workers 4 \
-derivedDataPath DerivedData \
| xcbeautify
Installer xcbeautify pour de meilleurs logs
# xcbeautify formats Xcode output for CI environments
brew install xcbeautify
# Use it by piping xcodebuild output:
xcodebuild build -scheme "MyApp" | xcbeautify
7. Résolution des problèmes courants
Le runner apparaît "Hors ligne" dans GitHub
Cela signifie généralement que le service launchd n'est pas en cours d'exécution. Vérifiez l'état du service et les journaux :
# Check service status
sudo ./svc.sh status
# View logs
cat ~/actions-runner/_diag/Runner_*.log | tail -50
# Restart the service
sudo ./svc.sh stop
sudo ./svc.sh start
La signature de code échoue avec "No signing certificate"
Le service launchd s'exécute sous un contexte utilisateur différent. Assurez-vous que le trousseau est accessible :
# Ensure the login keychain is unlocked for the runner user
security unlock-keychain -p "YOUR_PASSWORD" ~/Library/Keychains/login.keychain-db
# Or use a dedicated keychain in your workflow (recommended)
security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
Le simulateur ne démarre pas
Les simulateurs restent parfois bloqués. Réinitialisez-les entre les builds :
# Shutdown all running simulators
xcrun simctl shutdown all
# Erase all simulator data (nuclear option)
xcrun simctl erase all
# Boot a specific simulator
xcrun simctl boot "iPhone 16 Pro"
Espace disque insuffisant
Les builds Xcode génèrent beaucoup de données. Configurez un nettoyage automatique :
# Clean old DerivedData
rm -rf ~/Library/Developer/Xcode/DerivedData/*
# Remove old simulator runtimes
xcrun simctl runtime delete all
# Clean Homebrew cache
brew cleanup --prune=7
# Remove old Xcode archives
rm -rf ~/Library/Developer/Xcode/Archives/*
8. Analyse des coûts
Voici une comparaison détaillée des coûts pour différentes tailles d'équipe et volumes de builds :
| Taille de l'équipe | Builds/mois | Coût runner GitHub hébergé | Coût My Remote Mac | Économies mensuelles |
|---|---|---|---|---|
| Développeur solo | 100 builds (10 min moy.) | 80 $/mois | 75 $/mois | 5 $/mois |
| Petite équipe (5) | 500 builds (10 min moy.) | 400 $/mois | 75 $/mois | 325 $/mois |
| Équipe moyenne (15) | 1500 builds (10 min moy.) | 1 200 $/mois | 179 $/mois (M4 Pro) | 1 021 $/mois |
| Entreprise (50+) | 5000+ builds | 4 000+ $/mois | 358 $/mois (2x M4 Pro) | 3 642+ $/mois |
En résumé : Pour les équipes effectuant plus de ~100 builds par mois, un Mac Mini M4 auto-hébergé est immédiatement rentable. Et comme les builds sont plus rapides sur du matériel persistant avec des caches préchauffés, votre équipe gagne aussi du temps de développement.
Guides connexes
Agent de build Jenkins Mac
Configurez des agents de build Jenkins sur un Mac Mini dédié pour le CI/CD iOS.
Fastlane + serveur Mac dédié
Automatisez les builds iOS, les captures d'écran et le déploiement TestFlight avec Fastlane.
Runner GitLab CI Mac
Enregistrez un runner GitLab sur un Mac Mini dédié pour les pipelines CI iOS/macOS.