The Problem
As the author of a several Flutter applications & Dart packages, I have spent entirely too much of my life scouring the internet and piecing together tutorials for CI/CD workflows for Flutter applications & Dart packages, so I decided to publish my own. The following article is meant to demonstrate a simple workflow for a Flutter/Dart package using GitHub Actions, my preferred platform for CI/CD. You can see a fully working example of the workflow here.
Create a Code Coverage account
To publish code coverage results, you will want to create a code coverage account. You can simplify the process by authenticating with GitHub. This example uses codecov.io but coveralls should have a similar process.
1. Add a new repository
From the Repositories tab, select "Add a new repository".
2. Find your repository
You will be taken to a page that allows you to search for the repository that you want to add coverage support to. Find your repository and select it.
3. Save your upload token
On the next screen, you'll be presented with an Upload token. Save this - it will be added as a secret to your project to upload code coverage results.
Configure Code Coverage
Next you'll need to set up the GitHub integration with Code Coverage. The easiest way to do this is to select the GitHub Integration action from the Account settings page.
From there you'll be redirected to add the integration in GitHub. You can set up code coverage to be configured for all repositories or only select. I set mine up for specific repositories. We'll also want to add that code coverage token from the previous step as a new secret. Call the secret CODECOV_TOKEN
.
Setting up the GitHub Action
We could create a GitHub Action from our IDE of choice or directly from GitHub. For the sake of brevity, let's use GitHub.
1. Create an action
From the Actions tab of your GitHub repository, select "set up a workflow yourself". You could use the Dart workflow to get a simple workflow set up for you, but this workflow will only work if you do not need Flutter as a dependency. You can see an example that just uses the Dart workflow here.
2. Name the workflow.
name: Test
You can name the workflow whatever you want. Because this workflow is only testing the package, I've given it the name test.
3. Define workflow triggers.
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
Triggers determine when a GitHub Action runs. You can learn more about them here. For our workflow, we probably want to run tests for each pull request to know whether something broke before we merge. We also want to run tests on pushes directly to master in case an administrator accidentally (or intentionally in my case) pushes a bad commit without going through the PR process.
4. Create the job to test
Now it's time to define the meat of our workflow file. Let's examine each step individually.
a. Specify the running platform
jobs:
test:
runs-on: macos-latest
steps:
You can specify any number of platforms to run your jobs on. In this case we use macOS, but this is only really necessary if you're building iOS/macOS apps. From here, we will define the steps that will be ran as part of the workflow. Think of each step as a command to be executed.
b. Add step to checkout project
- uses: actions/checkout@v1
The first step is to check out the project so that we may run additional commands against it.
c. Add step to setup Flutter
- name: Install Flutter
uses: subosito/flutter-action@v1.3.2
Next, we install Flutter so that we may execute Flutter commands in later steps.
d. Add step to install dependencies
- name: Install dependencies
run: flutter pub get
Next, we install the project dependencies.
e. Add step to run tests with coverage
- name: Test app
run: flutter test --coverage
This workflow would be nothing without our command to run our tests, so let's add that. Notice that to generate coverage when running tests, we need to use the --coverage
flag. By default the code coverage will be output to coverage/lcov.info
.
f. Add step to publish coverage results
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: coverage/lcov.info
Finally, we publish our code coverage results using the previously defined token. The token is made available as an environment variable on the secrets object. We also need to specify the location of our code coverage results.
NOTE: Adding the token is optional, it is automatically grabbed by the codecov action, but for the sake of this article I am being explicit.
Complete workflow file
We have finished building our workflow file. It should closely resemble the following:
name: Test
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
test:
runs-on: macos-latest
steps:
- uses: actions/checkout@v1
- name: Install Flutter
uses: subosito/flutter-action@v1.3.2
- name: Install app dependencies
run: flutter pub get
- name: Test app
run: flutter test --coverage
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: coverage/lcov.info
This step allows us to publish our code coverage results using the previously defined token. The token is made available as an environment variable on the secrets object. We also need to specify the location of our code coverage results.
5. Commit your changes, have a tea break
All that's left is to commit these changes and we're done! You should immediately be able to see your action running.
Conclusion
Hopefully you now have a better understanding of how GitHub Actions work and how to can set up a simple workflow for your Flutter application or package without involving any pesky 3rd party services. Happy trails on your DevOps journey. ๐๐๐
Sup?! Iโm Ryan Edge. I am a Software Engineer at Superformula and a semi-pro open source contributor. If you liked this article, be sure to follow and spread the love! Happy trails