1. Зачем использовать self-hosted Mac-раннер?
GitHub-hosted macOS раннеры удобны, но дороги. При стоимости $0.08 в минуту команда, использующая 75 часов сборок в месяц, платит около $360. Выделенный Mac Mini M4 от MyRemoteMac стоит $75/месяц с неограниченным временем сборки, что даёт такое же (или лучшее) оборудование за долю стоимости.
| Характеристика | GitHub-Hosted раннер | Self-hosted MyRemoteMac |
|---|---|---|
| Стоимость | $0.08/мин (~$350/мес за 75 ч) | $75/мес (неограниченные минуты) |
| Архитектура | Intel x86 (некоторые M1) | Apple M4 (последняя модель) |
| Скорость сборки | ~12 мин (средний проект) | ~4 мин (тот же проект) |
| Постоянный кеш | Нет (эфемерный) | Да (постоянный диск) |
| Пользовательское ПО | Ограничено | Полный root-доступ |
| Параллельные задачи | 5 (бесплатно) / 20 (платно) | Неограниченно (ваше оборудование) |
Ключевое преимущество: Поскольку раннер постоянный, DerivedData, кеши SPM и CocoaPods сохраняются между сборками. Одно это может сократить время сборки на 50–70% по сравнению с эфемерными GitHub-hosted раннерами, которые каждый раз начинают с нуля.
2. Предварительные требования
Прежде чем начать, убедитесь, что у вас есть следующее:
- Сервер Mac Mini M4 от MyRemoteMac (от $75/мес)
- Аккаунт GitHub с правами администратора вашего репозитория или организации
- SSH-доступ к вашему Mac Mini (предоставляется с подпиской MyRemoteMac)
- Аккаунт Apple Developer (для подписи кода и профилей обеспечения)
- Базовое знакомство с YAML и командами терминала
3. Шаг 1: Подключение к Mac Mini по SSH и установка Xcode
Сначала подключитесь к Mac Mini M4 через SSH. Вы получили учётные данные при настройке сервера MyRemoteMac.
Подключение через 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
Установка Xcode Command Line Tools
# Install Command Line Tools
xcode-select --install
# Accept the license agreement
sudo xcodebuild -license accept
# Verify installation
xcode-select -p
# /Library/Developer/CommandLineTools
Установка Xcode (полная версия)
Для сборок iOS вам нужно полное приложение Xcode. Самый быстрый способ установить его на безголовом сервере — использовать 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
Установка 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. Шаг 2: Установка раннера GitHub Actions
Теперь скачаем и настроим агент раннера GitHub Actions. Перейдите в ваш репозиторий на GitHub, откройте Settings > Actions > Runners > New self-hosted runner и выберите macOS + ARM64.
Скачивание и настройка
# 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
Установка как постоянный сервис launchd
Запуск агента в интерактивном режиме подходит для тестирования, но для продакшена нужен автозапуск при загрузке и перезапуск при сбое. GitHub предоставляет встроенный скрипт установки сервиса для 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
Теперь сервис будет автоматически запускаться при загрузке и перезапускаться при аварийном завершении процесса. Вы можете убедиться, что раннер отображается как "Idle" на странице Settings > Actions > Runners вашего GitHub репозитория.
Настройка раннера для нескольких репозиториев (уровень организации)
# 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. Шаг 3: Создание рабочего процесса сборки iOS
Создайте файл рабочего процесса в вашем репозитории по пути .github/workflows/ios-build.yml. Этот рабочий процесс будет выполняться на вашем self-hosted раннере Mac Mini M4.
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
Добавление подписи кода для релизных сборок
Для деплоя в TestFlight или App Store добавьте шаги подписи кода. Храните сертификаты и профили обеспечения как 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. Шаг 4: Оптимизация производительности
Одно из главных преимуществ self-hosted раннера — постоянное кеширование. Вот ключевые оптимизации для максимальной отдачи от вашего Mac Mini M4.
Включение кеширования DerivedData
Поскольку раннер постоянный, DerivedData сохраняется между сборками. Используйте постоянный путь DerivedData:
# 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 -
Кеширование 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
Параллельное выполнение тестов
# 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
Установка xcbeautify для улучшенных логов
# xcbeautify formats Xcode output for CI environments
brew install xcbeautify
# Use it by piping xcodebuild output:
xcodebuild build -scheme "MyApp" | xcbeautify
7. Устранение типичных проблем
Раннер отображается как "Offline" в GitHub
Обычно это означает, что сервис launchd не запущен. Проверьте статус сервиса и логи:
# 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
Подпись кода не работает — "No signing certificate"
Сервис launchd работает под другим контекстом пользователя. Убедитесь, что Keychain доступен:
# 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
Симулятор не запускается
Симуляторы иногда зависают. Сбрасывайте их между сборками:
# 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"
Заканчивается дисковое пространство
Сборки Xcode генерируют много данных. Настройте автоматическую очистку:
# 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. Анализ стоимости
Вот детальное сравнение стоимости для разных размеров команд и объёмов сборок:
| Размер команды | Сборок/месяц | Стоимость GitHub-Hosted | Стоимость MyRemoteMac | Ежемесячная экономия |
|---|---|---|---|---|
| Один разработчик | 100 сборок (в среднем 10 мин) | $80/мес | $75/мес | $5/мес |
| Малая команда (5) | 500 сборок (в среднем 10 мин) | $400/мес | $75/мес | $325/мес |
| Средняя команда (15) | 1500 сборок (в среднем 10 мин) | $1,200/мес | $179/мес (M4 Pro) | $1,021/мес |
| Корпоративная (50+) | 5000+ сборок | $4,000+/мес | $358/мес (2x M4 Pro) | $3,642+/мес |
Итог: Для команд, выполняющих более ~100 сборок в месяц, self-hosted Mac Mini M4 окупается мгновенно. А поскольку сборки быстрее на постоянном оборудовании с тёплыми кешами, ваша команда экономит и время разработчиков.
Похожие руководства
Агент сборки Jenkins на Mac
Настройте агенты сборки Jenkins на выделенном Mac Mini для iOS CI/CD.
Fastlane + выделенный Mac-сервер
Автоматизируйте сборки iOS, скриншоты и деплой в TestFlight с помощью Fastlane.
Раннер GitLab CI на Mac
Зарегистрируйте GitLab Runner на выделенном Mac Mini для iOS/macOS CI пайплайнов.