Figure 1: Pre-commit running within GitLab CI

Figure 1: Pre-commit running within GitLab CI

I’ve been using pre-commit as my tool to set up hooks to run when I commit to Git. It helps me catch gotchas such as fixing line endings, fixing whitespace, refusing to commit on linter errors, and so on. Often, I’ve noticed with working on teams is it’s fairly easy for a new contributor to forget to set up pre-commit on their development machine. Heck, even I forget to run pre-commit install every time I clone a repository.

It makes sense to run pre-commit on every push: pre-commit in CI enforces that everyone is going to follow the the policy specified in your pre-commit configuration. If you don’t run pre-commit in your CI, could code quality suffer?

To make this happen, I’ve published a docker image and documented how to do this in my pre-commit-docker git repository. I will also describe how to use pre-commit-docker in this post.

§Using GitHub Actions

Add the following text to .github/workflowspre-commit.yml :

name: Run pre-commit

on: push

jobs:
  pre-commit:
    runs-on: ubuntu-latest
    container:
      image: registry.gitlab.com/winny/pre-commit-docker:latest
      env:
        PRE_COMMIT_HOME: .pre-commit-cache
    steps:
      - uses: actions/checkout@v3
      - name: Allow workspace
        run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
      - name: Cache .pre-commit-cache
        uses: actions/cache@v3
        with:
          path: |
            .pre-commit-cache            
          key: ${{ runner.os }}-pre-commit-cache-${{ hashFiles('.pre-commit-config.yaml') }}
      - name: Run pre-commit
        run: pre-commit run -a

Because of how pre-commit operates, removal of $PRE_COMMIT_HOME causes pre-commit to download the hooks and runtimes from the internet. As a result, I strongly encourage caching this folder. That is what the actions/cache action is doing in this example.

§Using GitLab CI

Add the following job to your .gitlab-ci.yml .

pre-commit:
  stage: test
  image: registry.gitlab.com/winny/pre-commit-docker:latest
  variables:
    PRE_COMMIT_HOME: .pre-commit-cache
  cache:
    - key:
        files: [.pre-commit-config.yaml]
      paths: [.pre-commit-cache]
  script:
    - pre-commit run -a

Same story with the pre-commit.cache key. This enables pre-commit to run consistently fast for the majority of CI jobs.

§What’s in this docker image?

The docker image used in these pipelines includes pre-commit. Any image that includes pre-commit should suffice. You can pull it via: docker pull registry.gitlab.com/winny/pre-commit-docker . This will pull the images from registry.gitlab.com.

You can see the Dockerfile here. It’s a Debian image that includes pre-commit. Here’s the gist of it:

FROM debian:bullseye-slim

RUN apt-get update \
    && apt-get install -y --no-install-recommends git pre-commit \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

So, pretty simple. One might modify this to include additional dependencies needed by the project’s specific pre-commit runs.

§That’s all

I’ve been using this pre-commit CI job at my job and for personal projects. It’s nice to know all CI jobs got my back here, and there’s one less thing I need to look for when reviewing others’ work.

Keep on, continuous integration.