Tutorial 9: Flutter Deployment with GitHub Actions and Microsoft App Center
Platform-Based Programming (CSGE602022) — held by Faculty of Computer Science Universitas Indonesia, Odd Semester 2024/2025
Learning Objectives
After completing this tutorial, students are expected to be able to:
- Understand the concepts of continuous integration and continuous deployment
- Understand the concept and usage of GitHub Actions
- Understand the concept and usage of Microsoft App Center
- Implement continuous integration and continuous deployment in Flutter applications using GitHub Actions and Microsoft App Center
- Implement GitHub Actions to automatically build and release Flutter applications
Introduction to CI/CD
CI/CD, short for Continuous Integration and Continuous Deployment, is an important concept in software development related to GitHub Actions. This concept provides ways to automate and improve quality and speed in software development.
Continuous Integration (CI) focuses on continuously merging (integrating) code changes into a shared repository by team members. When a developer makes changes to code and submits it to the repository (like we do with GitHub), the CI system will automatically run a series of tests and verifications to ensure that the changes don't break or interfere with existing functionality. In other words, CI helps identify problems earlier in the development cycle.
Continuous Deployment (CD), in turn, involves automation to deploy code changes that have passed the CI process to production or testing environments. When code changes are declared safe after going through a series of CI tests, CD allows deploying these changes automatically to servers or other environments without manual intervention. This helps speed up the development process and improves responsiveness to changing business needs.
When we use GitHub Actions in CI/CD, every time there are changes to the repository, GitHub Actions can trigger CI workflows to run tests and verification. If everything succeeds, the CD workflow can be activated to release these changes to production or testing environments.
By using CI/CD, development teams can ensure that changes made don't break application quality or performance. This also speeds up product release time and improves efficiency in overall software lifecycle management.
Introduction to GitHub Actions
GitHub Actions is a feature provided by GitHub that enables automation in the software development cycle. In other words, GitHub Actions allows us to create and customize automated workflows to perform certain tasks every time there are changes to a GitHub repository.
These workflows can be set up to run various automated actions or scripts, such as testing, building applications, or releasing new versions. The goal is to help development teams automate these processes so they can focus on writing code and developing features without worrying too much about administrative steps.
For example, when there are code changes in a GitHub repository, GitHub Actions can automatically run workflows that you've specified. These workflows can include steps like testing whether the changes don't break existing functionality, building new applications, and even releasing new versions if needed.
It's important to note that GitHub Actions uses special configuration files (usually named .github/workflows/file-name.yml
) in the repository. This file contains descriptions of the steps that GitHub Actions should execute.
With GitHub Actions, collaboration in software development can become more efficient because many tasks can be automated. This provides additional flexibility for developers and teams to customize their workflows according to project needs.
Here's an example of a simple workflow we can use to build and test JavaScript application code using the Yarn package manager:
name: Build and test
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: yarn install
- name: Build
run: yarn build
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 16
- name: Install dependencies
run: yarn install
- name: Run tests
run: yarn test
Introduction to Microsoft App Center
Microsoft App Center is a cloud service that provides various features to facilitate the build, test, release, and monitoring of applications. This service can be used for various platforms, such as Android, iOS, Windows, macOS, and others. In this tutorial, we will use this service to automatically build and release Flutter applications.
Microsoft App Center provides many great features for free such as continuous integration, UI testing, continuous delivery, detailed crash and error reports from applications showing how the error logs, and Analytics features. This time, we won't use all available features because Flutter is not yet officially supported by App Center. Currently, the languages and frameworks that are officially supported are Kotlin, Java, React Native, Xamarin, and Unity. In this tutorial, we will focus only on building and releasing applications on App Center. For more details, you can read the App Center documentation.
Tutorial: Basic Configuration in App Center
In this tutorial, you will deploy the Flutter application you created in previous tutorials to App Center. Here are the initial steps in the application deployment process:
-
Create an account on App Center using your GitHub account (top button).
-
After successfully creating an account and signing in, create a new organization by accessing the Add new -> Add new organization menu. Fill in the organization name you want.
-
Create a new app slot by pressing the
Add app
button. -
Fill the application name with
Mental Health Tracker
. You do not need to select a release type. ChooseAndroid
as the OS andJava / Kotlin
as the platform. Then, click "Add new app". -
Open the Distribute menu and open the Groups menu.
-
Create a new group by pressing the "Add Group" button. Name it
Public
and provide public access by changing the toggle onAllow public access
. Press theCreate Group
button to create a new group. We do this so that the APK later created by App Center can be accessed publicly.
If you're using an organization to organize your project code, follow these additional steps:
-
Open the Authorized OAuth Apps site with the account registered in the organization that owns the project code.
-
Click
App Center
. -
Find the organization that owns your project code, then click the
Grant
button to give App Center access to the organization.
At this point, you have completed the basic setup in App Center. Next, you will set up scripts and signing for the Flutter application.
Tutorial: Basic Flutter Application Signing Configuration
To publish the application on App Center, the Flutter application must be signed using a key to guarantee its authenticity. Therefore, we will create a key for the application and set up automation so that CI/CD scripts (both on GitHub Actions and App Center) can run smoothly.
-
Create a keystore.
For Mac OS or Linux users, run the following command in the Terminal:
keytool -genkey -v -keystore ~/release-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias release
For Windows users, run the following command in Command Prompt:
keytool -genkey -v -keystore %userprofile%\release-keystore.jks -storetype JKS -keyalg RSA -keysize 2048 -validity 10000 -alias release
Provide a secure password for the keystore and save or remember this password, don't forget it. Fill in the required information until the process is complete. Then, move this file to the project's root folder.
The command you just ran is useful for storing the keystore file named
release-keystore.jks
in your home directory with the aliasrelease
.noteIf Terminal or Command Prompt does not recognize the
keytool
command, please follow the additional guide on the official Flutter web page under Create an upload keystore to add thekeytool
command to your environment path. -
Add the following syntax to the
.gitignore
file in the project's root folder so that the keystore is not counted as a file in the Git repository. This is done because the keystore is a confidential file that needs to be protected like an account password.# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
*.keystore
*.jks -
Open the file
/android/app/build.gradle
and find thebuildTypes
section.buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now,
// so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}Change this section to the following:
signingConfigs {
release {
storeFile file("../../release-keystore.jks")
storePassword = "$System.env.KEY_PASSWORD"
keyAlias = "release"
keyPassword = "$System.env.KEY_PASSWORD"
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
At this point, you have completed the basic configuration for application signing. Next, you will modify the GitHub Actions script and create a new script to build the application on App Center.
Tutorial: Creating GitHub Actions Scripts
-
Generate a base64 string representing the keystore file that we will store as an environment variable later.
For Mac OS or Linux users, run the
openssl base64 -in release-keystore.jks
command in the Terminal at the root folder to generate a base64 string. Save the generated string temporarily.For Windows users, open the project root folder in File Explorer. Then right-click, click "Show more options", and then click "Git Bash here". A Git Bash terminal will open.
[Image descriptions of Git Bash process]
In the terminal, run the command
openssl base64 -in release-keystore.jks
. This command will generate a base64 string. Save the generated string temporarily.Here's an example of the result of running this command.
[Image of base64 output]
-
Create repository secrets in the GitHub repository with the following specifications:
i.
GH_TOKEN
contains a GitHub (Personal Access) Token from the repository admin for automated release purposes.If you are the repository admin, access this page to create a Personal Access Token. Give your token a unique name. Under "Repository access", choose "Only select repositories" and select your Flutter project repository. Open the "Repository permissions" section, and grant at least read-only access to the following permissions:
- Secrets
Grant read and write access to at least the following permissions:
- Actions
- Workflows
- Contents
Copy the generated Personal Access Token and save it elsewhere, as you won't be able to see this token again through GitHub. Then, fill the
GH_TOKEN
repository secret with the Personal Access Token you copied.ii.
KEY_JKS
contains thebase64
string from the keystore file you created earlier.iii.
KEY_PASSWORD
contains the password you used when creating the keystore file.Finally, your repository secrets page will look like this.
[Image of GitHub Secrets page]
-
If it doesn't exist already, create a
.github/workflows
folder in the application's root folder. -
Create three new files in the
.github/workflows
folder with the following specifications:noteAssumed that the
staging
branch is used to contain application code before release and themain
branch is used for code release.i.
staging.yml
; functions to check if the codebase on thestaging
branch is free of errors when runningflutter analyze
. This script is only triggered when there's a commit on thestaging
branch.name: Staging
# Controls when the workflow will run
on:
# Triggers the workflow on push or pull request events but only for the develop branch
push:
branches: [staging]
pull_request:
branches: [staging]
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
name: Analyze
# The type of runner that the job will run on
runs-on: ubuntu-latest
steps:
- name: Checkout the code
uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: "zulu"
java-version: "17"
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
channel: "stable"
- name: Get packages
run: flutter pub get
- name: Analyze
run: flutter analyzeii.
pre-release.yml
; functions to check if the application build process can run without errors. If there are no errors, the APK file can be accessed as an artifact. This script is only triggered when there's a pull request from thestaging
branch to themain
branch.name: Pre-Release
# Controls when the workflow will run
on:
# Triggers the workflow on pull request events but only for the main branch
pull_request:
branches: [main]
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "Build and Pre-Release APK"
releases:
name: Build and Pre-Release APK
# The type of runner that the job will run on
runs-on: ubuntu-latest
steps:
- name: Checkout the code
uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: "zulu"
java-version: "17"
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
channel: "stable"
- name: Get packages
run: flutter pub get
- name: Generate Java keystore
env:
KEY_JKS: ${{ secrets.KEY_JKS }}
run: echo "$KEY_JKS" | base64 --decode > release-keystore.jks
- name: Build APK
env:
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
run: flutter build apk --split-per-abi
- name: Pre-release APK by uploading it to Artifacts
uses: actions/upload-artifact@v3
with:
name: APKS
path: build/app/outputs/flutter-apk/*.apkiii.
release.yml
; functions to build the application and release it as a GitHub Release. This script is only triggered when there's a commit on themain
branch.# This is a basic workflow to help you get started with Actions
name: Release
# Controls when the workflow will run
on:
# Triggers the workflow on push events but only for the main branch
push:
branches: [main]
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "Build and Release APK"
releases:
name: Build and Release APK
# The type of runner that the job will run on
runs-on: ubuntu-latest
steps:
- name: Checkout the code
uses: actions/checkout@v4
- name: Get version from pubspec.yaml
id: version
run: echo "::set-output name=version::$(grep "version:" pubspec.yaml | cut -c10-)"
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: "zulu"
java-version: "17"
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
channel: "stable"
- name: Get packages
run: flutter pub get
- name: Generate Java keystore
env:
KEY_JKS: ${{ secrets.KEY_JKS }}
run: echo "$KEY_JKS" | base64 --decode > release-keystore.jks
- name: Build APK
env:
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
run: flutter build apk --split-per-abi
- name: Get current date
id: date
run: echo "::set-output name=date::$(TZ='Asia/Jakarta' date +'%A %d-%m-%Y %T WIB')"
- name: Release APK
uses: ncipollo/release-action@v1
with:
allowUpdates: true
artifacts: "build/app/outputs/flutter-apk/*.apk"
body: "Published at ${{ steps.date.outputs.date }}"
name: "v${{ steps.version.outputs.version }}"
token: ${{ secrets.GH_TOKEN }}
tag: ${{ steps.version.outputs.version }} -
Save these three files and push them to the repository. Check if the application is successfully built and released automatically by GitHub Actions.
If your application is successfully created and automatically released, then congratulations! At this point, we have completed the workflow on GitHub. Next, we will create a new script for building on App Center and configure the application further on the App Center website.
Tutorial: Adding CI/CD Scripts for App Center
In this section, we will add continuous integration and continuous delivery scripts to the Flutter application repository so that App Center can automatically build and generate the application's APK file.
-
Open the
/android/app
folder. -
Create a new file named
appcenter-post-clone.sh
and fill the file with the following code:#!/usr/bin/env bash
# Place this script in project/android/app/
cd ..
# fail if any command fails
set -e
# debug log
set -x
cd ..
git clone -b beta https://github.com/flutter/flutter.git
export PATH=`pwd`/flutter/bin:$PATH
flutter channel stable
flutter doctor
echo "Installed flutter to `pwd`/flutter"
# export keystore for release
echo "$KEY_JKS" | base64 --decode > release-keystore.jks
# build APK
# if you get "Execution failed for task ':app:lintVitalRelease'." error, uncomment next two lines
# flutter build apk --debug
# flutter build apk --profile
flutter build apk --release
# copy the APK where AppCenter will find it
mkdir -p android/app/build/outputs/apk/; mv build/app/outputs/apk/release/app-release.apk $_ -
Open the
/android/.gitignore
file and change it to the following. This is done so that App Center can detect the application as an Android application.# add comment for app center
# gradle-wrapper.jar
# /gradlew
# /gradlew.bat
/.gradle
/captures/
/local.properties
GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
key.properties
**/*.keystore
**/*.jks -
Save the file and push it to the repository. Ensure that this script and the new
.gitignore
have been pushed to themain
branch. -
Make sure the files
gradlew
,gradlew.bat
, andgradle/wrapper/gradle-wrapper.jar
in theandroid/
directory have also been pushed to GitHub. This is important so that Microsoft App Center can recognize your repository as a Flutter project.
After creating the script, we will configure the application in App Center to be automatically built and released.
Tutorial: Advanced Configuration in App Center
-
Open the App Center website and open the application project.
-
Open the
Build
menu and select GitHub as the repository service provider. Choose the application repository for your group project. -
Open your main branch (
main
ormaster
, adjust as needed) and click theConfigure build
button. -
Follow these settings:
- Enable Environment variables
- Fill with the following name and value:
JAVA_HOME
:$(JAVA_HOME_17_X64)
KEY_JKS
:<base64 of your keystore>
KEY_PASSWORD
:<password of your keystore>
- Check
Sign builds
, and within it, checkMy Gradle settings are entirely set to handle signing automatically
. - Check
Distribute builds
and theGroups
radio button. Check theContributors
andPublic
groups in the dropdown menu. - Check
Build status badge
.
Notes:
- Don't forget to replace
KEY_JKS
andKEY_PASSWORD
with the actual values. - Don't forget to create a
Public
group for public application distribution. - Copy the build badge link in
Markdown
format and paste it into yourREADME.md
.
-
Click the
Save & Build
button to save the configuration and start the first build process.You can check the public link for application publication in App Center through the Distribute -> Groups -> Public menu. You can also copy the public link from the application publication.
-
Copy the public link from the application publication and paste it into
README.md
Closing Remarks
Congratulations, you have successfully deployed your Flutter application to App Center. You can check the application you've deployed by downloading the APK file from App Center and installing it on your smartphone.
And we have officially completed the PBP Tutorial for the Odd Semester 2024/2025! Thank you for having followed and completed all the PBP tutorials for the Odd Semester 2024/2025. The teaching team wants to express appreciation to all students who have participated and contributed to this course. The teaching team sees the effort and dedication you have shown in facing the challenges of multiplatform application development in this course.
During the lab and assignment process, we have explored the fundamental concepts and principles underlying web and mobile application development using Django and Flutter. You have learned about architectures, features, and tools that can help in building robust and responsive applications on both platforms.
The teaching team hopes that the labs and assignments can provide a deeper understanding of the potential and challenges in multiplatform application development and provide you with useful skills that can be applied in your career as a software developer.
However, learning does not stop here. The development world continues to evolve rapidly, and it is important to keep up with the latest developments in the industry. The teaching team encourages you to continue learning and keep your skills relevant by reading other references, taking advanced courses, and participating in real-world projects.
In closing, remember that multiplatform application development is an exciting and potential-filled field. Continue to explore and innovate, and the teaching team is confident that you have a bright future as a software developer. Thank you and best of luck on your journey!
Contributors
- Muhammad Nabil Mu'afa
- Adrian Hakim Utomo (Translator)
Credits
This tutorial was developed based on a blog entry written by Muhammad Athallah. All tutorials and instructions in this repository are designed so that students taking the Platform-Based Programming course can complete the tutorial during lab sessions.