Properly configure `typos` in `pre-commit-hooks.nix` (now `git-hooks.nix`)
2024-04: Ongoing Upstream Discussion
There is an ongoing upstream discussion for a pull request of mine that tries to mitigate this issues of the typos
integration with pre-commit-hooks.nix
I present in the following. I don’t know if it is ever getting merged – many opinions, limited time, stressed maintainers, complicated edge cases, different valid variants of usage of typos, stressed me – not blaming anyone!. However, IMHO, you should configure typos
as follows, and you are good to go:
Terminology & Background
typos
is the new shining star in the sky of source code spell checkers.- pre-commit-hooks.nix is a seamless integration of https://pre-commit.com git hooks with Nix. (It was recently renamed to
git-hooks.nix
upstream.) pre-commit
is the binary used to combine all linters, formatters, and other checkers in a single command using a.pre-commit-config.yml
. This is the tool invoked by the git hooks, however, it is a standalone component.
pre-commit
doesn’t need to be used with git hooks at all. In fact, pre-commit
can be used as single entry into all checks without ever being used as a git hook. So, $ pre-commit run --all-files
just run all checks on the repository. I use it that way and that’s perfectly valid and productive.
My Expectation
IMHO, $ pre-commit run typos [--all-files]
must behave exactly like $ typos
without any doubt or exception. typos
is the base, pre-commit
the additional convenience. Not the other way around!
Problems
pre-commit
is meant to pass all files that should be checked to the tools as argument, such as$ some-checker-tool src/foo.c src/bar.c include/bar.h
(this information comes from.pre-commit-config.yaml
)typos
is supposed to be invoked on the tree, i.e.,$ typos
- Using a
.typos.toml
file one can exclude certain files or directories. - This is the setup I’ve seen in dozens of projects.
- But yes, you can also invoke
typos
like this:$ typos src/foo.c src/bar.c include/bar.h
, but this feels wrong.
- Using a
typos
has a massive memory and CPU usage when thousands of files are passed to it as argument, even when the typos-flag--force-excluded
is usedpre-commit-hooks.nix
is structured in a way that it can’t use the excludes of the.typos.toml
file (my PR is trying to fix that)- Just setting
pass_filenames = false
as default got negative feedback upstream typos
exclude rules uses globs whereas pre-commit uses regex. This makes the implementation harder.- I have a project where over 70.000 files in a folder are excluded in
.typos.toml
. However,pre-commit-hooks.nix
creates a.pre-commit-config.yaml
file that instructspre-commit
to pass ALL OF THEM totypos
which let my computer explode - Again, I do not use the git hooks but
pre-commit
as centralized binary to run all checks. With the current default upstream definitions howpre-commit
should invoketypos
, I can’t runpre-commit run [typos] --all-files
without additional configuration.
But again. It is entirely unacceptable and counter-intuitive that $ pre-commit typos --all-files
behaves differently from running just $ typos
with a proper .typos.toml
.
Conclusion, Outlook, and Solutions
There is no one to blame for the stuck upstream discussion, just an unfortunate situation and people that use typos
in different ways. I don’t think it is worth it spending more time in this time sink upstream. (Locally applicable) solutions to this problem are:
- The
typos
utility should be fixed to not explode your PC when it receives that many arguments (I never investigated this, and I do not have the capacity to, unfortunately) - Setting
hooks.typos.pass_filenames = false
is perfectly fine IMHO. Just lettypos
figure out the excludes by itself by reading its configuration file. - Otherwise, you can set
hooks.typos.excludes = (builtins.fromTOML (builtins.readFile ../.typos.toml).files or { }).extend-exclude or [ ]);
as long as your excludes do not use globs (*
).
So the following code snippet shows my solution (broken down to what’s relevant). For simplicity, the code snipped uses a Nix channel to fetch nixpkgs:
let pkgs = import <nixpkgs> { }; # Commit ID from 2024-01-08 pre-commit-hooks = import (builtins.fetchTarball "https://github.com/cachix/pre-commit-hooks.nix/tarball/ea07fa07f222a5c4baacbcdbf529276ef0ddc6ca"); in # Configured with the module options defined in `modules/pre-commit.nix`: pre-commit-hooks.run { src = ./.; # Set the pkgs to get the tools for the hooks from. tools = pkgs; hooks = { typos = { enable = true; pass_filenames = false; # As this is a string and not a Nix Path upstream, the excludes can # never be evaluated by pre-commit-hooks.nix. settings.configPath = ".typos.toml"; }; }; }
It is unfortunate that such a (IMHO) basic thing became such a time sink. However, I hope I could help you.
0 Comments