Build, Test and Deploy your Android Application📱 with GitHub Actions 🤖

jforatier

Julien Foratier

Posted on November 26, 2021

Build, Test and Deploy your Android Application📱  with GitHub Actions 🤖

My Workflow

As an Android developer, I would like to put under control the developments of an oss sandbox application gathering some practices in a functional application.

In a craftmanship approach I like to work with practices such as industrialization, continuous quality control, functional orientation and fast delivery.

You will find in this repo a some practices and patterns on workflow files that I will detail below.

Submission Category: Phone Friendly

Yaml File / Link to Code

In the project you can find different workflow files :

Here is the main Build workflow 👉

name: Build

on:
  push:
    branches: [ main ] # Just in case main was not up to date while merging PR
  pull_request:
    types: [ opened, synchronize ]

jobs:
  build-and-test:
    name: Build, Lint and Test
    runs-on: macos-latest
    timeout-minutes: 20

    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Set up our JDK environment
        uses: actions/setup-java@v2
        with:
          distribution: 'adopt'
          java-version: '11'

      - name: Cache Gradle and wrapper
        uses: actions/cache@v2
        with:
          path: |
            ~/.gradle/caches
            ~/.gradle/wrapper
          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
          restore-keys: |
            ${{ runner.os }}-gradle-

      # Decode Google services configuration file from secrets
      # - name: Decode google-services.json
      #  env:
      #    FIREBASE_CONFIG: ${{ secrets.FIREBASE_CONFIG }}
      #  run: echo $FIREBASE_CONFIG > app/google-services.json

      # Run emulator
      - name: Run integration test on emulator
        uses: reactivecircus/android-emulator-runner@v2
        with:
          api-level: 29
          script: ./gradlew createDebugCoverageReport --stacktrace

      # Generate jacoco report
      - name: Generate report
        run: ./gradlew jacocoTestReport

      # Upload report
      - name: Upload Reports
        uses: actions/upload-artifact@v2
        if: always()
        with:
          name: reports
          path: |
            /build/coverage-report
            app/build/reports

      # Upload coverage report to Codacy
      - name: Run codacy-coverage-reporter
        uses: codacy/codacy-coverage-reporter-action@v1
        with:
          project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}

  static-analysis:
    name: Execute analyse on code
    continue-on-error: true
    runs-on: ubuntu-latest
    timeout-minutes: 20

    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Set up our JDK environment
        uses: actions/setup-java@v2
        with:
          distribution: 'adopt'
          java-version: '11'

      - name: Cache Gradle and wrapper
        uses: actions/cache@v2
        with:
          path: |
            ~/.gradle/caches
            ~/.gradle/wrapper
          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
          restore-keys: |
            ${{ runner.os }}-gradle-

      # Check the code with detekt, you can remove this job if you don't use detekt
      - name: Run detekt Linter
        run: ./gradlew detekt

      # Check the code with ktlint, you can remove this job if you don't use ktlint
      - name: Run Kotlin Linter
        run: ./gradlew ktlintCheck

      # Check the code with Android linter (need assemble)
      - name: Run Android Linter
        run: ./gradlew lint

      # Check the code with Spotless
      - name: Run Spotless
        run: ./gradlew spotlessCheck

  generate-release-apk:
    name: Try generate Releasable
    runs-on: ubuntu-latest
    environment: Release
    timeout-minutes: 20
    needs:
      - build-and-test

    steps:
      - name: Checkout
        uses: actions/checkout@v2
        with:
          fetch-depth: 0

      - name: Set up our JDK environment
        uses: actions/setup-java@v2
        with:
          distribution: 'adopt'
          java-version: '11'

      - name: Decode Keystore
        env:
          ENCODED_STRING: ${{ secrets.KEYSTORE }}
        run: |
          TMP_KEYSTORE_FILE_PATH="${RUNNER_TEMP}"/keystore
          mkdir "${TMP_KEYSTORE_FILE_PATH}"
          echo $ENCODED_STRING | base64 -di > "${TMP_KEYSTORE_FILE_PATH}"/keystore_file.jks

      - name: Cache Gradle and wrapper
        uses: actions/cache@v2
        with:
          path: |
            ~/.gradle/caches
            ~/.gradle/wrapper
          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
          restore-keys: |
            ${{ runner.os }}-gradle-

      - name: Build Release
        run: ./gradlew app:assembleRelease
        env:
          SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS }}
          SIGNING_KEY_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD }}
          SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD }}
Enter fullscreen mode Exit fullscreen mode

In this file you can see 3 main sections :

  • build-and-test
  • static-analysis
  • generate-release-apk

CI workflow chaining

The build-and-test part is in charge of building application (and check for build failure) and running tests, both unit and instrumented. For instrumented it used android-emulator-runner to prepare a device in order to run. At the end a Jacoco task is merging different coverage result and upload them to Codacy

In parallel of this task, static-analysis is running some of android popular linters : ktLint, detekt, Android lint, Spotless

The last task generate-release-apk is waiting for build-and-test to complete and succeed before running then it will try to build an releaseable apk without publish it.

The real publishing task is in the Release Workflow which do the same job but in an other part of the delivery workflow, when a new version-tag is added. You can notice that the workflow deploy the final apk to firebase through firebase-app-distribution task.

Finally the *.apk file is available to Firebase App Distribution
App distribution ready to serve

Additional Resources / Info

Do not hesitate to explore the repository

GitHub logo boitakub / Bogadex

🎲 BoardGameGeek collections explorer application using Hilt, Coroutines, Flow, Jetpack (Room, ViewModel) based on MVVM architecture.

Bogadex

Bogadex

Build Maintainability Rating Reliability Rating Security Rating Bugs Vulnerabilities Technical Debt Code Smells Lines of Code Coverage Kotlin API License gitmoji Twitter

Bogadex is a small demo and functionnal application based on modern Android application tech-stacks and MVVM architecture.
This project aim to regroup and present most of current practices and patterns.
Also dealing with data (from BoardGameGeek) and presenting them in elegants ways

Bogadex - Screenshot

Download 📲

Go to the Releases to download the latest APK.

Features ✨

Tech Stack & Libraries 🧬

This project takes advantage of best practices, and many popular libraries and tools in the Android ecosystem.

  • Kotlin - 100% Kotlin - Code and Scripts

  • Flow for asynchronous.

  • Lifecycle - dispose of observing data when lifecycle state changes.

  • ViewModel - UI related data holder, lifecycle aware.

  • Hilt-Dagger for dependency injection.

  • JetPack

    • Compose - UI build 100% with Jetpack Compose
    • WorkManager - Updating and maintaining data up-to-date periodically and asynchronous
    • DataStore - for shared preferences
    • Room -…




And some of the Github Actions it used :

💖 💪 🙅 🚩
jforatier
Julien Foratier

Posted on November 26, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related