Foreword
I have been playing around with a neat tool called husky for my TypeScript repositories. It allows you to create Git hooks that you could subscribe to. I use it as part of my development tools. I use husky, alongside commitlint, to lint commit messages so that they are guaranteed to adhere to the Conventional Commits standard. I have been discovering some interesting use cases for it like running the code linter on staged files using lint-staged.
I have recently come across an interesting article that demonstrates how to run tests before code is pushed to a remote repository. It used husky v0 so it still configured its hooks using JavaScript. Nevertheless, it makes use of a pre-push
hook which is conceptually the same with the current version of husky today.
When the pre-push
hook is triggered, husky executes a git diff HEAD --quiet && npm test
command. The git diff
command is used to check whether there are local uncommitted changes in the repository. It silently exits with an exit code of 1
if there are changes. Thus, the npm test
command only runs when there are no local uncommitted changes.
Problem
When there are local uncommitted changes in the repository, husky throws an ambiguous error: failed to push some refs
exception message. This can definitely baffle a developer. There should be a better way to directly convey the error message.
Solution
The solution can be to tinker with the bash scripts individually provided by Husky. Edit the .husky/pre-push
file, like so:
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
if git diff HEAD --quiet; then
npm run test
else
echo "Cannot run pre-push tests due to local uncommitted changes"
exit 1
fi
In this bash script, the same git diff
command is ran and returns an exit code of 0
(no difference) or 1
(with difference). If there are differences, the tests are ran. Otherwise, a friendly error message is presented and exits.
Closing Thoughts
Running tests before pushing to a remote repository is a cool idea indeed. It is worth noting that this approach is only feasible when your tests can be run locally and fast. If you are going to run a lot of tests that take a while, you should better delegate that task to your continuous integration pipeline instead.
Special thanks to Kacper Wdowik for writing the article that served as inspiration for this one. Cheers! ๐