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 |
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.