Navigate back to the homepage

GitHub Actions - A basic workflow to get you started

Dion Segijn
March 29th, 2020 · 3 min read

With the increasing popularity of GitHub Actions people around me start to show more interest but don’t exactly know where to start. Getting it up and running might feel like a big step though this article will demonstrate that it’s very easy!

This article won’t discuss which of CircleCI, Bitrise, GitHub Actions or other platforms is better as CI or how they compare. I used them all and they can all do the basic stuff I’m going to demonstrate in this article. However, recently I configured GitHub Actions for some open-source projects and commercial ones. The documentation, extensibility with plugins and the fact that you have everything in one place when using GitHub as your repository is really great.

When you start with GitHub actions there are a couple of things you need to know:

  • GitHub Actions are configured with Workflows
  • Workflows are written in yaml
  • Everything you do happens in a folder named .github in the root of your repository, This is also where workflows live.

What is a workflow?

A workflow is a configuration file in your repository that is triggered by an event that will run tasks defined by you.

The documentation of GitHub explains very well what a workflow contains:

“Workflows must have at least one job, and jobs contain a set of steps that perform individual tasks. Steps can run commands or use an action. You can create your own actions or use actions shared by the GitHub community and customize them as needed.”
Source: configuring-a-workflow

Basic workflow

Let’s create a workflow that runs the tests of your Android project.

1) Create a folder in the root of your project named .github with a folder inside named workflows. This is where you’ll be putting all your workflows.

2) Now let’s add the following workflow in there. Create a file in the workflow directory and name it however you want (for example: ci-{appName}.yaml where appName is replaced with the name of your app). Mine is ci-konfetti.yaml for example.

1name: CI
2
3on: push
4
5jobs:
6 run-test:
7 runs-on: ubuntu-latest
8 steps:
9 - name: Checkout
10 uses: actions/checkout@v2
11
12 - name: Run tests
13 run: ./gradlew test

This is a very basic workflow named CI containing one job named run-test. The job runs two steps in consecutive order:

  1. Checking out your repository (Action)
  2. Run tests in the root directory of your repository (Command)

As mentioned earlier in the quote above, a step can either be a command (bash) or an action. An action is basically a plugin written by you or someone else that can easily be reused. The checkout step is such an action, open-sourced by GitHub here.

When you push this workflow to your repository it will look like this:

Succesful GitHub action

But there’s more to it. How does the workflow know when to run?

On line 3 we specified the workflow to run on each push to the repository, it will checkout the specific commit of that push. Instead of push there’s a very wide variety of events to listen and trigger your workflow with. You can find more about that here: events-that-trigger-workflows

Choosing system image

For each job you need to specify on what system it should run. This workflow is running on ubuntu-latest, this comes preinstalled with a bunch of things. Since our workflow is running successfully without the need of any extra configuration the ubuntu system image has everything we need for our Android project. More on what images are available and what they have preinstalled here: software-installed-on-github-hosted-runners

Caching

Above setup works pretty well. However if you happen to have a large Android project with a lot of dependencies you might want to improve this setup by adding caching. Every time a workflow is triggered it will start with a clean image from scratch. Nothing is cached, the current workflow doesn’t know anything of the previous workflow. Since there is no Gradle caching in place every build will be seen as a clean build.

Luckily there’s an out of the box solution (GitHub Action) for this, again open-sourced and provided by GitHub. More on that here: https://github.com/actions/cache

Now let’s put this action in-between the already defined steps.

1- name: Gradle cache
2 uses: actions/cache@v1
3 with:
4 path: ~/.gradle/caches
5 key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}
6 restore-keys: |
7 ${{ runner.os }}-gradle-

Here’s an example of me adding it to Konfetti an open-source project of mine: ee95bb9

The action will automatically add an extra step to download the Gradle cache (of the previous build) in the beginning of the workflow and when it’s done with the workflow it will automatically upload your cache.

Each repository can store up to 5GB of cache. Or as GitHub puts it:

“A repository can have up to 5GB of caches. Once the 5GB limit is reached, older caches will be evicted based on when the cache was last accessed. Caches that are not accessed within the last week will also be evicted.”

Source: actions/cache

For a larger project where I migrated to Github Actions and added caching I saved up to 5 minutes in build time.

Table showing improvements when adding github cache

In the image I was migrating a large project from Bitrise to GitHub Actions. After adding the caching we got a consistent 7-8 minute build time.

Using Kotlin in your Gradle files

If you’re using Kotlin in your Gradle files you need to make a small adjustment, see example below:

1- name: Gradle cache
2 uses: actions/cache@v1
3 with:
4 path: ~/.gradle/caches
5 key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }}
6 restore-keys: |
7 ${{ runner.os }}-gradle-

See how I added .kts to hashFiles(‘*/.gradle.kts’)

That’s it!

Now that I got you up to speed with the basics there’s a lot more that you can do with GitHub Actions. I’d recommend checking out the marketplace with existing actions you could add to your repository or play around with the available triggers to get the most out of GitHub actions and see what works for your codebase(s). You can find more details about this at: events-that-trigger-workflows

More articles from Dion Segijn

Konfetti - Simple 3D animation on a 2D Android canvas

Implementing a 3D rotation effect on a 2D canvas on Android might be easier than you think. This article demonstrates the three things you need in order to achieve that.

October 22nd, 2020 · 2 min read

GitHub Actions - Detect silently added permissions part-2

In the second part we continue with the extracted permissions and use diff to detect any changes. The results will be posted to the Pull Request.

April 29th, 2020 · 2 min read
© 2020–2021 Dion Segijn
Link to $https://twitter.com/DionSegijnLink to $https://github.com/DanielMartinusLink to $https://www.linkedin.com/in/dionsegijn/Link to $https://www.instagram.com/dionsegijn/