|||

Flutter CI/CD with Azure Devops & Firebase - Android - Part 2

This is a guide on how to build and deploy flutter hello world apps both iOS and Android from a private git repo. This is part 2 of the series for Android apps. The code will be built in Azure devops pipeline and will be deployed to firebase for distribution on test devices.

logo

Pre-reqs

  • Same as Part 1 of the series
  • Android Studio, especially the keytool part of the Android studio installation.

Build Phase - Android

Lets create our second job in the azureops.yaml file to build our Android app. The azureops.yaml should like below

trigger:
  - master

jobs:
  - job: android
    pool:
      vmImage: "macos-latest"
    variables:
      - group: secrets
      - group: general
      - name: androidReleaseDir
        value: $(build.artifactStagingDirectory)/build/app/outputs/flutter-apk
      - name: apkFile
        value: $(androidReleaseDir)/app-release.apk
    steps:
      - task: FlutterInstall@0
        displayName: "Install Flutter"
        inputs:
          mode: "auto"
          channel: "stable"
          version: "custom"
          customVersion: "2.2.3"
      - task: FlutterBuild@0
        inputs:
          target: apk
          projectDirectory: "."
          buildNumber: ""

This should build an unsigned apk that we will sign and distribute in the next sections.

Code Signing - Android - Prerequisites

To sign the built apk, we need to generate a key using Android studio’s keytool cli app by running the command below on a command line.

keytool -genkey -v -keystore android-key.jks -storetype JKS -keyalg RSA -keysize 2048 -validity 10000 -alias helloworld-counterapp

This will prompt a few questions below, some questions are optional and can be skipped as well:

question answer
Enter keystore password abc123
What is your first and last name Enter your fullname here
What is the name of your organizational unit? helloapps
What is the two-letter country code for this unit AU
Enter key password for helloworld-counterapp abc123

Note: Its probably better to have a separate password for keystore and the key password itself from a security standpoint.

Now upload the generated android-key.jks file to our pipeline as a secure file and add the two variables below into the secrets group. For more info on how to secrets see the part-1 of this series.

variable value
androidKeystorePassword abc123
androidKeyPassword abc123

Code Signing - Android

Now add the following section to the azureops yaml to add the code signing steps

- task: AndroidSigning@3
  displayName: "Signing and aligning APK file(s) **/*.apk"
  inputs:
    apkFiles: "**/*.apk"
    apksign: true
    apksignerKeystoreFile: android-key.jks
    apksignerKeystorePassword: "$(androidKeystorePassword)"
    apksignerKeystoreAlias: helloworld-counterapp
    apksignerKeyPassword: "$(androidKeyPassword)"

- task: CopyFiles@2
  inputs:
    contents: "**/*.apk"
    targetFolder: "$(build.artifactStagingDirectory)"

- task: PublishBuildArtifacts@1
  inputs:
    artifactName: "drop"

Upload signed build to firebase for distribution

To upload the signed apk to firebase, we need to Register an Android project in firebase.

Androd package name app nickname
com.example.hello_world_counter_app hellocounter android app

Download the google-services.json file as indicated on the console and place it next to build.gradle under the android folder as indicated on the screen and follow the wizard to the last step.

Google-services file

Note: Make sue to click Get Started” on the new created app distribution project otherise the upload may fail.

Go back into the project settings and note the App ID of this android project we just setup and create that as variable under the Azure Devops pipeline general group and call it androidFirebaseDistAppId. With that inplace lets add the final step in the android job as below.

- task: Bash@3
  displayName: "Upload to firebase app distribution"
  inputs:
    targetType: "inline"
    script: |
      npm i -g firebase-tools
      ls -la $(androidReleaseDir)
      firebase appdistribution:distribute "$(apkFile)" \
        --app "$(androidFirebaseDistAppId)" \
        --release-notes "From Azure Devops" \
        --groups "beta-testers" \
        --token "$(firebasetoken)"

This should upload the signed build to firebase and all the beta testers will be notified to download and test the app. Here’s the full file for reference.

trigger:
  - master

jobs:
  - job: android
    pool:
      vmImage: "macos-latest"
    variables:
      - group: secrets
      - group: general
      - name: androidReleaseDir
        value: $(build.artifactStagingDirectory)/build/app/outputs/flutter-apk
      - name: apkFile
        value: $(androidReleaseDir)/app-release.apk
    steps:
      - task: FlutterInstall@0
    displayName: "Install Flutter"
    inputs:
      mode: "auto"
      channel: "stable"
      version: "custom"
      customVersion: "2.2.3"

  - task: FlutterBuild@0
    displayName: "Build unsigned APK"
    inputs:
      target: apk
      projectDirectory: "."
      buildNumber: ""

  - task: AndroidSigning@3
    displayName: "Signing and aligning APK file(s) **/*.apk"
    inputs:
      apkFiles: "**/*.apk"
      apksign: true
      apksignerKeystoreFile: android-key.jks
      apksignerKeystorePassword: "$(androidKeystorePassword)"
      apksignerKeystoreAlias: helloworld-counterapp
      apksignerKeyPassword: "$(androidKeyPassword)"

  - task: CopyFiles@2
    displayName: "Copy apk to artifact directory"
    inputs:
      contents: "**/*.apk"
      targetFolder: "$(build.artifactStagingDirectory)"

  - task: PublishBuildArtifacts@1
    displayName: "Publish signed apk as artifact"
    inputs:
      artifactName: "drop"

  - task: Bash@3
    displayName: "Upload to firebase app distribution"
    inputs:
      targetType: "inline"
      script: |
        npm i -g firebase-tools
        ls -la $(androidReleaseDir)
        firebase appdistribution:distribute "$(apkFile)" \
          --app "$(androidFirebaseDistAppId)" \
          --release-notes "From Azure Devops" \
          --groups "beta-testers" \
          --token "$(firebasetoken)"
Up next How to samples with AWS CDK Flutter CI/CD with Azure Devops & Firebase - iOS - Part 1
Latest posts Refactor react code to use state store instead of multiple useState hooks Notes on Python Threat Modelling - Using Microsoft STRIDE Model WCAG - Notes Flutter CI/CD with Azure Devops & Firebase - iOS - Part 1 Flutter CI/CD with Azure Devops & Firebase - Android - Part 2 How to samples with AWS CDK A hashicorp packer project to provision an AWS AMI with node, pm2 & mongodb Some notes on Zeebe (A scalable process orchestrator) Docker-Compose in AWS ECS with EFS volume mounts Domain Driven Design Core Principles Apple Push Notifications With Amazon SNS AWS VPC Notes Building and Deploying apps using VSTS and HockeyApp - Part 3 : Windows Phone Building and Deploying apps using VSTS and HockeyApp - Part 2 : Android Building and Deploying apps using VSTS and HockeyApp - Part 1 : iOS How I diagnosed High CPU usage using Windbg WCF service NETBIOS name resolution woes The troublesome Git-Svn Marriage GTD (Getting things done) — A simplified view Javascript Refresher Sharing common connection strings between projects A simple image carousel prototype using Asp.net webforms and SignalR Simple logging with NLog Application logger SVN Externals — Share common assembly code between solutions Simple async in .net 2.0 & Winforms Clean sources Plus Console 2 — A tabbed console window