{"id":18952309,"url":"https://github.com/step-security/paths-filter","last_synced_at":"2026-04-10T20:01:34.793Z","repository":{"id":210328565,"uuid":"726295987","full_name":"step-security/paths-filter","owner":"step-security","description":"Conditionally run actions based on files modified by PR, feature branch or pushed commits","archived":false,"fork":false,"pushed_at":"2024-04-22T12:39:24.000Z","size":481,"stargazers_count":0,"open_issues_count":11,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-04-22T13:51:38.464Z","etag":null,"topics":["step-security-maintained-actions"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/step-security.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2023-12-02T01:09:02.000Z","updated_at":"2024-04-23T14:10:57.934Z","dependencies_parsed_at":"2023-12-02T02:31:10.216Z","dependency_job_id":"d4e0ef5e-36d7-4b46-b7b1-7e1f87d2d164","html_url":"https://github.com/step-security/paths-filter","commit_stats":null,"previous_names":["step-security/paths-filter"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/step-security%2Fpaths-filter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/step-security%2Fpaths-filter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/step-security%2Fpaths-filter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/step-security%2Fpaths-filter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/step-security","download_url":"https://codeload.github.com/step-security/paths-filter/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239951640,"owners_count":19723912,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["step-security-maintained-actions"],"created_at":"2024-11-08T13:32:43.507Z","updated_at":"2026-04-10T20:01:34.785Z","avatar_url":"https://github.com/step-security.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![StepSecurity Maintained Action](https://raw.githubusercontent.com/step-security/maintained-actions-assets/main/assets/maintained-action-banner.png)](https://docs.stepsecurity.io/actions/stepsecurity-maintained-actions)\n\n# StepSecurity Maintained Action - Paths Changes Filter\n\nForked from: [dorny/paths-filter](https://github.com/dorny/paths-filter)\n\n[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\nbranch, or by the recently pushed commits.\n\nRun slow tasks like integration tests or deployments only for changed components. It saves time and resources, especially in monorepo setups.\nGitHub workflows built-in [path filters](https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#onpushpull_requestpaths)\ndon't allow this because they don't work on a level of individual jobs or steps.\n\n**Real world usage examples:**\n\n- [sentry.io](https://sentry.io/) - [backend.yml](https://github.com/getsentry/sentry/blob/2ebe01feab863d89aa7564e6d243b6d80c230ddc/.github/workflows/backend.yml#L36)\n- [GoogleChrome/web.dev](https://web.dev/) - [lint-workflow.yml](https://github.com/GoogleChrome/web.dev/blob/3a57b721e7df6fc52172f676ca68d16153bda6a3/.github/workflows/lint-workflow.yml#L26)\n- [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)\n\n## Supported workflows\n\n- **Pull requests:**\n  - Workflow triggered by **[pull_request](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#pull_request)**\n    or **[pull_request_target](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#pull_request_target)** event\n  - Changes are detected against the pull request base branch\n  - Uses GitHub REST API to fetch a list of modified files\n  - Requires [pull-requests: read](https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs) permission\n- **Feature branches:**\n  - Workflow triggered by **[push](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#push)**\n  or any other **[event](https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows)**\n  - The `base` input parameter must not be the same as the branch that triggered the workflow\n  - Changes are detected against the merge-base with the configured base branch or the default branch\n  - Uses git commands to detect changes - repository must be already [checked out](https://github.com/actions/checkout)\n- **[Merge queue](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-a-merge-queue):**\n  - Workflow triggered by **[merge_group](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#merge_group)**\n  - The `base` and `ref` input parameters default to commit hashes from the event\n    unless explicitly specified.\n  - Uses git commands to detect changes - repository must be already [checked out](https://github.com/actions/checkout)\n- **Master, Release, or other long-lived branches:**\n  - Workflow triggered by **[push](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#push)** event\n  when `base` input parameter is the same as the branch that triggered the workflow:\n    - Changes are detected against the most recent commit on the same branch before the push\n  - Workflow triggered by any other **[event](https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows)**\n  when `base` input parameter is commit SHA:\n    - Changes are detected against the provided `base` commit\n  - Workflow triggered by any other **[event](https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows)**\n  when `base` input parameter is the same as the branch that triggered the workflow:\n    - Changes are detected from the last commit\n  - Uses git commands to detect changes - repository must be already [checked out](https://github.com/actions/checkout)\n- **Local changes**\n  - Workflow triggered by any event when `base` input parameter is set to `HEAD`\n  - Changes are detected against the current HEAD\n  - Untracked files are ignored\n\n## Example\n\n```yaml\n- uses: step-security/paths-filter@v4\n  id: changes\n  with:\n    filters: |\n      src:\n        - 'src/**'\n\n  # run only if some file in 'src' folder was changed\n- if: steps.changes.outputs.src == 'true'\n  run: ...\n```\n\nFor more scenarios see [examples](#examples) section.\n\n## Notes\n\n- Paths expressions are evaluated using [picomatch](https://github.com/micromatch/picomatch) library.\n  Documentation for path expression format can be found on the project GitHub page.\n- Picomatch [dot](https://github.com/micromatch/picomatch#options) option is set to true.\n  Globbing will also match paths where file or folder name starts with a dot.\n- It's recommended to quote your path expressions with `'` or `\"`. Otherwise, you will get an error if it starts with `*`.\n- Local execution with [act](https://github.com/nektos/act) works only with alternative runner image. Default runner doesn't have `git` binary.\n  - Use: `act -P ubuntu-latest=nektos/act-environments-ubuntu:18.04`\n\n## What's New\n\n- New major release `v4` after update to Node 24 [Breaking change]\n- Add `ref` input parameter\n- Add `list-files: csv` format\n- Configure matrix job to run for each folder with changes using `changes` output\n- Improved listing of matching files with `list-files: shell` and `list-files: escape` options\n- Paths expressions are now evaluated using [picomatch](https://github.com/micromatch/picomatch) library\n\n## Usage\n\n```yaml\n- uses: step-security/paths-filter@v4\n  with:\n    # Defines filters applied to detected changed files.\n    # Each filter has a name and a list of rules.\n    # Rule is a glob expression - paths of all changed\n    # files are matched against it.\n    # Rule can optionally specify if the file\n    # should be added, modified, or deleted.\n    # For each filter, there will be a corresponding output variable to\n    # indicate if there's a changed file matching any of the rules.\n    # Optionally, there can be a second output variable\n    # set to list of all files matching the filter.\n    # Filters can be provided inline as a string (containing valid YAML document),\n    # or as a relative path to a file (e.g.: .github/filters.yaml).\n    # Filters syntax is documented by example - see examples section.\n    filters: ''\n\n    # Branch, tag, or commit SHA against which the changes will be detected.\n    # If it references the same branch it was pushed to,\n    # changes are detected against the most recent commit before the push.\n    # If it is empty and action is triggered by merge_group event,\n    # the base commit in the event will be used.\n    # Otherwise, it uses git merge-base to find the best common ancestor between\n    # current branch (HEAD) and base.\n    # When merge-base is found, it's used for change detection - only changes\n    # introduced by the current branch are considered.\n    # All files are considered as added if there is no common ancestor with\n    # base branch or no previous commit.\n    # This option is ignored if action is triggered by pull_request event.\n    # Default: repository default branch (e.g. master)\n    base: ''\n\n    # Git reference (e.g. branch name) from which the changes will be detected.\n    # Useful when workflow can be triggered only on the default branch (e.g. repository_dispatch event)\n    # but you want to get changes on a different branch.\n    # If this is empty and action is triggered by merge_group event,\n    # the head commit in the event will be used.\n    # This option is ignored if action is triggered by pull_request event.\n    # default: ${{ github.ref }}\n    ref:\n\n    # How many commits are initially fetched from the base branch.\n    # If needed, each subsequent fetch doubles the\n    # previously requested number of commits until the merge-base\n    # is found, or there are no more commits in the history.\n    # This option takes effect only when changes are detected\n    # using git against base branch (feature branch workflow).\n    # Default: 100\n    initial-fetch-depth: ''\n\n    # Enables listing of files matching the filter:\n    #   'none'  - Disables listing of matching files (default).\n    #   'csv'   - Coma separated list of filenames.\n    #             If needed, it uses double quotes to wrap filename with unsafe characters.\n    #   'json'  - File paths are formatted as JSON array.\n    #   'shell' - Space delimited list usable as command-line argument list in Linux shell.\n    #             If needed, it uses single or double quotes to wrap filename with unsafe characters.\n    #   'escape'- Space delimited list usable as command-line argument list in Linux shell.\n    #             Backslash escapes every potentially unsafe character.\n    # Default: none\n    list-files: ''\n\n    # Relative path under $GITHUB_WORKSPACE where the repository was checked out.\n    working-directory: ''\n\n    # Personal access token used to fetch a list of changed files\n    # from GitHub REST API.\n    # It's only used if action is triggered by a pull request event.\n    # GitHub token from workflow context is used as default value.\n    # If an empty string is provided, the action falls back to detect\n    # changes using git commands.\n    # Default: ${{ github.token }}\n    token: ''\n\n    # Optional parameter to override the default behavior of file matching algorithm.\n    # By default files that match at least one pattern defined by the filters will be included.\n    # This parameter allows to override the \"at least one pattern\" behavior to make it so that\n    # all of the patterns have to match or otherwise the file is excluded.\n    # An example scenario where this is useful if you would like to match all\n    # .ts files in a sub-directory but not .md files.\n    # The filters below will match markdown files despite the exclusion syntax UNLESS\n    # you specify 'every' as the predicate-quantifier parameter. When you do that,\n    # it will only match the .ts files in the subdirectory as expected.\n    #\n    # backend:\n    #  - 'pkg/a/b/c/**'\n    #  - '!**/*.jpeg'\n    #  - '!**/*.md'\n    predicate-quantifier: 'some'\n```\n\n## Outputs\n\n- For each filter, it sets output variable named by the filter to the text:\n  - `'true'` - if **any** of changed files matches any of filter rules\n  - `'false'` - if **none** of changed files matches any of filter rules\n- For each filter, it sets an output variable with the name `${FILTER_NAME}_count` to the count of matching files.\n- 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.\n- `changes` - JSON array with names of all filters matching any of the changed files.\n\n## Examples\n\n### Conditional execution\n\n\u003cdetails\u003e\n  \u003csummary\u003eExecute \u003cb\u003estep\u003c/b\u003e in a workflow job only if some file in a subfolder is changed\u003c/summary\u003e\n\n```yaml\njobs:\n  tests:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v6\n    - uses: step-security/paths-filter@v4\n      id: filter\n      with:\n        filters: |\n          backend:\n            - 'backend/**'\n          frontend:\n            - 'frontend/**'\n\n    # run only if 'backend' files were changed\n    - name: backend tests\n      if: steps.filter.outputs.backend == 'true'\n      run: ...\n\n    # run only if 'frontend' files were changed\n    - name: frontend tests\n      if: steps.filter.outputs.frontend == 'true'\n      run: ...\n\n    # run if 'backend' or 'frontend' files were changed\n    - name: e2e tests\n      if: steps.filter.outputs.backend == 'true' || steps.filter.outputs.frontend == 'true'\n      run: ...\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eExecute \u003cb\u003ejob\u003c/b\u003e in a workflow only if some file in a subfolder is changed\u003c/summary\u003e\n\n```yml\njobs:\n  # JOB to run change detection\n  changes:\n    runs-on: ubuntu-latest\n    # Required permissions\n    permissions:\n      pull-requests: read\n    # Set job outputs to values from filter step\n    outputs:\n      backend: ${{ steps.filter.outputs.backend }}\n      frontend: ${{ steps.filter.outputs.frontend }}\n    steps:\n    # For pull requests it's not necessary to checkout the code\n    - uses: step-security/paths-filter@v4\n      id: filter\n      with:\n        filters: |\n          backend:\n            - 'backend/**'\n          frontend:\n            - 'frontend/**'\n\n  # JOB to build and test backend code\n  backend:\n    needs: changes\n    if: ${{ needs.changes.outputs.backend == 'true' }}\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - ...\n\n  # JOB to build and test frontend code\n  frontend:\n    needs: changes\n    if: ${{ needs.changes.outputs.frontend == 'true' }}\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - ...\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eUse change detection to configure matrix job\u003c/summary\u003e\n\n```yaml\njobs:\n  # JOB to run change detection\n  changes:\n    runs-on: ubuntu-latest\n    # Required permissions\n    permissions:\n      pull-requests: read\n    outputs:\n      # Expose matched filters as job 'packages' output variable\n      packages: ${{ steps.filter.outputs.changes }}\n    steps:\n    # For pull requests it's not necessary to checkout the code\n    - uses: step-security/paths-filter@v4\n      id: filter\n      with:\n        filters: |\n          package1: src/package1\n          package2: src/package2\n\n  # JOB to build and test each of modified packages\n  build:\n    needs: changes\n    strategy:\n      matrix:\n        # Parse JSON array containing names of all filters matching any of changed files\n        # e.g. ['package1', 'package2'] if both package folders contains changes\n        package: ${{ fromJSON(needs.changes.outputs.packages) }}\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - ...\n```\n\n\u003c/details\u003e\n\n### Change detection workflows\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003ePull requests:\u003c/b\u003e Detect changes against PR base branch\u003c/summary\u003e\n\n```yaml\non:\n  pull_request:\n    branches: # PRs to the following branches will trigger the workflow\n      - master\n      - develop\n  # Optionally you can use the action in the merge queue\n  # if your repository enables the feature.\n  merge_group:\n    branches:\n      - master\n      - develop\njobs:\n  build:\n    runs-on: ubuntu-latest\n    # Required permissions\n    permissions:\n      pull-requests: read\n    steps:\n    - uses: actions/checkout@v6\n    - uses: step-security/paths-filter@v4\n      id: filter\n      with:\n        filters: ... # Configure your filters\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003eFeature branch:\u003c/b\u003e Detect changes against configured base branch\u003c/summary\u003e\n\n```yaml\non:\n  push:\n    branches: # Push to following branches will trigger the workflow\n      - feature/**\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v6\n      with:\n        # This may save additional git fetch roundtrip if\n        # merge-base is found within latest 20 commits\n        fetch-depth: 20\n    - uses: step-security/paths-filter@v4\n      id: filter\n      with:\n        base: develop # Change detection against merge-base with this branch\n        filters: ... # Configure your filters\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003eLong lived branches:\u003c/b\u003e Detect changes against the most recent commit on the same branch before the push\u003c/summary\u003e\n\n```yaml\non:\n  push:\n    branches: # Push to the following branches will trigger the workflow\n      - master\n      - develop\n      - release/**\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v6\n    - uses: step-security/paths-filter@v4\n      id: filter\n      with:\n        # Use context to get the branch where commits were pushed.\n        # If there is only one long-lived branch (e.g. master),\n        # you can specify it directly.\n        # If it's not configured, the repository default branch is used.\n        base: ${{ github.ref }}\n        filters: ... # Configure your filters\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003eLocal changes:\u003c/b\u003e Detect staged and unstaged local changes\u003c/summary\u003e\n\n```yaml\non:\n  push:\n    branches: # Push to following branches will trigger the workflow\n      - master\n      - develop\n      - release/**\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v6\n\n      # Some action that modifies files tracked by git (e.g. code linter)\n    - uses: johndoe/some-action@v1\n\n      # Filter to detect which files were modified\n      # Changes could be, for example, automatically committed\n    - uses: step-security/paths-filter@v4\n      id: filter\n      with:\n        base: HEAD\n        filters: ... # Configure your filters\n```\n\n\u003c/details\u003e\n\n### Advanced options\n\n\u003cdetails\u003e\n  \u003csummary\u003eDefine filter rules in own file\u003c/summary\u003e\n\n```yaml\n- uses: step-security/paths-filter@v4\n      id: filter\n      with:\n        # Path to file where filters are defined\n        filters: .github/filters.yaml\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eUse YAML anchors to reuse path expression(s) inside another rule\u003c/summary\u003e\n\n```yaml\n- uses: step-security/paths-filter@v4\n      id: filter\n      with:\n        # \u0026shared is YAML anchor,\n        # *shared references previously defined anchor\n        # src filter will match any path under common, config and src folders\n        filters: |\n          shared: \u0026shared\n            - common/**\n            - config/**\n          src:\n            - *shared\n            - src/**\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eConsider if file was added, modified or deleted\u003c/summary\u003e\n\n```yaml\n- uses: step-security/paths-filter@v4\n      id: filter\n      with:\n        # Changed file can be 'added', 'modified', or 'deleted'.\n        # By default, the type of change is not considered.\n        # Optionally, it's possible to specify it using nested\n        # dictionary, where the type of change composes the key.\n        # Multiple change types can be specified using `|` as the delimiter.\n        filters: |\n          shared: \u0026shared\n            - common/**\n            - config/**\n          addedOrModified:\n            - added|modified: '**'\n          allChanges:\n            - added|deleted|modified: '**'\n          addedOrModifiedAnchors:\n            - added|modified: *shared\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eDetect changes in folder only for some file extensions\u003c/summary\u003e\n\n```yaml\n- uses: step-security/paths-filter@v4\n      id: filter\n      with:\n        # This makes it so that all the patterns have to match a file for it to be\n        # considered changed. Because we have the exclusions for .jpeg and .md files\n        # the end result is that if those files are changed they will be ignored\n        # because they don't match the respective rules excluding them.\n        #\n        # This can be leveraged to ensure that you only build \u0026 test software changes\n        # that have real impact on the behavior of the code, e.g. you can set up your\n        # build to run when Typescript/Rust/etc. files are changed but markdown\n        # changes in the diff will be ignored and you consume less resources to build.\n        predicate-quantifier: 'every'\n        filters: |\n          backend:\n            - 'pkg/a/b/c/**'\n            - '!**/*.jpeg'\n            - '!**/*.md'\n```\n\n\u003c/details\u003e\n\n### Custom processing of changed files\n\n\u003cdetails\u003e\n  \u003csummary\u003ePassing list of modified files as command line args in Linux shell\u003c/summary\u003e\n\n```yaml\n- uses: step-security/paths-filter@v4\n  id: filter\n  with:\n    # Enable listing of files matching each filter.\n    # Paths to files will be available in `${FILTER_NAME}_files` output variable.\n    # Paths will be escaped and space-delimited.\n    # Output is usable as command-line argument list in Linux shell\n    list-files: shell\n\n    # In this example changed files will be checked by linter.\n    # It doesn't make sense to lint deleted files.\n    # Therefore we specify we are only interested in added or modified files.\n    filters: |\n      markdown:\n        - added|modified: '*.md'\n- name: Lint Markdown\n  if: ${{ steps.filter.outputs.markdown == 'true' }}\n  run: npx textlint ${{ steps.filter.outputs.markdown_files }}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003ePassing list of modified files as JSON array to another action\u003c/summary\u003e\n\n```yaml\n- uses: step-security/paths-filter@v4\n  id: filter\n  with:\n    # Enable listing of files matching each filter.\n    # Paths to files will be available in `${FILTER_NAME}_files` output variable.\n    # Paths will be formatted as JSON array\n    list-files: json\n\n    # In this example all changed files are passed to the following action to do\n    # some custom processing.\n    filters: |\n      changed:\n        - '**'\n- name: Lint Markdown\n  uses: johndoe/some-action@v1\n  with:\n    files: ${{ steps.filter.outputs.changed_files }}\n```\n\n\u003c/details\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstep-security%2Fpaths-filter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstep-security%2Fpaths-filter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstep-security%2Fpaths-filter/lists"}