1. Warum Self-Hosted GitLab Runner auf Mac?
GitLab bietet Shared Runner unter Linux, aber das Bauen von iOS-Apps erfordert macOS auf Apple-Hardware. GitLabs eigene macOS-Shared-Runner sind eingeschränkt und teuer. Ein Self-Hosted Mac Mini M4 Runner bietet Ihnen:
GitLabs kostenlose Stufe umfasst 400 CI/CD-Minuten auf Shared Runnern. Auf Self-Hosted gibt es kein Limit.
Bauen Sie auf demselben M4-Chip, auf dem die Geräte Ihrer Nutzer laufen. Kein Rosetta-Übersetzungsaufwand.
Installieren Sie jede Xcode-Version, Simulatoren, Tools und Abhängigkeiten, die Sie benötigen.
DerivedData-, SPM-Pakete- und CocoaPods-Caches bleiben zwischen Pipeline-Durchläufen erhalten.
2. GitLab Runner installieren
Verbinden Sie sich per SSH mit Ihrem Mac Mini M4 und installieren Sie den GitLab Runner mit Homebrew:
# Connect to your Mac Mini M4
ssh admin@your-server-ip
# 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 GitLab Runner
brew install gitlab-runner
# Verify installation
gitlab-runner --version
# Version: 17.7.0
# Git revision: ...
# Git branch: 17-7-stable
# GO version: go1.22.10
# Built: ...
# OS/Arch: darwin/arm64
Als macOS-Dienst installieren
# Install the runner as a launchd service
# This ensures it starts automatically on boot
brew services start gitlab-runner
# Verify the service is running
brew services list | grep gitlab-runner
# gitlab-runner started admin ~/Library/LaunchAgents/homebrew.mxcl.gitlab-runner.plist
# Check runner status
gitlab-runner status
# gitlab-runner: Service is running
3. Den Runner registrieren
Gehen Sie zu Ihrem GitLab-Projekt (oder Ihrer Gruppe) und navigieren Sie zu Settings > CI/CD > Runners > New project runner. Kopieren Sie das Registrierungstoken.
Registrierung mit dem neuen Runner-Registrierungsablauf (GitLab 16+)
# Register the runner using the authentication token from GitLab UI
# (GitLab 16+ uses authentication tokens instead of registration tokens)
gitlab-runner register \
--non-interactive \
--url "https://gitlab.com/" \
--token "YOUR_RUNNER_AUTHENTICATION_TOKEN" \
--executor "shell" \
--description "mac-mini-m4-runner" \
--tag-list "macos,apple-silicon,m4,ios,xcode"
Registrierung mit Legacy-Registrierungstoken (GitLab 15 und älter)
# For older GitLab instances using registration tokens
gitlab-runner register \
--non-interactive \
--url "https://gitlab.com/" \
--registration-token "YOUR_REGISTRATION_TOKEN" \
--executor "shell" \
--description "mac-mini-m4-runner" \
--tag-list "macos,apple-silicon,m4,ios,xcode" \
--run-untagged="false"
Runner-Konfiguration überprüfen
# View the runner config file
cat ~/.gitlab-runner/config.toml
# Expected output:
# concurrent = 2
# check_interval = 0
#
# [session_server]
# session_timeout = 1800
#
# [[runners]]
# name = "mac-mini-m4-runner"
# url = "https://gitlab.com/"
# token = "..."
# executor = "shell"
# [runners.cache]
# MaxUploadedArchiveSize = 0
# Adjust concurrency based on your hardware:
# Mac Mini M4 (16GB): concurrent = 2
# Mac Mini M4 Pro (24GB): concurrent = 3
# Mac Mini M4 Pro (48GB): concurrent = 4
Bearbeiten Sie ~/.gitlab-runner/config.toml, um die Parallelität anzupassen:
# Edit the config
nano ~/.gitlab-runner/config.toml
# Set concurrent to match your hardware capacity
concurrent = 2
# Restart the runner to apply changes
gitlab-runner restart
Der Runner sollte nun als Online in Ihrem GitLab-Projekt unter Settings > CI/CD > Runners erscheinen.
4. .gitlab-ci.yml für iOS erstellen
Erstellen Sie eine .gitlab-ci.yml-Datei im Stammverzeichnis Ihres Repositorys. Diese vollständige Pipeline baut, testet und deployt Ihre iOS-App:
# .gitlab-ci.yml
stages:
- setup
- build
- test
- deploy
variables:
SCHEME: "MyApp"
WORKSPACE: "MyApp.xcworkspace"
DESTINATION: "platform=iOS Simulator,name=iPhone 16 Pro,OS=18.2"
DERIVED_DATA: "${CI_PROJECT_DIR}/DerivedData"
# Only run on our Mac runner
default:
tags:
- macos
- m4
# ---- SETUP ----
setup:
stage: setup
script:
- sudo xcode-select -s /Applications/Xcode-16.2.app/Contents/Developer
- xcodebuild -version
- swift --version
# Install CocoaPods if using Podfile
- |
if [ -f "Podfile" ]; then
pod install --repo-update
fi
cache:
key: pods-${CI_COMMIT_REF_SLUG}
paths:
- Pods/
- .spm-cache/
# ---- BUILD ----
build:
stage: build
needs: ["setup"]
script:
- |
xcodebuild build \
-workspace "${WORKSPACE}" \
-scheme "${SCHEME}" \
-destination "${DESTINATION}" \
-derivedDataPath "${DERIVED_DATA}" \
-clonedSourcePackagesDirPath ".spm-cache" \
CODE_SIGNING_ALLOWED=NO \
| xcbeautify
cache:
key: derived-data-${CI_COMMIT_REF_SLUG}
paths:
- DerivedData/
- .spm-cache/
artifacts:
paths:
- DerivedData/
expire_in: 1 hour
# ---- TEST ----
unit_tests:
stage: test
needs: ["build"]
script:
- |
xcodebuild test \
-workspace "${WORKSPACE}" \
-scheme "${SCHEME}" \
-destination "${DESTINATION}" \
-derivedDataPath "${DERIVED_DATA}" \
-resultBundlePath "TestResults.xcresult" \
-parallel-testing-enabled YES \
| xcbeautify
artifacts:
when: always
paths:
- TestResults.xcresult/
reports:
junit: TestResults.xcresult/report.junit
expire_in: 7 days
after_script:
- xcrun simctl shutdown all 2>/dev/null || true
# ---- DEPLOY ----
deploy_testflight:
stage: deploy
needs: ["unit_tests"]
only:
- main
script:
- |
# Install or update Fastlane
which fastlane || brew install fastlane
# Run Fastlane beta lane
fastlane beta
environment:
name: testflight
variables:
MATCH_PASSWORD: ${MATCH_PASSWORD}
APP_STORE_CONNECT_API_KEY_ID: ${APP_STORE_KEY_ID}
APP_STORE_CONNECT_API_ISSUER_ID: ${APP_STORE_ISSUER_ID}
APP_STORE_CONNECT_API_KEY_CONTENT: ${APP_STORE_KEY_CONTENT}
CI/CD-Variablen in GitLab hinzufügen
Navigieren Sie zu Settings > CI/CD > Variables in Ihrem GitLab-Projekt und fügen Sie diese Variablen als "Masked" und "Protected" hinzu:
-
MATCH_PASSWORD- Passwort für die Fastlane Match-Verschlüsselung -
APP_STORE_KEY_ID- App Store Connect API Key ID -
APP_STORE_ISSUER_ID- App Store Connect Issuer ID -
APP_STORE_KEY_CONTENT- Der Inhalt der .p8-Schlüsseldatei
5. Performance optimieren
GitLab Cache-Konfiguration
GitLab Runner unterstützt lokales Caching für Shell-Executors. Da Ihr Runner persistent ist, sind lokale Caches extrem effizient:
# In .gitlab-ci.yml, configure cache per branch:
cache:
key: "${CI_COMMIT_REF_SLUG}"
paths:
- DerivedData/
- .spm-cache/
- Pods/
policy: pull-push
# For test jobs that don't modify cache, use pull-only:
unit_tests:
cache:
key: "${CI_COMMIT_REF_SLUG}"
paths:
- DerivedData/
policy: pull
Artefakte für stufenübergreifende Daten verwenden
# Pass build artifacts between stages efficiently
build:
artifacts:
paths:
- DerivedData/Build/Products/
expire_in: 2 hours
# The test stage receives the built products without rebuilding
test:
needs: ["build"] # only download artifacts from the build job
script:
- xcodebuild test-without-building \
-scheme "${SCHEME}" \
-destination "${DESTINATION}" \
-derivedDataPath "${DERIVED_DATA}"
Parallele Testausführung
# Split tests across parallel jobs using GitLab's parallel keyword
unit_tests:
stage: test
parallel: 2
script:
- |
# Use test plan partitioning or custom splitting
xcodebuild test \
-workspace "${WORKSPACE}" \
-scheme "${SCHEME}" \
-destination "${DESTINATION}" \
-derivedDataPath "${DERIVED_DATA}" \
-parallel-testing-enabled YES \
-maximum-parallel-testing-workers 4
Geplante Cache-Bereinigung
# On the Mac Mini, set up a weekly cleanup cron job
crontab -e
# Add these lines:
# Clean DerivedData older than 7 days every Sunday at 3 AM
0 3 * * 0 find ~/builds/*/DerivedData -maxdepth 0 -mtime +7 -exec rm -rf {} + 2>/dev/null
# Clean old GitLab Runner builds older than 14 days
0 4 * * 0 find ~/builds -maxdepth 2 -mtime +14 -type d -exec rm -rf {} + 2>/dev/null
# Clean Homebrew cache monthly
0 5 1 * * /opt/homebrew/bin/brew cleanup --prune=30 2>/dev/null
6. Fehlerbehebung
Runner wird in GitLab als "offline" angezeigt
Überprüfen Sie den Status und die Logs des Runner-Dienstes:
# Check service status
brew services list | grep gitlab-runner
# View logs
cat /usr/local/var/log/gitlab-runner.log
# Restart the service
brew services restart gitlab-runner
# Verify connectivity to GitLab
gitlab-runner verify
Fehler "Permission denied" während der Builds
Der Runner benötigt möglicherweise Zugriff auf das Xcode-Entwicklerverzeichnis:
# Ensure the runner user has Xcode access
sudo xcode-select -s /Applications/Xcode-16.2.app/Contents/Developer
sudo xcodebuild -license accept
# If using simulators, ensure the user can access them
xcrun simctl list devices
Cache wird nicht wiederhergestellt
Stellen Sie sicher, dass die Cache-Schlüssel konsistent sind und die Pfade existieren:
# Check cache directory permissions
ls -la ~/builds/
# The shell executor stores caches locally by default
# Verify the cache directory in config.toml:
cat ~/.gitlab-runner/config.toml
# Ensure [runners.cache] section has the right settings
# For local caching (most efficient for persistent runners):
# [runners.cache]
# Type = "" # empty = local cache
Xcode-Build hängt oder läuft in eine Zeitüberschreitung
Dies wird oft durch Schlüsselbund-Zugriffsaufforderungen oder Simulatorprobleme verursacht:
# Unlock the keychain before builds
security unlock-keychain -p "YOUR_PASSWORD" ~/Library/Keychains/login.keychain-db
# Kill stuck simulators
xcrun simctl shutdown all
pkill -f "Simulator.app" 2>/dev/null || true
# Set a build timeout in .gitlab-ci.yml
build:
timeout: 30 minutes
7. FAQ
Kann ich einen Docker-Executor unter macOS verwenden?
Docker unter macOS führt Linux-Container in einer VM aus, die nicht auf macOS-APIs, Xcode oder iOS-Simulatoren zugreifen kann. Für iOS-Builds müssen Sie den Shell-Executor verwenden. Docker eignet sich für serverseitiges Swift oder andere Linux-basierte Aufgaben, die neben Ihrem Mac-Runner laufen.
Wie registriere ich den Runner für eine GitLab-Gruppe?
Gehen Sie zu Settings > CI/CD > Runners > New group runner Ihrer GitLab-Gruppe. Verwenden Sie das Gruppen-Runner-Token anstelle eines Projekt-Tokens. So wird der Runner für alle Projekte innerhalb der Gruppe verfügbar.
Sollte ich den Shell-Executor oder den SSH-Executor verwenden?
Verwenden Sie den Shell-Executor. Er führt Befehle direkt auf dem Mac aus, was vollen Zugriff auf Xcode, Simulatoren und den Schlüsselbund gibt. Der SSH-Executor ist für Remote-Maschinen gedacht, was unnötig ist, wenn der Runner bereits auf dem Mac läuft.
Kann ich sowohl GitLab- als auch GitHub Actions-Runner auf demselben Mac ausführen?
Ja. Beide Runner sind leichtgewichtig und können auf demselben Mac Mini M4 koexistieren. Achten Sie nur darauf, den kombinierten Ressourcenverbrauch zu berücksichtigen, wenn Sie die Parallelitätsstufen für jeden Runner festlegen.
Wie aktualisiere ich den GitLab Runner?
# Update via Homebrew
brew upgrade gitlab-runner
# Restart the service
brew services restart gitlab-runner
# Verify the new version
gitlab-runner --version
Suchen Sie einen vollständig verwalteten GitLab Runner? Testen Sie Cloud-Runner
Überspringen Sie die Einrichtung komplett. Cloud-Runner bietet vorkonfigurierte, dedizierte GitLab Runner auf Mac-Hardware — keine Installation oder Wartung erforderlich.