Automating Code Checks and Tests with Git Hooks
Motivation:
The most common way to add checks to your codebase is to use husky and lint-staged. I have used husky many times to add git hooks to the code base it’s one of the easiest ways to integrate any hooks. Recently I wanted to introduce a pre-commit hook to one of our projects. And my go-to approach was to integrate Husky and lint-staged. When I tried to integrate my project, The pre-commit hooks were not working as expected. After some digging, I realised that the newer version of Husky has some changes in the integration process. Husky(> V5) make use of Git’s(V 2.9) core.hooksPath feature. Basically, it lets you tell Git to not use .git/hooks/ but any other directory. This made me think if there is a way to do this natively?
Dive into the example code here
Native solution:
Git Hooks are the scripts you can place in a hooks directory to trigger any actions at certain points in Git’s execution process. These scripts can be invoked at any given points like pre-commit, post-commit, pre-push, pre-rebase etc, You can find all the available hooks here.
By default, the hooks directory is at .git/hooks/. If you open any of your project which is initialised by git, You can check all the sample hooks.
Implementation :
Let’s create a workflow where every time someone tries to commit something, we will run some checks like Linter, Prettier and Tests. And to be more efficient we will run these steps only for the files which are staged. We can implement this pre-commit hook in 4 steps.
Step 1 :
Create a file called .git-hooks at the root level, Inside this folder, create any of the hook files, In our case, it will be .git-hooks/pre-commit with no extensions, it will be a bash file.
Step 2 :
Open .git-hooks/pre-commit file and paste below code.
#!/bin/sh
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACMR | sed 's| |\\ |g')
# run linter on staged files
echo "Running Linter..⚒️⚒️⚒️"
./node_modules/.bin/eslint $STAGED_FILES --quiet --fix
LINTER_EXIT_CODE=$?
# run Prettier on staged files
echo "Running Prettier..✨✨✨"
./node_modules/.bin/prettier $STAGED_FILES --ignore-unknown --write
# add files auto-fixed by the linter and prettier
git add -f $STAGED_FILES
# check linter exit code
if [ $LINTER_EXIT_CODE -ne 0 ]; then
echo "No, no, no! fix those lint errors first..😠"
exit 1
else
echo "lint all good..👍"
fi
# run tests related to staged files
echo "Running Tests"
./node_modules/.bin/jest --bail --findRelatedTests $STAGED_FILES --passWithNoTests
JEST_EXIT_CODE=$?
# check jest exit code
if [ $JEST_EXIT_CODE -ne 0 ]; then
echo "Please you can do better than this..🙏🙏🙏"
exit 1
else
echo "test all good..👍"
fi
# return 0-exit code
echo "🎉 you are a rockstar..🔥🔥🔥"
exit 0
Before we go further, Let’s understand some important commands from above script.
Recommended by LinkedIn
If you are not much aware of bash then this cheat sheet will help
Step 3 :
Now that our script is ready, We need to tell Git to point our folder( .git-hooks ) instead of its default folder (.git/hooks ), whenever the git execution process starts.
To do that, we need to run the below command.
git config core.hooksPath ./.git-hooks
you can read more about git-config here.
Step 4:
Our git workflow is ready to use by now, Whenever there is a commit, our pre-commit hook will kick in and runs all the checks as mentioned in the script. But every time someone clones our repository they need to make sure that they run the config command as mentioned in step 3. We can automate this process by adding this command to our package.json file as below.
"scripts" : {
....
"postinstall": "git config core.hooksPath ./.git-hooks"
....
}
Now whenever npm install command is run, It will also configure git-hooks.
Note: Sometimes we might want to skip all the checks from git hooks, We can use--no-verify command which will bypass any git hooks and commit-msg. git add . git commit -m "skip git hooks" —-no-verify
References :
This article was originally published on our blog. Read the full version here: Automating Code Checks and Tests with Git Hooks
Penned by: Rahul Sawant