A blazing-fast, dependency-free GitHub Action that detects changed files and intelligently outputs a JSON array to power dynamic matrix strategies in your CI/CD pipelines.
Instead of waiting 10+ seconds for Node.js-based diff tools to boot up, this Composite Action runs natively in milliseconds using pure Bash, git, and jq.
- β‘οΈ Instant Execution: Native composite action, zero Docker startup time.
- π§ Monorepo Smart: Automatically parses deeply nested file changes into their top-level application folders.
- π» Ghost-Job Prevention: Automatically ignores deleted folders so your matrix jobs don't crash with "folder not found".
- π‘οΈ Regex Filtering: Effortlessly ignore documentation, tests, or config changes.
- π Initial Commit Safe: Gracefully handles first-time pushes without crashing.
| Input | Description | Required | Default |
|---|---|---|---|
target_folder |
The parent folder to monitor. Outputs immediate subdirectories. Use . for root. |
No | . |
exclude |
Comma-separated patterns to ignore (e.g., README.md,docs/,*.txt). |
No | "" |
base_sha |
Base commit SHA to compare against. | No | PR Base or Previous Push |
head_sha |
Head commit SHA to compare. | No | Current SHA |
| Output | Description | Example |
|---|---|---|
matrix |
JSON array of changed folders (drop-in ready for strategy.matrix). |
["apps/web", "apps/api"] |
files |
JSON array of exactly what files changed. | ["apps/web/src/App.tsx"] |
any_changed |
Boolean indicating if anything changed after exclusions. | true or false |
Imagine a repository with an apps/ directory containing frontend and backend. If a pull request only modifies apps/backend/src/main.go, this workflow will dynamically only spin up a build job for the backend.
name: CI
on: [pull_request]
jobs:
detect-changes:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.changes.outputs.matrix }}
any_changed: ${{ steps.changes.outputs.any_changed }}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0 # Required to accurately compare commits
- name: Get Changed Apps
id: changes
uses: harryvasanth/smart-changed-files@v1
with:
target_folder: "apps"
exclude: "README.md,*.md,.gitignore"
build:
needs: detect-changes
if: needs.detect-changes.outputs.any_changed == 'true'
runs-on: ubuntu-latest
strategy:
matrix:
app: ${{ fromJson(needs.detect-changes.outputs.matrix) }}
steps:
- uses: actions/checkout@v6
- name: Build ${{ matrix.app }}
run: |
cd ${{ matrix.app }}
npm run buildJust want to know if you should run your linting pipeline?
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Check for JS/TS changes
id: changes
uses: harryvasanth/smart-changed-files@v1
- name: Run Linter
if: steps.changes.outputs.any_changed == 'true'
run: npm run lint