paths-filter/README.md

568 lines
20 KiB
Markdown
Raw Permalink Normal View History

# Paths Changes Filter
2020-05-20 15:03:08 +00:00
2021-07-08 03:32:59 +00:00
[GitHub Action](https://github.com/features/actions) that enables conditional execution of workflow steps and jobs, based on the files modified by pull request, on a feature
2021-04-19 20:18:44 +00:00
branch, or by the recently pushed commits.
2020-05-20 15:03:08 +00:00
2021-04-19 20:18:44 +00:00
Run slow tasks like integration tests or deployments only for changed components. It saves time and resources, especially in monorepo setups.
2021-07-08 03:32:59 +00:00
GitHub workflows built-in [path filters](https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#onpushpull_requestpaths)
2020-12-17 07:57:31 +00:00
don't allow this because they don't work on a level of individual jobs or steps.
2020-05-20 15:03:08 +00:00
**Real world usage examples:**
2021-07-08 03:52:11 +00:00
- [sentry.io](https://sentry.io/) - [backend.yml](https://github.com/getsentry/sentry/blob/2ebe01feab863d89aa7564e6d243b6d80c230ddc/.github/workflows/backend.yml#L36)
- [GoogleChrome/web.dev](https://web.dev/) - [lint-workflow.yml](https://github.com/GoogleChrome/web.dev/blob/3a57b721e7df6fc52172f676ca68d16153bda6a3/.github/workflows/lint-workflow.yml#L26)
2022-12-03 13:03:55 +00:00
- [blog post Configuring python linting to be part of CI/CD using GitHub actions](https://dev.to/freshbooks/configuring-python-linting-to-be-part-of-cicd-using-github-actions-1731#what-files-does-it-run-against) - [py_linter.yml](https://github.com/iamtodor/demo-github-actions-python-linter-configuration/blob/main/.github/workflows/py_linter.yml#L31)
2021-07-08 03:52:11 +00:00
## Supported workflows
2020-09-14 15:19:32 +00:00
- **Pull requests:**
- Workflow triggered by **[pull_request](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#pull_request)**
2020-09-14 15:19:32 +00:00
or **[pull_request_target](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#pull_request_target)** event
- Changes are detected against the pull request base branch
2021-07-08 03:32:59 +00:00
- Uses GitHub REST API to fetch a list of modified files
- Requires [pull-requests: read](https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs) permission
- **Feature branches:**
- Workflow triggered by **[push](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#push)**
or any other **[event](https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows)**
- The `base` input parameter must not be the same as the branch that triggered the workflow
2021-04-19 20:18:44 +00:00
- Changes are detected against the merge-base with the configured base branch or the default branch
2020-09-14 15:19:32 +00:00
- Uses git commands to detect changes - repository must be already [checked out](https://github.com/actions/checkout)
2021-04-19 20:18:44 +00:00
- **Master, Release, or other long-lived branches:**
- Workflow triggered by **[push](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#push)** event
2021-04-19 20:18:44 +00:00
when `base` input parameter is the same as the branch that triggered the workflow:
- Changes are detected against the most recent commit on the same branch before the push
- Workflow triggered by any other **[event](https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows)**
when `base` input parameter is commit SHA:
- Changes are detected against the provided `base` commit
- Workflow triggered by any other **[event](https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows)**
2021-04-19 20:18:44 +00:00
when `base` input parameter is the same as the branch that triggered the workflow:
- Changes are detected from the last commit
2020-09-14 15:19:32 +00:00
- Uses git commands to detect changes - repository must be already [checked out](https://github.com/actions/checkout)
2020-11-22 19:59:32 +00:00
- **Local changes**
- Workflow triggered by any event when `base` input parameter is set to `HEAD`
2021-04-19 20:18:44 +00:00
- Changes are detected against the current HEAD
2020-11-22 19:59:32 +00:00
- Untracked files are ignored
2020-09-14 15:19:32 +00:00
## Example
2021-07-08 03:52:11 +00:00
```yaml
- uses: dorny/paths-filter@v3
id: changes
with:
filters: |
src:
- 'src/**'
# run only if some file in 'src' folder was changed
2021-07-08 03:33:53 +00:00
- if: steps.changes.outputs.src == 'true'
run: ...
```
2021-07-08 03:52:11 +00:00
For more scenarios see [examples](#examples) section.
2020-09-14 15:19:32 +00:00
2021-07-08 03:52:11 +00:00
## Notes
- Paths expressions are evaluated using [picomatch](https://github.com/micromatch/picomatch) library.
2021-04-19 20:18:44 +00:00
Documentation for path expression format can be found on the project GitHub page.
- Picomatch [dot](https://github.com/micromatch/picomatch#options) option is set to true.
2021-04-19 20:18:44 +00:00
Globbing will also match paths where file or folder name starts with a dot.
- It's recommended to quote your path expressions with `'` or `"`. Otherwise, you will get an error if it starts with `*`.
- Local execution with [act](https://github.com/nektos/act) works only with alternative runner image. Default runner doesn't have `git` binary.
- Use: `act -P ubuntu-latest=nektos/act-environments-ubuntu:18.04`
2020-09-14 15:19:32 +00:00
2021-07-08 03:52:11 +00:00
## What's New
2020-09-14 15:19:32 +00:00
- New major release `v3` after update to Node 20 [Breaking change]
2021-04-11 18:49:05 +00:00
- Add `ref` input parameter
2021-02-20 10:28:11 +00:00
- Add `list-files: csv` format
- Configure matrix job to run for each folder with changes using `changes` output
- Improved listing of matching files with `list-files: shell` and `list-files: escape` options
- Paths expressions are now evaluated using [picomatch](https://github.com/micromatch/picomatch) library
2020-09-14 15:19:32 +00:00
2021-04-19 20:18:44 +00:00
For more information, see [CHANGELOG](https://github.com/dorny/paths-filter/blob/master/CHANGELOG.md)
2020-09-14 15:19:32 +00:00
2021-07-08 03:52:11 +00:00
## Usage
2020-09-14 15:19:32 +00:00
```yaml
- uses: dorny/paths-filter@v3
2020-09-14 15:19:32 +00:00
with:
# Defines filters applied to detected changed files.
2021-04-19 20:18:44 +00:00
# Each filter has a name and a list of rules.
2020-09-14 15:19:32 +00:00
# Rule is a glob expression - paths of all changed
# files are matched against it.
# Rule can optionally specify if the file
2021-04-19 20:18:44 +00:00
# should be added, modified, or deleted.
# For each filter, there will be a corresponding output variable to
2020-09-14 15:19:32 +00:00
# indicate if there's a changed file matching any of the rules.
2021-04-19 20:18:44 +00:00
# Optionally, there can be a second output variable
2020-09-14 15:19:32 +00:00
# set to list of all files matching the filter.
2021-04-19 20:18:44 +00:00
# Filters can be provided inline as a string (containing valid YAML document),
# or as a relative path to a file (e.g.: .github/filters.yaml).
2020-09-14 15:19:32 +00:00
# Filters syntax is documented by example - see examples section.
filters: ''
2021-04-19 20:18:44 +00:00
# Branch, tag, or commit SHA against which the changes will be detected.
# If it references the same branch it was pushed to,
2020-09-14 15:19:32 +00:00
# changes are detected against the most recent commit before the push.
2021-04-19 20:18:44 +00:00
# Otherwise, it uses git merge-base to find the best common ancestor between
2020-09-14 15:19:32 +00:00
# current branch (HEAD) and base.
# When merge-base is found, it's used for change detection - only changes
2021-04-19 20:18:44 +00:00
# introduced by the current branch are considered.
2020-09-14 15:19:32 +00:00
# All files are considered as added if there is no common ancestor with
# base branch or no previous commit.
# This option is ignored if action is triggered by pull_request event.
# Default: repository default branch (e.g. master)
base: ''
2021-04-11 18:49:05 +00:00
# Git reference (e.g. branch name) from which the changes will be detected.
2021-04-19 20:18:44 +00:00
# Useful when workflow can be triggered only on the default branch (e.g. repository_dispatch event)
# but you want to get changes on a different branch.
2021-04-11 18:49:05 +00:00
# This option is ignored if action is triggered by pull_request event.
# default: ${{ github.ref }}
ref:
2021-04-19 20:18:44 +00:00
# How many commits are initially fetched from the base branch.
2020-09-14 15:19:32 +00:00
# If needed, each subsequent fetch doubles the
# previously requested number of commits until the merge-base
2021-04-19 20:18:44 +00:00
# is found, or there are no more commits in the history.
2020-09-14 15:19:32 +00:00
# This option takes effect only when changes are detected
# using git against base branch (feature branch workflow).
# Default: 100
2020-09-14 15:19:32 +00:00
initial-fetch-depth: ''
# Enables listing of files matching the filter:
# 'none' - Disables listing of matching files (default).
2021-02-20 10:21:30 +00:00
# 'csv' - Coma separated list of filenames.
2021-04-19 20:18:44 +00:00
# If needed, it uses double quotes to wrap filename with unsafe characters.
# 'json' - File paths are formatted as JSON array.
# 'shell' - Space delimited list usable as command-line argument list in Linux shell.
# If needed, it uses single or double quotes to wrap filename with unsafe characters.
# 'escape'- Space delimited list usable as command-line argument list in Linux shell.
# Backslash escapes every potentially unsafe character.
2020-09-14 15:19:32 +00:00
# Default: none
list-files: ''
# Relative path under $GITHUB_WORKSPACE where the repository was checked out.
working-directory: ''
2021-04-19 20:18:44 +00:00
# Personal access token used to fetch a list of changed files
2021-07-08 03:32:59 +00:00
# from GitHub REST API.
2021-04-19 20:18:44 +00:00
# It's only used if action is triggered by a pull request event.
2021-07-08 03:32:59 +00:00
# GitHub token from workflow context is used as default value.
2021-04-19 20:18:44 +00:00
# If an empty string is provided, the action falls back to detect
2020-09-14 15:19:32 +00:00
# changes using git commands.
# Default: ${{ github.token }}
token: ''
# Optional parameter to override the default behavior of file matching algorithm.
# By default files that match at least one pattern defined by the filters will be included.
# This parameter allows to override the "at least one pattern" behavior to make it so that
# all of the patterns have to match or otherwise the file is excluded.
# An example scenario where this is useful if you would like to match all
# .ts files in a sub-directory but not .md files.
# The filters below will match markdown files despite the exclusion syntax UNLESS
# you specify 'every' as the predicate-quantifier parameter. When you do that,
# it will only match the .ts files in the subdirectory as expected.
#
# backend:
# - 'pkg/a/b/c/**'
# - '!**/*.jpeg'
# - '!**/*.md'
predicate-quantifier: 'some'
2020-09-14 15:19:32 +00:00
```
## Outputs
2021-07-08 03:52:11 +00:00
2021-04-19 20:18:44 +00:00
- For each filter, it sets output variable named by the filter to the text:
2021-07-08 03:52:11 +00:00
- `'true'` - if **any** of changed files matches any of filter rules
- `'false'` - if **none** of changed files matches any of filter rules
2021-04-19 20:18:44 +00:00
- For each filter, it sets an output variable with the name `${FILTER_NAME}_count` to the count of matching files.
- If enabled, for each filter it sets an output variable with the name `${FILTER_NAME}_files`. It will contain a list of all files matching the filter.
- `changes` - JSON array with names of all filters matching any of the changed files.
2020-09-14 15:19:32 +00:00
2021-07-08 03:52:11 +00:00
## Examples
2020-09-14 15:19:32 +00:00
2021-07-08 03:52:11 +00:00
### Conditional execution
2020-09-14 15:19:32 +00:00
<details>
<summary>Execute <b>step</b> in a workflow job only if some file in a subfolder is changed</summary>
2020-05-20 15:03:08 +00:00
```yaml
2020-05-20 22:31:39 +00:00
jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
2020-05-20 22:31:39 +00:00
id: filter
with:
filters: |
backend:
2020-09-14 15:19:32 +00:00
- 'backend/**'
2020-05-20 22:31:39 +00:00
frontend:
2020-09-14 15:19:32 +00:00
- 'frontend/**'
2020-05-20 22:31:39 +00:00
# run only if 'backend' files were changed
2020-09-14 15:19:32 +00:00
- name: backend tests
2020-05-20 22:31:39 +00:00
if: steps.filter.outputs.backend == 'true'
run: ...
# run only if 'frontend' files were changed
2020-09-14 15:19:32 +00:00
- name: frontend tests
2020-05-20 22:31:39 +00:00
if: steps.filter.outputs.frontend == 'true'
run: ...
# run if 'backend' or 'frontend' files were changed
- name: e2e tests
if: steps.filter.outputs.backend == 'true' || steps.filter.outputs.frontend == 'true'
run: ...
2020-05-20 15:03:08 +00:00
```
2021-07-08 03:52:11 +00:00
2020-09-14 15:19:32 +00:00
</details>
<details>
<summary>Execute <b>job</b> in a workflow only if some file in a subfolder is changed</summary>
2020-05-20 15:03:08 +00:00
```yml
jobs:
2020-09-14 15:19:32 +00:00
# JOB to run change detection
changes:
runs-on: ubuntu-latest
# Required permissions
permissions:
pull-requests: read
# Set job outputs to values from filter step
outputs:
backend: ${{ steps.filter.outputs.backend }}
frontend: ${{ steps.filter.outputs.frontend }}
steps:
# For pull requests it's not necessary to checkout the code
- uses: dorny/paths-filter@v3
id: filter
with:
2020-09-14 15:19:32 +00:00
filters: |
backend:
- 'backend/**'
frontend:
- 'frontend/**'
# JOB to build and test backend code
backend:
needs: changes
if: ${{ needs.changes.outputs.backend == 'true' }}
2020-09-14 15:19:32 +00:00
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- ...
2020-09-14 15:19:32 +00:00
# JOB to build and test frontend code
frontend:
needs: changes
if: ${{ needs.changes.outputs.frontend == 'true' }}
2020-09-14 15:19:32 +00:00
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- ...
```
2021-07-08 03:52:11 +00:00
2020-09-14 15:19:32 +00:00
</details>
<details>
<summary>Use change detection to configure matrix job</summary>
```yaml
jobs:
# JOB to run change detection
changes:
runs-on: ubuntu-latest
# Required permissions
permissions:
pull-requests: read
outputs:
# Expose matched filters as job 'packages' output variable
packages: ${{ steps.filter.outputs.changes }}
steps:
# For pull requests it's not necessary to checkout the code
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
package1: src/package1
package2: src/package2
# JOB to build and test each of modified packages
build:
needs: changes
strategy:
matrix:
# Parse JSON array containing names of all filters matching any of changed files
# e.g. ['package1', 'package2'] if both package folders contains changes
2021-02-24 10:24:20 +00:00
package: ${{ fromJSON(needs.changes.outputs.packages) }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- ...
```
2021-07-08 03:52:11 +00:00
</details>
2021-07-08 03:52:11 +00:00
### Change detection workflows
2020-09-14 15:19:32 +00:00
<details>
<summary><b>Pull requests:</b> Detect changes against PR base branch</summary>
```yaml
on:
pull_request:
2021-04-19 20:18:44 +00:00
branches: # PRs to the following branches will trigger the workflow
2020-09-14 15:19:32 +00:00
- master
- develop
jobs:
build:
runs-on: ubuntu-latest
# Required permissions
permissions:
pull-requests: read
2020-09-14 15:19:32 +00:00
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
2020-09-14 15:19:32 +00:00
id: filter
with:
filters: ... # Configure your filters
```
2021-07-08 03:52:11 +00:00
2020-09-14 15:19:32 +00:00
</details>
<details>
<summary><b>Feature branch:</b> Detect changes against configured base branch</summary>
```yaml
on:
push:
branches: # Push to following branches will trigger the workflow
- feature/**
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
# This may save additional git fetch roundtrip if
# merge-base is found within latest 20 commits
fetch-depth: 20
- uses: dorny/paths-filter@v3
2020-09-14 15:19:32 +00:00
id: filter
with:
base: develop # Change detection against merge-base with this branch
filters: ... # Configure your filters
```
2021-07-08 03:52:11 +00:00
2020-09-14 15:19:32 +00:00
</details>
<details>
<summary><b>Long lived branches:</b> Detect changes against the most recent commit on the same branch before the push</summary>
```yaml
on:
push:
2021-04-19 20:18:44 +00:00
branches: # Push to the following branches will trigger the workflow
2020-09-14 15:19:32 +00:00
- master
- develop
- release/**
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
2020-09-14 15:19:32 +00:00
id: filter
with:
2021-04-19 20:18:44 +00:00
# Use context to get the branch where commits were pushed.
# If there is only one long-lived branch (e.g. master),
2020-09-14 15:19:32 +00:00
# you can specify it directly.
# If it's not configured, the repository default branch is used.
base: ${{ github.ref }}
filters: ... # Configure your filters
```
2021-07-08 03:52:11 +00:00
2020-09-14 15:19:32 +00:00
</details>
2020-11-22 19:59:32 +00:00
<details>
<summary><b>Local changes:</b> Detect staged and unstaged local changes</summary>
```yaml
on:
push:
branches: # Push to following branches will trigger the workflow
- master
- develop
- release/**
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
2020-11-22 19:59:32 +00:00
2021-04-19 20:18:44 +00:00
# Some action that modifies files tracked by git (e.g. code linter)
2020-11-22 19:59:32 +00:00
- uses: johndoe/some-action@v1
# Filter to detect which files were modified
2021-04-19 20:18:44 +00:00
# Changes could be, for example, automatically committed
- uses: dorny/paths-filter@v3
2020-11-22 19:59:32 +00:00
id: filter
with:
base: HEAD
filters: ... # Configure your filters
```
2021-07-08 03:52:11 +00:00
2020-11-22 19:59:32 +00:00
</details>
2021-07-08 03:52:11 +00:00
### Advanced options
2020-09-14 15:19:32 +00:00
<details>
<summary>Define filter rules in own file</summary>
```yaml
- uses: dorny/paths-filter@v3
2020-09-14 15:19:32 +00:00
id: filter
with:
# Path to file where filters are defined
filters: .github/filters.yaml
```
2021-07-08 03:52:11 +00:00
2020-09-14 15:19:32 +00:00
</details>
<details>
<summary>Use YAML anchors to reuse path expression(s) inside another rule</summary>
```yaml
- uses: dorny/paths-filter@v3
2020-09-14 15:19:32 +00:00
id: filter
with:
# &shared is YAML anchor,
# *shared references previously defined anchor
# src filter will match any path under common, config and src folders
filters: |
shared: &shared
- common/**
- config/**
src:
- *shared
- src/**
```
2021-07-08 03:52:11 +00:00
2020-09-14 15:19:32 +00:00
</details>
<details>
<summary>Consider if file was added, modified or deleted</summary>
```yaml
- uses: dorny/paths-filter@v3
2020-09-14 15:19:32 +00:00
id: filter
with:
# Changed file can be 'added', 'modified', or 'deleted'.
2021-04-19 20:18:44 +00:00
# By default, the type of change is not considered.
# Optionally, it's possible to specify it using nested
# dictionary, where the type of change composes the key.
# Multiple change types can be specified using `|` as the delimiter.
2020-09-14 15:19:32 +00:00
filters: |
shared: &shared
- common/**
- config/**
2020-09-14 15:19:32 +00:00
addedOrModified:
- added|modified: '**'
allChanges:
- added|deleted|modified: '**'
addedOrModifiedAnchors:
- added|modified: *shared
2020-09-14 15:19:32 +00:00
```
2021-07-08 03:52:11 +00:00
</details>
2020-09-14 15:19:32 +00:00
<details>
<summary>Detect changes in folder only for some file extensions</summary>
```yaml
- uses: dorny/paths-filter@v3
id: filter
with:
# This makes it so that all the patterns have to match a file for it to be
# considered changed. Because we have the exclusions for .jpeg and .md files
# the end result is that if those files are changed they will be ignored
# because they don't match the respective rules excluding them.
#
# This can be leveraged to ensure that you only build & test software changes
# that have real impact on the behavior of the code, e.g. you can set up your
# build to run when Typescript/Rust/etc. files are changed but markdown
# changes in the diff will be ignored and you consume less resources to build.
predicate-quantifier: 'every'
filters: |
backend:
- 'pkg/a/b/c/**'
- '!**/*.jpeg'
- '!**/*.md'
```
</details>
2021-07-08 03:52:11 +00:00
### Custom processing of changed files
2020-09-14 15:19:32 +00:00
<details>
<summary>Passing list of modified files as command line args in Linux shell</summary>
```yaml
- uses: dorny/paths-filter@v3
2020-09-14 15:19:32 +00:00
id: filter
with:
# Enable listing of files matching each filter.
# Paths to files will be available in `${FILTER_NAME}_files` output variable.
# Paths will be escaped and space-delimited.
2021-04-19 20:18:44 +00:00
# Output is usable as command-line argument list in Linux shell
2020-09-14 15:19:32 +00:00
list-files: shell
# In this example changed files will be checked by linter.
# It doesn't make sense to lint deleted files.
# Therefore we specify we are only interested in added or modified files.
filters: |
markdown:
- added|modified: '*.md'
- name: Lint Markdown
if: ${{ steps.filter.outputs.markdown == 'true' }}
run: npx textlint ${{ steps.filter.outputs.markdown_files }}
```
2021-07-08 03:52:11 +00:00
2020-09-14 15:19:32 +00:00
</details>
<details>
<summary>Passing list of modified files as JSON array to another action</summary>
```yaml
- uses: dorny/paths-filter@v3
2020-09-14 15:19:32 +00:00
id: filter
with:
# Enable listing of files matching each filter.
# Paths to files will be available in `${FILTER_NAME}_files` output variable.
# Paths will be formatted as JSON array
list-files: json
2021-04-19 20:18:44 +00:00
# In this example all changed files are passed to the following action to do
2020-09-14 15:19:32 +00:00
# some custom processing.
filters: |
changed:
- '**'
- name: Lint Markdown
uses: johndoe/some-action@v1
with:
files: ${{ steps.filter.outputs.changed_files }}
2020-09-14 15:19:32 +00:00
```
2021-07-08 03:52:11 +00:00
2020-09-14 15:19:32 +00:00
</details>
2021-07-08 03:52:11 +00:00
## See also
2021-02-01 20:58:39 +00:00
- [test-reporter](https://github.com/dorny/test-reporter) - Displays test results from popular testing frameworks directly in GitHub
2020-09-14 15:19:32 +00:00
2021-07-08 03:52:11 +00:00
## License
2020-09-14 15:19:32 +00:00
The scripts and documentation in this project are released under the [MIT License](https://github.com/dorny/paths-filter/blob/master/LICENSE)