Введение в CI/CD для Swift проектов
Современная разработка мобильных и десктопных приложений на Swift требует не только качественного кода, но и надежного процесса интеграции и доставки. CI/CD (Continuous Integration / Continuous Deployment) — это практика, которая позволяет автоматизировать сборку, тестирование и развертывание приложений. Для Swift проектов, особенно под iOS и macOS, GitHub Actions является одним из самых удобных и популярных инструментов.
В этой статье мы разберем, как настроить полноценный CI/CD пайплайн для Swift проектов с использованием GitHub Actions. Вы узнаете, как автоматизировать сборку, запускать юнит-тесты и даже отправлять приложение в TestFlight или на Mac App Store.
Почему GitHub Actions для Swift?
GitHub Actions предоставляет встроенные возможности для работы с экосистемой Apple. В отличие от сторонних сервисов, он полностью интегрирован с репозиторием и не требует дополнительной настройки плагинов. Основные преимущества:
- Бесплатные минуты для публичных репозиториев (до 2000 минут в месяц)
- Готовые экшены для Xcode, SwiftLint, CocoaPods и SPM
- Поддержка macOS раннеров с предустановленным Xcode
- Гибкая настройка через YAML-файлы
Базовая настройка CI для Swift проекта
Начнем с простого пайплайна, который будет собирать проект и запускать тесты при каждом пуше в ветку main или при создании Pull Request.
Создайте файл .github/workflows/swift-ci.yml в корне репозитория:
name: Swift CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build-and-test:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Select Xcode version
run: sudo xcode-select -s /Applications/Xcode_15.2.app
- name: Build
run: xcodebuild build -scheme MyApp -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15,OS=17.2' | xcpretty
- name: Run tests
run: xcodebuild test -scheme MyApp -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15,OS=17.2' | xcpretty
Этот workflow выполняет два основных шага: сборку проекта и запуск тестов. Обратите внимание на использование xcpretty — утилиты, которая форматирует вывод Xcodebuild в читаемый вид.
Дополнительная проверка качества кода
Добавим статический анализ с помощью SwiftLint. Это поможет поддерживать единый стиль кода в команде:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: SwiftLint
uses: norio-nomura/action-swiftlint@3.2.1
with:
args: --strict
Теперь при каждом коммите будет проверяться соответствие кода правилам SwiftLint. Если найдутся нарушения, workflow завершится с ошибкой.
Продвинутый CI/CD: тестирование и деплой
После базовой настройки можно перейти к более сложным сценариям. Рассмотрим пайплайн, который включает:
- Сборку для нескольких симуляторов
- Тестирование с покрытием кода
- Автоматическую публикацию в TestFlight (для iOS) или Mac App Store
Сборка и тестирование с покрытием
Для сбора данных о покрытии тестами используйте флаг -enableCodeCoverage YES:
jobs:
test-coverage:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Test with coverage
run: |
xcodebuild test \
-scheme MyApp \
-sdk iphonesimulator \
-destination 'platform=iOS Simulator,name=iPhone 15,OS=17.2' \
-enableCodeCoverage YES \
-derivedDataPath Build/DerivedData | xcpretty
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
directory: Build/DerivedData/Build/ProfileData
Автоматический деплой в TestFlight
Для публикации в TestFlight потребуется настроить App Store Connect API и сохранить ключи в секретах GitHub:
jobs:
deploy-testflight:
runs-on: macos-latest
if: github.ref == 'refs/heads/release'
steps:
- uses: actions/checkout@v4
- name: Install provisioning profiles
run: |
echo "${{ secrets.APPSTORE_CONNECT_KEY }}" > /tmp/AuthKey.p8
xcodebuild -exportArchive \
-archivePath Build/MyApp.xcarchive \
-exportPath Build/export \
-exportOptionsPlist ExportOptions.plist
- name: Upload to TestFlight
env:
APP_STORE_CONNECT_USERNAME: ${{ secrets.APPSTORE_USERNAME }}
APP_STORE_CONNECT_PASSWORD: ${{ secrets.APPSTORE_PASSWORD }}
run: |
xcrun altool --upload-app \
-f Build/export/MyApp.ipa \
-u "$APP_STORE_CONNECT_USERNAME" \
-p "$APP_STORE_CONNECT_PASSWORD" \
--type ios
Важно: Никогда не храните пароли и ключи в коде. Используйте GitHub Secrets (Settings → Secrets and variables → Actions).
Оптимизация и лучшие практики
Чтобы CI/CD работал быстро и надежно, следуйте этим рекомендациям:
- Кэширование зависимостей: сохраняйте SPM-пакеты и Pods между запусками
- Параллельные джобы: разделите линтинг, тестирование и сборку на разные jobs
- Матрица тестирования: запускайте тесты на нескольких версиях iOS/macOS одновременно
Пример кэширования SPM
steps:
- uses: actions/cache@v3
with:
path: .build
key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }}
restore-keys: |
${{ runner.os }}-spm-
- name: Resolve SPM packages
run: xcodebuild -resolvePackageDependencies -scheme MyApp
Матрица для тестирования на разных устройствах
jobs:
matrix-test:
strategy:
matrix:
device: ['iPhone 15', 'iPad Pro (12.9-inch) (6th generation)']
os: ['17.2', '16.4']