An Introduction to Github Actions

An Introduction to Github Actions

Github Actions enables you to create custom software development lifecycle workflows directly in your Github repository. These workflows are made out of different tasks so-called actions that can be run automatically on certain events.

This enables you to include Continues Integration (CI) and continuous deployment (CD) capabilities and many other features directly in your repository.

In this article, we are going to look at the core concepts of Github Actions and even dive into creating your own custom workflows.

Why care about Github Actions?

Before we get into the technical detail let's discussed why developers should care about Github Actions in the first place and which benefits they provide.

Build into Github:

Github Actions is fully integrated into Github and therefore doesn't require and external site. This means that it can be managed in the same place as all your other repository related features like pull requests and issues.

Multi-container testing:

Actions allow you to test multi-container setups by adding support for Docker and docker-compose files to your workflow.

Multiple CI templates:

Github provides multiple templates for all kinds of CI (Continous Integration) configurations which make it extremely easy to get started. You can also create your own templates which you can then publish as an Action on the Github Marketplace.

Great free plan:

Actions are completely free for every open-source repository and include 2000 free build minutes per month for all your private repositories which is comparable with most CI/CD free plans. If that is not enough for your needs you can pick another plan or go the self-hosted route.

Core concepts

Below is a list of the core concepts used in Github Actions that you should be familiar with when using it or reading the documentation.

Actions:

Actions are the smallest portable building block of a workflow and can be combined as steps to create a job. You can create your own Actions or use publicly shared Actions from the Marketplace.

Event:

Events are specific activities that trigger a workflow run. For example, a workflow is triggered when somebody pushes to the repository or when a pull request is created. Events can also be configured to listen to external events using Webhooks.

Runner:

A runner is a machine with the Github Actions runner application installed. Then runner waits for available jobs it can then execute. After picking up a job they run the job's actions and report the progress and results back to Github. Runners can be hosted on Github or self-hosted on your own machines/servers.

Job:

A job is made up of multiple steps and runs in an instance of the virtual environment. Jobs can run independently of each other or sequential if the current job depends on the previous job to be successful.

Step:

A step is a set of tasks that can be executed by a job. Steps can run commands or actions.

Workflow:

A Workflow is an automated process that is made up of one or multiple jobs and can be triggered by an event. Workflows are defined using a YAML file in the .github/workflows directory.

Using workflow and action templates

The easiest way to get your workflow going is by using one of the many workflow and action templates available on the Github Marketplace. If you are not sure which actions could be useful you can take a look at the provided suggestions from Github which are unique for every repository.

Adding a workflow template:

On the main page of your repository navigate to Actions.

Repository main page
Repository main page

Then pick a template you would like to use and click Set up this workflow.

nodejs action template
Node.js action template

Lastly, you can make changes in the editor and commit the action to your repository using the Start commit button.

Workflow editor
Workflow editor

Adding an action template to your workflow:

Action templates can be found on the Github Marketplace or directly in the workflow editor on the far right side.

Action templates in workflow editor
Action templates in the workflow editor

The template can be added by copying the code of the actions and pasting it into your .yml file. The unique action name and version number need to be defined with the uses keyword.

Action installation
Action installation

Note: Some actions require you to set certain variables which we will get into later.

Configuring a workflow

Now that you know how to use templates for your workflow you may still be wondering about the syntax used to write and configure workflows yourself.

This chapter will cover the general syntax as well as the core concepts of workflows and will provide all you need to know to start writing your own workflows.

Creating a workflow file:

Workflows can be created inside the .github/workflows directory by adding a .yml workflow file. For example, add .github/workflows/continuous-deployment.yml to your project.

After creating the file you can start working on your workflow.

General syntax:

Github Actions files are written using YAML syntax and have eighter a .yml or .yaml file extension. If you're new to YAML and want to learn more, I would recommend these two articles ,,Learn YAML in five minutes" or ,,Introduction to YAML".

Here are the most important concepts for the workflow file.

Name:

The name of your workflow that is displayed on the Github actions page. If you omit this field, it is set to the file name.

name: Continuous Deployment

On:

The on keyword defines the Github events that trigger the workflow. You can provide a single event, array or events or a configuration map that schedules a workflow.

on: push
# or
on: [pull_request, issues]

Jobs:

A workflow run is made up of one or more jobs. Jobs define the functionality that will be run in the workflow and run in parallel by default.

jobs:
  my-job:
    name: My Job
    runs-on: ubuntu-latest
    steps:
    - name: Print a greeting
      run: |
        echo Hello there!

More about that in a later section.

Env:

Env defines a map of environment variables that are available to all jobs and steps in the workflow. You can also set environment variables that are only available to a job or step.

env:
  CI: true

Choosing an environment:

It's important to run your workflows in the right environments so you can make sure that they will succeed in production circumstances. In this section, we will talk about how you can define the OS and software versions your workflow will run on and how you can configure your own build matrix to run a workflow in multiple environments.

Runs-on:

The runs-on keyword lets you define the OS (Operating System) your workflow should run on, for example, the latest version of ubuntu.

runs-on: ubuntu-latest

Build matrix:

A build matrix allows you to test across multiple operating systems, platforms and language versions at the same time. You can specify a build matrix using the strategy keyword and pass it to runs-on.

runs-on: ${{ matrix.os }}
strategy:
  matrix:
    os: [ubuntu-16.04, ubuntu-18.04]
    node: [6, 8, 10]

Here you run your project on two operating systems and three different versions of Node.js. For more information about a build matrix and the strategy keyword visit the documentation.

Caching dependencies:

Workflow runs often reuse the same output as the previous ones and can therefore be cached for an increase in performance. Every job run on Github-hosted runners starts in a clean virtual environment and don't use cache by default.

Caching is made possible by the Github cache action which will attempt to restore a cache based on the key you provide. If no match for the cache key is found it will create a new one after the successful completion of the job.

Input parameters:

  • key (Required): The key identifies the cache and is created when saving the cache.
  • path (Required): The file path of the directory you want to cache or restore.
  • restore-key (Optional):  An ordered list of alternative keys to use for finding the cache if no cache hit occurred for the key.

Output parameters:

  • cache-hit: Boolean variable with success state of the cache action

Example saving npm cache:

name: Caching with npm

on: push

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v1

    - name: Cache node modules
      uses: actions/cache@v1
      with:
        path: node_modules
        key: ${{ runner.OS }}-build-${{ hashFiles('**/package-lock.json') }}
        restore-keys: |
          ${{ runner.OS }}-build-${{ env.cache-name }}-
          ${{ runner.OS }}-build-
          ${{ runner.OS }}-

    - name: Install Dependencies
      run: npm install

    - name: Build
      run: npm build

    - name: Test
      run: npm test

In this example, we cache the node_modules directory of a Node project so the dependencies don't have to be installed with every single workflow run.

Persisting workflow data:

Artifacts allow you to share data between running jobs and save them after the workflow is complete. An artifact is a file or collection of files produced during a workflow run.

To share data between jobs:

  • Upload the file to an archive before the job is complete
  • After uploading the file can be used in other jobs within the same workflow

There are some common use cases for saving artifacts.

  • Log files
  • Test results, failures, and screenshots
  • Stress test performance output and code coverage results

Passing data between jobs:

You can use the upload-artifact and download-artifact actions to share data between jobs in a workflow.

name: Share data between jobs

on: [push]

jobs:
  upload-data-job:
    name: Upload data to artifact
    runs-on: ubuntu-latest
    steps:
      - shell: bash
        run: |
          echo Hello World! > hello-world.txt
      - name: Upload hello world file
        uses: actions/upload-artifact@v1
        with:
          name: hello-world
          path: hello-world.txt

  download-data-job:
    name: Download data from artifact
    needs: upload-data-job
    runs-on: windows-latest
    steps:
      - name: Download hello world file
        uses: actions/download-artifact@v1
        with:
          name: hello-world
      - shell: bash
        run: |
          value=`cat hello-world/hello-world.txt`
          echo $value > hello-world/hello-world.txt
      - name: Upload hello world to artifact
        uses: actions/upload-artifact@v1
        with:
          name: hello-world
          path: hello-world.txt

In this example, one service creates a file and uploads it to the artifact using the upload-artifact action. The download job then downloads the file and prints it to the console.

For more examples about persisting workflow data visit the official documentation.

Managing a workflow run

You can see and manage your workflow runs on the workflow page of your repository. It allows you to start, cancel and rerun jobs as well as see the results of already complete ones.

Workflow page
Workflow page

You will get a list of all the workflow run in your repository and you can get a more detailed view by clicking on a specific one.

Workflow detail page
Workflow detail page

More information can be found in the official documentation.

Building Actions

You can create actions by writing custom code that interacts with your repository and use them in your workflows or publish them on the Github Marketplace.

Github allows you to build Docker and Javascript actions which both require a metadata file with the name of action.yml to define the inputs, outputs and main entry point of your action.

Metadata syntax:

As stated above both Docker and javascript actions require a metadata file with the filename action.yml. Here is a list of the available fields and a small description of their functionality.

  • name (Required) - The name of the action that will be used to identify it in each job
  • description (Required) - A short description of the functionality of the action
  • runs (required) - The command to run when the action executes
  • author (Optional) - The name of the action's author
  • inputs (Optional) - A list of input parameters that can be passed in runtime
  • output (Optional) - A list of output parameters that subsequent actions can use later in the workflow

An example file could look something like this:

name: 'Hello World'
description: 'Greet someone and record the time'
inputs:
  who-to-greet:  
    description: 'Who to greet'
    required: true
    default: 'World'
outputs:
  time:
    description: 'The time we greeted you'
runs:
  using: 'node12'
  main: 'index.js'

Javascript Actions:

JavaScript actions can run directly on any of the GitHub-hosted virtual machines, and separate the action code from the environment used to run the code. They execute faster and are easier to build compared to the Docker alternative.

Docker Action:

Docker container package your actions code with the environment and thereby create a more consistent and reliable unit of work. A Docker container allows you to use specific versions of an operating system, dependencies, tools, and code.

Here are some resources that can help you get started with writing your first actions and publishing them:

Source:

Conclusion

You made it all the way until the end! I hope that this article helped you understand Github actions and how you can use them to automate your Github workflow.

If you have found this useful, please consider recommending and sharing it with other fellow developers. If you have any questions or feedback, let me know on twitter or using my contact form.

Read these next: