Skip to content

GitHub Actions are powerful but can be frustrating to work with. This repo contains documentation and examples intended to help developers (myself included) hit the ground running when setting up a project using GitHub's CI.

License

Notifications You must be signed in to change notification settings

DAKLabb/gh-actions

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation



Overview

GitHub Actions are both powerful and broadly accessible. With minimal effort and little/no cost, you can build out a robust CI/CD pipeline that is versioned along with your code and smoothly integrated with your release process. That said, the GitHub-provided documentation leaves something to be desired. The goal of this project is to provide helpful docs/examples to allow those new to GitHub Actions (or those like myself who get rusty between projects) to hit the ground running and build out effective workflows quickly. While my goal is to make these docs as complete as possible, feel free to reach out to me via email (david@daklabb.dev) if you have a question not answered here or would like to engage me to help you build out your CI/CD pipelines.

Creating a Workflow

To add a GitHub workflow to your repo, simply create a .github directory at the root of your project, and within this, create a workflows directory. You can then create yaml files in this directory to define your repo's workflows. For the simple "hello world" style workflow discussed below, I have created ci.yaml in this repo.

Triggers

There are lots of ways to trigger GitHub Actions, but the 5 I use most often are:

  • PR Sync: run the CI when a PR is opened/updated against a specific branch
  • Branch Push: run when code is pushed to a particular branch
  • Tag Publish: run when a tag is created in the repo
  • Cron: run on a schedule
  • Manual: run whenever manually triggered

Triggers live in the on stanza.

PR Sync

One of my key goals in adding workflows to a project is to maintain code quality. I may want to enforce style conventions with a linter, or perhaps run unit tests, or even just make sure my code builds. Generally, the goal will be that the main branch is stable/correct, and I will want to make sure that any PRs run checks before allowing code to be merged in. Note: Adding branch protection rules allows us to actually enforce this.

While there are a number of pull_request triggers, I tend to focus on those targeting specific branches. In this example, I trigger the CI on any PR targeting any of my key branches (main, staging, dev).

on:
  pull_request:
    branches:
      - main
      - staging
      - dev

Branch Push

In addition to having CI that protects prod/staging/dev branches by running workflows on PRs, you may also want to have CI that runs on pushes to particular branches. For example, if you have environments that you deploy your various builds to (main branch -> production, staging branch -> staging, dev branch -> develop) you may want to trigger CI to update those deployments when you push to a particular branch.

In this example, I trigger the CI on pushes to any of my key branches (main, staging, dev).

on:
  push:
    branches:
      - main
      - staging
      - dev

Tag Publish

As with pushes to branches, you can also trigger a workflow when a tag is pushed. Let's say I want to publish my Python SDK every time I create a semantically versioned tag in my repo. I could do something like this and push to pypi every time a new tag matching my naming convention is pushed to my repo.

on:
  push:
    tags:
      - 'v[0-9]+.[0-9]+.[0-9]+'

Cron

When managing client code, I like to write regression tests that run against the production system it is designed to interact with. This is a great use case for cron-based CI jobs. This example will run my CI job every day at 6am GMT. The benefit of jobs like this is that they can detect issues before work hours to alert you to issues before your users find them.

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

Manual

If you are reading this, you may have tried developing GitHub Actions before and come to the conclusion that they are a pain to test. This is a fact, but one method that can make life a bit easier is to add a manual trigger to your workflow. This allows you to trigger it ad-hoc for testing. It can also be useful for ad hoc integration tests or other useful workflows discussed in more details in a later section.

on:
  workflow_dispatch:

Environment & Secrets

Within the workflow/actions you can create env variables using env. This can be done at the workflow level, at the job level, or at the step level. These can be hardcoded values, but they can also be templated from the action context (more on this later) or from repo secrets (see screenshot below).

Action secrets and variable

Jobs/Steps

A workflow is made up of a series of jobs, and jobs are composed of steps. If the needs and if field of jobs are left blank, they will all run at the same time (as is the case here for jobs hello-world and hello-again).

Conditional Execution

Sometimes you don't want all the jobs in your workflow to run at the same time. Perhaps you create assets in one step that you need in a later one, or perhaps you have a long-running step that you don't want to trigger on all pushes.

Other Jobs

If you don't want to run a job until an earlier one succeeds, you can use the needs field to only run the job once the named job(s) have succeeded. The example below will only run job 3 once jobs 1 and 2 have passed.

job3:
  needs:
    - job1
    - job2

Basic Logic

You may also want to only run a job or step under certain conditions. In this example, "conditional-job" is only run when the workflow is triggered manually. Similarly, the optional step only runs if the input is set to true.

conditional-job:
  needs: hello-world
  if: ${{ github.event_name == 'workflow_dispatch' }}
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
    - name: optional step
      if: ${{ github.event.inputs.dump_context == 'true' }}

Using Open-Source Steps

In addition to writing your own GitHub Actions, you can use public open-source ones. In the example above, you can see me using the checkout action to check out my project. Below are some that I use frequently, but there are a lot out there. As with any other time you are using someone else's code, be cautious, especially if the job takes in any secrets.

Action Description
actions/checkout@v4 This action checks out your repository so your workflow can access it.
actions/setup-python@v5 This is used to setup Python, but there are a number of other setup actions available from GitHub as well as other developers.
upload and download artifacts Upload/Download actions artifacts from your workflow runs. More information and examples below.
actions/cache@v4 This action allows caching dependencies and build outputs to improve workflow execution time. More information and examples below.

Action Versioning

As you can see in the table above, I am referencing specific version of those actions (e.g., actions/checkout@v4). It is a good idea to periodically review all external actions and migrate to newer versions as they are released.

Useful Info

[skip ci]

For whatever reason, you may not want to run your CI on every push to your repo. Maybe it takes too long, maybe it is costly, maybe the commit is a work in progress and is going to fail CI. As documented by GitHub you can easily avoid triggering your workflow using any of the following commit messages: [skip ci], [ci skip], [no ci], [skip actions], [actions skip]

Example Workflows/Actions

Below are some example workflows/actions that might be useful. As with anything, this is just one way of doing things.

Retry

Unfortunately, GitHub doesn't expose an easy mechanism for retrying failing actions/workflows/jobs. After encountering this issue recently, I decided to document the solution I went with here.

Link Checking

Nothing makes a website seem more defunct than a bunch of dead links. That said, when you are moving fast, it can be a headache to keep on top of changes, and downright impossible to stay on top of links to external sites that end up restructuring. There are already great tools out there to solve this problem, and this uses one of them (linkchecker) to run checks via GitHub Actions in a simple and easy to customize way. More information can be found in the action's repo here.

For a quick example implementation, see the link-checking.yaml workflow running here.

Note:

This repo is a work in progress. I intend to continue adding to this as my schedule allows. Please consider opening issues or messaging me (david@daklabb.dev) if there is anything you would like to see me add.

About

GitHub Actions are powerful but can be frustrating to work with. This repo contains documentation and examples intended to help developers (myself included) hit the ground running when setting up a project using GitHub's CI.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published