Run job only if folder changed

Hello,

I currently have a job as part of my deploy workflow which connects to my db, runs migrations and seeds the database. That takes about 2 minutes total. I was wondering if there was any way to only this job or those commands IF the db folder changed?

I know I can write a bash script and use some sort of git command to check but I was wondering if there is an in-built way to do this?

Thanks!

@nahtnam,

There is not a built-in way to do that. You need to use some commands or actions to list all the modified files and check if all of them are in the db folder.
Suppose the db folder is located at the root of the repository, you can reference to the example below:

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

  job_for_db:
    name: Job for 'db' folder
    needs: check
    if: needs.check.outputs.run_job == 'true'
    runs-on: ubuntu-latest
    steps:
      - name: run for 'db' folder
        run: echo "This job runs only when all the modified files are under the 'db' folder."

The following is a similar case as reference:

2 Likes

Thank you! That did the trick, however I’m stuck on another issue. I want my next job (deploy) to wait for database to finish before running, but if database is skipped then run it immediately. Any idea how to do that?

I found a “hack”. I just moved the if statement you provided to every command inside of the database job instead of at the job level if that makes sense

@nahtnam,

I recommend you to use needs and Job status check functions to do that.
In your case, you can try like as below:

jobs:
  . . .

  database:
    name: job database
    . . .

  deploy:
    name: job deploy
    needs: database
    if: always()
    . . .

In this way:

  • If job database is executed, job deploy will run after database completes, regardless of whether database is success or failure.
  • If job database is skipped, job deploy also will run.
1 Like

That worked, thank you so much! Just wondering, where did you learn all of this? The docs are kind of hard to sort through, so I was wondering if there was another guide or something

@nahtnam,

In fact, the official documentation has provided a quite detailed guidance of these knowledge and technologies. The appropriate engineering team also will update the documentation in time for the new features, or improve some descriptions in the documentation so that readers can better understand the relevant knowledge.

You can pay more attention to these documents, and learn almost all the technical guidance about GitHub Actions.
Of course, if you have any questions or troubles on GitHub Actions, please feel free to contact us. We will provide you with help and support as mush as we can.

Thank you, I think its just a lot of information all in one place so its a little overwhelming. Also I am facing an issue.

So I made a change to my prisma folder (database) and for some reason my database job was still skipped:

check_database:
    outputs:
      run_job: ${{ steps.check_files.outputs.run_job }}
    runs-on: ubuntu-latest
    needs: test
    if: github.ref == 'refs/heads/master'
    steps:
      - name: Checkout code
        uses: actions/checkout@v2
        with:
          fetch-depth: 2

      - name: check modified files
        id: check_files
        run: |
          echo "=============== list modified files ==============="
          git diff --name-only HEAD^ HEAD

          echo "========== check paths of modified files =========="
          git diff --name-only HEAD^ HEAD > files.txt
          while IFS= read -r file
          do
            echo $file
            if [[ $file = prisma/* ]]; then
              echo "This modified file is under the 'prisma' folder."
              echo "::set-output name=run_job::true"
              break
            else
              echo "::set-output name=run_job::false"
            fi
          done < files.txt

  database:
    runs-on: ubuntu-latest
    needs: check_database
    if: needs.check.outputs.run_job == 'true' && github.ref == 'refs/heads/master'
    env:
      DATABASE_URL: ${{ secrets.PRODUCTION_DATABASE_URL }}
    steps:
      - uses: actions/checkout@v2
      - name: Use Node.js 12.x
        uses: actions/setup-node@v1
        with:
          node-version: '12.x'
      - run: npm ci
      - run: npx prisma generate
      - run: npx prisma migrate up --experimental
      - run: npx ts-node prisma/_seeds/production/index.ts

In the check_database job, I see this output

=============== list modified files ===============
// bunch of files here
========== check paths of modified files ==========
prisma/_seeds/development/index.ts
This modified file is under the 'prisma' folder.

so it looks like it is recognizing the change but for some reason the database job is still skipped.

@nahtnam,

You are referencing to an inconsistent job id in the if conditional, the expression syntax is “needs.<job id>.outputs.<output name>”.

needs: check_database
if: needs.check.outputs.run_job == 'true' && ...

it should be “check_database”, not “check”, you do not set job “database” needs any one job which named “check”, and there is no job “check” in your workflow.
So, please correct your workflow like as below, and try again:

needs: check_database
if: needs.check_database.outputs.run_job == 'true' && ...

My mistake, thank you so much!

@nahtnam,
You’re welcome. And glad that my suggestions can help you solve the problems.
If you have any other questions about this ticket, feel free to tell me.