Creating and maintaining your CI/CD workflows in a single config file in smaller projects or teams is still manageable. Circle CI provides a simple syntax in which you can use different environments or executors and write commands and jobs that can be reused in different workflows.
But, if you add more Flutter build flavors and settings, write more kinds of tests, deploy on various app stores like Apple or Google Play Store, or work with a larger team, it’s challenging to write all of these in a single file. I’ve seen some going over a thousand lines!
Before we write in the orb way, let’s see how we set up Flutter’s counter app in CircleCI.
Use CircleCI workflows for Flutter
In Flutter, you need the main workflow that runs on every code change, either for a pull request or commits sent to the main branch. Your main workflow for integration contains setting up the project SDK and downloading package dependencies, checking lint, testing, and building the code.
Create a new Flutter project
I’m using Flutter’s default counter app as an example because it’s simple enough to give you a clear picture of how you can use the techniques you use here in your projects, whether simple or complex.
To create a new Flutter app, run:
flutter create circleci_flutter_demo
Add Circle CI to your Flutter project
Create a folder named .circleci
in the project’s root directory:
md .circleci
Create a file named config.yml
:
touch .circleci/config.yml
Then, add the following at the topmost part of the file:
orbs:
flutter: circleci/flutter@1.1.0
version: 2.1
executors:
android:
docker:
- image: 'cimg/android:2022.06'
The orbs
is the parameter that contains the reusable packages or orbs we need for our workflows. circleci/flutter is an example of an orb; it includes the basic commands and instructions for your Flutter projects.
The executors
are the environments or platforms where our workflows are running.
Next, add the following commands
:
commands:
analyze:
description: >-
Run lint checks Flutter code base on the rules set on
`analysis_options.yml`
steps:
- run:
command: flutter analyze .
name: Analyze
build-android:
description: Builds APK for Android
steps:
- run:
command: flutter build apk
name: Build APK
format:
description: Run checks if Flutter code is formatted
steps:
- run:
command: >-
flutter format --set-exit-if-changed . || { echo 'Format check
failed'; exit 1; }
name: Analyze
test:
description: Runs the tests of your Flutter app
steps:
- run:
command: flutter test
name: Test
These commands can be reused on different jobs.
Next, add the following jobs
:
jobs:
build-android:
environment:
FLUTTER_CHANNEL: stable
FLUTTER_VERSION: 3.0.1
executor: android
steps:
- checkout
- flutter/install_sdk_and_pub:
flutter_version: 3.0.1
- build-android
tests:
environment:
FLUTTER_CHANNEL: stable
FLUTTER_VERSION: 3.0.1
executor: android
steps:
- checkout
- flutter/install_sdk_and_pub:
flutter_version: 3.0.1
- format
- analyze
- test
Jobs can run in different environments and can be a combination of both inline or reusable commands. Jobs can be reused on different workflows.
Last, add the following workflows
:
workflows:
main:
jobs:
- tests
- build-android:
requires:
- tests
Create your project’s repo on GitHub
Circle CI integrates seamlessly with GitHub and other platforms like Bitbucket. You can commit your local changes and upload your Flutter project to the new Git repository.
Configure project on Circle CI
In your projects dashboard, select the repository of your Flutter project, then click Set Up Project.
You should see a successful workflow run.
The config already ends up having 67 lines of code. Note that this workflow doesn’t include integration tests, iOS builds, and deployment__.__
Let’s write a config file like creating orbs and add more workflow steps.
Write The Orb Way
Orbs in Circle CI are reusable packages shared by other developers to help you speed up project development. You can explore the orbs published here.
To make it manageable for the developer to write an orb, Circle CI provides an orb starter template:
.circleci
└── src
├── commands
├── executors
├── jobs
├── workflows
└── orb.yml
└── config.yml
The config.yml
file here is generated using orb packing. Orb packing is a tool available in the Circle CI local CLI.
it’s the orb way
Prepare the template source directory
The template source directory of your Circle CI configuration makes writing workflows in a human-readable way. Files are isolated and become more manageable in the long run.
@orb.yml
In the root of the template source directory, create a file named orb.yml
:
version: 2.1
orbs:
flutter: circleci/flutter@1.1.0
This is where you add orbs your project uses.
Create commands
Create the following files inside the commands directory:
analyze.yml
description: Run lint checks if Flutter code is formatted
steps:
- run:
name: Analyze
command: >-
flutter format --set-exit-if-changed . || { echo 'Lint check failed';
exit 1; }
build_android.yml
description: "Builds APK artifact for Android"
steps:
- run:
name: "Build APK"
command: flutter build apk
format.yml
description: "Run checks if Flutter code is formatted"
steps:
- run:
name: "Analyze"
command: |-
flutter format --set-exit-if-changed . || { echo 'Format check failed'; exit 1; }
test.yml
description: "Runs the tests of your Flutter app"
steps:
- run:
name: "Test"
command: |-
flutter test
Create executors
Create the following inside the executors
directory:
android.yml
docker:
- image: cimg/android:2022.06
macos.yml
macos:
xcode: 13.4.1
default.yml
docker:
- image: cimg/base:stable
Create jobs
Create the following inside the jobs
directory:
tests.yml
executor: android
steps:
- checkout
- flutter/install_sdk_and_pub:
flutter_version: 3.0.1
- format
- analyze
- test
build-android.yml
executor: android
steps:
- checkout
- flutter/install_sdk_and_pub:
flutter_version: 3.0.1
- build-android
build-ios.yml
executor: macos
steps:
- checkout
- flutter/install_sdk_and_pub:
flutter_version: 3.0.1
- flutter/install-pod-ios
- build-ios
Create workflows
Create the following inside the workflows
directory:
jobs:
- tests
- build-android:
requires:
- tests
- build-ios:
requires:
- tests
Pack the source directory
To generate the config.yml
file, you’ll need first to install the Circle CI local CLI tool.
For macOS with Brew:
brew install circleci
For Windows with Chocolatey:
choco install circleci-cli -y
See local CLI guide.
Next, pack the source directory:
cd .circleci && circleci config pack src > config.yml
Last, validate the configuration:
circleci config validate
If you’re using GNU Make, you can add this rule:
.PHONY: pack
pack:
circleci -h >/dev/null 2>&1 || { echo >&2 "Install the circleci cli with brew install"; }
cd .circleci && circleci config pack src > config.yml
circleci config validate
Now, you can pack your source directory like this:
make pack
Read how you can use GNU Make for Flutter projects.
Furthermore, you can also create shell scripts or bat executables.
See the demo project here.
It’s a wrap!
Writing your workflows Circle CI is made human-readable with orb packing. Your workflow configuration is split up into different files, which means fewer merge conflicts, files have specific responsibilities, and each has fewer lines of code.
There are other concepts like reusable config wherein you set parameters and reuse blocks of commands. The reusable config feature compliments the orb packing, making your workflows more manageable.
Let me know what you think at hi@joshuamdeguzman.com.