Logo

dev-resources.site

for different kinds of informations.

Building and Deploying Flutter Apps from CI with Fastlane: How to Reuse Code Efficiently

Published at
5/27/2021
Categories
flutter
ios
fastlane
android
Author
danielgomezrico
Categories
4 categories in total
flutter
open
ios
open
fastlane
open
android
open
Author
15 person written this
danielgomezrico
open
Building and Deploying Flutter Apps from CI with Fastlane: How to Reuse Code Efficiently

We use fastlane for our flutter apps, for ios and android builds, it give us a common language to define the setup and reuse code for:

  • Generating a changelog based on our changelog style
  • Validate code style, formatting and lint
  • Unit testing
  • Verify that the project autogenerated code is been built fine.
  • Verify that the project builds fine for each platform.
  • Deploy to firebase app distribution for testing
  • Deploying to each app store

There are some tricks to be able to do it:

  • Create a parent fastfile where you have the reusable logic and one for each platform ios/fastfile and android/fastfile.
  • Have an easy way to run tasks from the root of the project, our idea was to create a lane sh_on_root to run all flutter commands from each platform fastfile.
  • Create a Gemfile for each platform and define .ruby-version file to have a defined environment to run, helping us to avoid problems on CI server requiring some specific ruby version to run fastlane in there.

Another thing we noticed is that using --no-pub --suppress-analytics params from flutter commands saved us some time on CI if you want to try that's a good start.

I will show an example of some usefull lanes and some workflows for them, you should think about your needs and call the lanes you need for each step but try to reuse for each platform some tasks having the parent fastfile file.

Show me the setup

  1. Create the Gemfile for each platform
  2. android/Gemfile
  3. ios/Gemfile

Contents:

source "https://rubygems.org"

gem 'cocoapods'
gem "fastlane"

plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
eval_gemfile(plugins_path) if File.exist?(plugins_path)
Enter fullscreen mode Exit fullscreen mode
  1. Create the .ruby-version so the CI (in our case Bitrise) have a clue of which ruby version should be used
  2. ios/.ruby-version
  3. android/.ruby-version

Contents:

2.6.5
Enter fullscreen mode Exit fullscreen mode
  1. Create 3 fastlane files, 1 to be reused by each platform, and 1 for each platform (check that from each platform fastlane we import the parent one with import "../../scripts/Fastfile")
  • fastfile:
opt_out_usage

# Have an easy way to get the root of the project
def root_path
  Dir.pwd.sub(/.*\Kfastlane/, '').sub(/.*\Kandroid/, '').sub(/.*\Kios/, '').sub(/.*\K\/\//, '')
end

# Have an easy way to run flutter tasks on the root of the project
lane :sh_on_root do |options|
  command = options[:command]
  sh("cd #{root_path} && #{command}")
end

# Tasks to be reused on each platform flow
lane :fetch_dependencies do
  sh_on_root(command: "flutter pub get --suppress-analytics")
end

# Tasks to be reused on each platform flow
lane :build_autogenerated_code do
  sh_on_root(command: "flutter pub run intl_utils:generate && flutter pub run build_runner build --delete-conflicting-outputs")
end

# Tasks to be reused on each platform flow
lane :lint do
  sh_on_root(command: "flutter format --suppress-analytics --set-exit-if-changed -n lib/main.dart lib/src/ test/")
end

# Tasks to be reused on each platform flow
lane :test do |options|
  sh_on_root(command: "flutter test --no-pub --coverage --suppress-analytics")
end
Enter fullscreen mode Exit fullscreen mode
  • ios/fastfile:
import "../../scripts/Fastfile"

default_platform(:ios)

platform :ios do
  # Updates XCode project settings to use a different code signing based on method
  private_lane :archive do |options|
    method = options[:method]

    profile_name = method == 'ad-hoc' ? "Distribution - Staging (adhoc)" : "Production"
    update_code_signing_settings(profile_name: profile_name)

    build_app(export_method: method)
  end

  private_lane :authenticate_apple_store do
    p8_path = path_for_secret("your .p8 file path")

    app_store_connect_api_key(
      key_id: "your key id",
      issuer_id: "your used id",
      key_filepath: p8_path,
      duration: 1200, # optional
      in_house: false,
    )
  end

  lane :build do |options|
    # Reuse parent fastfile tasks
    fetch_dependencies
    build_autogenerated_code


    sign_enabled = options[:sign_enabled] || false
    sign_param = sign_enabled ? '' : '--no-codesign'

    config_only = options[:config_only] || false
    config_param = config_only ? '--config-only' : ''

    sh_on_root(command: "flutter build ios --no-pub --suppress-analytics --release #{sign_param} #{config_param}")
  end

  lane :deploy_staging do
    build(sign_enabled: true)
    archive(method: "ad-hoc")

    upload_symbols_to_crashlytics(dsym_path: "your.app.dSYM.zip")

    firebase_app_distribution(
      app: "yours app id from firebase",
      ipa_path: "your.ipa",
      groups: "developers, staging",
      release_notes: changelog
    )
  end

  lane :deploy_staging_testflight do
    build(sign_enabled: true)
    archive(method: "app-store")

    authenticate_apple_store

    # Reuse parent fastfile tasks
    test

    upload_to_testflight(
      ipa: "your.ipa",
      reject_build_waiting_for_review: true,
      skip_waiting_for_build_processing: false,
      distribute_external: true,
      notify_external_testers: true,
      groups: "Your testers"
    )
  end

  lane :deploy_production do    
    # All certificates and .p8 file should be fine on runnning machine
    build(sign_enabled: true, config_only: true)
    archive(method: "app-store")
    authenticate_apple_store

    # Reuse parent fastfile tasks
    test

    deliver(
      ipa: "your.ipa",
      skip_metadata: true,
      skip_screenshots: true,
      submit_for_review: false,
      force: false,
      automatic_release: false,
      submission_information: {
        add_id_info_serves_ads: false,
        add_id_info_uses_idfa: false,
        export_compliance_uses_encryption: false,
      },
      precheck_include_in_app_purchases: false,
    )
  end
end
Enter fullscreen mode Exit fullscreen mode
  • android/fastfile
import "../../scripts/Fastfile"

default_platform(:android)

platform :android do
  private_lane :build_apk do
    # Reuse parent fastfile tasks
    fetch_dependencies
    build_autogenerated_code

    sh_on_root(command: "flutter build apk --release")
  end

  lane :build do
    # Reuse parent fastfile tasks
    fetch_dependencies
    build_autogenerated_code

    sh_on_root(command: "flutter build appbundle --no-pub --release --suppress-analytics")
  end

  lane :deploy_staging do
    build_apk

    # Reuse parent fastfile tasks
    test

    firebase_app_distribution(
      app: "your app id",
      groups: "your testers",
      android_artifact_type: "APK",
      android_artifact_path: "#{root_path}/build/app/outputs/flutter-apk/app-release.apk",
      firebase_cli_path: "/usr/local/bin/firebase"
    )
  end

  lane :deploy_production do
    build

    # Reuse parent fastfile tasks
    test

    supply(
      track: 'beta',
      aab: "../build/app/outputs/bundle/release/app-release.aab",
      json_key: path_for_secret("your play store.json"),
      skip_upload_apk: true, # Upload the aab instead of apk
      skip_upload_metadata: true,
      skip_upload_changelogs: true,
      skip_upload_images: true,
      skip_upload_screenshots: true
    )
  end
end
Enter fullscreen mode Exit fullscreen mode
fastlane Article's
30 articles in total
Favicon
From days to minutes: Build and publish React Native apps using Fastlane and Github Actions
Favicon
[Boost]
Favicon
The Ultimate Guide to Automating Android APK Builds and Firebase Distribution with Fastlane 🚀
Favicon
From Manual to Automatic: The Magic of CI/CD Pipelines! 🤯
Favicon
Protegendo credenciais em automatização de builds no Fastlane com Variáveis de Ambiente
Favicon
GUI for fastlane?
Favicon
Steps to Add and Upload testflight Using Fastlane
Favicon
Simplifying Flutter Deployment with FastLane
Favicon
🚀 Added new YouTube series on "Flutter CI/CD with GitLab and Fastlane" !
Favicon
Automate publishing your android app to Google Play Store with Fastlane and Github Actions
Favicon
GitHub Actions CI/CD for Flutter Fastlane (iOS) with possible mistakes
Favicon
Criando uma conta e configurando um projeto no CodeMagic
Favicon
Automatically add new devices to the Apple Developer Portal (GitHub Actions & Fastlane)
Favicon
Fastlane: 深入浅出一看你就懂
Favicon
Fastlane: iOS 和 Android 的自动化构建工具
Favicon
Fastlane with React Native — Part-1
Favicon
Deploy iOS Application to AppCenter via Github Actions (Manual Code SignIn)
Favicon
Single point versioning with Fastlane for React Native
Favicon
Unable to use Fastlane on macOS Monterey 12.1
Favicon
Automating Flutter App Versioning Using Fastlane Plugin
Favicon
How To Automate App Releases using Fastlane and SemVer for Hybrid Applications
Favicon
Building and Deploying Flutter Apps from CI with Fastlane: How to Reuse Code Efficiently
Favicon
How to run Fastlane and GitLab with AppStore Connect API
Favicon
Automating mobile application deployments using Fastlane and CI/CD tools
Favicon
React Native e2e tests and Automatic Deploys (Detox + Fastlane + CircleCI)
Favicon
Deploy React Native app on Playstore with Fastlane and Github Actions ( 1 / 2 )
Favicon
Deploy React Native app on Playstore with Fastlane and Github Actions ( 2 / 2 )
Favicon
CI/CD in iOS Development
Favicon
Como configurar Fastlane para Firebase App Distribution (Android e iOS)
Favicon
Flutter + Fastlane + Github Actions

Featured ones: