Auto Committing with Github Actions

May 23, 2020

We usu­al­ly have tools that can auto­mat­i­cal­ly format or lint code for us, run­ning on file save or as a pre­com­mit hook.

How­ev­er, if the tool takes a long time to run, it can be dis­rup­tive to your flow. Github Actions can be used to run com­mands on your behalf and commit the fixes back to your branch. Here’s how to con­fig­ure an action that does that. In this exam­ple, I will use Scalafix as an exam­ple of a tool that can take a sig­nif­i­cant amount of time to run (on the order of min­utes for larger pro­jects), but any other tool can be sub­sti­tut­ed (e.g. eslint --fix).

Create a file in <project root>/.github/workflows, and put the fol­low­ing:

name: "Scalafix"
on: [pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
      with:
        ref: ${{ github.event.pull_request.head.ref }}
    - name: Set up JDK 1.8
      uses: actions/setup-java@v1
      with:
        java-version: 1.8
    - name: Run Scalafix
      # JVM settings as recommended by Scalafix
      env:
        JAVA_OPTS: -Xmx8G -Xms1G -Xss8M
      run: sbt all compile:scalafix test:scalafix
    - name: Commit files and push
      # We run git diff first to check if there are any files changed
      # before committing, since git commit will throw exit code 1 if there
      # are no files to commit
      run: |
        git config --local user.email "action@github.com"
        git config --local user.name "GitHub Action"
        git diff --quiet HEAD || git commit -am "Github Action: Scalafix"
        git push

This is a work­flow that is trig­gered on the pull_request event. Feel free to tweak it trig­ger on other events.

By default, actions/checkout will check­out to a detached HEAD, since pull request refs (stored in refs/pulls/12/merge) are not reg­u­lar branch­es (e.g. refs/heads/update-readme). In order to be able to able to commit back to the branch itself, check­out the branch cor­re­spond­ing to the pull request, using the ${{ github.event.pull_request.head.ref }} Github con­text vari­able.

Next, do Scalafix things, or insert the tool of your choice. Giving Scalafix enough memory is impor­tant to pre­vent the Github Actions runner from crash­ing from OOM errors. Github Actions run­ners are all stan­dard­ized with a 2-core CPU and 7GB of memory, which seems to be suf­fi­cient for Scalafix.

Once the tool(s) is done run­ning, there may be some changed files in the work­ing direc­to­ry. git commit returns an exit code of 1 if there are no files to commit, so check for the pres­ence of changed files with git diff --quiet HEAD before com­mit­ting all changed files, oth­er­wise the work­flow will appear to fail. Final­ly, push the commit to the branch.