Creating Automated CI/CD Workflows for Android Apps with GitHub Actions

Creating Automated CICD Workflows for Android Apps with GitHub Actions

Creating Automated CI/CD Workflows for Android Apps with GitHub Actions

GitHub Actions offers a solid solution for Continuous Integration and Continuous Deployment (CI/CD) requirements, meeting the growing demand for user-friendly tools in this space. What distinguishes GitHub Actions is its seamless connection with your codebase, which enables more than just typical DevOps chores. This tool allows you to automate workflows that are triggered by events in your repository. For example, you can set up a workflow to assign labels automatically whenever a new issue is opened .Rather of going further into the enormous possibilities of GitHub Actions, let’s compare it to other competitors. Following that, I’ll walk you through the process of configuring GitHub Actions for Android projects

GitHub Actions for Android

GitHub Actions has emerged as a popular choice for Continuous Integration/Continuous Deployment (CI/CD) workflows. In this guide, we will explore the fundamentals of setting up CI/CD pipelines for Android projects using GitHub Actions.

Workflow scripts are stored in the .github/workflows/ directory within the root directory of your repository. For instance, the full path for a file named push.yaml would be .github/workflows/push.yaml. The snippet below represents the initial section of push.yml and illustrates the fundamental configuration of a basic workflow triggered after every push to the master or develop branches.

				
					name: Push 
on: 
push: 
branches: [ "develop", "master" ] 
workflow_dispatch: 
jobs: 
build: 
name: Build 
runs-on: ubuntu-latest 
steps: 
 - run: echo "The job was automatically triggered by a ${{ github.event_name }} event
				
			

Explanation of new commands: 

  •  name: This field specifies the name of the workflow, which will be visible on the GitHub Actions page, allowing users to identify the purpose of this specific workflow. 
  • on: In the ‘on’ section, you define the events that can trigger the workflow. In this example, the workflow is triggered for every push event occurring in the master or develop branches of the repository. 
  • workflow_dispatch: When this flag is present, it enables manual triggering of the workflow directly from the GitHub interface. Users can initiate this workflow at their discretion. 
  • jobs: Workflows can contain one or more jobs, each of which can run in parallel (the default behavior) or sequentially. In this case, there is a single job named build with a name parameter set to ‘Build’. 
  • runs-on: This field specifies the type of machine environment in which the job will execute. For example, ubuntu-latest signifies that the job will run on a machine with the latest version of Ubuntu. 
  • steps: Each job consists of a series of steps. These steps can include shell commands or actions from the GitHub Marketplace. In this specific example, there is a single step demonstrated, using the run keyword to execute an echo command. The message being echoed contains details about the event that triggered the workflow. 

These components collectively form the structure of a GitHub Actions workflow, allowing developers to automate various tasks and processes in their repositories. 

Android-Specific GitHub Actions

For Android development, GitHub Actions offers specific steps tailored to the Android ecosystem. Let’s explore a workflow that builds and signs a release build of an Android app: The next example will show the bottom part of the push.yml file with all the steps needed to build the project after every push event:

				
					jobs: 
  build: 
    name: Build 
    runs-on: ubuntu-latest 
    steps: 
        - run: echo "The job was automatically triggered by a ${{ github.event_name }} event." 
        - run: echo "This job is running on a ${{ runner.os }} server hosted by GitHub!" 
    - uses: actions/checkout@v3 
    - run: echo "The ${{ github.repository }} repository has been cloned." 
    - run: echo "Setting up JDK"  
    - name: set up JDK 11 
      uses: actions/setup-java@v3 
      with: 
        java-version: '11' 
        distribution: 'temurin' 
        cache: gradle 
    - run: echo "The workflow is now ready to test your code." 
    - name: Grant execute permission for gradlew 
      run: chmod +x gradlew 
    - run: echo "Building Debug APK."   
    - name: Build with Gradle 
      run: ./gradlew build 
    - run: echo "Build status report=${{ job.status }}." 
				
			

In the following sequence, the  step, uses: actions/checkout#v3, performs the task of checking out the repository onto the machine where the job is being executed. 

Continuing, the subsequent action includes a distinctive name field, providing a description of the uses: actions/setup-java@v3 action. Following this, there are additional details provided within the with tag. This section specifies the Java version and the build tool in use. In this specific instance, Gradle is the designated build tool. 

Further messages are displayed, along with some adjustments to permissions. Towards the end of the script, the final command run: .gradlew build is executed. This command effectively builds the debug version of the project. A subsequent print statement reports the status of the build process. 

In the next example, we’ll walk through a job designed to build the release version of our project and sign it using a keystore stored within GitHub secrets. 
				
					jobs: 
  build: 
    name: Generate App Bundle 
    runs-on: ubuntu-latest 
    steps: 
    - uses: actions/checkout@v3 
    - name: set up JDK 11 
      uses: actions/setup-java@v3 
      with: 
        java-version: '11' 
        distribution: 'temurin' 
        cache: gradle 
    - name: run: chmod +x gradlew 
    - name: Bundle 'release' with Gradle 
      run: ./gradlew bundleRelease 
    - name: Sign AAB 
      id: sign_aab 
      uses: r0adkll/sign-android-release@v1 
      with: 
        releaseDirectory: app/build/outputs/bundle/release 
        signingKeyBase64: ${{ secrets.SIGNING_KEYSTORE }} 
        alias: ${{ secrets.SIGNING_ALIAS }} 
        keyStorePassword: ${{ secrets.SIGNING_KEYSTORE_PASSWORD }} 
        keyPassword: ${{ secrets.SIGNING_ALIAS_PASSWORD }}   
    - run: echo "Build status report=${{ job.status }}." 
    - name: Upload App Bundle 
      uses: actions/upload-artifact@v1 
      with: 
        name: aab 
        path: ${{steps.sign_aab.outputs.signedReleaseFile}}
				
			

Moving on to the new commands introduced in this example, the first one is run: ./gradlew bundleRelease. Unlike the previous build command in our initial example, this triggers the creation of a release build variant and generates an Android App Bundle (.aab) file instead of the regular .apk file. To produce an APK, a simple command like ./gradlew buildRelease can be used. 

The next command, uses: r0adkill/sign-android-release@v1, is a community-created action. It takes the freshly generated build file, in this case, the .aab file, and signs it using the specified certificate and variables within the with: container. Notably, GitHub secrets are utilized as a part of this command, enhancing security and confidentiality. 

In this second example, the final new command is uses: actions/upload-artifact@v1. This action stores a variable at the specified path and assigns it a given name. This stored artifact can be accessed later within the same workflow, enabling seamless continuity of tasks. Further details on this process will be explored in the next and final example. 

Deploying to GitHub Packages 

In the final step, we will examine another job that utilizes the previously executed job to obtain the resulting Android App Bundle (AAB) file. This AAB file will then be uploaded to a GitHub Release within the specified repository. 

				
					jobs: 
  release: 
   name: Release App Bundle 
   needs: build 
   runs-on: ubuntu-latest 
   steps: 
     - name: Download AAB from build 
       uses: actions/download-artifact@v1 
       with: 
         name: aab 
     - name: Create Release 
       id: create_release 
       uses: actions/create-release@v1 
       env: 
         GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 
       with: 
         tag_name: Tag Name 
         release_name: Release Name 
     - name: Upload Release AAB 
       id: upload_release_asset 
       uses: actions/upload-release-asset@v1.0.1 
       env: 
         GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 
       with: 
         upload_url: ${{ steps.create_release.outputs.upload_url }} 
         asset_path: aab/app-release.aab 
         asset_name: ${{ github.event.repository.name }}.aab 
         asset_content_type: application/zip 
     - run: echo "Upload returned with status=${{ job.status }}." 
				
			

In this section, there are three key functions, culminating in a final status update for the upload job. Before delving into each command individually, let’s address a new addition to the workflow. The ‘needs’ field presupposes the existence of another job with the name ‘build’, which this job relies on. It potentially utilizes any assets the preceding job might have uploaded. 

Now, focusing on the commands, the process begins with uses: actions/download-artifact@v1. This command downloads an asset named ‘aab’, the same asset uploaded in our previous example. It assumes that a job similar to the one in the prior example has run earlier, providing the necessary artifacts. 

Moving forward, uses: actions/create-release@v1 attempts to create a new GitHub release for the specified repository. It uses the GitHub token stored in secrets for authentication. This function requires both a ‘tag_name’ and ‘release_name’, for which we’ve inserted placeholders to simplify the example. 

The final new function introduced in this section is uses: actions/upload-release-asset@1.0.1. This action takes the ‘.aab’ file from our asset using the ‘asset_path’, as defined by the ‘asset_content_type’ variable and named by ‘asset_name’. The action attempts to upload this asset to the specified ‘upload_url’ path. Upon completion, it emits a success status, which we capture and print in the subsequent ‘echo’ command. 

Conclusion 

GitHub Actions offers a powerful platform for automating Android CI/CD pipelines. This guide provides a foundation to set up basic and advanced workflows. With the support of the GitHub community, customizing actions for specific needs becomes accessible.  

Fill out the form and we'll be in touch as soon as possible.

    CloudZenix Services