Ignore paths

Don’t run the workflow if changes were only made to certain paths, such as the docs directory.

Remember to use quotes so stop YAML evaluation of the ** glob.

Paths ignore attribute

Use paths-ignore attribute.

From example ignoring paths in the docs.

on:
  push:
    paths-ignore:
      - "docs/**"

If your docs directory has a lot of files in it that you edit often, then it is worth skipping that at least.

You don’t have to be thorough. Like I wouldn’t put your LICENSE file in there as that hardly changes.

Paths attribute

This will only build .js files and ignore others.

on:
  push:
    paths:
    - "**.js"

This is dangerous because you might leave your workflow file, Makefile, package files, etc. So can avoid being too restrictive accidentally by using the exclusion in the previous section instead.

Positive and negative paths

Or using a ! symbol in the paths attribute. The order matters. Here we include a directory and then exclude a subdirectory.

on:
  push:
    paths:
      - "sub-project/**"
      - "!sub-project/docs/**"

Skip a whole job

Place an if condition on the job.

Syntax:

jobs:
  job-name:
    if: CONDITION

Not Pull Requests

Here is an example where the entire job is skipped on a pull request so therefore runs only directly on the main branch.

on:
  push:
    - main
  pull_request:
    - main

jobs:
  build-deploy:
    name: Build and deploy
    
    if: ${{ github.event_name != 'pull_request' }}
    
    steps:
      - name: Build
      
      - name: Deploy

Skip on CI commit message

Add a condition to skip if the commit message containts a phrase - here we use [ci skip].

jobs:
  deploy-docs:
    if: "!contains(github.event.commits[0].message, '[ci skip]')"

    steps:
      # ...

In general I like to use an exclusion rule to exclude changes in docs/ for example from causing an app rebuild. I don’t why you would this CI skip example above as you have to remember to use. But I found it somewhere so added here.

Skip a step

Add a check for a given step.

Event name

Here we run the workflow on both a Push and a Pull Request, but we only perform the deploy step when the event is not a pull request i.e. when doing a push to master (or main or latest etc.).

on:
  push:
  pull_request:

jobs:
  build-deploy:
    steps:
      - name: Build
        # ...
        
      - name: Deploy 
        if: ${{ github.event_name != 'pull_request' }}
        # ...
      
      - name: Clean-up # This step will ALWAYS run
        # ...

Note we don’t have to specify a branch name.

You might like using != 'pull_request' as it is flexible - it will include a scheduled cron event. Or you might make this narrower as == 'push'.

Or with a boolean - no quotes.

if: github.event.pull_request.merged == true

You can also check you are on main using this, from Hugo docs:

if: github.ref == 'refs/heads/main'

Tags

Or maybe you want the job to run on main and on a Pull Request, but only deploy if there is a tag.

on:
  push:
    - main
  pull_request:
    - main
    
jobs:
  build-deploy:
    steps:
      - name: Build
        # ...
        
      - name: Deploy 
        if: startsWith(github.ref, 'refs/tags/')
        # ...
      
      - name: Clean-up # This step will ALWAYS run
        # ...