First of all, why Lefthook?
I will point some reasons to you choose lefthook when you need to decide a git hooks manager.
1. Configuration
Lefthook is very straightforward and has a smooth setup, that can be worthy if you want to test if it's what you are looking for. We need a yaml file only, with a bunch of possibilities like docker support, flexible list of files and glob/regexp filters.
2. Velocity
As Lefhook is written in go, and we can run commands in parallel, that grant for us a faster execution, not being possible in some others git hooks managers.
3. Environment agnostic
Lefthook is a go binary with wrappers to other languages. So, it gives you the possibility to share your config with teams using different programming languages, from front end to back end for instance.
I hope that you are already convinced. If are not, take a look how simple is the setup.
Setup
Installation
npm install lefthook --save-dev
Once installed, a new file named lefthook.yml will be on your project root. You can now add the needed configuration to git hooks.
Editing config file (lefthook.yml)
Git has a bunch of hooks that you can handle with the lefthook. Here we have a list with all, so feel free to get exactly what you want. This article will handle the pre-commit
and pre-push
ones, presenting a real example.
Pre-commit
In the lefthook.yml, we can add the git hook name, followed by the commands that will run:
pre-commit:
parallel: true
commands:
eslint:
stage_fixed: true
glob: "*.{ts,tsx}"
run: yarn lint --fix {staged_files} --quiet # See "yarn lint" as eslint --ext ts,tsx
test:
run: vitest related {staged_files} --run --silent
Step by step
- Parallel
This property will do all commands run in parallel (really?), saving time, mainly in big projects. This is great as in CI/CD as local.
- Commands
Here we can name all commands needed for the hook. In the example we have the eslint to ensure the code consistency, and test to ensure quality over the app.
-
Inside eslint
- stage_fixed: This is related to run git add after fix the files when the command has finished.
- glob: Filter the files that will be on {staged_files} using the extension in this case.
- run: The command.
- {staged_files}: All files being modified at the moment (in the staging area). Note that even the command running with selected extensions (
--ext
flag), we still need the glob pattern because we are setting the files directly with staged_files. Eslint command will ignore the flag in this case.
- Inside test command
We have added the run property only, and we already know what it is, but I want to mention the related
flag from vitest. This allow us to run all tests that import a file (in this case a list of).
e.g.
// checkout.test.ts
import {Checkout} from ../Checkout
describe('Checkout', () => ...)
In this case, if we change the Checkout
component, it will be on our {staged_files} list, and even not changing tests, vitest will look for all tests importing the component and will run each one, like the checkout.test.js
.
see docs: vitest related
Pre-push
pre-push:
parallel: true
commands:
lint:
run: yarn lint . --quiet
test:
run: vitest --run --silent
Well, nothing new here. We are now running all tests before push, and also running the eslint in all files. It is useful for companies that cannot pay for infrastructure to run tests on CI/CD, but still want to have some quality assurance.
It's important notice that if you have something in your staging area that makes the test pass before the push, the tests will run without errors even if the code being pushed is with an error.
Registering hooks
After the config file is done, we can run:
lefthook install
It will do the hooks be registered and run when it's time. Remember to run it whenever you change the lefhook.yml file, or change something related the commands, for instance remove a command from package json that is being used by your hooks.
After this, it's time to commit and forget it (at least until need to change again).
Top comments (1)