With the evolution of Github Actions, we took the initiative and integrated a simple (but quite effective) CI / CD into our small, but already 2 years old, live project.

What for?

Perhaps there are some developers who do not make mistakes, but I am not one of them, so occasionally some small overloads or crashes happen and you have to urgently release a new version with an edit or rollback to the previous one. But those hours and days in which users stumble upon application crashes do not remain without traces both with customers and in the mood of a responsible developer.

How to minimize fails in production, I will tell you below.

Why do I personally have such fails?

  • Unreliable part of code
  • There is some kind of library and it crashes situationally
  • Some library (usually analytics) is updated to an unstable version.

Code review, Unit tests, static code analysis, UI tests, manual testing will help us with 1 point.

With 2-3 points – only UI tests and manual testing.

It only remains to automate this. At this stage, the choice fell on the then just appeared Github Actions, since the project code is also on Github. I must say right away that for a free GitHub account there are 2,000 free Action minutes per month.

How to start?

There is full of ready-made examples for various languages ​​and frameworks. This thing is configured through the YAML configuration file, which is located in the project repository.

Simple example for Android:

Description: for each push to any branch, a job is launched on the GitHub virtual machine with the Ubuntu OS. Task steps checking out our code, setting up JDK, running Gradle tasks to build.

In case of unsuccessful passage of any step, we will see the following picture:

you can also see the logs there:

Conveniently, with a Pull Request, they will immediately show us that our check sequence has failed.

And if you have GitHub integration with Slack, then also so:

And now step-by-step:

  1. Unit tests

You wrote Unit tests using JUnit, mockito, etc.

Now your tests are included in the verification sequence by adding the appropriate gradle task.

  • Static code analysis

You can use simple linters (detekt for kotlin, pmd for java).
Or a more complex option is sonarqube.

In the case of simple linters (for example, we have both java and kotlin):

in case of sonarqube:

  • UI tests

My approach is one “Smoke” test that simulates standard user actions in the application – to enter, select a product, place an order, track an order. You can use UIAutomator, Espresso, Kaspresso.

There are also 2 options to run here – an emulator on a GitHub virtual machine or cloud services such as Firebase Test Lab.
There are ready-made implementations for using the emulator inside GitHub: one and two.

In the case of Firebase Test Lab, you need to work with Google Cloud Platform through the gcloud CLI

For this to work, you need to create a project in Firebase, create a service account with admin rights in Google Cloud Console and upload the resulting JSON key to base64 in GitHub secrets for authorization in gcloud.

The general config in my case looked like this. The task is launched by a PR event in the master

It seems simple. The effectiveness depends on the tests written and the selected code analysis rules. It is possible to write several independent jobs to run them in parallel. In my case, everything goes sequentially. The verification process takes about 15 minutes on our project (virtual machine GitHub 2-core CPU, 7 GB RAM, 14 GB of SSD), but in fact, it is not critical, because while you “review” with your eyes, the results of these tests are coming up.

UI tests help out most of all – it happens that during their passage analytics crash after updating the library and you just understand that you should not update it.

Through gcloud, you can also deliver builds to Firebase App Distribution, release to Google Play, etc.