Use Pre-commit in Any Repository Without Installing
Janne Kemppainen |Pre-commit is a great tool written in Python that can be used to manage and maintain pre-commit hooks in Git projects. The traditional way to set it up is to install it globally or locally to the project Python virtual environment with pip. Here I’m going to share how to use pre-commit without installing it at all.
Pre-commit is only needed to configure the pre-commit hooks for a repository. After the initial setup it isn’t really needed until you need to edit the configs again. Therefore, we can just download the executable Python zip archive, install the hooks, and then delete the extra files.
I have previously written about using Make for Python development. In this post I’m going to continue using a Makefile because I think it is a clean and easy way to provide setup scripts, but you can choose to use any method you want. The commands should be easy to adapt to a Bash script if that’s what you prefer.
Your system needs to have the make
command available for this to work properly.
Setup without installing
So, if you go to the pre-commit documentation and check the installation instructions you’ll notice that it can be installed as a 0-dependency zipapp. You can download the .pyz
file from the GitHub releases page and run it with Python. The zipapp contains everything that’s needed to setup the Git hooks, so you don’t need to worry about virtual environments and such.
The zipapp file is always named in this format: pre-commit-<version-number>.pyz
, so we can easily download the version that we want.
Create a new file called Makefile
at the root of your project. Note that this file must be indented with tabs!
PRECOMMIT_VERSION="2.18.0"
.PHONY: pre-commit
pre-commit:
wget -O pre-commit.pyz https://github.com/pre-commit/pre-commit/releases/download/v${PRECOMMIT_VERSION}/pre-commit-${PRECOMMIT_VERSION}.pyz
python3 pre-commit.pyz install
python3 pre-commit.pyz install --hook-type commit-msg
rm pre-commit.pyz
Now you can run the command make pre-commit
inside your project. It downloads the .pyz
file and saves it as pre-commit.pyz
, then it installs any configured hooks, and finally removes the zipapp.
The hooks can work at different stages, and each stage needs to be installed separately. The default installation uses the pre-commit type that works before commits. You can use the --hook-type
argument to install the stages that you want, for example commit-msg to check the commit message.
With this method you don’t have to worry about which version of pre-commit
the other developers are using since it always downloads the one that you’ve defined in the Makefile. Your project doesn’t need to contain a Python virtual environment for local installation, so this makes it easy to setup hooks for Node.js projects too.
Configure the hooks
Since we don’t have a configuration file yet, the setup doesn’t do anything meaningful. Here you can find a list of supported hooks.
For a Node.js project your .pre-commit-config.yaml
file (at the root of the project) could look like this:
default_stages: [commit]
repos:
- repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
rev: v4.1.0
hooks:
- id: commitlint
stages: [commit-msg]
additional_dependencies: ["@commitlint/config-conventional"]
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v2.2.1
hooks:
- id: prettier
The configuration format defines repositories that contain the hook definitions. This example installs commitlint-pre-commit-hook and Prettier.
The Prettier hook checks the formatting of your files and makes them pretty if they don’t follow your chosen style. If you’re using plugins with Prettier then you need to declare them under the additional_dependencies
section. Find more information from the prettier mirror repository.
The commitlint-pre-commit-hook works at the commit-msg stage, which is why I added that in the Makefile too. It checks the structure of your commit messages to make sure that they follow the defined convention. Commitlint requires a configuration file, you can find more details from the reference configuration. A simple configuration file could look like this commitlint.config.js
(note that this time the filename does not start with a dot):
module.exports = {
extends: ["@commitlint/config-conventional"],
};
In this example I use the Conventional Commits specification. This needs to be defined as an additional dependency in the pre-commit configuration too!
Now, if you run make pre-commit
again, it will install these two hooks inside your Git repository. The next time you commit changes it will first check the files with Prettier. If the changed files don’t follow the style configuration they will be automatically formatted and you need to commit the changes again.
Only after the file checks have passed it moves on to checking the commit message. If it doesn’t follow the style convention the whole commit will be rejected, and you’ll have to fix the message and try again.
The Conventional Commits specification uses commit messages that look like “fix: prevent unauthorized login”, “feat: add new color options”, “test: add unit tests for login view”, and so on. When this format is enforced the release numbers can be calculated automatically with semantic versioning.
Conclusion
Setting up pre-commit is quite quick and easy with Makefiles. You don’t need to worry about installing the command to globally to the developer machine or locally to a virtual environment. As long as make
, wget
and python3
are available on the system this thing will work. The setup instructions are easy to add to the repository README file, so new team members can quickly start working on new features.
If you liked this post or have any feedback you can respond to the embedded tweet below!
Discuss on Twitter
You don't have to install pre-commit in order to use it in your Git repository https://t.co/s5oNM96TfX
— Janne Kemppainen (@pakstech) April 20, 2021
Previous post
Markdown Kanban with ObsidianNext post
Add an RSS Feed to Your Hugo Blog