This is a simple android application that will be used to demo android CI/CD process using GitHub Action as part of ACL session.
Continuous integration
is a practice of integrating source code into a shared repository frequently, then each commit is verified by an automated system to check for errors or get reports on code quality.
Examples of continuous integration processes for android projects include:
- Performing build.
- Linting - Analyzing source code to check for language syntax violation.
- Executing tests ie. unit tests and instrumentation tests.
- Generating reports. Eg tests reports, lint reports etc.
Continuous delivery
is a practice of getting changes(ie. new features, bug fixes, configuration changes) into production or into the users’/testers’ hands safely and quickly or frequently.
Examples of continuous delivery processes for android projects include:
- Creating a GitHub release.
- Deploying to Firebase App Distribution.
- Deploying to Google Play Store.
- Updating automated documentation. Eg. when using automated documentation tools like Dokka.
- GitHub Actions.
- Gitlab CI/CD
- Jenkins
- Circle CI
- Travis CI
- Team City
Simplified overview of the project's CI/CD pipeline:
In order to create a workflow which will contain the jobs required for the CI/CD processes, create a new directory in the project's root destination from android studio or your file explorer.
From Android Studio create
.github/workflows
directory in the project's root destination. Like so.
From file explorer create
.github->workflows
in the project's root destination.
Create a new YAML file inside the workflows directory. Eg. build.yml
Add the code from the snippets below in the YAML file that you've created.
Start by defining the name of the workflow and what events will trigger the execution of the workflow on the specified branch(es).
name: Build # name of the workflow
on:
push: # specifies events to trigger the workflow
branches: [ feature ] # branches that trigger the workflow
...continued # do not include in workflow file
The first job which is the build workflow is defined below the jobs:
tag.
Start by declaring the name of the job which is optional. Then declaring the OS version of the runner that will execute the job on. Examples of OS versions include: linux-latest
, windows-latest
and mac-os-latest
.
The next part is declaring the steps to be executed in this job. The first step is an action execution to checkout the code from the repository to the runner then setup JDK version 11 on the the runner and finally execute ./gradlew build
gradle command.
...continuation # do not include in workflow file
jobs: # groups the jobs to be executed in this workflow
build: # defines a job called build
name: 🔨 Build # [optional] name of the job
runs-on: ubuntu-latest # the job will be executed on ubuntu runner. Other include: Microsoft Windows & MacOS runners
steps: # groups together all the steps that run in build job
# Checks out code from the VCS to the runner
- name: Checkout code # [optional] specifies the name of the step
uses: actions/checkout@v2 # specifies which action and version to execute ie. checkout@v2
# Setup JDK Version 11 in the runner
- name: Set up JDK 11
uses: actions/setup-java@v1
with:
java-version: '11'
# Allow permissions to make gradle executable - This can removed by adding the gradlew file permission
# directly into the repository using `git update-index --chmod=+x gradlew`
# - name: Make gradle executable
# run: chmod +x ./gradlew
# Execute gradle build command with stacktrace flag
- name: Build with gradle
run: ./gradlew build --stacktrace # Execute gradle script to build project
Link to
actions/checkout@v2
README/documentation
Link to
actions/setup-java@v1
README/documentation
Link to
actions/upload-artifact@v1
README/documentation
This job checkout code to the runner and executes ./gradlew lint
gradle command which performes lint check and auto-generates a test report which is then uploaded as an artifact by actions/upload-artifact@v2
action so that it can be accessed later by the user.
An example of the lint report generated by the Lint Job can be found here.
lint-check: # defines another job called lint
name: 🔍 Lint
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Lint check
run: ./gradlew lint # Execute gradle script to perform lint check
- name: Generate lint report
uses: actions/upload-artifact@v2 # Uses upload-artifact@v2 action to upload lint report artifact
with: # Define extra parameters
name: lint_report.html # Name of the artifact to be uploaded
path: app/build/reports/lint-results-debug.html # Specifies the path where the artifact to be uploaded is located
This job execute ./gradlew test
gradle command which executes the unit tests present in the codebase and auto-generates a test report which is then uploaded as an artifact.
This job needs to wait until lint-check
job executes successfully hence the need for the need:[job-name1, job-name2]
tag.
An example of the unit tests report generated by the Unit Tests Job can be found here.
unit-tests: #Defines another job called unit tests
name: 🧪 Unit Tests
needs: [ lint-check ] # This job's execution is dependant on whether `lint-check` job completes successfully
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Execute unit tests
run: ./gradlew test --stacktrace # Execute gradle script to execute unit tests
- name: Generate test report # Generates tests reports as an artifact
uses: actions/upload-artifact@v2
with:
name: unit_tests_report.html
path: app/build/reports/tests/testDebugUnitTest/
This job generates a debug APK by executing ./gradlew assembleDebug
gradle command. The generated debug APK is then uploaded as an artifact by executing actions/upload-artifact@v1
action.
This job's execution dependes on whether build, lint-check and unit-tests jobs succed or not.
generate-apk: # Job to generate debug apk
name: ⚙️Generate APK
needs: [build, lint-check, unit-tests]
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up JDK 11
uses: actions/setup-java@v1
with:
java-version: '11'
- name: Build debug apk
run: ./gradlew assembleDebug --stacktrace
- name: Upload debug apk
uses: actions/upload-artifact@v1
with:
name: Android-CI-CD
path: app/build/outputs/apk/debug/app-debug.apk
-- WIP explanation
create-release: # Job to create a new github release and upload the generated apk
name: 🎉 Create Release
needs: [ generate-apk ]
runs-on: ubuntu-latest
steps:
- name: Download APK from build
uses: actions/download-artifact@v1
with:
name: Android-CI-CD
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
- name: Upload Release APK
id: upload_release_asset
uses: actions/[email protected]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: Android-CI-CD/app-debug.apk
asset_name: Android-CI-CD.apk
asset_content_type: application/zip
Deploying to Firebase App Distribution
-- WIP explanation
firebase-deploy:
name: 📨 Deploy to Firebase App Distribution
needs: [ generate-apk ]
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v1
- name: Download APK from build
uses: actions/download-artifact@v1
with:
name: Android-CI-CD
- name: Upload Artifact to Firebase App Distribution
uses: wzieba/[email protected]
with:
appId: ${{secrets.FIREBASE_APP_ID}}
token: ${{secrets.FIREBASE_TOKEN}}
groups: testers
file: Android-CI-CD/app-debug.apk
// ToDo ?:(