GitHub will skip your Actions workflows if you include [skip ci] in your commit message. But it's all or nothing. Skipping individual jobs takes a bit more work to setup, as I discovered!

A while back GitHub announced a handy new feature to allow easily skipping your GitHub Action workflows by including [skip ci] (amongst other options) in your commit message.

While this is great for blanket skipping your entire CI workflow, what if you want to skip just an individual job?

In my case, I had a .NET Blazor Server project with the following jobs in my standard CI workflow:

  • Build - Approx 3 mins runtime
  • Run Cypress UI Tests - Approx 9 mins runtime
  • Push to Octopus Deploy - Approx 45 secs runtime

For a normal workflow run, I want each of these jobs to run one after the other, and for a failure in one to prevent the subsequent jobs from running.

To achieve this sequence, normal practice is to just make each job dependent on the one previous to it, like this:

jobs:
build:
name: Build and Unit Tests
steps:
- .........
uitest:
name: UI Tests
needs: build
steps:
- .........
push:
name: Push to Octopus Deploy
needs: uitest
steps:
- .........

Making the UI Tests job optional

For minor code changes where I don't need to run my suite of UI tests, I wanted to be able to skip that middle step:

  • Build
  • Skipped: Run Cypress UI Tests
  • Push to Octopus Deploy

Along similar lines to GitHub's approach, I wanted to be able to trigger the skipping of this job by including [skip ui-tests] in my commit message.

The tricky bit here is having the Push Packages job be conditionally dependant on the success of the UI Tests job. If the UI Tests job runs and fails, the Push Packages job should not run. If the UI Tests job is skipped however, the Push Packages job should instead just be dependent on the success of Build job.

Skipping a job based on commit message

Firstly, to allow skipping the middle UI Tests job, I added the following if condition which checks for [skip ui-tests] in the latest commit message:

uitest:
name: UI Tests
needs: build
if: "!contains(github.event.head_commit.message, '[skip ui-tests]')"
steps:
- .........

Updating the Push Packages job

To ensure that the Push Packages job always runs, whether or not the UI Tests job is skipped, I added the following if condition, which allows it to accept either success or skipped as a valid result of the optional UI Tests step, while always requiring the Build step to have completed:

push:
name: Push to Octopus Deploy
needs: uitest
if: always() && needs.build.result == 'success' && (needs.uitest.result == 'success' || needs.uitest.result == 'skipped')
steps:
- .........

The Result

Now I have a convenient way to fast track my builds, for changes that don't require retesting, taking my build time from approximately 10+ minutes:

To (approximately) under 5 minutes with the inclusion of [skip ui-tests] in my commit messages:

(Side note: in case you're wondering, the variability in runtime for the Build and Unit Tests steps shown in the two examples above is due to caching of dependencies.  The first run required a full restore of Nuget/Node packages whereas the second run pulled these from GitHub's cache).