Skip to main content

Working with Path Filtering

1. Filtering by Changed Paths

Filter Description Example
github.event.path Path of the file that triggered the workflow github.event.path
github.event.paths List of paths that were changed in the event github.event.paths
github.event.path_filter Filter to match changed paths github.event.path_filter

Example: Using path filters in a workflow

on:
  push:
    paths:
      - 'src/**'
      - 'test/**'

jobs:
  build:
    runs-on: ubuntu-latest
    if: ${{ github.event.paths != null }}
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Run tests
        run: |
          echo "Running tests on changed paths"
Note: The paths filter is evaluated against the list of files changed in the event. If any file matches, the workflow runs.

2. Ignoring Paths

Option Values Description
paths-ignore string[] Array of paths to ignore in the event. Supports glob patterns.
ignore string[] Alternative name for paths-ignore (deprecated in favor of paths-ignore).

Example: Using paths-ignore

on:
  push:
    paths-ignore:
      - 'docs/**'
      - '*.md'
      - 'README.md'

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
Note: The paths-ignore filter is evaluated before the paths filter, so if a path matches both, it will be ignored.

3. Using Glob Patterns

Pattern Description Example
* Matches any single directory or file name src/* matches src/index.js
** Matches any number of directories or files recursively **/*.js matches src/index.js and src/lib/utils.js
? Matches any single character (except path separators) file?.txt matches file1.txt
[abc] Matches any one character inside brackets test[123].js matches test1.js, test2.js
[!abc] Matches any one character NOT inside brackets test[!123].js matches test4.js, testA.js
{a,b,c} Matches any of the comma-separated alternatives {src,lib}/*.js matches src/index.js, lib/utils.js

Example: Filtering by file extension

on:
  push:
    paths:
      - '**/*.js'
      - '**/*.ts'

Example: Excluding directories

on:
  push:
    paths:
      - '**/*.js'
      - '!**/node_modules/**'
Note: Glob patterns in GitHub Actions are case-sensitive by default.

4. Filtering Documentation Changes

Filter Description Example
docs/** Matches all files in the docs directory and subdirectories docs/index.md, docs/api/reference.md
README.md Matches the root README file README.md
docs/**/*.md Matches all Markdown files in docs and subdirectories docs/guide/intro.md, docs/advanced/features.md

Example: Using Path Filters in a Workflow

on:
  push:
    paths:
      - 'docs/**'
      - 'README.md'

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Build docs
        run: |
          echo "Building documentation..."
Note: Path filters in GitHub Actions are case-sensitive and support glob patterns.
Warning: Changes to files not matching the path filter will not trigger the workflow.

5. Filtering Source Code

Filter Description Example
paths Include or exclude files based on paths paths: ['src/**', '!src/old/**']
paths-ignore Exclude files based on paths paths-ignore: ['docs/**']
branches Include or exclude branches branches: ['main', 'feature/*']
branches-ignore Exclude branches branches-ignore: ['release/*']
tags Include or exclude tags tags: ['v1.*']
tags-ignore Exclude tags tags-ignore: ['v0.*']

Example: Using path filters in a workflow

on:
  push:
    paths:
      - 'src/**'
      - '!src/old/**'
    branches:
      - 'main'

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
Note: Path filters are evaluated relative to the repository root.

6. Filtering by File Extensions

Extension Description Usage Example
.js JavaScript files if: ${{ endsWith(github.event_path, '.js') }}
.ts TypeScript files if: ${{ endsWith(github.event_path, '.ts') }}
.json JSON configuration files if: ${{ endsWith(github.event_path, '.json') }}
.yml YAML files (used in workflows) if: ${{ endsWith(github.event_path, '.yml') }}
.md Markdown documentation files if: ${{ endsWith(github.event_path, '.md') }}

Example: Using file extension filtering in a workflow

on:
  push:
    paths:
      - '*.js'
      - '*.ts'

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Check for JS/TS files
        run: echo "Processing JavaScript or TypeScript files"
Note: The paths filter supports glob patterns and can be used to limit workflow execution based on file extensions.

7. Using Negation Patterns

Pattern Description Example
!pattern Negation pattern to exclude files or directories matching the given pattern !src/**/*.js
!file Exclude a specific file !README.md
!dir/ Exclude a directory and all its contents !tests/

Example: Exclude JavaScript files in src directory

on:
  push:
    paths:
      - '**'
      - '!src/**/*.js'

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
Note: Negation patterns are processed after inclusion patterns, so they can override previous inclusions.

8. Combining Path and Branch Filters

Filter Type Example Description
paths ['src/**', 'tests/**'] Triggers workflow on changes to files matching the path pattern
branches ['main', 'develop'] Triggers workflow only on changes to specified branches
paths-ignore ['docs/**'] Excludes files matching the pattern from triggering workflow
branches-ignore ['feature/*'] Excludes branches matching the pattern from triggering workflow

Example: Combining Path and Branch Filters

on:
  push:
    paths:
      - 'src/**'
      - 'tests/**'
    branches:
      - 'main'
      - 'develop'

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
Note: When both paths and branches are specified, the workflow runs only if both conditions match.
Warning: If paths-ignore or branches-ignore is used, it takes precedence over the inclusion filters.

9. Detecting Modified Files

Property Description Example
github.event.changed_files List of files changed in the event github.event.changed_files
github.event.before SHA of the previous commit github.event.before
github.event.after SHA of the current commit github.event.after

Example: Check if a file was modified

on:
  push:
    branches:
      - main

jobs:
  check-files:
    runs-on: ubuntu-latest
    steps:
      - name: Check modified files
        id: check
        run: |
          if [[ "${{ github.event.changed_files }}" == *"src/main.js"* ]]; then
            echo "main.js was modified"
          else
            echo "main.js was not modified"
          fi
Note: The github.event.changed_files is only available in push and pull_request events.

10. Running Conditional Steps

Example: Using if condition to run a step only on main branch

name: Conditional Step Example
on:
  push:
    branches:
      - main
      - develop

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Run only on main branch
        if: github.ref == 'refs/heads/main'
        run: |
          echo "Running on main branch"
Condition Description Usage
github.ref Current branch or tag ref if: github.ref == 'refs/heads/main'
github.event_name Name of the event that triggered the workflow if: github.event_name == 'push'
github.actor User who triggered the workflow if: github.actor == 'octocat'
github.event.pull_request.head.repo.full_name Full name of the repository for PR head if: github.event.pull_request.head.repo.full_name == 'octocat/fork'
Note: The if condition supports complex expressions using logical operators like &&, ||, and !.

Example: Complex condition with && and ||

name: Complex Conditional Step
on:
  push:
    branches:
      - main
  pull_request:
    types: [opened, synchronize]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Run on main or PR from fork
        if: (github.ref == 'refs/heads/main') || (github.event.pull_request.head.repo.full_name != github.repository)
        run: |
          echo "Running conditional step"

11. Using Path Filters with Matrix

GitHub Actions allows combining path filters with matrix strategies to run jobs only when specific files change. This is useful for optimizing workflows by avoiding unnecessary runs.

Field Description Example
paths Array of paths to filter changes on ['src/**', 'test/**']
paths-ignore Array of paths to ignore changes on ['docs/**']
matrix Matrix strategy to define job variations { os: ['ubuntu-latest', 'windows-latest'] }

Example: Path Filtered Matrix Job

jobs:
  build:
    if: ${{ github.event_name == 'pull_request' || github.event_name == 'push' }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest]
        node-version: [14, 16, 18]
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
      - name: Install dependencies
        run: npm install
Note: Path filters apply to the pull_request, push, and workflow_run events. They do not apply to scheduled or workflow_dispatch events.
Warning: When using paths and matrix, the job will run for each matrix combination only if the path filter matches. If no paths match, the job is skipped entirely.

12. Optimizing CI with Path Filters

Filter Description Usage Notes
paths Include workflows when changes occur in specified paths paths: ['src/**', 'test/**'] Supports glob patterns
paths-ignore Exclude workflows when changes occur in specified paths paths-ignore: ['docs/**'] Overrides paths if both are present

Example: Using path filters to optimize CI

on:
  push:
    paths:
      - 'src/**'
      - 'test/**'

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run tests
        run: npm test
Note: Path filters can significantly reduce CI runtime by skipping workflows when irrelevant files change.

Best Practices for Path Filters

  • Use paths to limit workflows to relevant file changes
  • Combine with branches or tags for more control
  • Avoid overly broad patterns that match unintended files

Was this article helpful?