Skip to content

Understanding GitHub Actions & Workflows

Brian Scholer edited this page Jan 8, 2022 · 1 revision

Terminology

Some of the terms can be confusing if you're not already well-versed with GitHub Actions.

GitHub Actions

This is the name of GitHub's CI system in general, sometimes abbreviated GHA, sometimes shortened to "actions" in sentences, but this can be confusing due to the meaning of an "action" within the system.

This is the top-level of what you define within GHA. The workflow is a single document that specifies:

  • The triggers (events) that cause it to be executed.
  • The jobs that execute, along with with the relationship between jobs (dependency).

A job is a set of steps to execute. The job is also the unit that gets run on a specific runner (virtual machine).

Other than dependency relationships and concurrency limits, jobs execute in parallel.

Step

A step is a unit of execution (task) within an individual job. The run step (which runs commands on the runner) is built in.

Other steps use the uses keyword and reference an action to execute.

Actions are self-contained code hosted in a GitHub repository, that makes up a single step in your workflow (even if it does a lot behind the scenes). Most of what is done within a job is an action of some kind, even very common operations like "checkout" are done as an action, whose code is maintained and hosted in a repository.

When an action is referenced in a step, it also includes a specific ref in the source repo to load, which is how "versioning" is done. The actual clone/checkout for a particular action is handled automatically.

Types of Actions

Actions can be written as one of 3 types:

  • NodeJS
  • Docker
  • Composite

Since the GitHub runner application is NodeJS, actions written in node are kind of the "native" type.

Docker actions use a dockerfile, so anything you can package into a container could be used as an action, however they can only be used in Linux runners.

Composite actions run a set of steps, similar to what would be run in a job. Previously, they only supported run steps, but have recently been expanded to allow the use of actions.

Reusable workflows allow you to define a workflow with optional inputs and outputs, and call it from another workflow.

There are some limitations to this functionality, like not allowing additional nested workflows. In addition, the calling workflow cannot use the called workflow as a piece of a larger job: a individual job defined in the calling workflow can call a single external workflow and do nothing else. That means the reusable workflow defines the entirety of what happens in each of its jobs.

A calling workflow can define additional jobs that have its own steps (jobs that don't call a reusable workflow).

Relating the terms to Ansible

We can relate the pieces of GitHub Actions to Ansible terms. The analogy is not perfect, but may be helpful nonetheless.

Workflow == Playbook

The workflow, like an Ansible playbook, is a single document containing one or more plays.

Job == Play

The job is similar to a play: it defines where things will run and some other properties about the run.

Steps == Tasks

The steps in a job are like the tasks in a play, each performing some action (in the general sense).

Actions == Modules/Roles

A GitHub action could be thought of like an Ansible module: it's some code that does something, accepts inputs (options), provides outputs (return).

A composite action could be thought of somewhat similarly to an Ansible role, in the sense that you can invoke it as a single step (task) via include_role in your job (play), and it's made up of several steps (tasks). A slight difference is that Ansible will show each task in the role individually, and GitHub will mostly show the composite action's steps' output combined as a single step.

Reusable Workflows == import_playbook

The playbook/play analogy actually works decently well with reusable workflows, in the sense that when you import_playbook, in an Ansible playbook, you are bringing the entire playbook in, including all its plays (you can't use it piecemeal within an existing play).

But unlike import_playbook, reusable workflows do allow for inputs and outputs.