GitHub Actions: Beyond CI/CD

Exploring the new GitHub Actions: don’t just automate code; automate your software development life cycle

Pierre Nicolas Durette
Slalom Build
6 min readFeb 5, 2020

--

Laptop screen showing the GitHub Actions homepage, next to a framed schnauzer picture.

Note: this post assumes the reader possesses a minimum understanding of continuous integration (CI) and using GitHub for version control.

After more than a year in beta, GitHub Actions has finally made it to general availability at GitHub Universe 2019. What is GitHub Actions? Think of it as GitHub’s own integrated TravisCI or CircleCI, but it can do so much more than build, test, and deploy code.

GitHub Actions vs. the World: Workflows

In TravisCI, CircleCI, or even GitLab CI/CD¹, developers author one YAML file that describes the steps to run—typically to build/test/push/deploy software—in response to a small set of events such as pushes, pull requests, tags, API triggers or a cron schedule.

In GitHub Actions, this is called a workflow, and a repository can contain more than one, each in its own YAML file.

Anatomy of a Workflow

Workflows are made up of one or more jobs. Each job runs in its own environment — Ubuntu, Windows, macOS or self-hosted — and can be run in parallel or chained to other jobs.

A default ‘Simple Workflow’: a ‘build’ job with its 3 steps.

In turn, jobs run a sequence of steps. Each step is either:

  • a shell command: run: echo Hello, world!
  • an action:uses: actions/checkout@v1.

More on actions later. Each step can also have an optional name for display during a run.

Workflow expressions

The expression syntax is particularly concise and elegant: GitHub Actions exposes rich contextual information and functions that can be expanded or used to control flow with ‘if’ conditional statements:

steps:
- run: echo Hi ${{ github.actor }}!
if: runner.os == 'Linux'

In this example, the user login name is provided by the github.actor property, while the operating system name of the current runner is provided by the runner.os property.

GitHub Actions adopts useful features already popularized by similar tools such as matrix builds. These run the same job in parallel across each permutation of operating systems, platforms, and versions specified:

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

The job configuration above specifies two OS and three Node versions, which would produce a total of six concurrent runs. The matrix context exposes the value of the current configuration anywhere within the job, in this case matrix.os and matrix.node.

Power of Events

One element that puts GitHub Actions ahead of similar tools is the vast array of events available. A workflow can be triggered by virtually anything that can happen inside GitHub. For example:

on: [create, delete]

This would trigger on the creation or deletion of a branch or tag.

Other available events:

  • push
  • pull_request
  • pull_request_review, pull_request_review_comment
  • Branch & tag: create, delete
  • issues, issue_comment
  • Repository made public
  • Wiki page created/updated: gollum
  • label
  • … and much more.

Even better, you can fine-grain which activity type(s) within an event to act on. For example the pull_request event includes these activities:

  • assigned, unassigned
  • labeled, unlabeled
  • opened, closed, reopened, edited
  • ready_for_review, review_requested

Which can be leveraged like so:

on:
pull_request:
types: [opened, reopened]

For the push and pull_request events, GitHub Actions adds extensive filtering options for branches, tags, and file paths as well. The latter allowing the creation of unique workflows for distinct microservices in a monorepo.

GitHub Actions workflows can be run on:

  • schedule using cron syntax
  • External API trigger (repository_dispatch): specific workflows can be triggered by a simple call to the GitHub API.

Actions: Abstract, Reuse, Share

Still, the biggest feature of GitHub Actions is undeniably the actions themselves:

Actions are the smallest portable building block of a workflow. You can create your own actions, use actions shared from the GitHub community, and customize public actions. (Official Documentation)

Actions are comparable to “open source blackboxes”: they abstract complex tasks as one workflow step. Similar to functions they can have parameters and outputs. But most of all they’re designed to be open and made available to other repositories.

Using Actions

As seen earlier, a workflow job step uses an action. Workflows can use actions in many ways:

  • From within the repository: uses: <path>
  • From a Docker image: uses: docker://<repository>:<tag>
  • From another GitHub repository: uses: <user>/<repo>@<ref>

Consider an example of the GitHub repository action:

- uses: actions/checkout@v1

Here, action/checkout@v1 means “release v1 of action at https://github.com/actions/checkout”. Branches, tags and commits can be used instead of releases to pin actions to specific versions. For example:

- uses: repo/action@a-branch  # a branch
- uses: repo/action@v1.0.4 # a tag
- uses: repo/action@6ab37fc # a commit
- uses: repo/action # the 'master' branch

Inputs

Often the first action used in a job, actions/checkout checks out the appropriate commit. Under the hood this action does way more than a simple git checkout, but that complexity is hidden. What if the checkout must be customized? Like many actions, actions/checkout accepts various input parameters:

- uses: actions/checkout@v1
with:
submodules: true
fetch-depth: 1

Actions hosted on GitHub must define a metadata file called action.yml which will describe the available inputs, outputs, and more. For the example above see: https://github.com/actions/checkout/blob/v1.0.0/action.yml.

Official Actions

GitHub provides a number of readily available actions under the actions organization:

Third-Party Action Example: HashiCorp Terraform

GitHub Actions really shines when it comes to third-party actions. HashiCorp maintains the aptly-named hashicorp/terraform-github-actions (docs) that can, amongst other things:

  • Install a specific version of Terraform
  • Provide usable action outputs when relevant. For example a step running a terraform plan that has changed will output tf_actions_plan_has_changes true which can be used to control subsequent behaviour
  • Create Pull Request comments when relevant. For example a terraform plan, fmt, or validate ran on a Pull Request will show the diff of the plan right in that Pull Request!
Pull Request message following a failed ‘terraform fmt’ (format).

Actions in the GitHub Marketplace

GitHub Actions are being touted as being, “A community-led approach to software automation” which is in part driven by the GitHub Marketplace.

The GitHub Marketplace for Actions listing over 1500 Actions as of December 2019.

Small sample of community actions:

More Ways to Find Actions

Important note: earlier during the beta period, GitHub Actions used HCL as opposed to YAML. Some actions (including first-party) might have not yet been converted to the new format.

Ways to get started

  • The GitHub Actions documentation;
  • From any repository, select the ‘Actions’ tab and pick from a list of workflow templates automatically detected based on your project type
  • Use the ‘Actions’ tab powerful workflow editor to add Marketplace actions directly—featuring auto-complete, smart YAML indentation, error detection and more.

[1] GitLab CI/CD does have an include feature that technically allows more than one YAML file.

--

--

Pierre Nicolas Durette
Slalom Build

Montréal-native in Toronto. Engineer. Tech, things, code, automation, design, synth pop, schnauzer.