paths-filter icon indicating copy to clipboard operation
paths-filter copied to clipboard

Is there a way to list only folders with changes

Open ahrenstein opened this issue 2 years ago • 6 comments

I'm working on a repo where this GitHub Action would be very useful. The folder structure is:

.
└── .github
    └── workflows
        └── deploy.yml
├── README.md
└── Services
    ├── serviceone
    │   └── bunch_of_files
    ├── servicetwo
    │   └── bunch_of_files
    └── servicethree
        └── bunch_of_files

The deploy.yml file currently looks like

name: 'Deploy'

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Check which services were modified
        uses: dorny/paths-filter@v2
        id: filter
        with:
          list-files: shell
          filters: |
            services:
              - 'Services/**'
      - name: Deploy changed services
        if: ${{ steps.filter.outputs.services == 'true' }}
        run: |
          echo "All changes:"
          for i in ${{ steps.filter.outputs.services_files }}; do echo $i | awk ; done

Instead of outputting the individual files changes in Services/** is there a way to just output the folders containing changes such as

Services/serviceone
Services/servicethree

Thanks!

ahrenstein avatar May 16 '22 19:05 ahrenstein

I have exactly the same requirement! I want to trigger a Terraform plan, based on changed files in a certain directory, so if this is possible it would be really nice!

nilsdebruin avatar Jun 03 '22 07:06 nilsdebruin

For what its worth I was able to accomplish this for my use case by following the example: Use change detection to configure matrix job From the Readme https://github.com/dorny/paths-filter#conditional-execution

ndpete avatar Jul 12 '22 19:07 ndpete

@ndpete what did you do to go from changed files to the directories? Did you add an extra step with some shell scripts?

ca-scribner avatar Jul 21 '22 20:07 ca-scribner

So my filters are just slightly different than the example. The key on the filter is a directory name and the value is the directory. After the matrix is setup I just set a default working-directory to the output from the matrix which is the key and therefore the directory I need to run from. Below is the example modified similiar to what I have.

jobs:
  # JOB to run change detection
  changes:
    runs-on: ubuntu-latest
    outputs:
      # Expose matched filters as job 'packages' output variable
      packages: ${{ steps.filter.outputs.changes }}
    steps:
    # For pull requests it's not necessary to checkout the code
    - uses: dorny/paths-filter@v2
      id: filter
      with:
        filters: |
          package1: src/package1
          package2: src/package2

  # JOB to build and test each of modified packages
  build:
    needs: changes
    strategy:
      matrix:
        # Parse JSON array containing names of all filters matching any of changed files
        # e.g. ['package1', 'package2'] if both package folders contains changes
        package: ${{ fromJSON(needs.changes.outputs.packages) }}
    runs-on: ubuntu-latest
    defaults:
       run:
         working-directory: src/${{ matrix.package }}
    steps:
      - uses: actions/checkout@v2
      - ...

So the two key points are key in the filter is the directory name and then the part:

defaults:
      run:
        working-directory: src/${{ matrix.package }}

This sets the working directory for the matrix jobs to src/{whatever the current matrix job is}

Does that make any sense? I could mock up a repo that shows the process if needed.

ndpete avatar Jul 22 '22 16:07 ndpete

@ndpete - does this mean you have to update the workflow file each time you add another "package" in the src directory?

in past I used shell script similar to this:

for f in {{ steps.filter.outputs.packages }}; do echo $(dirname $f);done | sort | cut ... | uniq

I put all of the above in a composite action so I have simple workflow, but it would be a lot nicer if it was part of the paths-filter action itself :(

also, it's best to include an aggregate check which can be used in branch protection rules as the actual checks vary due to the matrix set up

vincentgna avatar Aug 11 '22 05:08 vincentgna

@ahrenstein @vincentgna

I ended up with this hacky solution:

jobs:
  changes:
    runs-on: ubuntu-latest
    # Required permissions
    # permissions:
    #   pull-requests: read
    outputs:
      # Expose matched filters as job 'packages' output variable
      packages: ${{ steps.filter.outputs.changes }}
      directories: ${{ steps.transform.outputs.directories }}
    steps:
    # For pull requests it's not necessary to checkout the code
    - name: Checkout
      uses: actions/checkout@v3
    - uses: dorny/paths-filter@v2
      id: filter
      with:
        base: ${{ github.ref }}
        list-files: shell
        filters: |
          azure:
            - azure/**
    - name: transform to directories
      id: transform
      continue-on-error: false
      run: |
        folders=()
        for f in ${{ steps.filter.outputs.azure_files }}; \
          do \
            echo "Adding $(dirname $f) to folders"; \
            folders+=($(dirname $f)); \
        done
        unique_folders=($(printf "%s\n" "${folders[@]}" | sort -u | tr '\n' ' '))
        echo "directories=$(jq --compact-output --null-input '$ARGS.positional' --args -- ${unique_folders[@]})" >> $GITHUB_OUTPUT

terraform:
    needs: changes
    name: "Terraform"
    runs-on: ubuntu-latest
    strategy:
      matrix:
        directory: ${{ fromJSON(needs.changes.outputs.directories) }}
    steps:
      - name: test
         run: echo ${{ matrix.directory }}

It would be great to add an option that simulates it, something like list-directories: shell. I don't mind adding a PR if it's accepted by the owners.

Shaked avatar Nov 22 '22 15:11 Shaked