GitHub Actions – Race workflow executions and cancelling current runs

GitHub Actions is a popular CI/CD service with a rich set of features. The YAML-based configuration gives plenty of flexibility when it comes to configuring parallelism, matrix builds, and step dependencies.

A common need that DevOps engineers have is to enforce that a given CI workflow will always deploy the latest commit and any previous running jobs for the same workflow will be forcefully canceled, if another comes in in the queue.

Let’s look at the easiest way how to achieve that.

If we start with a very simple GitHub Action recipe:

name: Build and Deploy

on:
  push:
    branches: ["main"]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Use Node.js
        uses: actions/setup-node@v3

      - name: Install dependencies
        run: npm install

      - run: npm run build
      - run: npm run deploy

If you have a busy project, where many people commit and push during the day, there might be scenarios where multiple commits are pushed too close to each other in the axis of time.

This will inevitably mean that these build and deploy jobs will contest and the queue might become quite large.

This is fine for the majority of cases, and in such cases, it’s just a matter of waiting for the queue of jobs to clean up by itself.

But there might be projects that require the latest commit’s workflow run to be executed as quickly as possible, as soon as it’s pushed, canceling any currently running tasks.

To achieve this, a simple workflow change is needed:

concurrency: 
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

By simply adding these lines of code, every workflow will execute the latest commits and their workflows as soon as they are pushed, canceling any currently running jobs.

Note that this will not affect any other workflow files, because the “group” parameter enforces this concurrency rule only to jobs that fall under the same group.