GitHub Actions: Beyond CI/CD
Exploring the new GitHub Actions: don’t just automate code; automate your software development life cycle
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.
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:
- Basic actions:
checkout
,upload-artifact
,create-release
- The
setup
series, which provides ready-made configurable environments for working with specific programming languages:setup-node
,setup-python
,setup-go
,setup-ruby
,setup-dotnet
,setup-java
,setup-elixir
,setup-haskell
- Cloud platforms:
gcloud
,aws
,azure
- Miscellaneous tasks, e.g. stale (mark Issue and Pull Requests that did not have recent interactions), labeler (label/triage Pull Requests based on paths modified)
- … and much more.
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 outputtf_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
, orvalidate
ran on a Pull Request will show thediff
of the plan right in that Pull Request!
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.
Small sample of community actions:
- PR Merge Bot: merge Pull Requests automatically as soon as certain conditions are met
- Atlassian Jira GitHub Actions: create issues from
//TODO:
or#TODO:
found in code - Next SemVers: takes a semantic version (e.g.
1.2.3
) and outputs the next patch (1.2.4
), minor (1.3.0
) and major (2.0.0
) versions
More Ways to Find Actions
- The
awesome-actions
repository has a great categorized list of links to community actions, documentation, tutorials, examples, and more:
https://github.com/sdras/awesome-actions - The
github-actions
topic on GitHub itself:
https://github.com/topics/github-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.