Conditionally run actions based on files modified by PR, feature branch or pushed commits
Go to file
Michal Dorner 3f845744aa
Export files matching rules (#32)
* Export files matching rules

* Improve debug output

* Fix PR test workflow

* Always quote output path + fix PR test

* Use proper single quote escaping in workflow file

* Improve error handling and docs for list-files input parameter
2020-08-30 21:18:14 +02:00
__tests__ Export files matching rules (#32) 2020-08-30 21:18:14 +02:00
.github Export files matching rules (#32) 2020-08-30 21:18:14 +02:00
.vscode Implement fetching, filtering and tests 2020-05-21 00:31:16 +02:00
dist Export files matching rules (#32) 2020-08-30 21:18:14 +02:00
src Export files matching rules (#32) 2020-08-30 21:18:14 +02:00
.editorconfig Extend filter syntax with optional specification of file status: add, modified, deleted (#22) 2020-08-04 20:57:29 +02:00
.eslintignore Initial commit 2020-05-20 17:03:08 +02:00
.eslintrc.json Support reusable paths blocks via yaml anchors (#13) 2020-06-19 23:39:06 +02:00
.gitattributes Extend filter syntax with optional specification of file status: add, modified, deleted (#22) 2020-08-04 20:57:29 +02:00
.gitignore Initial commit 2020-05-20 17:03:08 +02:00
.prettierignore Initial commit 2020-05-20 17:03:08 +02:00
.prettierrc.json Implement fetching, filtering and tests 2020-05-21 00:31:16 +02:00
action.yml Export files matching rules (#32) 2020-08-30 21:18:14 +02:00
CHANGELOG.md Add support for pull_request_target (#30) 2020-08-13 21:55:56 +02:00
jest.config.js Initial commit 2020-05-20 17:03:08 +02:00
LICENSE Support push event (#10) 2020-06-15 21:49:10 +02:00
package-lock.json Support reusable paths blocks via yaml anchors (#13) 2020-06-19 23:39:06 +02:00
package.json Support reusable paths blocks via yaml anchors (#13) 2020-06-19 23:39:06 +02:00
README.md Merge branch 'master' into develop 2020-08-14 22:03:31 +02:00
tsconfig.json Initial commit 2020-05-20 17:03:08 +02:00

paths-filter status

Paths filter

With this Github Action you can execute your workflow steps only if relevant files are modified.

It saves time and resources especially in monorepo setups, where you can run slow tasks (e.g. integration tests or deployments) only for changed components. Github workflows built-in path filters doesn't allow this because they doesn't work on a level of individual jobs or steps.

Supported workflows:

  • Action triggered by pull_request event:
    • changes detected against the pull request base branch
  • Action triggered by push event:
    • changes detected against the most recent commit on the same branch before the push
    • changes detected against the top of the configured base branch (e.g. master)

Usage

Filter rules are defined using YAML format. Each filter has a name and set of rules. Rule is a glob expressions. Optionally you specify if the file should be added, modified or deleted to be matched. For each filter there will be corresponding output variable to indicate if there's a changed file matching any of the rules. Output variables can be later used in the if clause to conditionally run specific steps.

Inputs

  • token: GitHub Access Token - defaults to ${{ github.token }} so you don't have to explicitly provide it.
  • **working-directory: Relative path under $GITHUB_WORKSPACE where the repository was checked out. Useful only if you checked out your repository under custom path.
  • base: Git reference (e.g. branch name) against which the changes will be detected. Defaults to repository default branch (e.g. master). If it references same branch it was pushed to, changes are detected against the most recent commit before the push. This option is ignored if action is triggered by pull_request event.
  • filters: Path to the configuration file or directly embedded string in YAML format.

Outputs

  • For each rule it sets output variable named by the rule to text:
    • 'true' - if any of changed files matches any of rule patterns
    • 'false' - if none of changed files matches any of rule patterns

Notes

  • minimatch dot option is set to true - therefore globbing will match also paths where file or folder name starts with a dot.
  • You can use YAML anchors to reuse path expression(s) inside another rule. See example in the tests.
  • It's recommended to put quote your path expressions with ' or ". Otherwise you will get an error if it starts with *.
  • If changes are detected against the previous commit and there is none (i.e. first push of a new branch), all filter rules will report changed files.
  • You can use base: ${{ github.ref }} to configure change detection against previous commit for every branch you create.

Example

on:
  push:
    branches:
      - master
  pull_request:
    branches:
      - master
jobs:
  tests:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - uses: dorny/paths-filter@v2.2.1
      id: filter
      with:
        # inline YAML or path to separate file (e.g.: .github/filters.yaml)
        filters: |
          backend:
            - 'backend/**/*'
          frontend:
            - 'frontend/**/*'          

    # run only if 'backend' files were changed
    - name: backend unit tests
      if: steps.filter.outputs.backend == 'true'
      run: ...

    # run only if 'frontend' files were changed
    - name: frontend unit tests
      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: ...

If your workflow uses multiple jobs, you can put paths-filter into own job and use job outputs in other jobs if statements:

on:
  pull_request:
    branches:
      - master
jobs:
  changes:
    runs-on: ubuntu-latest
    # 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@v2.2.1
      id: filter
      with:
        # Filters stored in own yaml file
        filters: '.github/filters.yml'
  backend:
    needs: changes
    if: ${{ needs.changes.outputs.backend == 'true' }}
    steps:
      - ...
  frontend:
    needs: changes
    if: ${{ needs.changes.outputs.frontend == 'true' }}
    steps:
      - ...

How it works

  1. If action was triggered by pull request:
    • If access token was provided it's used to fetch list of changed files from Github API.
    • If access token was not provided, top of the base branch is fetched and changed files are detected using git diff-index <SHA> command.
  2. If action was triggered by push event
    • if base input parameter references same branch it was pushed to, most recent commit before the push is fetched
    • If base input parameter references other branch, top of that branch is fetched
    • changed files are detected using git diff-index FETCH_HEAD command.
  3. For each filter rule it checks if there is any matching file
  4. Output variables are set

Difference from similar projects:

  • Has Changed Path
    • detects changes from previous commit
    • you have to configure checkout action to fetch some number of previous commits
    • outputs only single true / false value if any of provided paths contains changes
  • Changed Files Exporter
    • outputs lists with paths of created, updated and deleted files
    • output is not directly usable in the if clause
  • Changed File Filter
    • allows change detection between any refs or commits
    • fetches whole history of your git repository
    • might have negative performance impact on big repositories (github by default fetches only single commit)