Irbisa · Cheatsheet
Mobile CI/CD (Cross-platform)
Fastlane, GitHub Actions, signing, TestFlight, Play Internal, build flavors
What is the difference between continuous integration, continuous delivery and continuous deployment, and why does mobile rarely do the last one?
What does a CI/CD pipeline for a mobile app look like, from lint to store upload?
# .github/workflows/android.yml: fast lane on PRs, emulator tests only on mainname: Android CIon: pull_request: { branches: [main] } push: { branches: [main] }jobs:…What is Fastlane and what does it automate?
# 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…The release build works on every laptop in the team and fails on a clean CI checkout. What do you check first?
#!/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"…How do you sign an Android app, and what is 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")…How does the pipeline authenticate to Google Play and App Store Connect without anyone's personal account?
Google Sign-In works in the APK your CI built and fails for everyone who installed the app from Play. What changed between the two?
#!/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'…How does a headless CI runner get and use iOS signing certificates?
# 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(…What does each extra build flavor cost your CI, and how do you keep it from multiplying the pipeline?
# .github/workflows/build.yml: which variants each lane buildson: pull_request: push: branches: [main]jobs:…Who should set
versionCodeandCFBundleVersionin a pipeline, and where does the next value come from?# 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:…Nobody touched the code and Monday's build fails. What drifts on its own in a mobile pipeline?
# 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…The first crash report from the release build is a wall of hex addresses. What should the pipeline have done at build time?
# 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)…How do you keep a UI test suite in CI trustworthy when it is flaky?
// Make the device a declared build input, so every run uses the same imageandroid { testOptions { managedDevices { localDevices { create("pixel8api36") {…QA signed off on build 812 and the release job then rebuilt from the same tag. Why is that not the same build?
jobs: build: runs-on: macos-15 steps: - uses: actions/checkout@v6 - run: bundle exec fastlane ios build_rc build_number:$GITHUB_RUN_NUMBER…Your team ships every two weeks, review takes days, and a bad build cannot be recalled. How do you structure branches and releases around that?
The PR pipeline takes forty minutes and the macOS runners cost more than the laptops. Where do you get the time and the money back?
# .github/workflows/pr.yml: the levers that actually move the numbername: PRon: pull_request: paths: ['android/**', 'shared/**', 'gradle/**'] # a README change builds nothingconcurrency:…A pull request arrives from a stranger's fork. What stops it from walking away with your signing key?
# 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:…Someone left the company with a copy of the release keystore and the iOS distribution certificate on their laptop. What can you actually rotate?
Marketing wants the feature live on both platforms Tuesday morning; Android is approved and iOS is still in review. How do you ship?