workflow-builder

GH Actions Workflow Builder

The quick and easy way to design a GitHub Actions workflow

MichaelCurrin - workflow-builder Made for GH Actions

Table of contents

About

Contributing

See Contributing guide.

Tips

If you want to know available options for a workflow and if you YAML syntax is valid, edit your workflow on GitHub rather than in a local file. Click on a field or at an indent level where you want to add a field, the press CTRL+SPACE. Youโ€™ll see available options for that context. This can save reading through the docs.

Resources

YAML syntax

Name

Example names for your workflow:

name: CI
name: CI Build
name: CI Test
name: Node CI
name: Deno CI
name: Python CI
name: Deno CI
name: Deploy GH Pages
name: Release

Triggers

For more options such as building on schedule, see my Triggers cheatsheet.

You can combine the sections below - for example, you can run a task on pushes main, on a nightly schedule and also whenever you press the run button (without having to make a commit).

On a commit push

Push a local commit or commit using the GitHub UI to trigger these on conditions.

The workflow above has been set up to not run if there are just changes in your docs directory. This is useful to reduce a run that gives no benefit and would still take up processing minutes allocate to your account. If you actually have content in your docs directory that matters like for a documentation site, then of course you can remove the ignore parts.

On a tag or release

Create a tag or a release to trigger your workflow.

On manual button press

Add this to your workflow.

This allows you to run this workflow manually by a button pressed in the Actions tab.

Simple

No arguments.

on: workflow_dispatch

Parametrized

Here the job expects to inputs.

on:
  workflow_dispatch:
    inputs:
      name:
        description: 'Person to greet'
        required: true
        default: 'Mona the Octocat'

      home:
        description: 'location'
        required: false
        default: 'The Octoverse'

See Workflow dispatch in my Dev Cheatsheets for more info.

On a schedule

Run on a given frequency, using Crontab notation.

Daily at midnight:

on:
  schedule:
    - cron:  "0 0 * * *"

This is useful for building a site, deploying an application or publishing to a package registry. Also, if you have any code quality or security scans such as a CodeQL workflow.

Job setup

Name

Give a job a name using a key under jobs and a pretty name under name field.

The job name might be similar to the workflow fileโ€™s name at the top, but I find it better to be more specific. Like โ€œNode CIโ€ for the workflow and โ€œBuild and deployโ€ as a job name.

Some samples:

jobs:
  build:
    name: Build
jobs:
  build-test:
    name: Build and test
jobs:
  build-deploy:
    name: Build and deploy

Operating systems

Define the operating system for a job.

Run on Ubuntu - this is the most common flow:

jobs:
  build:
    name: Build

    runs-on: ubuntu-latest

Run on a matrix of operating systems - useful if you need to test your app can install and run on all.

jobs:
  build:
    strategy:
      matrix:
        os: [ubuntu-latest, macOS-latest, windows-latest]
      runs-on: ${{ matrix.os }}

Note that quotes are not needed in YAML, whether for a string or array of strings.

You can also pin like ubunutu-18.04.

For a list of available systems, see GitHub Hosted Runners in the docs.

Environment variables

env:
  VAR_A: Hello

jobs:
  build:
    env:
      VAR_B: World

Steps

Define steps for a job to run

This section covers some common snippets across languages, for some of my common flows that build, test and deploy.

A note on wording - โ€œsetupโ€ is a noun, while โ€œset upโ€ is a verb. See both under the setup definition on the Merriam-Webster online dictionary.

Cookbook

For more details, please explore the Workflows section in my Code Cookbook.

That covers:

Outline

This is an outline of a generic workflow.

It includes emojis to brighten up the log and make it easier to scan visually. For set up, you might use something related to the language, like a snake for Python.

steps:
  - name: Checkout ๐Ÿ›Ž๏ธ
    uses: actions/checkout@v2

  - name: Set up Foo โš™๏ธ  # e.g. Python ๐Ÿ, Node or Ruby ๐Ÿ’Ž
    uses: actions/setup-foo@v2
    with:
      foo-version: '1.x'

  - name: Cache dependencies ๐Ÿ’พ
    uses: actions/cache@v2
    with:
      # ...

  - name: Install dependencies ๐Ÿ”ง
    run: # ...

  - name: Check formatting ๐ŸŽจ
    run: # ...

  - name: Lint ๐Ÿง
    run: # ...

  - name: Test ๐Ÿšจ
    run: # ...

  - name: Build ๐Ÿ—๏ธ
    run: # ...

  - name: Deploy to GitHub Pages ๐Ÿš€
    uses: # ...

Checkout

Using the checkout action.

steps:
  - name: Checkout ๐Ÿ›Ž๏ธ
    uses: actions/checkout@v2

Set up environment

Node

Node CI samples.

GH Actions comes with Node and Yarn set up already. But you can use an action if you ned more recent version, or to use a matrix of Node or Yarn versions.

Using the setup-node action.

steps:
  - name: Set up Node.js โš™๏ธ
    uses: actions/setup-node@v2
    with:
      node-version: '14.x'

Use a matrix of Node versions.

steps:
  strategy:
    matrix:
      node-version: [10.x, 12.x, 14.x]

  steps:
    - name: Checkout ๐Ÿ›Ž๏ธ
      uses: actions/checkout@v2

    - name: Use Node.js ${{ matrix.node-version }} โš™๏ธ
      uses: actions/setup-node@v2
      with:
        node-version: ${{ matrix.node-version }}

Deno

Deno CI samples.

Using the setup-deno action.

steps:
  - name: Set up Deno โš™๏ธ ๐Ÿฆ•
    uses: denolib/setup-deno@v2
    with:
    deno-version: v1.x

Go

Go CI samples.

Using the setup-go action.

steps:
  - name: Set up Go โš™๏ธ โฉ
    uses: actions/setup-go@v2
    with:
      go-version: 1.15

Rust

steps:
  - name: Install rustfmt
    run: rustup component add rustfmt
  - name: Check formatting
    run: cargo fmt --all --check
  - name: Build
    run: cargo build --tests --workspace
  - name: Test
    run: cargo test --workspace

Python

Python CI samples.

Using the setup-python action.

steps:
  - name: Set up Python โš™๏ธ ๐Ÿ
    uses: actions/setup-python@v2
    with:
      python-version: 3.x

Ruby

Using the setup-ruby action.

steps:
  - name: Set up Ruby โš™๏ธ ๐Ÿ’Ž
    uses: actions/setup-ruby@v1
    with:
      ruby-version: 2.7

Install dependencies

This section is not needed for Go or Deno where packages are installed on running, building, testing, etc.

See GH Actions Cache guide.

The cache step is optional but makes the build faster. It will load dependencies from the cache, if the dependencies file is unchanged. Also adds a clean-up step for you to save dependencies to the cache.

Python

steps:
  - name: Cache Python packages ๐Ÿ’พ
    uses: actions/cache@v2
    with:
      path: ~/.cache/pip
      key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
      restore-keys: |
        ${{ runner.os }}-pip-
        ${{ runner.os }}-

  - name: Install dependencies ๐Ÿ”ง
    run: |
      python -m pip install --upgrade pip
      pip install -r requirements.txt

Node

See related workflows here.

For NPM projects:

steps:
  - name: Cache NPM packages ๐Ÿ’พ
    uses: actions/cache@v2
    with:
      path: ~/.npm
      key: ${{ runner.OS }}-node-${{ hashFiles('**/package-lock.json') }}
      restore-keys: |
        ${{ runner.OS }}-node-
        ${{ runner.OS }}-

  - name: Install dependencies ๐Ÿ”ง
    run: npm install

For Yarn projects:

steps:
  - name: Get Yarn cache dir ๐Ÿ’พ
    id: yarn-cache
    run: echo "::set-output name=dir::$(yarn cache dir)"

  - uses: actions/cache@v1 ๐Ÿ’พ
    with:
      path: ${{ steps.yarn-cache.outputs.dir }}
      key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
      restore-keys: |
        ${{ runner.os }}-yarn-

  - name: Install dependencies ๐Ÿ”ง
    run: yarn install

This uses Yarnโ€™s cache directory. On Ubuntu this is ~/.cache/yarn/v6.

Ruby

This will setup Ruby in the environment, install gems with Bundler and even cache the gems for faster builds.

steps:
  - name: Set up Ruby ๐Ÿ’Ž
    uses: actions/setup-ruby@v1
    with:
      ruby-version: '2.7'
      bundler-cache: true

This Ruby setup flow works great for Jekyll projects too. Just make sure to add jekyll gem to your Gemfile and add a build step as below.

steps:
  - name: Build ๐Ÿ—
    run: bundle exec jekyll build --trace

Then your workflow will set up Ruby and Jekyll then build _site directory.

If you want to persist the _site directory output to serve as a GH Pages site, see the GitHub Pages section.

See more Jekyll samples and info in my Jekyll CI recipes.

Build

Create your production build output

Bundle your package or app so it can be installed, or build your website assets.

Make

This depends on setting up Makefile with a build target.

steps:
  - name: Build ๐Ÿ—๏ธ
    run: make build

Node

steps:
  - name: Build ๐Ÿ—๏ธ
    run: npm run build
steps:
  - name: Build ๐Ÿ—๏ธ
    run: yarn build

Jekyll

After setting up Ruby and installing dependencies with Bundler.

Build the site with Jekyll.

steps:
  - name: Build ๐Ÿ—๏ธ
    run: bundle exec jekyll build --trace

If you want to persist the _site directory output to serve as a GH Pages site, see the GitHub Pages section.

Deploy

GitHub Pages

This action will take a given build output directory (like dist, build or _site) and commit it as a single commit on the gh-pages branch at the root path. This can then be served as static assets (HTML, CSS and JS) on a GH Pages site.

For more info and related workflows and actions, see GH Pages in my Code Cookbook.

- name: Deploy to GitHub Pages ๐Ÿš€
  if: ${{ github.event_name != 'pull_request' }}
  uses: peaceiris/actions-gh-pages@v3
  with:
    github_token: ${{ secrets.GITHUB_TOKEN }}
    publish_dir: _site

Steps:

  1. Run your build command. This could be anything - such using Jekyll, MkdDocs, or npm run build (for React, Vue or Next.js).
  2. Then set up this action to point to that directory e.g. in _site or build.
  3. The action will copy the content root of the gh-pages branch (this it is default behavior).
  4. When that commit is pushed, then your GH Pages site will reload, using the latest content.

Note the use of the if condition on this step. This means that your entire workflow can run on a push to your main branch. But on a push to a Pull Request branch, the earlier build steps will run but the deploy step at the end will be skipped. This is useful to avoid deploy your work in progress branch to your production site.

Commits and Pull Requests

Workflows out in the world

Here are some workflows I have set up for my projects which Iโ€™d like to share.