Flutter Deployment using Github Actions and Microsoft App Center
Introduction to CI/CD
CI/CD, an abbreviation for Continuous Integration and Continuous Deployment, is a crucial concept in software development that is closely related to GitHub Actions. This concept provides a way to automate and enhance the quality and speed of software development.
Continuous Integration (CI) focuses on continuously integrating code changes into a shared repository by team members. When a developer makes changes to the code and pushes it to the repository (as we do with GitHub), the CI system automatically runs a series of tests and verifications to ensure that the changes do not break or disrupt existing functionality. In other words, CI helps identify issues earlier in the development cycle.
Continuous Deployment (CD), on the other hand, involves automation to release code changes that have passed the CI process into production or testing environments. When code changes are deemed safe after passing a series of CI tests, CD enables the automatic deployment of these changes to servers or other environments without manual intervention. This helps speed up the development process and improve responsiveness to changing business needs.
When we use GitHub Actions in CI/CD, every time there is a change in the repository, GitHub Actions can trigger CI workflows to run tests and verifications. If everything is successful, CD workflows can be activated to release these changes to production or testing environments.
By utilizing CI/CD, development teams can ensure that the changes made do not degrade the quality or performance of the application. It also accelerates product release times and improves efficiency in managing the overall software lifecycle.
Below is a visualization diagram of the CI/CD flow:
Introduction to Github Actions
GitHub Actions is a feature provided by GitHub to enable automation in the software development lifecycle. In other words, GitHub Actions allows us to create and customize automated workflows to perform specific tasks whenever there are changes in a GitHub repository.
These workflows can be configured to execute various actions or scripts automatically, such as running tests, building applications, or releasing new versions. The goal is to assist development teams in automating these processes, allowing them to focus on writing code and developing features without worrying too much about administrative steps.
For example, when there’s a code change in a GitHub repository, GitHub Actions can automatically trigger a workflow that you’ve defined. This workflow may include steps like testing whether the change doesn’t break existing functionality, building a new application, and even releasing a new version if necessary.
It’s important to note that GitHub Actions leverages special configuration files (usually named .github/workflows/name-file.yml) within the repository. These files contain descriptions of the steps that should be executed by GitHub Actions.
With GitHub Actions, collaboration in software development can become more efficient as many tasks can be automated. This provides additional flexibility for developers and teams to customize their workflows according to project needs.
Below is an example of a simple workflow that we can use to build and test JavaScript-based 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 a wide range of features to streamline the process of building, testing, releasing, and monitoring applications. This service can be used for various platforms such as Android, iOS, Windows, macOS, and more. In this tutorial, we will utilize this service to automatically build and release Flutter applications.
Microsoft App Center offers a plethora of cool features for free, including continuous integration, UI testing, continuous delivery, detailed crash and error reports showcasing the error logs, and Analytics features. However, in this tutorial, we won’t be utilizing all available features since Flutter is not yet officially supported by App Center. Currently, the languages and frameworks officially supported are Kotlin, Java, React Native, Xamarin, and Unity. In this tutorial, our focus will solely be on building and releasing applications on App Center. For further clarification, you can delve deeper into the App Center documentation.
On this article, we will go trough step by step on how to deploy a flutter app into App Center. Without further ado, lets check it out:
- Create a new account using your GitHub account, the reason behind it to make it easier for App Center to detect you project repository|
2. Continue to create a new organization by clicking Add new => Add new organization and fill with the organization name that you want
3. Create a new application slot by pressing the Add app button.
4. Fill in the application name with your app name. You don’t need to select the release type. Select Android as OS and Java/Kotlin as platform.
5. Open the Distribute menu and open the Groups menu.
6. Create a new group by pressing the add button. Give it the name Public and provide public access by changing the toggle to Allow public access. Press the Create Group button to create a new group. We do this so that the APK that will be created by the App Center can be accessed publicly.
If you use organizations to organize your project code, then follow these additional steps. These steps serve to provide repository access to the App Center on GitHub.
- Open the Authorized OAuth Apps site with an account registered with the organization that has the project code.
- Click App Center.
- Search for the name of the organization that has your project code, then click the Grant button to give the organization access to the App Center.
Up to this point, you have done basic settings in the App Center. Next, you will set up the script and sign the Flutter application project.
Basic Setting for Flutter App Signing
To publish an application on App Center, Flutter applications must be signed or authenticated using a key to ensure the authenticity of the released application. Therefore, we will create a key for the application and set up automation so that CI/CD scripts (both those on GitHub Actions and App Center) can run smoothly.
- Generate a keystore
For MacOS or Linux, run this command on terminal:
keytool -genkey -v -keystore ~/release-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias release`
If you’re using Windows, run this command instead on Command Prompt:
keytool -genkey -v -keystore %userprofile%\release-keystore.jks -storetype JKS -keyalg RSA -keysize 2048 -validity 10000 -alias release`
This command is useful for saving a keystore file with the name release-keystore.jks in your home directory with the alias release. Move the file into the root of the application project folder. If Terminal or Command Prompt does not recognize the keytool command, please follow the additional guidance in the Notes section of the official flutter website in the Create an upload keystore section to insert the keytool command into the environment path.
2. Add the following syntax to the .gitignore file in the root of the application project 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 and 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
3. Open the /android/app/build.gradle file and look for the buildTypes 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 the section to be as follows.
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 done the basic settings for application signing process. Next, you will modify the GitHub Actions script and create a new script to build application files in the App Center.
Creating GitHub Actions Script
- Generate a base64 string as a representation of the keystore file which we will save as an environment variable later. Run the command `openssl base64 -in release-keystore.jks` in the root folder to generate the base64 string. Save the resulting string temporarily. The following is an example of the results obtained after executing the command.
2. Create repository secrets in the GitHub repository with the following specifications.
- GH_TOKEN contains the GitHub (Personal Access) Token from the repository admin for automated release purposes.
- KEY_JKS contains the base64 string from the keystore file that you created previously.
- KEY_PASSWORD contains the password you used when you created the keystore file.
3. If it doesn’t exist yet, create a .github/workflows folder in the application root.
4. Create three new files in the .github/workflows folder with the following specifications:
It is assumed that the staging branch is used to hold application code before release and the main branch is used to release application code.
- staging.yml; functions to check whether the codebase in the staging branch is free from errors when performing flutter analysis. This script is only triggered when there is a commit on the staging 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: '11'
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
channel: 'stable'
- name: Get packages
run: flutter pub get
- name: Analyze
run: flutter analyze
- pre-release.yml; serves to check whether 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 is a pull request from the staging branch to the main 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: '11'
- 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/*.apk
- release.yml; functions to carry out the application build process and release applications as Releases on the GitHub repository. This script is only triggered when there is a commit on the main 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: '11'
- 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 the file and push to the repository. Check whether the app was successfully created and released by GitHub Actions automatically.
If your application is successfully created and released automatically, then congratulations! Up to this point, we have secured the workflow on GitHub. Next, we’ll create a new script to build in App Center and further configure the app on the App Center website.
Here is the structure of your .github folder after doing this tutorial.
Creating CI/CD Script for App Center
In this section, we will incorporate continuous integration and continuous delivery scripts into the Flutter application repository to enable App Center to automatically build and generate the APK application files.
- Open the /android/app folder.
- Create a new file with the name 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 $_
3. Open the /android/.gitignore file and change the file to the following. This is done so that the 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
4. Save the file and push to the repository. Make sure this script and the new .gitignore have been pushed to the main branch.
Advanced Configuration in App Center
- Go to the App Center website and open the app project.
2. Open the Build menu and select GitHub as the application repository provider service. Select your group project application repository.
3. Open your main branch (main or master, please adjust) and click the Configure button.
4. Follow the following settings.
Notes:
- Don’t forget to replace KEY_JKS and KEY_PASSWORD with the actual values.
- Don’t forget to create a Public group for public distribution of the application.
- Copy the build badge link in Markdown format and paste it into README.md.
5. Click the Save & Build button to save the configuration and start the initial build process.
You can check the public link of the application publication in the App Center via the Distribute -> Groups -> Public menu. The following is an example of what App Center looks like when a public link from an app publication is accessed by a user.
6. Copy the public link from the app publication and paste it into README.md
End of Story
Congratulations, you have successfully deployed your Flutter application to App Center. You can verify the deployed application by downloading the APK file from App Center and installing it on your smartphone. This allows you to experience firsthand how your application functions in a real-world environment. Take this opportunity to thoroughly test its features and performance to ensure a seamless user experience. If you encounter any issues or have feedback, you can iterate on your development process to enhance your application further. Keep up the great work!
Reference