Omnihook
The Git Hook Manager You Didn’t Know You Needed
Introduction
What brought you here ?
Whatever it is, I promise you this article will keep you “hooked”!
On we go!
Alright! What’s omnihook ?
It’s a global-git-hook manager.
Wait, what do you mean global-gitook?
You can configure a global git hook and that automatically applies to any git repo that you clone on your machine.
For example if you run:
git config --global core.hooksPath $HOME/.git_hooks/
and create a file named pre-commit inside that directory with some commands in it, those commands will execute every time you run git commit , but the problem would be that the git hooks inside .git/hooks/ of your repo will not run, meaning global hooks overshadow your local hooks and override them.
But you could simply update your global hook ($HOME/.git_hooks/pre-commit) to look like:
#!/bin/sh
# Write your global pre-commit hook here
echo "This is the global hook"
# Also run the repo-local hook if it exists
if [ -f .git/hooks/pre-commit ]; then
.git/hooks/pre-commit
if [ $? -ne 0 ]; then
echo "Repo-local hook failed. Aborting commit."
exit 1
fi
fi
This command will make sure both hooks run, of course one after the other one completes successfully (cuz, one ridiculous problem at a time).
Now, this is pretty much what omnihook tries to achieve, but is a better organizer.
What if you have multiple things you wanna run in your global pre-commit hook ? That pre-commit file is gonna look huge.
So, omnihook has it’s own hooks directory $HOME/.omnihook/hooks/<hook-type>/
$HOME/.omnihook/hooks/pre-commit/ for example
and you can place multiple pre-commit type scripts inside the directory, name them whatever you want as long as they’re chmod +x ed.
and your global pre-commit hook will now look something like:
#!/bin/sh
# Call Omnihook to run managed hooks
if command -v omnihook >/dev/null 2>&1; then
omnihook run --type pre-commit
if [ $? -ne 0 ]; then
echo "OmniHook detected an issue. Aborting commit."
exit 1
fi
fi
# Also run the repo-local hook if it exists
if [ -f .git/hooks/pre-commit ]; then
.git/hooks/pre-commit
if [ $? -ne 0 ]; then
echo "Repo-local hook failed. Aborting commit."
exit 1
fi
fi
As you can see we’re calling omnihook run --type pre-commit instead of a command or a script, which will run all the hooks/scripts inside $HOME/.omnihook/hooks/pre-commit/ (IN PARALLEL! how cool is that!)
Okay!
✨ Features
- install hooks,
- uninstall them,
- disable one if you don’t want it,
- enable it back when you’re done bypassing a security mechanism and potentially violating a policy!
- organise your hooks in a centralised hooks repo, I know this sounds like it was copied from pre-commit ( it was XD) , anyhooo
- oh, and it runs all them hooks in parallel (save time!)
Omnihook does it all!
TLDR;
Quick start
# install omnihook latest version
go install github.com/vjayajv/omnihook@latest
# this configures core hookspath, creates it, and sets up everything needed
omnihook configure
# adds two example hooks, one for commit-msg and the other for secrets check
omnihook install --url https://github.com/vjayajv/omnihook-test-hooks.git
Here’s the repo:
Oh wait!!
Here’s a good reason why global might be the way to go,
— Say, you‘re a DevSecOps engineer trying to prevent Developers from committing credentials or secrets into their code, and you want them to get to use pre-commit hooks to run a credential scan every time they run git commit and blocks them from committing credentials to code;
Just ask them to use our pre-commit hook in their repo that runs a credential scan.
And they start working on a new repo, so we make it a requirement that this hook has to be a default in all of their repos, we can ask them to do this but will they ? while some may, it’s really a pain-point if we’re being honest, right ?
Assuming they do it for all new repos they create, we can safely say now we’re compliant with credential scanning across the org , right ?
WRONG!
Well, not entirely wrong, but there’s corner case with not a very high chance but still,
what if the dev is working on a non-work repo on a work-machine which somehow uses work-related credentials (WOAH! breaking multiple policies there mate!) and tries to push them to the repo.
I KNOW!
That’s exactly why it HAS to be a global hook and not just a local one, because that way, they don’t have to painfully (trust me it is) copy this hook to every repo they clone. And don’t have to worry about credentials leaking from a different channel (although very unlikely, we don’t want to take a risk).
That’s all folks! That’s a pretty good case!