Skip to content
file

GitHub Action

Changed Files with filter

v3.2.0 Latest version

Changed Files with filter

file

Changed Files with filter

A GitHub Action that outputs a list of changed files in pull requests and commits

Installation

Copy and paste the following snippet into your .yml file.

              

- name: Changed Files with filter

uses: yumemi-inc/changed-files@v3.2.0

Learn more about this action in yumemi-inc/changed-files

Choose a version

CI

Changed Files

A GitHub Action that outputs a list of changed files in pull requests and commits. It is useful when you want to run steps or jobs based on changed files.

Usage

See action.yml for available action inputs and outputs. Note that this action requires contents: read permission.

Supported workflow trigger events

Works on any event. Basically it works as is, but if you want to customize it, refer to the Specify comparison targets section.

Use a list of files

Use list of file names from files output.

- uses: yumemi-inc/changed-files@v3
  id: changed
- run: |
    for file in ${{ steps.changed.outputs.files }}; do
      # do somethihg..
      echo "$file"
    done

Note: This action only gets a list of file names. If you need access to a file, use actions/checkout to check out the files.

By default, they are separated by spaces, but if you want to change the separator, specify it with separator input.

- uses: yumemi-inc/changed-files@v3
  id: changed
  with:
    separator: ','

If you want to output in JSON instead of plain text like above, specify it with format input (default is plain).

- uses: yumemi-inc/changed-files@v3
  id: changed
  with:
    format: 'json'

filter files

The list of files can be filtered by specifying patterns input.

- uses: yumemi-inc/changed-files@v3
  id: changed
  with:
    patterns: |
      **/*.{yml,yaml}
      !doc/**

To filter by file status, specify statuses input.

- uses: yumemi-inc/changed-files@v3
  id: changed
  with:
    patterns: |
      **/*.{yml,yaml}
      !doc/**
    statuses: 'added'
about file status

There are four statuses for changed files: added, modified, renamed, and removed. File statuses are displayed as an icon in pull requests:

image

Note that renamed files will have renamed status even if edited.

To specify multiple statuses, separate them with non-alphabetic characters, such as a space, ,, and |.

statuses: 'added|modified|renamed'

Alternatively, you can specify statuses to exclude with exclude-statuses input.

exclude-statuses: 'removed'

Whether a particular file is included

Often we are only interested in whether a particular file is included, not the list of files. You can check it like steps.<id>.outputs.files != null (for JSON, '[]' instead of null), but you can also use exists output.

- uses: yumemi-inc/changed-files@v3
  id: changed
  with:
    patterns: '!**/*.md'
- if: steps.changed.outputs.exists == 'true' # or fromJSON(steps.changed.outputs.exists)
  run: # do something..

This is useful for controlling step execution.

examples

Used as test execution condition

- uses: actions/checkout@v4
- uses: yumemi-inc/changed-files@v3
  id: changed
  with:
    patterns: '**/*.js'
- if: steps.changed.outputs.exists == 'true'
  run: npm run test

Add a label to a pull request:

- uses: yumemi-inc/changed-files@v3
  id: changed
  with:
    patterns: |
      **/*.js
      !server/**
- env:
    GH_REPO: ${{ github.repository }}
    GH_TOKEN: ${{ github.token }}
  run: |
    gh pr edit ${{ github.event.number }} ${{ steps.changed.outputs.exists == 'true' && '--add-label' || '--remove-label' }} 'frontend'

Annotate new files in a pull request using workflow commands:

- uses: yumemi-inc/changed-files@v3
  id: changed
  with:
    patterns: '**/*.xml'
    statuses: 'added'
- if: steps.changed.outputs.exists == 'true'
  run: |
    for file in ${{ steps.changed.outputs.files }}; do
      echo "::notice file=$file::New XML file added. Please check .."
    done

image

For more information on workflow commands, see Workflow commands for GitHub Actions.

Warn with a comment on a pull request:

- uses: yumemi-inc/changed-files@v3
  id: changed-src
  with:
    patterns: |
      **/*.{js,ts}
      package.json
- uses: yumemi-inc/changed-files@v3
  id: changed-build
  with:
    patterns: 'dist/**'
- if: steps.changed-src.outputs.exists == 'true' && steps.changed-build.outputs.exists != 'true'
  uses: yumemi-inc/comment-pull-request@v1
  with:
    comment: ':warning: Please check if you forgot to build.'

Make the job fail:

- uses: yumemi-inc/changed-files@v3
  id: changed
  with:
    patterns: 'CHANGELOG.md'
    exclude-statuses: 'removed'
- if: steps.changed.outputs.exists != 'true' && github.base_ref == 'main'
  run: |
    echo "::error::CHANGELOG.md is not updated."
    exit 1

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/changed-files@v3
  with:
    patterns: '!**/*.md'
    run: # do something..

Use number of changed lines

changes output is the total number of changed lines. This can be used in comparison expressions.

- uses: yumemi-inc/changed-files@v3
  id: changed
  with:
    patterns: '!doc/**'
    exclude-statuses: 'removed'
- if: 100 < steps.changed.outputs.changes
  run: # do something..

additions output and deletions output can also be used in the same way (additions + deletions = changes).

examples

Add a label to a pull request:

- uses: yumemi-inc/changed-files@v3
  id: changed
  with:
    patterns: '!doc/**'
    exclude-statuses: 'removed'
- env:
    GH_REPO: ${{ github.repository }}
    GH_TOKEN: ${{ github.token }}
  run: |
    gh pr edit ${{ github.event.number }} ${{ 100 < steps.changed.outputs.changes && '--add-label' || '--remove-label' }} 'large PR'

Warn with a comment on a pull request:

- uses: yumemi-inc/changed-files@v3
  id: changed
  with:
    patterns: '!doc/**'
    exclude-statuses: 'removed'
- if: 100 < steps.changed.outputs.changes
  uses: yumemi-inc/comment-pull-request@v1
  with:
    comment: ':warning: Changes have exceeded 100 lines.'

Specify comparison targets

Simply, the change files are determined between head-ref input and base-ref input references. The behavior is the same as yumemi-inc/path-filter, so refer to it for details, but this Changed Files action has the following limitations:

  • The upper limit for the number of files is 3,000 when used with the default value in pull_request, pull_request_target, push, and merge_group events (there is a head commit immediately after the base commit, like a merge commit), and 300 otherwise.
  • Always performs three-dot comparison, does not support two-dot.

These limitations are because this aciton uses GitHub API. If these limitations are a problem, use yumem-inc/path-filter. Although there are no functions such as filter by status or output the number of changed lines, but since it does not have the above limitations, it works as a complete path filter.

My recommendation is to use this Changed Files action, which has functions useful for checking pull requests, in pull_request, pull_request_target and merge_group events. There is no problem unless it is a large pull request with 3,000 files. For other events, you don't need many functions, so use yumem-inc/path-filter.

Tips

Control job execution

Set this action's exists output to the job's output, and reference it in subsequent jobs.

outputs:
  exists: ${{ steps.changed.outputs.exists }}
steps:
  - uses: yumemi-inc/changed-files@v3
    id: changed
    with:
      patterns: '**/*.{kt,kts}'
examples

Run two jobs in parallel, then run a common job:

jobs:
  changed:
    runs-on: ubuntu-latest
    permissions:
      contents: read
    outputs:
      exists-src: ${{ steps.changed-src.outputs.exists }}
      exists-doc: ${{ steps.changed-doc.outputs.exists }}
    steps:
      - uses: yumemi-inc/changed-files@v3
        id: changed-src
        with:
          patterns: 'src/**'
      - uses: yumemi-inc/changed-files@v3
        id: changed-doc
        with:
          patterns: 'doc/**'
  job-src:
    needs: [changed]
    if: needs.changed.outputs.exists-src == 'true'
    runs-on: ubuntu-latest
    steps:
      ...
  job-doc:
    needs: [changed]
    if: needs.changed.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:
      ...

Process output list with JavaScript

Use JSON format output and actions/github-script.

- uses: yumemi-inc/changed-files@v3
  id: changed
  with:
    format: 'json'
- uses: actions/github-script@v6
  env:
    FILES: ${{ steps.changed.outputs.files }}
  with:
    script: |
      const { FILES } = process.env;
      const files = JSON.parse(FILES);
      files.forEach(file => {
        // do something..
        console.log(file);
      });

Comment to patterns input

Characters after # are treated as comments. Therefore, you can write an explanation for the pattern as a comment.

- uses: yumemi-inc/changed-files@v3
  id: changed
  with:
    patterns: |
      # sorces
      **/*.{js,css,png}
      !dist/** # exclude built files

      # documents
      doc/**
      !doc/**/*.png # exclude image files

List of changed files and list after filtering

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 other inputs of filtering conditions. For example, display them in the job summary like this:

- uses: yumemi-inc/changed-files@v3
  id: changed
  with:
    patterns: '!**/*.md'
- run: |
    {
      echo '### files before filtering'
      echo '```json'
      cat '${{ steps.changed.outputs.action-path }}/files.json' | jq
      echo '```'
      echo '### files after filtering'
      echo '```json'
      cat '${{ steps.changed.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.

About the glob expression of pattern input

Basically, it complies with the minimatch library used in this action. Please refer to the implementation in action.yml for the specified options.