Кожен мобільний розробник знає: ручне збирання та публікація забирає до 5 годин на тиждень. На великих проєктах помилка при підписі коду може затримати реліз на добу. GitHub Actions автоматизує цей конвеєр: після пуша код тестується, збирається, підписується та публікується без участі людини. Для iOS обов'язковий macOS-раннер (наприклад, macos-14 на Apple Silicon), для Android підходить звичайний Linux. Безкоштовні хвилини macOS тарифікуються в 10x — на активних проєктах ліміт закінчується швидко. Self-hosted Mac mini в офісі вирішує проблему вартості, але потребує адміністрування. На одному з проєктів ми скоротили час релізу з 3 годин до 30 хвилин після впровадження описаного нижче пайплайну.
Чому CI/CD критичний для мобільних застосунків?
Без CI/CD кожен реліз перетворюється на ризик: помилки підпису, пропущені тести, невірні конфіги. Автоматизація гарантує, що кожен пуш відповідає стандартам якості. Після впровадження CI/CD команда може випускати оновлення щодня, а тестування стає невід'ємною частиною процесу.
iOS: чому підпис коду — головний біль?
GitHub надає безкоштовні macOS-раннери (macos-14, Apple Silicon). Хвилини macOS тарифікуються в 10x від Linux — при активній розробці безкоштовний ліміт закінчується швидко. Self-hosted macOS-раннер на Mac Mini в офісі вирішує проблему вартості, але додає адміністрування.
Підпис коду на GitHub Actions — через fastlane match або імпорт сертифіката з secrets:
---
- name: Import certificate
run: |
echo "${{ secrets.DISTRIBUTION_CERTIFICATE_P12 }}" | base64 --decode > cert.p12
security create-keychain -p "${{ secrets.KEYCHAIN_PASSWORD }}" build.keychain
security import cert.p12 -k build.keychain -P "${{ secrets.CERT_PASSWORD }}" -T /usr/bin/codesign
security set-keychain-settings -lut 21600 build.keychain
security unlock-keychain -p "${{ secrets.KEYCHAIN_PASSWORD }}" build.keychain
security list-keychains -d user -s build.keychain login.keychain
"}
Note: The original had "run: |" and then the commands. I split each command onto its own line, preserving the exact text. The YAML structure is: top-level "---", then a list item with "name" and "run". The "run" is a literal block scalar indicated by "|", so the commands are indented under it. I used 2 spaces for the list item indentation and 4 spaces for the block content (standard YAML). The original had no indentation after "run: |", but I added proper indentation for the block. The content of each command is unchanged. The final newline after the last command is included. The JSON string contains real newline characters.<ï½endâofâthinkingï½>{"code": "---
- name: Import certificate
run: |
echo "${{ secrets.DISTRIBUTION_CERTIFICATE_P12 }}" | base64 --decode > cert.p12
security create-keychain -p "${{ secrets.KEYCHAIN_PASSWORD }}" build.keychain
security import cert.p12 -k build.keychain -P "${{ secrets.CERT_PASSWORD }}" -T /usr/bin/codesign
security set-keychain-settings -lut 21600 build.keychain
security unlock-keychain -p "${{ secrets.KEYCHAIN_PASSWORD }}" build.keychain
security list-keychains -d user -s build.keychain login.keychain
Це ручний підхід — працює, але крихкий при оновленні сертифіката. У продакшені краще fastlane match readonly: true з MATCH_PASSWORD в secrets. Часті помилки: сертифікат закінчився (оновлюйте provisioning profile раз на рік), пароль keychain не передано в secrets, несумісність версій fastlane (фіксуйте в Gemfile.lock).
Як налаштувати підпис коду на GitHub Actions?
Використовуйте fastlane match з окремим репозиторієм для сертифікатів. Додайте в secrets змінні MATCH_PASSWORD, MATCH_GIT_BASIC_AUTHORIZATION та вкажіть readonly: true. Workflow імпортує профілі автоматично.
Повний workflow для iOS
---
name: iOS CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- name: Select Xcode
run: sudo xcode-select -s /Applications/Xcode_16.0.app
- name: Cache CocoaPods
uses: actions/cache@v4
with:
path: Pods
key: ${{ runner.os }}-pods-${{ hashFiles('Podfile.lock') }}
- name: Install pods
run: bundle exec pod install
- name: Run tests
run: |
bundle exec fastlane scan \
--scheme "MyApp" \
--device "iPhone 16" \
--code-coverage true \
--output-files "test-results.xml"
- name: Upload test results
uses: actions/upload-artifact@v4
with:
name: test-results
path: test-results.xml
if-no-files-found: error
deploy-beta:
needs: test
runs-on: macos-14
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Deploy to TestFlight
env:
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
APP_STORE_CONNECT_API_KEY: ${{ secrets.ASC_API_KEY }}
run: bundle exec fastlane release
needs: test — deploy-beta запускається тільки якщо тести пройшли. if: github.ref == 'refs/heads/main' — деплой тільки з main.
Android: як спростити пайплайн?
---
jobs:
android-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Cache Gradle
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
- name: Build and test
run: ./gradlew test assembleRelease
- name: Sign APK
uses: r0adkll/sign-android-release@v1
with:
releaseDirectory: app/build/outputs/apk/release
signingKeyBase64: ${{ secrets.SIGNING_KEY }}
alias: ${{ secrets.KEY_ALIAS }}
keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}
keyPassword: ${{ secrets.KEY_PASSWORD }}
- name: Upload to Firebase App Distribution
uses: wzieba/Firebase-Distribution-Github-Action@v1
with:
appId: ${{ secrets.FIREBASE_APP_ID }}
token: ${{ secrets.FIREBASE_TOKEN }}
groups: qa-team
file: app/build/outputs/apk/release/app-release-signed.apk
Linux-раннер для Android — безкоштовно без обмеження хвилин (на публічних репо). Gradle-кеш економить 3–5 хвилин на прогоні.
Як прискорити збірку?
Кешування залежностей — основний спосіб. Для iOS використовуйте actions/cache для Pods або SPM. Для Android — для Gradle cache. Також можна обмежити кроки лише потрібними платформами та запускати тести паралельно.
Як протестувати застосунок на декількох пристроях?
---
strategy:
matrix:
device: ["iPhone 15", "iPhone SE (3rd generation)", "iPad Pro (12.9-inch)"]
jobs:
test:
runs-on: macos-14
steps:
- name: Run tests on ${{ matrix.device }}
run: xcodebuild test -scheme MyApp -destination "platform=iOS Simulator,name=${{ matrix.device }}"
Паралельно запускає тести на трьох пристроях — сумарний час не збільшується, покриття розширюється.
Що дає матричне тестування?
Матриця пристроїв дозволяє виявити баги, специфічні для конкретних моделей та версій iOS. Без неї ви ризикуєте пропустити помилки, які проявляються лише на старих девайсах. Паралельні джоби з матрицею не сповільнюють пайплайн — всі вони виконуються одночасно.
Таблиця: порівняння CI/CD для iOS та Android
| Параметр | iOS | Android |
|---|---|---|
| Раннер | macOS (macos-14) | Linux (ubuntu-latest) |
| Підпис | code signing (fastlane match) | Signing APK (keystore) |
| Деплой | TestFlight / App Store | Google Play / Firebase |
| Кешування | CocoaPods / SPM | Gradle cache |
| Тести | XCTest | JUnit / Espresso |
Додаткова таблиця: приблизний час виконання
| Етап | iOS | Android |
|---|---|---|
| Збірка | 15–25 хв | 10–20 хв |
| Тести | 10–15 хв | 5–10 хв |
| Підпис | 2–5 хв | 1–2 хв |
| Деплой | 5–10 хв | 3–7 хв |
Що входить у налаштування CI/CD
Наша команда підготує:
- Робочі workflows для iOS та Android
- Налаштування підпису коду з fastlane match
- Кешування залежностей для прискорення збірок
- Матричне тестування на декількох пристроях
- Інтеграцію з TestFlight, Google Play або Firebase
- Детальну документацію з підтримки
- Навчання вашої команди (1–2 години)
Наш досвід: 8+ років у мобільній розробці, понад 20 впроваджених CI/CD рішень. Ми знаємо, як обійти типові помилки та прискорити пайплайн до максимуму. Документація GitHub Actions та fastlane match допоможуть заглибитися в деталі.
Строки та вартість
Базові workflows (test + build) для однієї платформи — 3–5 днів. Повна конфігурація з підписом коду, матрицею пристроїв, кешуванням, деплоєм: 1–2 тижні. Вартість розраховується індивідуально — ми підбираємо рішення під ваш проєкт та бюджет.
Хочете автоматизувати релізи? Зв'яжіться з нами для консультації. Замовте впровадження пайплайну та звільніть час розробників.







