CI CD with Fastlane - tungpham6195/flutter GitHub Wiki
Welcome to CI-CD with Fastlane
Refer Fastlane documents
⛔️ WARNING: TRY NOT TO INSTALL GEM
WITH SUDO ⛔️
-
Install Bundle:
gem install bundler
-
Install Fastlane by using Brew:
brew install fastlane
-
Install Fastlane by using Gem:
gem install fastlane
-
Xcode command line tools:
xcode-select --install
-
Install Dotenv:
gem install dotenv
-
Make sure you local is using UTF-8 by updating
.bash_profile
or.zshrc
export LC_ALL=en_US.UTF export LANG=en_US.UTF-8
-
Setup CredentialsManager for uploading to AppStore: Refer this guide
- Run
fastlane fastlane-credentials add --username <your_email>
, then following the guide
- Run
-
Ignore these files:
#fastlane
ios/fastlane/report.xml
ios/fastlane/README.md
android/fastlane/report.xml
android/fastlane/README.md
-
Go to
android/
folder:cd android/
-
then:
fastlane init
and following the instruction -
Setup Supply for uploading .aab file to Google Play Store: Refer this guide
-
Rename that json file you have just downloaded to
play-store-console-service-key.json
-
Store it at
$HOME/deployment/anth/play-store-console-service-key.json
- Test the connection to Google Play Store after setting up
Supply
:
fastlane run validate_play_store_json_key json_key:$HOME/deployment/anth/play-store-console-service-key.json
- Update your Appfile:
json_key_file("../../../../deployment/anth/play-store-console-service-key.json") # Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one package_name("com.thunhung.android") # e.g. com.krausefx.app
-
Run
fastlane supply init
to dowload the metadata from Google Play Console. e.g:
- Test the connection to Google Play Store after setting up
-
-
Go to
ios/
folder:cd ios/
-
Then:
fastlane init
and following the instruction -
Add Cocoapods: add this line
gem "cocoapods"
toios/Gemfile
-
Then run
sudo bundle install
to updateGemfile
We use this method to connect to App Store Connect API which is used for uploading ios app
Recommend using Method 1: App Store Connect API key for this setup
- Setup App Store Connect API: Refer this guide
-
Go to App Store Connect API:
-
Create new .p8 key:
NOTE: select the role which is possible to be used for uploading app, e.g: Admin, Developer, App Manager
-
After creating, down load .p8 file and store it in
$HOME/deployment/anth/AuthKey_1236GWG52Z.p8
NOTE: This file can be dowloaded 1 time only after creating, please keep it safety
-
Open .p8 with text editor then copy the content of its to other editor place
-
Repalce all break-line with
\n
-
Create App Store API key as json file instead of using .p8: Refer this guide
File name:
$HOME/deployment/anth/app-store-connect-service-key.json
{ "key_id": "D403SF739", "issuer_id": "7063b7fe-68a8-4acb-89be-165aa6465141", "key": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwESJDFKHSKJFHKSJDHHBHkwdwIBAQQghVhY1reB3nYpJRnw\neY0YHVfr/e11RO42iFJ3LMYEApKgCgYIKoZIzj0DAQehRANCAARZnLGGRA1nC0yi\nN3ym4ZRrUYp6/89/AS7G18fwtYFipFlXEkqif17vecGeVp6+9YfxAArADpNtz3KL\n1a7td4Nc\n-----END PRIVATE KEY-----", "duration": 1200 }
Where you can get key_id and issuer_id:
-
Setup Deliver for uploading .ipa file to App Store Connect: Refer this guide
- Update your Appfile:
app_identifier "com.thunhung.ios" # The bundle identifier of your app
apple_id "[email protected]" # Your Apple email address
itc_team_name "Huynh Thai An"
itc_team_id "232069864"
- Run
fastlane deliver init --use_live_version true
to dowload the metadata from App Store Connect. e.g:
Refer Firebase Distribution
- run
fastlane add_plugin firebase_app_distribution
in/android
and/ios
- then run
sudo bundle install
in/android
and/ios
- following this instruction for completing configuration for Android and IOS
- create service account in Google Cloud Console:
Refer this
guide
- store account service json file at:
$HOME/deployment/anth/kaylee-d2522-8adc06fcdda4.json
- you need to put
GOOGLE_APPLICATION_CREDENTIALS
into your.bash_profile
or.zshrc
:export GOOGLE_APPLICATION_CREDENTIALS ="$HOME/deployment/anth/kaylee-d2522-8adc06fcdda4.json"
- store account service json file at:
- create text file for storing metadata for uploading firebase distribution
-
groups.txt:
client-tester,internal_tester
-
testers.txt:
-
release-notes.txt:
this is your release note: - update the feature
-
Those files will be looked like this:
-
Refer dotenv
- Create general configuration in
$root/.env
(.env
file)
#Configuration for Firebase Distribution
FIREBASEAPPDISTRO_TESTERS_FILE=../testers.txt
FIREBASEAPPDISTRO_GROUPS_FILE=../groups.txt
FIREBASEAPPDISTRO_RELEASE_NOTES_FILE=../release-notes.txt
#Configuration for building IPA
GYM_OUTPUT_DIRECTORY=../build
GYM_CLEAN=true
GYM_XCARGS=-allowProvisioningUpdates
#split-debug-info for dart obfuscate
SPLIT_DEBUG_INFOPATH=build
CONTENT_IMAGE=../android/fastlane/metadata/android/vi/images/icon.png
-
Create configuration for each flavors
$root/.env.<your_flavor>
(.env.<your_flavor>
file)
- E.g: if you have 2 flavors prod and dev => you need to create 2
env
files =>.env.dev
and.env.prod
- We often have 2 kinds of environment: production and development
- Configuration for development: so everythings in this configuration will be used for deploying for internal testing
#Configuration for Firebase Distribution
#for Android
FIREBASE_ANDROID_APP_ID=1:165119837573:android:684877c544aa943455f0ec
FIREBASEAPPDISTRO_APK_PATH=../build/app/outputs/flutter-apk/app-dev-release.apk
#for IOS
FIREBASE_IOS_APP_ID=1:165119837573:ios:eb924c661bdcc60255f0ec
FIREBASEAPPDISTRO_IPA_PATH=../build/Runner.ipa
#flutter build config
TARGET=lib/main_dev.dart
APP_NAME=[Dev] Thu Nhung
- Configuration for production: this configuation will be used for uploading to app store
#Firebase config
#for Android
FIREBASE_ANDROID_APP_ID=1:165119837573:android:c496c80ec9ddfbbf55f0ec
#for IOS
FIREBASE_IOS_APP_ID=1:165119837573:ios:e70715c0bdadcf8a55f0ec
#App Store Connect
DELIVER_FORCE=true
DELIVER_AUTOMATIC_RELEASE=false
DELIVER_RUN_PRECHECK_BEFORE_SUBMIT=false
DELIVER_IPA_PATH=../build/Runner.ipa
DELIVER_API_KEY_PATH=../../../../deployment/anth/app-store-connect-service-key.json
DELIVER_OVERWRITE_SCREENSHOTS=true
#Google Play Store
SUPPLY_AAB=../build/app/outputs/bundle/prodRelease/app-prod-release.aab
SUPPLY_RELEASE_STATUS=draft
#flutter build config
TARGET=lib/main.dart
APP_NAME=Thu Nhung
- Create
GeneralFastFile
in$root/GeneralFastFile
:
import('../../CommonFastfile')
import('../../NotificationFastfile')
private_lane :load_env do
Dir.chdir('../..') do
Dotenv.load('.env')
Dotenv.load(".env.#{ENV['FASTLANE_LANE_NAME']}")
end
end
lane :clean_project do
Dir.chdir('../..') do
sh('flutter', 'clean')
sh('flutter', 'packages', 'get')
end
end
lane :flutter_build do
load_env
build
end
private_lane :build do
Dir.chdir('../..') do
command = ['flutter', 'build', build_type, '--flavor', flavor , '--target', ENV['TARGET'], '--obfuscate', '--split-debug-info', "#{ENV['SPLIT_DEBUG_INFOPATH']}"]
sh(
command: command
)
end
end
private_lane :build_type do
platform_name == 'ios'? platform_name : flavor == 'prod'? 'appbundle' : 'apk'
end
lane :build_ios_and_export_ipa do
ENV['GYM_SCHEME'] = flavor
ENV['GYM_EXPORT_METHOD'] = ENV['GYM_SCHEME'] == 'prod' ? 'app-store' : 'development'
gym
end
lane :uploading_firebase_distribution do
if platform_name == 'ios' then
ENV['FIREBASEAPPDISTRO_APP'] = ENV['FIREBASE_IOS_APP_ID']
ENV['FIREBASEAPPDISTRO_APK_PATH'] = ''
elsif platform_name == 'android' then
ENV['FIREBASEAPPDISTRO_APP'] = ENV['FIREBASE_ANDROID_APP_ID']
ENV['FIREBASEAPPDISTRO_IPA_PATH'] = ''
end
firebase_app_distribution(
service_credentials_file: ENV['GOOGLE_APPLICATION_CREDENTIALS']
)
firebase_distribution_notification
end
lane :upload_store do
if platform_name == 'ios' then
deliver
elsif platform_name == 'android' then
supply
end
upload_store_notification
end
lane :update_gem do
sh(command: ['bundle', 'update'])
end
- Create
CommonFastfile
in$root/CommonFastfile
:
lane :platform_name do
ENV['FASTLANE_PLATFORM_NAME']
end
lane :flavor do
ENV['FASTLANE_LANE_NAME']
end
- Create
NotificationFastfile
in$root/NotificationFastfile
:
private_lane :push_notification do |options|
sh('pwd')
notification(
title: 'Fastlane',
subtitle: options[:subtitle],
message: options[:message],
content_image: ENV['CONTENT_IMAGE'],
)
end
lane :firebase_distribution_notification do
push_notification(
subtitle: 'Firebase Distribution',
message: "Successfully distributed #{ENV['APP_NAME']} #{platform_name}"
)
end
lane :upload_store_notification do
push_notification(
subtitle: platform_name=='ios'? 'App Store Connect': 'Google Play Store',
message: "Successfully uploaded #{ENV['APP_NAME']}"
)
end
-
Update Fastfile action for Android and IOS
NOTE: your lane name should be the same as your flavor, e.g: dev, prod
- Fastfile of Android:
require 'dotenv' import('../../GeneralFastfile') default_platform(:android) platform :android do desc "build apk/aab" before_all do update_gem flutter_build end lane :dev do uploading_firebase_distribution end lane :prod do upload_store end end
- Fastfile of IOS:
require 'dotenv' import('../../GeneralFastfile') default_platform(:ios) platform :ios do desc "Build an ipa" before_all do update_gem clean_project flutter_build build_ios_and_export_ipa end lane :dev do uploading_firebase_distribution end lane :prod do upload_store end end
-
create script file with name
deploy.sh
and put it into<your_project>/deploy.sh
-
script content:
red='\e[31m' green='\e[32m' yellow='\e[33m' white='\e[97m' reset='\e[0m' bold='\e[1m' greenBg='\e[42m' yellowBg='\e[43m' magentaBg='\e[45m' you_re_here="You're here: " flavor='dev' run_fastlane() { bundle exec fastlane $1 } select_flavor() { echo "Welcome to the show\n" echo -e "${red}${bold}[1]${reset}${green}: [dev] Development${reset}" echo "${yellow}${bold}[2]${reset}${green}: [prod] Production${reset}" printf "${green}\nPlease select your flavor, default is ${magentaBg}${white}[dev]${reset} (Press ${white}${yellowBg}enter${reset} to use the default): ${reset}" read value if [ "$value" = "2" ] then flavor="prod" fi } deploy() { select_flavor cd ios echo "${you_re_here}${white}${bold}${greenBg}$(pwd)${reset}" run_fastlane $flavor cd ../android echo "${you_re_here}${white}${bold}${greenBg}$(pwd)${reset}" run_fastlane $flavor } deploy