DEV Community

Cover image for Manually Generate apk using Github Actions
Burhanuddin Rashid
Burhanuddin Rashid

Posted on

Manually Generate apk using Github Actions

For more visibility posting this on dev.to. The original article is posted on my website. Let's get started :)

Have you been in a situation where you want to test a feature from a specific branch? But you don't have an environment set up in the local? Or you are out somewhere and don't have access to your PC? Or your client/QA is pinging you all the time to provide APK with specific configuration and features?

In this blog, we will talk about how we can solve above problems by manually generating APK using Github actions. So Let's get started.

Prerequisite

  1. Github actions.
  2. Basic understanding of android build using gradle.
  3. Basic of bash scripting.

What problem we are trying to solve?

The problem might differ based on your working environment, team size, and clients. Let's try to understand each of them.

  1. Indie Developer:
    • If you are an Indie developer working solely on the project, then you might be overwhelmed by this approach. The reason might be
      1. Fewer dependencies of people on you.
      2. You have the environment set up in your local.
      3. No one asking for the APK to test on daily basis.
    • You can skip this whole article. But it's good to be aware of this approach because eventually, your team size will grow, and you need be well prepared for it.
  2. Setting up the environment:
    • Let say our non-technical client wants to test a feature on the stagging environment which we have pushed to a specific branch. There are two solutions to this.
      1. Call the developer and ask for the apk with the required parameters.
      2. Setup the android environment in the client local machine, check out the branch, tweak the parameters, and build the apk. (I don't think a non-technical client will like this solution)
    • We can avoid this by providing a GUI to build the apk with configurable parameters. That's exactly what run workflow UI does. (More on this next section)
    • The same problem mention above can be applied to QA as well. The major difference is QA is technically sound. They can get the apk by using GitHub trigger like push, pull_request, etc. The problem here is why to create this unnecessary push and pull request trigger to just generate apk. It will clutter the git history and unnecessary PR requests.
  3. Configurability :
    • The ability to build apk with configured parameters is something I find very useful. The most common parameters we configure in apps are.
      1. API Endpoints
      2. Secret/Access Keys.
      3. Other business based params like Threshold values, Number of Re attempts. etc.

How do we solve this problem ?

Github Actions provides various trigger events to run your workflow. Following is an example of some of the trigger events, like pushing a commit on the main branch, creating pull requests, tags, etc.

on:
  # Trigger the workflow on push or pull request,
  # but only for the main branch
  push:
    branches:
      - main

  pull_request:
    branches:
      - main

    branches:    
      # Push events on main branch
      - main
      # Push events to branches matching refs/heads/mona/octocat
      - 'mona/octocat'
      # Push events to branches matching refs/heads/releases/10
      - 'releases/**'

    tags:        
      - v1             # Push events to v1 tag
      - v1.*

Enter fullscreen mode Exit fullscreen mode

To trigger an event manually, Github actions have added a new trigger event called workflow_dispatch. Below is the sample code and UI. (More in the next section)

on: 
  workflow_dispatch:
    inputs:
      logLevel:
        description: 'Log level'     
        required: true
        default: 'warning'
      tags:
        description: 'Test scenario tags' 
Enter fullscreen mode Exit fullscreen mode

Alt Text

Setup

First we need to setup a configurable parameters in android and then we can update those params using GitHub inputs in the workflow. So will start with android first.

Configurable properties in android.

  1. We need to set up the configurable parameters in a file. In this case, we are putting this into local.properties file, which we usually do not check-in VCS. Note: It can be any file. We just to point to that file in build.gradle.
  2. Create or update the local.properties file like this.

    BASE_URL="https://production.example.com/api/v1"
    MAP_KEY="production_sample_key"
    RETRY_ATTEMPTS=3
    THRESHOLD_VALUE=0.05
    
  3. Update build.gradle in the app like this.

    def localProperties = new Properties()
    localProperties.load(new FileInputStream(rootProject.file("local.properties")))
    
    android {
    
        defaultConfig {
            //...
            buildConfigField("String", "APP_BASE_URL", localProperties['BASE_URL'])
            buildConfigField("int", "APP_RETRY_ATTEMPTS", localProperties['RETRY_ATTEMPTS'])
            buildConfigField("float", "APP_THRESHOLD_VALUE", localProperties['THRESHOLD_VALUE'])
        }
    
        buildTypes {
            debug {
                resValue("string", "maps_key", localProperties['MAP_KEY'])
            }
            release {
               //..
            }
        }
    }
    
  4. We can refer local properties using BuildConfig in this app like this.

    class MainActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            val txtConfig = findViewById<TextView>(R.id.txtConfig)
    
            val configText = """
                BASE URL :  ${BuildConfig.APP_BASE_URL}
                MAP_KEY :  ${getString(R.string.maps_key)}
                RE-ATTEMPT :  ${BuildConfig.APP_RETRY_ATTEMPTS}
                THRESHOLD VALUE :  ${BuildConfig.APP_THRESHOLD_VALUE}
            """.trimIndent()
    
            txtConfig.text = configText
        }
    }
    

    Note: Sometimes the local.properties do not create BuildConfig on sync. It will get generated when we build the apk. Ref

  5. The output will look like this when we run the apk.
    Alt Text

If you are facing any issues. You can check out the sample workflow app here. For setting the local.properties please checkout this mindork blog.

Setup Github workflow

We create yaml file inside .github/workflow folder. To trigger the workflow manually we will use workflow_dispatch as a trigger. This will show the Run Workflow UI which we have seen above.

name: Manual Generate APK
on:
  workflow_dispatch:
    /....
Enter fullscreen mode Exit fullscreen mode

To show the input config values, we use inputs here. The baseUrl is an input key which we will use to read the value.

name: Manual Generate APK
on:
  workflow_dispatch:
    inputs:

      baseUrl:
        description: 'Base URL'
        required: true
        default: 'https://production.example.com/api/v1'

      mapKey:
        description: 'Map Key'
        required: true
        default: 'production_sample_key'

      reAttempt:
        description: 'Number of re-attempts'
        default: '3'

      thresholdValue:
        description: 'Threshold value'
        default: '0.05'
Enter fullscreen mode Exit fullscreen mode

Setting required params as true show an asterisk indicating a mandatory field. The description is shown as the field name in the UI. The default param will set the default value in the input field.
Alt Text

We will add command to build the apk.

- name: Assemble app debug APK
  run: bash ./gradlew assembleDebug --stacktrace
Enter fullscreen mode Exit fullscreen mode

Setting up bash script

If we try to run this workflow now, it will fail with error local.properties not found. This is because we have not checked-in that file in our VCS. We can fix this by creating local.properties on the fly while running the github workflow. We can do this by running the linux command in the workflow which creates local.properties file with all config values.

It's a better to wrap this sequence of linux command into bash script. It will avoid the clutter code in the workflow and provide more flexibility to update the script.

Now we will create and checked in the bash script file update_properties.sh in the VCS which will look this.

#!/bin/bash

touch local.properties
echo "BASE_URL=\"$1\"" >> local.properties
echo "MAP_KEY=\"$2\"" >> local.properties
echo "RETRY_ATTEMPTS=$3" >> local.properties
echo "THRESHOLD_VALUE=$4" >> local.properties
Enter fullscreen mode Exit fullscreen mode

$1, $2,$3, and $4 are the 4 arguments that will be passed into the script from the inputs values.

We need to run the bash script with input values before building the apk. We get these values by using github.event.inputs.{key} properties. You can find the source file here.

- name: Print Params Values
  run: |
          bash update_properties.sh ${{ github.event.inputs.baseUrl }} ${{ github.event.inputs.mapKey }} ${{ github.event.inputs.reAttempt }} ${{ github.event.inputs.thresholdValue }}
- name: Assemble app debug APK
  run: bash ./gradlew assembleDebug --stacktrace
Enter fullscreen mode Exit fullscreen mode

Fun Fact: I was able to run the script after 17 failed attempts. So don't worry if the script does not work for the first time. Keep googling as I did 😂
Alt Text

Run the workflow

  • We can now run the workflow from UI in github action tab.

Alt Text

Alt Text

  • If you want to update the existing local.properties or any other file which you are already using then you need to change your script. Please check this branch for example.

Key things to remember

  • The workflow UI won't show up if the workflow is pushed into a separate branch but not in the main/master branch. The workflow will only be visible if it is pushed to main/master branch.
  • It won't run on a branch that does not have the workflow. The UI will look like this.

Alt Text

  • The linux text replace sed -i command will work on github action which runs on the linux. For mac we need use sed -i ""
  • Sometimes the local.properties do not create BuildConfig on sync. It will get generated when we build the apk. Ref

Conclusion

The GitHub manually trigger was something the dev asked for when the action was initially launched. Because it provides more flexibility and ease of use, especially for clients/QA's. They also have an API to trigger this workflow which opens the door for more automation.

In case of any question and issue, you can reach me on Twitter. Stay updated by subscribing to my blog. Thank you for taking the time to read this article. If you like this article, please like and share it with your friends/colleagues and spread the word.

Resources

  1. https://github.blog/changelog/2020-07-06-github-actions-manual-triggers-with-workflow_dispatch/
  2. https://blog.mindorks.com/using-local-properties-file-to-avoid-api-keys-check-in-into-version-control-system
  3. https://stackoverflow.com/questions/16745988/sed-command-with-i-option-in-place-editing-works-fine-on-ubuntu-but-not-mac
  4. https://abelsquidhead.com/index.php/2020/07/29/manually-trigger-actions-workflow-in-github-how-did-i-not-know-about-this/
  5. https://github.community/t/workflow-dispatch-event-not-working/128856

Top comments (0)