Irbisa · Шпаргалка
CI/CD мобилки
Fastlane, GitHub Actions, подпись, TestFlight, Play Internal, build flavors
Чем отличаются continuous integration, continuous delivery и continuous deployment, и почему мобильная разработка редко доходит до последнего?
Как выглядит CI/CD-пайплайн мобильного приложения от линтера до загрузки в стор?
# .github/workflows/android.yml: fast lane on PRs, emulator tests only on mainname: Android CIon: pull_request: { branches: [main] } push: { branches: [main] }jobs:…Что такое Fastlane и что он автоматизирует?
# fastlane/Fastfileplatform :android do lane :internal do gradle(task: "clean bundleRelease") upload_to_play_store(track: "internal", release_status: "draft") # takes the AAB gradle just built end…Релизная сборка собирается на всех ноутбуках команды и падает на чистом чекауте в CI. С чего начать разбор?
#!/usr/bin/env bash# Reproduce the CI environment locally: a clean clone, nothing from your working copy.set -euo pipefail TMP="$(mktemp -d)"git clone --depth 1 "file://$PWD" "$TMP/app"…Как подписывается Android-приложение и что такое Play App Signing?
// app/build.gradle.kts: release signing, entirely from environment variablesandroid { signingConfigs { create("release") { storeFile = rootProject.file(System.getenv("KEYSTORE_PATH") ?: "release.keystore") storePassword = System.getenv("KEYSTORE_PASSWORD")…Как пайплайн авторизуется в Google Play и App Store Connect без чьего-то личного аккаунта?
Вход через Google работает в APK, собранном вашим CI, и падает у всех, кто поставил приложение из Play. Что изменилось между этими двумя случаями?
#!/usr/bin/env bashset -euo pipefail # What you signed with (the upload key), NOT what users verify.keytool -list -v -alias upload -keystore upload.jks -storepass "$KEYSTORE_PASSWORD" | grep -E 'SHA1|SHA256'…Как headless-раннер CI получает и использует сертификаты для подписи iOS?
# fastlane/Fastfile: iOS signing on a CI runnerplatform :ios do lane :beta do setup_ci if is_ci # creates and unlocks a temporary keychain api_key = app_store_connect_api_key(…Во что CI обходится каждый лишний flavor и как не дать ему умножить пайплайн?
# .github/workflows/build.yml: which variants each lane buildson: pull_request: push: branches: [main]jobs:…Кто должен выставлять
versionCodeиCFBundleVersionв пайплайне и откуда берётся следующее значение?# fastlane/Fastfile: the store is the source of truth for the next numberplatform :ios do lane :beta do api_key = app_store_connect_api_key(key_id: ENV["ASC_KEY_ID"], issuer_id: ENV["ASC_ISSUER_ID"], key_content: ENV["ASC_KEY_P8"]) # Ask App Store Connect rather than trusting whatever is committed:…Код никто не трогал, а понедельничная сборка падает. Что в мобильном пайплайне уезжает само?
# Pin the image, then pin the toolchain inside itjobs: ios: runs-on: macos-15 # not macos-latest: the default Xcode moves under you steps: - uses: actions/checkout@v6…Первый краш-репорт из релизной сборки оказался стеной шестнадцатеричных адресов. Что пайплайн должен был сделать на этапе сборки?
# fastlane/Fastfile: symbols leave in the same lane that built the binaryplatform :ios do lane :beta do api_key = app_store_connect_api_key(key_id: ENV["ASC_KEY_ID"], issuer_id: ENV["ASC_ISSUER_ID"], key_content: ENV["ASC_KEY_P8"]) build_app(scheme: "MyApp", export_method: "app-store-connect") # also zips the dSYMs upload_to_testflight(api_key: api_key)…Как сохранить доверие к UI-тестам в CI, если они флакуют?
// Make the device a declared build input, so every run uses the same imageandroid { testOptions { managedDevices { localDevices { create("pixel8api36") {…QA подтвердил сборку 812, а релизная джоба пересобрала проект с того же тега. Почему это уже не та же самая сборка?
jobs: build: runs-on: macos-15 steps: - uses: actions/checkout@v6 - run: bundle exec fastlane ios build_rc build_number:$GITHUB_RUN_NUMBER…Команда релизится раз в две недели, ревью в сторе идёт днями, а плохую сборку нельзя отозвать. Как построить ветки и релизы вокруг этого?
PR-пайплайн идёт сорок минут, а macOS-раннеры стоят дороже ноутбуков команды. Откуда реально забрать время и деньги?
# .github/workflows/pr.yml: the levers that actually move the numbername: PRon: pull_request: paths: ['android/**', 'shared/**', 'gradle/**'] # a README change builds nothingconcurrency:…Пришёл pull request из форка незнакомого человека. Что мешает ему унести ваш ключ подписи?
# A fork PR builds with no secrets present. Keep it that way.name: CIon: { pull_request: , push: { branches: [main] } }permissions: contents: read # least privilege; the default token grant is widerjobs:…Человек ушёл из компании с копией релизного keystore и iOS-сертификата для дистрибуции на ноутбуке. Что вы реально можете ротировать?
Маркетинг хочет, чтобы фича вышла на обеих платформах во вторник утром; Android одобрен, iOS всё ещё на ревью. Как выпускать?