Executing GitHub Actions jobs or steps only when specific files change

 
 
  • Gérald Barré

Sometimes you want to execute a step or a job only when some files are modified. For instance, you may want to lint and deploy the documentation only when a file under docs is modified. This scenario is not directly supported by GitHub Actions. However, you can do it manually by using git, PowerShell Core, and GitHub Actions expressions.

The main idea is to use git diff to get the list of modified files. Then, you can filter the list of files using a regex (easy using PowerShell!). Finally, you can set the output of the step/job by using a workflow command.

#Conditional steps

Use the following workflow if you want to run a step conditionally.

.github/workflows/conditional-step.yml (YAML)
name: demo
on:
  push:
    branches:
      - 'main'

jobs:
  conditional_step:
    runs-on: 'ubuntu-20.04'
    steps:
    - uses: actions/checkout@v2
      with:
        # Checkout as many commits as needed for the diff
        fetch-depth: 2
    - shell: pwsh
      # Give an id to the step, so we can reference it later
      id: check_file_changed
      run: |
        # Diff HEAD with the previous commit
        $diff = git diff --name-only HEAD^ HEAD

        # Check if a file under docs/ or with the .md extension has changed (added, modified, deleted)
        $SourceDiff = $diff | Where-Object { $_ -match '^docs/' -or $_ -match '.md$' }
        $HasDiff = $SourceDiff.Length -gt 0

        # Set the output named "docs_changed"
        Write-Host "::set-output name=docs_changed::$HasDiff"

    # Run the step only with "docs_changed" equals "True"
    - shell: pwsh
      # steps.<step_id>.outputs.<output name>
      if: steps.check_file_changed.outputs.docs_changed == 'True'
      run: echo publish docs

#Conditional jobs

Use the following workflow if you want to run a job conditionally.

.github/workflows/conditional-job.yml (YAML)
name: sample
on:
  push:
    branches:
      - 'main'

jobs:
  conditional_job_check_files:
    runs-on: 'ubuntu-20.04'
    # Declare outputs for next jobs
    outputs:
      docs_changed: ${{ steps.check_file_changed.outputs.docs_changed }}
    steps:
    - uses: actions/checkout@v2
      with:
        # Checkout as many commits as needed for the diff
        fetch-depth: 2
    - shell: pwsh
      id: check_file_changed
      run: |
        # Diff HEAD with the previous commit
        $diff = git diff --name-only HEAD^ HEAD

        # Check if a file under docs/ or with the .md extension has changed (added, modified, deleted)
        $SourceDiff = $diff | Where-Object { $_ -match '^docs/' -or $_ -match '.md$' }
        $HasDiff = $SourceDiff.Length -gt 0

        # Set the output named "docs_changed"
        Write-Host "::set-output name=docs_changed::$HasDiff"

  # Run the job only with "docs_changed" equals "True"
  conditional_job:
    runs-on: 'ubuntu-20.04'
    needs: [ conditional_job_check_files ]
    if: needs.conditional_job_check_files.outputs.docs_changed == 'True'
    steps:
    - shell: pwsh
      run: echo publish docs

#Additional resources

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?Buy Me A Coffee💖 Sponsor on GitHub