Path Filter
ActionsA GitHub Action to filter changed files in pull requests and commits. It is useful when you want to run steps or jobs based on changed files.
Compared to GitHub's path filter, this action allows detailed control with steps and jobs, and there is no limit to the number of files. The behavior of this action is simple, just compare head ref and base ref. Works fast and can handle differences between thousands of files. Additionally, do not require code checkout in previous steps.
Note
For pull_request events, yumemi-inc/changed-files can also be used.
There are limitations due to using GitHub API, but in pull_request events, there are no problems and it has more functions.
See action.yml for available action inputs and outputs.
Note that this action requires contents: read permission.
Works on any event. Basically it works as is, but if you want to customize it, refer to the Specify comparison targets section.
If there are changes to the files specified by pattern input, exists output will be 'true'.
This is useful for controlling step execution.
- uses: yumemi-inc/path-filter@v2
id: filter
with:
patterns: |
**/*.{js,ts}
!tools/**
- if: steps.filter.outputs.exists == 'true' # or fromJSON(steps.filter.outputs.exists)
run: npm run testNote: In
patterninput, characters after#are treated as comments.
If you just want to run a Bash script, you can use run input.
In this case, there is no need to define id:, since exists output is not used.
- uses: yumemi-inc/path-filter@v2
with:
patterns: |
**/*.{js,ts}
!tools/**
run: npm run testThere is also run-if-not input and either will be executed.
examples of other uses
- uses: yumemi-inc/path-filter@v2
id: filter
with:
patterns: |
**/*.js
!server/**
- env:
GH_REPO: ${{ github.repository }}
GH_TOKEN: ${{ github.token }}
run: |
gh pr edit ${{ github.event.number }} ${{ steps.filter.outputs.exists == 'true' && '--add-label' || '--remove-label' }} 'frontend'- uses: yumemi-inc/path-filter@v2
id: filter-src
with:
patterns: |
**/*.ts
package.json
- uses: yumemi-inc/path-filter@v2
id: filter-build
with:
patterns: 'dist/**'
- if: steps.filter-src.outputs.exists == 'true' && steps.filter-build.outputs.exists != 'true'
run: |
echo "::error::Please check if you forgot to build."
exit 1- uses: yumemi-inc/path-filter@v2
id: filter
with:
patterns: 'CHANGELOG.md'
- if: github.base_ref == 'main' && steps.filter.outputs.exists != 'true'
run: |
echo "::error::CHANGELOG.md is not updated."
exit 1Set this action's exists output to the job's output, and reference it in subsequent jobs.
outputs:
exists: ${{ steps.filter.outputs.exists }}
steps:
- uses: yumemi-inc/path-filter@v2
id: filter
with:
patterns: '**/*.{kt,kts}'examples
jobs:
filter:
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
exists-src: ${{ steps.filter-src.outputs.exists }}
exists-doc: ${{ steps.filter-doc.outputs.exists }}
steps:
- uses: yumemi-inc/path-filter@v2
id: filter-src
with:
patterns: 'src/**'
- uses: yumemi-inc/path-filter@v2
id: filter-doc
with:
patterns: 'doc/**'
job-src:
needs: [filter]
if: needs.filter.outputs.exists-src == 'true'
runs-on: ubuntu-latest
steps:
...
job-doc:
needs: [filter]
if: needs.filter.outputs.exists-doc == 'true'
runs-on: ubuntu-latest
steps:
...
job-common:
needs: [job-src, job-doc]
# treat skipped jobs as successful
if: cancelled() != true && contains(needs.*.result, 'failure') == false
runs-on: ubuntu-latest
steps:
...Simply, the change files are determined between head-ref input and base-ref input references.
The default values for each workflow trigger event are as follows:
| event | head-ref | base-ref |
|---|---|---|
| pull_request | github.sha | github.base_ref |
| pull_request_target | refs/pull/<pr number>/merge | github.base_ref |
| push | github.sha | github.event.before1 / default branch |
| merge_group | github.sha | github.event.merge_group.base_sha |
| other events | github.sha | default branch |
Specify these inputs explicitly if necessary. Any branch, tag, or commit SHA can be specified for tease inputs2.
- uses: yumemi-inc/path-filter@v2
with:
head-ref: 'main' # branch to be released
base-ref: 'release-x.x.x' # previous release tag
patterns: '**/*.js'
run: |
...
npm run deployBy default, two-dot comparison is performed.
If you want to compare with three-dot, set use-merge-base input to 'true'.
They are output to a file in JSON format and can be accessed as follows:
${{ steps.<id>.outputs.action-path }}/files.json${{ steps.<id>.outputs.action-path }}/filtered_files.json.
more
Refer to these files when debugging head-ref, base-ref, and patterns inputs.
For example, display them in the job summary like this:
- uses: yumemi-inc/path-filter@v2
id: filter
with:
patterns: '!**/*.md'
- run: |
{
echo '### files before filtering'
echo '```json'
cat '${{ steps.filter.outputs.action-path }}/files.json' | jq
echo '```'
echo '### files after filtering'
echo '```json'
cat '${{ steps.filter.outputs.action-path }}/filtered_files.json' | jq
echo '```'
} >> "$GITHUB_STEP_SUMMARY"You may use these files for purposes other than debugging, but note that these files will be overwritten if you use this action multiple times in the same job.
And, in this action's run input, access them with Bash variables like $GITHUB_ACTION_PATH/files.json, but note that the Bash script in run input will not be executed if there are no files after filtering.
Basically, it complies with the minimatch library used in this action. Please refer to the implementation in action.yml for the specified options.
Footnotes
Path Filter is not certified by GitHub. It is provided by a third-party and is governed by separate terms of service, privacy policy, and support documentation.