DEV Community

Cover image for Build, Test and Deploy your Android Application📱  with GitHub Actions 🤖
Julien Foratier
Julien Foratier

Posted on

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 Status Super-Linter API API License gitmoji badge

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

  • Free and open source
  • List all you BoardGameGeekCollection

Tech stack & Open-source libraries 🧬

  • Minimum SDK level 23
  • Kotlin based, Coroutines + Flow for asynchronous.
  • Hilt for dependency injection.
  • JetPack
    • Lifecycle - dispose of observing data when lifecycle state changes.
    • ViewModel - UI related data holder, lifecycle aware.
    • Room - construct a database using the abstract layer.
    • Worker -
  • Architecture
    • MVVM Architecture (View - DataBinding - ViewModel - Model)
    • Bindables - Android DataBinding kit for notifying data changes to UI layers.
    • Business oriented with UseCases pattern
    • Repository pattern
  • Design
  • Retrofit2

And some of the Github Actions it used :

Discussion (0)