Pre-commit
One of the cornerstones of working with git is remembering to commit your work often. Frequent committing ensures that it is easier to identify and revert unwanted changes that you have introduced, because the code changes become smaller per commit.
However, as you have hopefully already seen in the course there are a lot of mental tasks to do before you actually write
git commit
in the terminal. The most basic thing is of course making sure that you have saved all your changes, and
you are not committing a not up-to-date file. However, this also includes tasks such as styling, formatting, making
sure all tests succeed, etc. All these mental to-do notes do not mix well with the principal of remembering to commit
often because you in principal have to do them every time.
The obvious solution to this problem is to automate all or some of our mental tasks every time that we make a commit. This
is where pre-commit hooks come into play, as they can help us attach additional tasks that should be run every time
that we do a git commit
.
Configuration
Pre-commit simply works by inserting whatever workflow we want to automate in between whenever we do a git commit
and
afterwards would do a git push
.
The system works by looking for a file called .pre-commit-config.yaml
that we can configure. If you execute
you should get a sample file that looks like
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.2.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
The file structure is very simple:
- It starts by listing the repositories where we want to get our pre-commits from, in this case https://github.com/pre-commit/pre-commit-hooks. This repository contains a large collection of pre-commit hooks.
- Next we need to define what pre-commit hooks we want to get by specifying the
id
of the different hooks. Theid
corresponds to anid
in this file: https://github.com/pre-commit/pre-commit-hooks/blob/master/.pre-commit-hooks.yaml
When we are done defining our .pre-commit-config.yaml
we just need to install it.
This will make sure that the file is automatically executed whenever we run git commit
.
❔ Exercises
-
Install pre-commit.
Consider adding
pre-commit
to arequirements_dev.txt
file, as it is a development tool. -
Next create the sample file:
-
The sample file already contains 4 hooks. Make sure you understand what each does and if you need them at all.
-
pre-commit
works by hooking into thegit commit
command, running whenever that command is run. For this to work, we need to install the hooks intogit commit
. Runto do this.
-
Try to commit your recently created
.pre-commit-config.yaml
file. You will likely not do anything, becausepre-commit
only checks files that are being committed. Instead try to runwhich will check every file in your repository.
-
Try adding at least another check from the base repository to your
.pre-commit-config.yaml
file.Solution
In this case we have added the
check-json
hook to our.pre-commit-config.yaml
file, which will automatically check that all JSON files are valid. -
If you have completed the optional module M7 on good coding practice you will have learned about the linter
ruff
.ruff
comes with its own pre-commit hook. Try adding that to your.pre-commit-config.yaml
file and see what happens when you try to commit files.Solution
This is one way to add the
ruff
pre-commit hook. We run both theruff
andruff-format
hooks, and we also add the--fix
argument to theruff
hook to try to fix what is possible. -
(Optional) Add more hooks to your
.pre-commit-config.yaml
. -
Sometimes you are in a hurry, so make sure that you also can make commits without running
pre-commit
e.g. -
Finally, figure out how to disable
pre-commit
again (if you get tired of it). -
Assuming you have completed the module on GitHub Actions, let's try to add a
pre-commit
workflow that automatically runs yourpre-commit
checks every time you push to your repository and then automatically commits those changes to your repository. We recommend that you make use of- this pre-commit action for installing and running
pre-commit
- this commit action to automatically commit the
changes that
pre-commit
makes
As an alternative you can configure the CI tool provided by the creators of
pre-commit
.Solution
The workflow first uses the
pre-commit
action to install and run thepre-commit
checks. Importantly we run it withcontinue-on-error: true
to make sure that the workflow does not fail if the checks fail. Next, we usegit diff
to list the changes thatpre-commit
has made and then we use thegit-auto-commit-action
to commit those changes. - this pre-commit action for installing and running
-
(Optional) Another integration between pre-commit and GitHub Actions that you may want to consider, is using GitHub Actions to automatically update your
.pre-commit-config.yaml
file. If you're done the module on GitHub Actions you would think this could be implemented using Dependabot, however this is currently not supported and there are currently no plans to support it. Instead, let's create a custom workflow that does this. Do the following:- Create a new workflow file called
update_pre_commit.yaml
in the.github/workflows
directory. - The workflow should run on a schedule, e.g. every week.
- The workflow should check out your code, install a recent version of python and then install pre-commit
- The workflow should then run pre-commit autoupdate to update
the
.pre-commit-config.yaml
file. - Finally, the workflow should then create a pull request with the changes. For this we recommend using this action.
Implement the workflow and run it to confirm that a PR is created with the changes.
Solution
# pre-commit auto-update workflow # Dependabot does not support updating pre-commit so this workflow will do that name: Pre-commit auto-update on: schedule: - cron: '0 0 * * 0' workflow_dispatch: {} # Allows manual executions permissions: contents: write pull-requests: write jobs: auto-update: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: 3.11 cache: 'pip' - name: Upgrade pip run: python -m pip install --upgrade pip - name: Install pre-commit run: pip install pre-commit - name: Run pre-commit autoupdate run: pre-commit autoupdate - name: Create Pull Request uses: peter-evans/create-pull-request@v7 with: token: ${{ secrets.GITHUB_TOKEN }} branch: update/pre-commit-autoupdate title: Auto-update pre-commit hooks commit-message: Auto-update pre-commit hooks body: Update versions of tools in pre-commit configs to latest version labels: dependencies
- Create a new workflow file called
That was all about how pre-commit
can be used to automate tasks. If you want to deep dive more into the topic you
can checkout this page on how to define your own pre-commit
hooks.