pre-commit : a Python framework for managing and maintaining multi-language pre-commit hooks

Introduction

Git hook scripts are useful for identifying simple issues before submission to code review.

We run our hooks on every commit to automatically point out issues in code such as missing semicolons, trailing whitespace , and debug statements.

By pointing these issues out before code review, this allows a code reviewer to focus on the architecture of a change while not wasting time with trivial style nitpicks.

As we created more libraries and projects we recognized that sharing our pre-commit hooks across projects is painful. We copied and pasted unwieldy bash scripts from project to project and had to manually change the hooks to work for different project structures.

We believe that you should always use the best industry standard linters.

Some of the best linters are written in languages that you do not use in your project or have installed on your machine. For example scss-lint is a linter for SCSS written in Ruby. If you’re writing a project in node you should be able to use scss-lint as a pre-commit hook without adding a Gemfile to your project or understanding how to get scss-lint installed.

We built pre-commit to solve our hook issues .

It is a multi-language package manager for pre-commit hooks.

You specify a list of hooks you want and pre-commit manages the installation and execution of any hook written in any language before every commit.

pre-commit is specifically designed to not require root access.

If one of your developers doesn’t have node installed but modifies a JavaScript file, pre-commit automatically handles downloading and building node to run eslint without root.

pre-commit installation, .pre-commit-config.yaml

pip install pre-commit

Once you have pre-commit installed, adding pre-commit plugins to your project is done with the .pre-commit-config.yaml configuration file.

Add a file called .pre-commit-config.yaml to the root of your project.

The pre-commit config file describes what repositories and hooks are installed.

 1---
 2
 3# .pre-commit-config.yaml
 4# ========================
 5#
 6# pre-commit clean
 7# pre-commit install
 8# pre-commit install-hooks
 9#
10# precommit hooks installation
11#
12# - pre-commit autoupdate
13#
14# - pre-commit run black
15#
16# continuous integration
17# ======================
18#
19# - pre-commit run --all-files
20#
21
22repos:
23  - repo: https://github.com/pre-commit/pre-commit-hooks
24    rev: v4.6.0
25    hooks:
26    - id: trailing-whitespace
27    - id: end-of-file-fixer
28    - id: check-yaml
29    - id: check-json
30    - id: fix-encoding-pragma
31      args: ['--remove']
32    - id: forbid-new-submodules
33    - id: mixed-line-ending
34      args: ['--fix=lf']
35      description: Forces to replace line ending by the UNIX 'lf' character.
36    # - id: pretty-format-json
37    #  args: ['--no-sort-keys']
38    - id: check-added-large-files
39      args: ['--maxkb=500']
40    - id: no-commit-to-branch
41      args: [--branch, staging]
42
43
44  - repo: https://github.com/asottile/reorder_python_imports
45    rev: v3.12.0
46    hooks:
47      - id: reorder-python-imports

pre-commit install

pre-commit install
pre-commit installed at .git/hooks/pre-commit

pre-commit install-hooks

If there is already a .pre-commit-config.yaml file.

pre-commit install-hooks
[INFO] Initializing environment for https://github.com/pre-commit/pre-commit-hooks.
[INFO] Installing environment for https://github.com/pre-commit/pre-commit-hooks.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...

pre-commit autoupdate

pre-commit autoupdate
Updating https://github.com/pre-commit/pre-commit-hooks...updating v1.4.0 -> v2.1.0.
git commit . -m "Add .pre-commit-config.yaml"
Trim Trailing Whitespace.................................................Passed
Fix End of Files.........................................................Passed
Check Yaml...............................................................Passed
Check for added large files..............................................Passed
[master b760551] Add .pre-commit-config.yaml
 2 files changed, 94 insertions(+), 5 deletions(-)
 create mode 100644 .pre-commit-config.yaml

pre-commit run –all-files

pre-commit run --all-files
pre-commit run --all-files
Trim Trailing Whitespace.................................................Failed
hookid: trailing-whitespace

Files were modified by this hook. Additional output:

Fixing README.md
Fixing django_based/funkwhale/layout/api/.pylintrc

Check Yaml...............................................................Passed
Check for added large files..............................................Passed
Makefile:26: recipe for target 'check_all' failed

Makefile

We can use a Makefile.

 1# Minimal makefile for Sphinx documentation
 2#
 3
 4# You can set these variables from the command line.
 5SPHINXOPTS    =
 6SPHINXBUILD   = sphinx-build
 7SPHINXPROJ    = Tutopython
 8SOURCEDIR     = .
 9BUILDDIR      = _build
10
11THIS_MAKEFILE := $(lastword $(MAKEFILE_LIST))
12
13# Put it first so that "make" without argument is like "make help".
14help:
15	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
16	@echo " "
17	@echo "Targets:"
18	@echo " "
19	@echo "- make check_all"
20	@echo "- make req"
21	@echo "- make update"
22	@echo "- make clearcache"
23	@echo " "
24
25clearcache:
26	poetry cache clear --all pypi
27
28
29check_all:
30	pre-commit run --all-files
31
32req:
33	poetry env info --path
34	poetry show --tree
35	poetry check
36	poetry export -f requirements.txt --without-hashes  > requirements.txt
37	cat requirements.txt
38
39update:
40	poetry update
41	@$(MAKE) -f $(THIS_MAKEFILE) req
42	pre-commit autoupdate
43
44
45.PHONY: help Makefile
46
47# Catch-all target: route all unknown targets to Sphinx using the new
48# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
49%: Makefile
50	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

Articles

.pre-commit-config.yaml examples

List of hooks repos