Irbisa · Cheatsheet

CI/CD & DevOps

GitHub Actions, Fastlane, Firebase Distribution, flavors

Senior Developer · 19 questions

  1. 01

    What is the difference between an APK and an AAB, and what does a signed release build for Google Play and the App Store involve?

    An APK is a package a device installs directly; an AAB (Android App Bundle) is a publishing format from which Google Play builds a slimmer APK per device, and Play requires it for new apps.

    // android/app/build.gradle.ktsimport java.util.Properties val keystoreProperties = Properties().apply {    val file = rootProject.file("key.properties")   // gitignored, written by CI    if (file.exists()) file.inputStream().use { load(it) }…
  2. 02

    How do you manage app versioning in Flutter: the version name, the build number, and who bumps them?

    version: 1.4.2+87 in pubspec.yaml holds two numbers: 1.4.2 is the version users see, and 87 is the build number, and the stores check it differently: Google Play requires versionCode higher than every previous upload; App Store Connect requires a higher build number for the same version.

  3. 03

    Trunk-based or GitFlow for a mobile app: how do you ship a hotfix to the store version while main already holds next week's unfinished features?

    Trunk-based development with a release branch per store version: the hotfix branches from the tag of the build users have, gets the fix cherry-picked from main, and ships as a new patch version, while main keeps moving untouched.

    # Store: 1.4.2 (tag v1.4.2). main: 1.5 work, unfinished parts behind flags.# 1) The fix is merged into main through a normal PR first.FIX=$(git log origin/main --format=%H -1 --grep='fix: crash on empty cart') # 2) Branch from what users run, not from main.git switch -c release/1.4 v1.4.2   # or switch to the existing release/1.4…
  4. 04

    What are flavors in Flutter, and how do you set up dev, staging and production builds?

    Flavors are build variants of one codebase: dev, staging and production compiled as separate apps that can sit side by side on one device, each with its own app id, name, icon, signing and backend.

    // android/app/build.gradle.ktsandroid {    flavorDimensions += "environment"    productFlavors {        create("dev") {            dimension = "environment"…
  5. 05

    How does --dart-define-from-file differ from --flavor, and how can a production-signed build end up calling the staging API?

    --flavor selects the native variant (app id, signing, Firebase files), while --dart-define and --dart-define-from-file compile constants into the Dart code.

    // flutter build appbundle --flavor prod --dart-define-from-file=config/prod.json// config/prod.json: {"API_URL": "https://api.example.com"}import 'package:flutter/services.dart' show appFlavor; class AppConfig {  // const and no defaultValue: a missing define is an empty string, never "dev"…
  6. 06

    What is fastlane, and what does it give you over a hand-written CI script?

    fastlane is a Ruby tool that wraps the long sequence of signing, building and uploading commands into named lanes in a Fastfile; fastlane beta then does the same thing on a laptop and on any CI machine.

    # ios/fastlane/Fastfiledefault_platform(:ios) platform :ios do  before_all do    setup_ci                      # temporary keychain; no-op outside CI…
  7. 07

    How do you choose between GitHub Actions, Codemagic, Bitrise and Xcode Cloud for a Flutter app?

    Choose by who maintains the pipeline and where the iOS build runs: Codemagic and Bitrise give ready Mac machines and mobile steps, GitHub Actions gives full control next to the code, and Xcode Cloud covers only the Apple side.

  8. 08

    How do you sign Android and iOS builds in CI without leaking the keys?

    Signing material lives only in the CI secret store, is decoded into a temporary file for the build and deleted when the job ends; nothing signing-related is ever committed.

    # .github/workflows/release.yml (Android part)jobs:  android:    runs-on: ubuntu-latest    environment: production          # secrets released only after approval    steps:…
  9. 09

    How do you distribute beta builds: Firebase App Distribution, TestFlight or Play testing tracks?

    On iOS the beta channel is TestFlight; on Android you choose between Firebase App Distribution for fast engineering builds and the Play Console's internal or closed testing tracks, which deliver through the store itself.

    # .github/workflows/beta.ymlname: betaon:  push:    branches: [main]…
  10. 10

    How do you automate Play Store and App Store uploads from CI?

    Both stores expose upload APIs, and CI reaches them with a machine credential: a Google Cloud service account for Play and an App Store Connect API key for Apple, never a person's login.

    # android/fastlane/Fastfiledefault_platform(:android) platform :android do  lane :internal do    upload_to_play_store(…
  11. 11

    What are the usual reasons App Review or Google Play rejects a Flutter app's submission, and how do you catch them before you submit?

    Most rejections come from what the reviewer sees, not from Dart: a sign-in they cannot pass, permissions or data a plugin brought in undeclared, iOS metadata showing Android, or an app too thin to count.

  12. 12

    The app works in debug but crashes or has no network in release. What are the usual causes, and how do you catch them before users do?

    A release build is a different binary: AOT-compiled, asserts stripped, shrunk by R8, signed with another key and built from the release manifest.

    #!/usr/bin/env bashset -euo pipefail # 1) Build the binary users will get; `flutter test` never builds it.flutter build apk --release --flavor prod \  --dart-define-from-file=config/prod.json \…
  13. 13

    The iOS CI job fails with "Generated.xcconfig must exist" although the runner has your Xcode. What is a fresh checkout missing?

    The files that flutter pub get generates: ios/Flutter/Generated.xcconfig and .flutter-plugins-dependencies are gitignored, and the Podfile reads that xcconfig to find the Flutter SDK and the plugin pods.

    #!/usr/bin/env bashset -euo pipefail # Pin the Xcode this project is tested with; images ship several.sudo xcode-select -s "/Applications/Xcode_${XCODE_VERSION}.app"…
  14. 14

    How would you design a CI/CD pipeline for a Flutter app: what runs on every pull request, what runs on release, and on which runners?

    Split it in two: a fast CI lane on every pull request that proves the code is correct, and a CD lane on main or a version tag that builds signed artifacts and ships them to testers and then the stores.

    # .github/workflows/ci.ymlname: cion:  pull_request:  push:    tags: ['v*']…
  15. 15

    What makes a Flutter app ready for its first production release, beyond passing tests?

    A release-ready app can be built and signed reproducibly from CI, tells you when it breaks in the field, can be forced to update, has risky features that can be switched off remotely, and meets each store's current technical and privacy rules.

  16. 16

    Every pull request waits 40 minutes on CI. Where does the time go in a Flutter pipeline, and what do you cut first?

    Read the per-step timings before changing anything.

    name: cion: [pull_request] concurrency:                       # a new push cancels the old run  group: ${{ github.workflow }}-${{ github.ref }}  cancel-in-progress: true…
  17. 17

    How do you roll out a release in stages, and what do you do when the new version turns out to be broken?

    A staged rollout limits how many users a bad build can reach, and a prepared response plan lets you stop the damage without waiting for store review.

  18. 18

    What is code push with Shorebird, what can a patch change, and how do you force users onto a new version when a patch cannot help?

    Shorebird ships new Dart code to an app already in the store: shorebird release builds the store version, shorebird patch fixes it, applied on the next launch.

    # The store build must come from Shorebird, not from plain `flutter build`.# Arguments after `--` are passed through to flutter build.shorebird release android --flavor prod -- --dart-define-from-file=config/prod.jsonshorebird release ios --flavor prod -- --dart-define-from-file=config/prod.json # Later: a Dart-only fix for users on 1.4.2+87, built with the same defines.…
  19. 19

    A monorepo with an app and fifteen packages runs every test for a one-line change in one of them. How do you make CI test only what that change can break?

    Filter by the dependency graph, not by the folder someone touched: Melos can select the packages changed since a git ref plus every package that depends on them, and CI analyzes and tests only that set.

    # pubspec.yaml at the repo root (pub workspace + Melos 7)name: acme_workspaceenvironment:  sdk: ^3.9.0workspace:  - apps/shop…