How to execute Jobs if folder updated (recursive)?

Here’s my setup:

folder structure

repo:
  subdir:
    env1:
    env2:

Workflow simple example I’d like to accomplish

on:
  push:
    paths:
      - subdir/**
jobs:
  deploy:
    runs-on:  ubuntu-latest
    strategy:
      matrix:
        environment: [env1, env2]
    if: (execute only if ${{ matrix.env }} has changes)

I want a list of jobs to execute only for subdirectories that have commits. Any help would be appreciated!

IMO, your best path here is to have a previous job figuring out where the changes are, then setting the output and using that output to build your matrix job.

@heathsnow,

  1. If the commits pushed to the sub-directories occur before triggering the workflow, the paths filter is enough to meet your request. You just need to set some patterns to match all the paths that you want to trigger the workflow runs on push event.
on:
  push:
    paths:
      - path-pattern-1
      - path-pattern-2
      . . .
      - path-pattern-N
  1. If the commits to the sub-directories are pushed via a job in the workflow, you can set an output in this job to check the sub-directories are updated. If the the sub-directories are updated, set the value of the output to be true, otherwise false.
    A simple demo:
jobs:
  check_commits:
    runs-on: ubuntu-latest
    outputs:
      subdir_updated: ${{ steps.check_subdir.outputs.subdir_updated }}
    steps:
      - name: push commits
        . . .

      - name: check if sub-directories updated
        id: check_subdir
        run: |
        # here you need to replace "(sub-directories are updated)" with the actual if statement
          if [[ (sub-directories are updated) ]]; then
            echo "::set-output name=subdir_updated::true"
          else
            echo "::set-output name=subdir_updated::false"
          fi

  deploy:
    needs: [check_commits]
    if: needs.check_commits.outputs.subdir_updated == 'true'
    runs-on:  ubuntu-latest
    strategy:
      matrix:
        environment: [env1, env2]
    steps:
      . . .

I don’t think the paths filter is enough. If I make a change to repo > subdir > env1 contents then I want a job that will only, for instance, have a step echo "Updates made to env1" but I don’t want to see that for env2 since no changes were made in that directory.

Not a bad idea, thanks. I wish there were a canned solution already. Really I’m just trying to work around the fact that GitHub doesn’t have templating yet. I have 20+ workflows that are exactly the same except they trigger based on different paths getting updated and I was hoping to condense it down to 1 workflow with a matrix strategy.

@heathsnow,

You can use “git diff” command to list all the changed files, and then check if all the changed files are under the directory “subdir/env1”. If all the changed files are under that directory, set an output with the value is true, otherwise false. Then according to the value of the output to run or not run the subsequent job which has the step echo “Updates made to env1”.
A simple example:

jobs:
  check:
    name: Check changed files
    outputs:
      run_job: ${{ steps.check_files.outputs.run_job }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2
        with:
          fetch-depth: 2

      - name: check files
        id: check_files
        run: |
          echo "=============== list changed files ==============="
          git diff --name-only HEAD^ HEAD
          
          echo "========== check paths of changed files =========="
          git diff --name-only HEAD^ HEAD > files.txt
          while IFS= read -r file
          do
            echo $file
            if [[ $file != "subdir/env1/"* ]]; then
              echo "This file is not under the directory 'subdir/env1'."
              echo "::set-output name=run_job::false"
              break
            else
              echo "::set-output name=run_job::true"
            fi
          done < files.txt

  job_for_env1:
    name: Job for env1
    needs: check
    if: needs.check.outputs.run_job == 'true'
    runs-on: ubuntu-latest
    steps:
      - name: run for env1
        run: echo "Updates made to env1"
1 Like