💸Save up to $132K/month in CI costs!👉 Try Free
Skip to main content
← Back to workflows

How to conditionally run GitHub Actions based on modified files in pull requests or commits?

dorny/paths-filte -
GitHub Action
v3.0.2
2,137
Contributors
Contributor - dornyContributor - JJContributor - jsoref

Optimize Your CI/CD Pipeline

Get instant insights into your CI/CD performance and costs. Reduce build times by up to 45% and save on infrastructure costs.

45% Faster Builds
60% Cost Reduction
Usage
- uses: dorny/paths-filter@v3
id: changes
with:
filters: |
src:
- 'src/**'

paths-filter logo

paths-filter

Conditionally run actions based on files modified by PR, feature branch or pushed commits


We've investigated a GitHub Action that could be a game-changer, especially for the developers who are working on some big project with lots of components - e.g., monorepo. It's designed to help you slim down your CI/CD processes by running workflows conditionally, based on which files have been modified.

This will allow you to target specific actions towards, for example, integration tests or deployments, only in the changed components, which will save tons of time and computing resources. Here's how it works: CircleCI processes the files changed in a PR, commits to a feature branch, or recent pushes and then it decides whether there are some steps or jobs that should be executed. The standard path filters coming from GitHub workflows are not really up to this, because those work at the workflow level only, not on a per-step or even per-job level.

In one word, invaluable tool to make CI/CD workflows efficient and custom, discarding unnecessary runs and focusing resources where they really matter. It is one of the best examples of optimization in technology for the service of very dynamic and sizeable coding environments.

How to Conditionally Execute Steps in GitHub Actions Based on Subfolder Changes

You can tailor workflow to react to changes in specific subfolders. This approach ensures that jobs are triggered only when relevant files are altered, which optimizes both time and computational resources.

jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
backend:
- 'backend/**'
frontend:
- 'frontend/**'

# Run backend tests only if backend files were changed
- name: backend tests
if: steps.filter.outputs.backend == 'true'
run: echo "Executing backend tests..."

# Run frontend tests only if frontend files were changed
- name: frontend tests
if: steps.filter.outputs.frontend == 'true'
run: echo "Executing frontend tests..."

# Run end-to-end tests if either backend or frontend files were changed
- name: e2e tests
if: steps.filter.outputs.backend == 'true' || steps.filter.outputs.frontend == 'true'
run: echo "Executing end-to-end tests..."

Key Points:

  • The dorny/paths-filter action is used to check if any files in the 'backend/' or 'frontend/' directories have changed. This is determined by defining filters within the action's with block.
  • Conditional statements (if:) are then applied to the subsequent steps to determine if they should execute based on whether changes were detected in the backend or frontend.
  • This selective execution is not only efficient but also reduces the likelihood of running unnecessary tests, saving valuable CI/CD resources and time.

How to Execute Specific Jobs in GitHub Actions Based on Subfolder Changes?

jobs:
# JOB to run change detection
changes:
runs-on: ubuntu-latest
permissions:
pull-requests: read
outputs:
backend: ${{ steps.filter.outputs.backend }}
frontend: ${{ steps.filter.outputs.frontend }}
steps:
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
backend:
- 'backend/**'
frontend:
- 'frontend/**'

# JOB to build and test backend code only if backend files were changed
backend:
needs: changes
if: ${{ needs.changes.outputs.backend == 'true' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: echo "Building and testing backend..."

# JOB to build and test frontend code only if frontend files were changed
frontend:
needs: changes
if: ${{ needs.changes.outputs.frontend == 'true' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: echo "Building and testing frontend..."
  • The workflow starts with a 'changes' job using the dorny/paths-filter action to detect changes specifically in the 'backend/' and 'frontend/' subfolders. This is crucial for projects that contain distinct components with separate development timelines.
  • Depending on whether changes are detected in these subfolders, conditional jobs for the backend and frontend are executed. This conditional execution is managed by the if: clauses, which check the outputs from the 'changes' job.

How to Use Change Detection to Configure Matrix Jobs in GitHub Actions

Implementing matrix jobs in GitHub Actions based on the results of change detection can drastically optimize your workflow. This setup is ideal for projects with multiple components or packages that may require independent testing or building.

jobs:
# JOB for detecting changes
changes:
runs-on: ubuntu-latest
permissions:
pull-requests: read
outputs:
packages: ${{ steps.filter.outputs.changes }}
steps:
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
package1: src/package1
package2: src/package2

# JOB to build and test each of the modified packages using a matrix strategy
build:
needs: changes
strategy:
matrix:
package: ${{ fromJSON(needs.changes.outputs.packages) }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: echo "Building and testing ${{ matrix.package }}"

Key Points:

  • The 'changes' job utilizes the dorny/paths-filter action to identify if there are any modifications in specific package directories like 'src/package1' or 'src/package2'. This is especially beneficial in a monorepo setup or when handling multiple related but independent codebases.
  • Upon detecting changes, the names of the affected packages are outputted and then utilized in the 'build' job. This job employs a matrix strategy, dynamically generating a job for each package that has changes. The matrix functionality efficiently manages parallel testing or building of multiple components.

How to detect changes to a pull request against the base branch in GitHub Actions?

Setting workflows to monitor changes within the pull requests against a defined branch ensures that CI processes remain efficient and relevant. In this setup, actions are triggered when changes are made against core branches, such as master or develop.

Explanation of the Code:

on:
pull_request:
branches:
- master
— develop

jobs:
build:
runs-on: ubuntu
permissions:
pull_request: read
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
with:
filters: ... # Define specific filters based on your project structure
  • A workflow will be triggered on a pull request that targets the master or develop branch. This focuses on the limitation of changes considered to those that target the critical branches, therefore ensuring the integrity and stability of your main development and production environments.
  • The dorny/paths-filter action filters the changes with respect to the base branch of the pull request. This action requires configuration using the filters matching the directory structure of your project and the components you have so that you can do a check on whatever has changed.
  • Set the needed permissions on the pull request to be "read" by the workflow. Ensure it doesn't give out too many privileges but allows access to the necessary data.

How to detect changes in feature branches against a configured base branch in GitHub Actions

Automated this through workflows in GitHub Actions to detect changes against a specified base branch (like develop) in feature branches. This way, testing and integration would be smoother in that CI tasks would trigger only relevant changes.

on:
push:
branches:
- feature/**
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 20
- uses: dorny/paths-filter@v3
id: filter
with:
base: develop # Change detection against merge-base with this branch
filters: ... # Configure your filters

When commits are pushed to a branch matching the feature/** pattern, they focus on new development for a feature. - The actions/checkout@v4 action is configured with a fetch-depth of 20 so that it potentially saves histories commits from being checked out; the merge base with the develop branch is within the last 20 commits, speeding up the checkout process. And then, the dorny/paths-filter@v3 action comes in to check which are the files or directories that change in regard to this develop branch.

It is set for actually determining if those changes are going to be pertinent with respect to feature development and then trigger all of the following steps in that build job.