Skip to main content

Flutter Pipe

GitFlow

In order to use the CI/CD methodologies, we have to work with GitFlow and synchronize development cycles within our team. To do that we choose GitFlow as a branching strategy. GitFlow is a widely used branching model in the industry. There are lots of variants of this workflow but simplified version will work with our CI/CD workflow.

git-workflow

By default, there will be two important branches named main and development and a bunch of other feature branches.

  • main : Protected branch. Every merge/push to main branch will trigger release pipeline automatically. At the end of the release pipeline, the release configured builds will be delivered to both AppStore and Google Play Store. So, direct pushes to main branch are not be permitted, rather you have to send merge requests to main branch to trigger release pipeline.

  • development : every merge/push to development branch will trigger test pipeline automatically. At the end of the pipeline the development configured builds will be delivered to Firebase App Distribution.

  • feature/my-awesome-feature : we have to develop our game with feature branches where every feature branch can contain bunch of commits.

    • These pushes will not trigger any of the pipelines. After development cycle ends, a Merge Request (aka: Pull Request / PR) should be created to merge feature branch into development branch.
    • After merge ends, development build pipeline will automatically be triggered and deliver test builds to Firebase App Distribution. After some development cycle like this, once we are happy about our builds, a new merge request should be created to merge development branch into main branch.
    • After merge request to the main branch is accepted, release configured build pipeline will automatically work and deliver release builds to App Store and Google Play Store.

How CI Script Works

  1. Our CI script will fetch the latest version Goverance Script of gitlab-ci.yml file from the AWS.
  2. Your code will fetch from the git repository with 1 depth by gitlab runner automatically.
  3. Get correct credentials from local cache
  4. Get Fastlane setup from AWS
  5. Run Fastlane to build and deploy.

Step by Step Implementation

Use flutter provided gitignore in this repository.

  • .gitlab-ci.yml file

.gitlab-ci.yml is a file in the root of your repository, which contains the CI/CD configuration. Gitlab CI uses the YAML (Yet Another Markup Language) language to run CI operations.

Create .gitlab-ci.yml file in the root of the repository and add this line on the top of yml file. This will add our base ci script to this project.

include: "https://gitlab.matchingham.gs/devops-snippets/flutter-pipe/-/raw/main/flutter-ci.yml"

Fill out the required environment variables. Like in the example below. All detailed environment variables are down below this documentation.

Normally you shouldn't edit this file frequently. If you want to change environment variables start a manual pipeline and edit those variables from Gitlab Web UI. You can find detailed information about how to start manual pipeline down below.

include: "https://gitlab.matchingham.gs/devops-snippets/flutter-pipe/-/raw/main/flutter-ci.yml"

variables:
FIREBASE_TESTER_GROUP_NAME: ""

.dev:
variables:
DART_DEFINE_VARIABLES: "build-env=dev"

.release:
variables:
DART_DEFINE_VARIABLES: "build-env=prod"

.android:
variables:
# must have android variables
ANDROID_APP_ID: "com.MatchinghamGames.devops"
ANDROID_APP_FIREBASE_ID: ""

ANDROID_KEYSTORE_FILE: "../../keys/user.keystore"
ANDROID_KEYSTORE_PASSWORD:
ANDROID_KEY_ALIAS:
ANDROID_KEY_PASSWORD: ""

.ios:
variables:
# must have ios variables
IOS_BUNDLE_ID: "com.matchingham.devops"
IOS_APP_FIREBASE_ID: ""
PLIST_PATH: "Runner/Info.plist"
  • Dart Define Environment Variables

Flutter uses dart define variables to pass environtment variables to application --dart-define MY_VAR=MY_VALUE you can use DART_DEFINE_VARIABLES variable in yml file. This file accept array of parameters seperated by comma ,. Example usage of this variable like so :

   DART_DEFINE_VARIABLES: "build-env=prod, MY_VAR=MY_VALUE, otherSomeEnvironmentVariable=Value"
  • Keystore relative path

In order to publish Android Builds to Google Play Store, keystore file name, alias and password must be entered correctly in .gitlab-ci.yml file.

If you want, you can specify different keystore for different environments.

pipeline will modify key.properties file and pass it to the gradle file before the build starts.

.android-dev:
variables:
...
ANDROID_KEYSTORE_FILE: "relative/path/to/production.keystore"
ANDROID_KEYSTORE_PASSWORD: ""
ANDROID_KEY_ALIAS: ""
ANDROID_KEY_PASSWORD: ""
...

.android.release:
variables:
...
ANDROID_KEYSTORE_FILE: "relative/path/to/production.keystore"
ANDROID_KEYSTORE_PASSWORD: ""
ANDROID_KEY_ALIAS: ""
ANDROID_KEY_PASSWORD: ""
...
  • Which machine to run pipeline. (Runners)

Tags can be assigned to variable MACHINE_TAG

variables:
MACHINE_TAG: "mg-3"
  • Versioning

Version Number

To create Semantic Versioning use the pubspec.yml file.

versioning

Version number string needs to be in X.X.X format, where every X is an integer. You should manage app version within the pubspec.yml file

Build Number

  • Version Code&Build Number defines the build number for android and iOS relatively.

Build numbers are automatically incremented by the pipeline, you don't need to increment in every for every single build.It is developers responsibility to set this version number in pubspec.yml file. Pipeline will handle build number for you. This way pipeline will ensure every single build is unique.

When development pipeline is triggered, pipeline will get the build number from the Firebase App Distribution and increment the latest number by one.

When release pipeline is triggered, pipeline will get the build number value from the App Store and Google Play Store. Then, script will make the comparison between values from stores and project settings then will assign the bigger one. If the biggest value is what it is on store page then it will increment build number by 1 from latest build number.

  • Flutter Versions

There are things currently ungoing with the flutter version.

.android:
variables:
...
FLUTTER_VERSION: ""
...

.ios:
variables:
...
FLUTTER_VERSION: ""
...
  • Release Notes

    If you place release-notes.md file in the root folder of your project, Pipeline will read its contents and forward it to Slack, Firebase and Gitlab Releases. To tag someone in slack using release-notes.md, use slack user id in release-notes.md file.

    You can use this markdown syntax explained in this document

    slack-tag

  • Slack Integration

Integrate Devops Team App to your slack channel by following Channel Info > Integrations > Add An App . In Search box search for Devops Team and add to channel.

slack-integration

Define the Slack channel ID to which notifications will be sent in the .gitlab-ci.yml file. To get the channel ID, go to your slack channel and type /sdt whoami then press enter. Here the current conversation ID is your channel id.

slack-channel-id

.default: 
variables:
SLACK_CHANNEL_ID: ""

Our Slack Configuration will keep the channel updated for every successful development or main branch build deliveries.

Manual Pipeline Start

To Start a pipeline go to gitlab repository of your project. Tap CI/CD > Pipelines on the left pane. In the pipelines page click the "Run Pipeline" button.

run-pipeline

After that you should select the correct branch of desired build. If you want release build use main branch or if you want development build use the development branch. You can see there are bunch of environment variables that you need to set. On the bottom of the page you can set your predefined variables and their values.

pipeline-variables

Click on run pipeline button and you will see the related jobs. After clicking the play button, related job will start running.

Create New Pipeline from any previous commit

It is possible to create a new pipeline from any previous commit. To do so you need to create a new tag pointing to desired commit. Tag name should not be contain ios, android or huawei. Then you can create a new pipeline using the Gitlab Web Interface CI/CD > Pipelines > Run Pipeline and choose the tag from dropdown menu.

old-commit-pipeline

Prevent autostart jobs

Pipeline will automatically start once a new push has been received to configured branches. This behavior can be prevented by passing [ci skip] of [skip ci] to the latest commit message. Pipeline will iterate exactly this words, location doesn't matter.

Crashlytics Debug Symbol Processing

Both iOS and Android Debug Symbols are processed after the build stage is finished.

Xcode Version

Now you can set project specific Xcode Version by using :

.ios:
variables:
XCODE_VERSION: 14.1.0

Jobs and Job Hierarchy

You can find the job inheritance image below.

job-hierarch