DEV Community

AlvBarros
AlvBarros

Posted on

Automate Flutter app delivery to AppCenter with GitHub Actions

Introduction

In this post, you'll see how to automate the delivery of your Flutter app to the testing team. For this to work, your app must have follow these conditions:

Got it? Let's begin.

What is Continuous Integration?

According to Atlassian's Max Rehkopf, it's about automatically running builds and tests with every change. This speeds up development, since it is happening automatically, the developer can focus on the development of new features and leave validation and testing to automated tools.

Ok, so what's Continuous Delivery then?

Taking inspiration from Pittet's post, CD is automatic deployment of the code on testing and/or production environment. It's often confused with Continuous Deployment, which is the automated release to the end customers.

In this post we'll focus on Continuous Delivery, since we'll be automating the deployment of your app to AppCenter.

Step 1 - Setting up a Runner for GitHub Actions

If your code is hosted on GitHub, the best approach to DevOps is to write GitHub Actions. You can read more about Actions here, but everything that you need to know will be explained later on. For now, you have to setup a Runner.

A runner is where your action's jobs will be run. It can be a hosted virtual environment, or you can self-host a runner in your machine.

To register your own machine as a runner, you must go to the Settings of your repository and follow the instructions on Settings > Actions > Runner.
To set up a runner, go to Settings > Actions > Runner

There you'll see a green button "New self-hosted runner". Click on it and then follow the steps. It is simply running a few commands on you terminal, so I'll not be covering it here.

In the end, you should be running your runner and it will be shown in the same page of the Settings, such as below.
Runners page displaying my machine as an Idle Runner

If you have any issues on this part, feel free to leave a comment below.

Step 2 - Set up your AppCenter app

On your AppCenter app's settings, go to Settings > App API Tokens. There, you can create a token that'll be used on our Actions workflow.

AppCenter app's Settings showing the Add API Tokens page

Click on "New API Token". You'll be asked to set up a name and access for your token. Set whatever you'd like, but the token must have Full access. Don't worry, only you can see this token.

When the pop-up with the token appears - such as the one below, copy the token and save it somewhere safe. You cannot see it again, and if you happen to lose it, you must delete it and create a new one.

AppCenter's pop-up with the API token censored

With your AppCenter API Token, go back to your repository on GitHub. On Settings > Secrets and variables > Actions, we'll create a Secret for our repository.

Settings > Secrets and variables > Actions

A secret is a variable that is used by our GitHub actions but is not displayed for who does not have permission. The automated workflow can use the value, but no one is able to see what it is. This enables the user to have environment variables such as AppCenter's API Token without it being compromised or leaked. For more information on secrets, refer to the documentation.

Now, click on "New repository secret" and create a APPCENTER_API_TOKEN and paste the value of the token previously created. If you've done everything correctly, this must be what you see on your page.

Secrets and variables page with the new created APPCENTER_API_TOKEN

Now we can finally start creating workflows for our repository.

Step 3 - Creating your GitHub Actions Workflow

First of all, you repository needs a .github folder on the root folder. Then, you create a workflows folder. There, we'll create a main.yml file. The end result should be something like this:

Printscreen of the .github/workflows folder

Note that the file doesn't have to be named main.yml, this is just for this example.

In the main.yml file, we must add some code so that it does what we want. Start by writing this:

name: Deploy App on AppCenter
on:
  push:
    branches:
      - main
      - 'releases/**'
Enter fullscreen mode Exit fullscreen mode
  • name: Self explanatory. Sets the name of this workflow.
  • on: Defines the triggers of this workflow. On our case, we want this workflow to be triggered on every push that happens on the main branch, or any other branch that starts with releases/, such as releases/1.0.0, releases/hotfix-login, etc.

For more information on on tag, see the documentation.

But still, it's not doing anything. So now we need to add Jobs. Jobs are what make up workflows, and are the steps you want to execute.

For this post, we'll set up these steps:

  • Set up Flutter;
  • Set up AppCenter CLI;
  • Build the app's package;
  • Deploy the package to AppCenter.

In this post, only Android will be covered. Follow me to know when the post for iOS is released!

Step 3.1 - Set up Flutter

Now that you have your main.yml file configured, we can add jobs to achieve what we want. Start by adding a jobs and a "Set up Flutter" step, such as the code below.

jobs:
  setup-flutter:
    name: Setup Flutter
    runs-on: self-hosted
    steps:
      - name: Check out repository code
        uses: actions/checkout@v3
      - name: Install jq
        uses: dcarbone/install-jq-action@v2.0.2
      - uses: subosito/flutter-action@v2
        with:
          channel: master
          architecture: x64
      - run: flutter pub get
Enter fullscreen mode Exit fullscreen mode

Let's take this apart:

  • jobs: Defines jobs that must be run for this workflow. These can be run in parallel or sequentially. By default, they are run in parallel. More information later.
  • runs-on: Defines on what kind of Runner this job can be run. Since we've defined our self-hosted runner in the previous steps, we just add this value.
  • steps: Defines the steps for this job. Each will be run sequentially.

If you pay attention, we already created 3 steps. They have two tags: name and uses. Name is self explanatory.

Uses defines what action is used on this step. In the first case, actions/checkout@v4 is used. This format says what repository and action is used. Below is the list of actions used:

  • actions/checkout@: Checks out the code in the runner. This makes sure that the workflow is being run on updated code.
  • dcarbone/install-jq-action: This actions installs jq on the runner machine. It's a lightweight command-line JSON processor. For more information, check it's page. This is not necessary to build Flutter, but it's used in other actions later on.
  • subosito/flutter-action: This action installs and runs Flutter commands. It's what we'll use to get dependencies and build our package. It can also be used to run our tests and check for coverage, but it's not being implemented in this guide. This action has a run parameter, which defines the specific command we want to be run.

If you pay attention, all these actions are actually public repositories that we can use. GitHub Actions have a public marketplace that you can add actions that have been developed by the community. To check what's the code is doing, you can go to the repository and check it. It's best to see what it's doing to make sure that you're not using anything with a specific vulnerability.

So to summarize, we've set up a workflow that on every push that happens on master or release/** installs Flutter and the Pub dependencies on the runner.

Now, we can add the next step.

Step 3.2 - Set up AppCenter CLI

This step is going to be easier since we already know what we're doing.

  setup-appcenter-cli: # must have node and npm installed!
      name: Setup AppCenter CLI
      runs-on: self-hosted
      steps:
      - name: Install appcenter-cli
        uses: charliealbright/appcenter-cli-action@v1.0.1
        with:
          token: '${{secrets.APPCENTER_API_TOKEN}}'
          command: 'appcenter help'
Enter fullscreen mode Exit fullscreen mode

We now have a new tag to go over:

  • with: This sets some parameters to be given to the action as an input. In this case, we add token and command. If you pay attention, we're using secrets.APPCENTER_API_TOKEN.

Secret is an object that contains every variable we've set in the repository's secret. Since we've set APPCENTER_API_TOKEN, this token can now be used in this action. Again, make sure that you know what your actions are doing.
We then add "${{" and "}}" around the variable so that Actions knows that it must change this value to the one set on the secrets.

So now we have two steps: setting up Flutter and AppCenter CLI.

Step 3.3 - Call Deploy Android job on the main workflow

We now can use some of the superpowers of GitHub Actions - creating different jobs on different files. Add this specific job below setup-appcenter-cli:

  deploy_android:
    name: Deploy Android
    needs: [setup-flutter, setup-appcenter-cli]
    uses: ./.github/workflows/deploy_android.yml
    with:
      file: './build/app/outputs/apk/release/app-release.apk'
      name: 'AlvaroBarrosC/GithubAction-Android'
Enter fullscreen mode Exit fullscreen mode

We now have two tags to go over:

  • needs: This sets some requirements for this job. In our case, it needs Flutter and AppCenter CLI to be set up. This also means that this Deploy job will be run sequentially.
  • uses: We've already seen this tag, but now we're giving a local path. This will use a deploy_android.yml file that we've created on workflows folder. So let's create the file.

As described previously, the with tag adds some inputs. In this case, we've added file and name.

  • file: This must be the path to the file that is generated by the Flutter build command. Above is the default output directory, but this can be changed.
  • name: This one is the username, slash and the app’s name on AppCenter. Mine is AlvaroBarrosC and GithubAction-Android, but you must change it to your own.

Step 4 - Create Deploy Android workflow

As said in the previous step, create the file on .github/workflow/deploy_android.yml, the same folder where your main.yml file is located. Then, you can paste this code:

name: Deploy Android App on AppCenter
on: 
  workflow_call:
    inputs:
      file:
        description: 'The path to the file to be released'
        required: true
        type: string
      name:
        description: 'The name of the app'
        required: true
        type: string
      group:
        description: 'The group that will have access to the version released'
        required: false
        type: string
        default: '"Collaborators"'
jobs:
  build:
    name: Build .apk file
    runs-on: self-hosted
    steps:
      - run: flutter build apk --release --verbose
  Deploy:
    name: Deploy file to AppCenter
    needs: [build]
    runs-on: self-hosted
    steps:
      - name: AppCenter CLI Action
        uses: charliealbright/appcenter-cli-action@v1.0.1
        with:
          token: ${{secrets.APPCENTER_API_TOKEN}}
          command: 'appcenter distribute release -f ${{inputs.file}} --app ${{inputs.name}} --group ${{inputs.group}}'
Enter fullscreen mode Exit fullscreen mode

You now can see how the inputs are defined on the top of the file. They have some metadata, such as required, type and description. You can also see them being used on the command parameter of the AppCenter CLI Action. By using ${{inputs.[field]}}`, you place the value given when the workflow was called.

We can now make a push on any change in the affected branches and see the workflow being run. Make sure your runner is running.

Step 5 - Deploy!

If you followed every step correctly, whenever you go to your Repository’s Actions tab, you can see your previous runs there. Also, you can debug and see the log of the steps.

Printscreen showing available workflows

Note that iOS deployment was not covered on this post.

The execution of the workflow, step by step

Thanks for reading!

If you've missed any of the steps or is encountering any problem, you can check the code on this repo. Feel free to comment on this post with any questions that you have.

Top comments (10)

Collapse
 
trungquan2k profile image
Trung Quan

Thanks. very details, I setup success now but i try push new commit and run ./run.sh in flutter project but this workflow has no runs yet. I try input all fields such as token appcenter but it still not working. You can make a video to guide more. Thanks

Collapse
 
alvbarros profile image
AlvBarros

Hey there! I'm sad to hear that.
If you could maybe send a link to your repo or even show any error message, I'm glad to help!

Anyway, if you've got your Runner running you should make a commit to either a master or a release/** branch.
If not, you can also add a way to the workflow to be "forced" on the Actions page.
Change on: push to on: workflow_dispatch just like the code below and then the button "Run workflow" should appear on Actions page.

Code difference; changing  raw `on: push` endraw  to  raw `on: workflow_dispatch` endraw

The button "Run workflow" on the Actions > Workflow page

Collapse
 
alvbarros profile image
AlvBarros

Use this commit specifically as a reference.
github.com/AlvBarros/flutter-appce...

Collapse
 
alvbarros profile image
AlvBarros • Edited

Hey guys, this is a longer post than average.
Feel free to leave any feedback if there's any details that I should've left out, or if you'd prefer if I split this post into multiple posts. Anyways, I'll be posting the guide to deploy iOS apps later. It includes some extra configuration for signing, that's why it's been left out. Thanks!

Collapse
 
hendisantika profile image
Hendi Santika

0s
Run flutter build apk --release --verbose
flutter build apk --release --verbose
shell: /usr/bin/bash -e {0}
/home/runner/work/_temp/d4cdd1ed-ffe7-47a6-b800-64ead539ec17.sh: line 1: flutter: command not found
Error: Process completed with exit code 127.

Image description

Image description

Collapse
 
hendisantika profile image
Hendi Santika

Where should I input these variables?
command: 'appcenter distribute release -f ${{inputs.file}} --app ${{inputs.name}} --group ${{inputs.group}}'

If I am not mistaken it should input by us on github action right?

CMIIW

Collapse
 
alvbarros profile image
AlvBarros

Hey there!

On this example, it's currently on deploy_android.yml. If you want to take a look at the file completed on this guide, click here to go to the file on the GitHub repo.

If you're still having issues, you could share the project link with me!
If it's private, maybe just add my user and then I can take a look.

Best of luck!

Thread Thread
 
hendisantika profile image
Hendi Santika • Edited

Yes, I already checked you git repo.

But still error ...

github.com/KominfoPemudaPersis/Qur...

github.com/KominfoPemudaPersis/Qur...

Collapse
 
alvbarros profile image
AlvBarros

This log shows a specific error: flutter: command not found.
This probably means that Flutter was not correctly set up in the Runner.
It's supposed to be set up on the Setup Flutter step of the job, so maybe take a look at that log to make sure it ran without any issue?

Collapse
 
hendisantika profile image
Hendi Santika

/usr/local/bin/appcenter distribute release -f ./build/app/outputs/apk/release/app-release.apk --app QuranPersis --group Collaborators --token ***
Error: Command 'distribute release -f ./build/app/outputs/apk/release/app-release.apk --app QuranPersis --group Collaborators --token ***' failed with exception "'QuranPersis' is not a valid application id"
Error: The process '/usr/local/bin/appcenter' failed with exit code 3

Where Can I get the application ID?