DEV Community

Cover image for Get changed files in github actions
Kars Barendrecht for Scienta

Posted on • Edited on

Get changed files in github actions

In the search of a proper way to lint our pull request (changes only). I came across some Github Actions that all require you to share your Github token. With some knowledge of git I immediately thought, why not solve this with git diff and some shell commands, thus without the use of a Github token. Git is present by default in the action containers anyway. Eventually I came up with the following solution. All this requires is a checkout of the codebase, with a step provided by github itself: actions/checkout@v2.

Get the commit-sha's to compare

In order to get the commit sha that the pull request was based off, we can use the Github's context, available in all actions. Here we have acces to github.event.pull_request.base.sha and github.sha.

Only include files that are still present

To only get the files that are changed and still present we can add the argument --diff-filter=ACMRT. This will only return files that are added, copied, modified, renamed or changed. In order to get the filenames only we also add --name-only.

Remove newlines

In order to get the output in a single line, without newlines, we can pipe the output to xargs: COMMAND | xargs.

Filter by filetype (optional)

To get changed files grouped by filetype we can also pipe grep using a regex as such: COMMAND | grep .ts$.

The finished command

Combining these arguments, we get something like this:
git diff --name-only --diff-filter=ACMRT ${{ github.event.pull_request.base.sha }} ${{ github.sha }} | grep .css$ | xargs.

Using the changed files in an actions workflow

To be able to use the files in another job, we have to use outputs. Here is a full workflow example.

jobs:
  changedfiles:
    runs-on: ubuntu-latest
    # Map a step output to a job output
    outputs:
      all: ${{ steps.changes.outputs.all}}
      ts: ${{ steps.changes.outputs.ts }}
    steps:
        # Make sure we have some code to diff.
      - name: Checkout repository
        uses: actions/checkout@v2
      - name: Get changed files
        id: changes
        # Set outputs using the command.
        run: |
          echo "::set-output name=all::$(git diff --name-only --diff-filter=ACMRT ${{ github.event.pull_request.base.sha }} ${{ github.sha }} | xargs)"
          echo "::set-output name=ts::$(git diff --name-only --diff-filter=ACMRT ${{ github.event.pull_request.base.sha }} ${{ github.sha }} | grep .ts$ | xargs)"
  lint:
    runs-on: ubuntu-latest
    # require the first job to have ran
    needs: changedfiles
    # only run there are changed files
    if: ${{needs.changedfiles.outputs.ts}}
    steps:
      - name: echo changed files
        run: echo ${{needs.changedfiles.outputs.ts}}

Enter fullscreen mode Exit fullscreen mode

Top comments (6)

Collapse
 
peterbe profile image
Peter Bengtsson

When I try that, I always get:

fatal: bad object fa811c9694fc26ae85d5433fccf264ce7c200b32
Enter fullscreen mode Exit fullscreen mode

That sha is the github.event.pull_request.base.sha thing.

Collapse
 
veotani profile image
Anton Orlov • Edited

It's because you fetch only latest commit. You can solve this issue by setting fetch-depth to 0 to retrieve full git history.

- uses: actions/checkout@v2
  with:
    fetch-depth: 0
Enter fullscreen mode Exit fullscreen mode
Collapse
 
parasense profile image
parasense

To elaborate on Anton Orlov's answer regarding fetch-depth.
To have git diff the current HEAD (depth:1) against the previous commit (depth:2), then one needs to have at least fetch-depth: 2, and if one wants to diff against deeper history one must set the depth accordingly. Many git work flows only fetch a depth:1 as an optimization, to avoid expensive IO copying entire repos when only the single commit is needed.

Collapse
 
william_fowler_a3f8fae149 profile image
William Fowler

Is there a version of this that will work when you just run a push? For example, if you push straight into master it'll simply compare the current revision to the last one? I imagine you'd have to replace github.event.pull_request.base.sha and maybe github.sha, but for the life of me I don't know what to replace them with.

Collapse
 
aamir_khan_b329b86069b4fe profile image
Aamir Khan

I'm getting error as
Run echo "::set-output name=all::$(git diff --name-only --diff-filter=ACMRT 91d2a5a7769c5ba847074bf19683a938f9135ab7 a0827f508c826ff2e55274efa0d8b860b46398f9 | xargs)"

shell: /bin/bash -e {0}
error: Could not access '91d2a5a7769c5ba847074bf19683a938f9135ab7'
error: Could not access '91d2a5a7769c5ba847074bf19683a938f9135ab7'

Collapse
 
jorgesanchezgit profile image
JorgeSanchezGit

I had this same error and I resolved it by updating the git version. You need git version 2.something on your runner. If you are running centos7 then the yum repo gives you version 1.something so you will have to install git from source. See: computingforgeeks.com/how-to-insta...

Even if your runner isnt running centos7, the solution will likely be installing the latest version of git on the runner.