Leveraging Lefthook to enforce commit guidelines at exile.watch
15 min read
Last updated
Was this helpful?
15 min read
Last updated
Was this helpful?
When it comes to collaboration with others (whether from the same organization, team, or in open source), having clear rules is essential. No one really enjoys chaos, and if you do, then godspeed.
Focusing on architecture from the start may bring some pain and frustration, but as time flies, a well-set architecture will save you far more headaches and money.
One of the key rules and architectural aspects are .
There are two groups of these hooks: client-side and server-side.
In this post, we will solely focus on client-side hooks, setting the stage for an efficient management system.
Understanding git hooks sets the stage, but managing them efficiently is where git hooks managers come into play.
However, there are already git hooks managers available that are fast, well-maintained, and battle-tested, so I didn't see the need to create a custom git hook manager.
Your contributions will be rejected if you won't follow this guideline.
With a clear framework for commit messages established, integrating Lefthook to enforce these standards was our next step, particularly within our Lerna environment.
If you're short on time, here's the gist: exile.watch relies heavily on dependencies structured as boilerplates for consumer applications to use.
And guess what? Lefthook is one of these crucial "boilerplates."
To give you an idea, here's how the @exile-watch/splinters
repo is organized, focusing just on the Lefthook part:
Right off the bat, 4 key components stand out:
.lefthook/commit-msg
withcommitlint.sh
bash script at the root of the repository
lefthook-config
having scripts
directory with commitlint.sh
lefthook.yml
in both - the root of the repository and lefthook-config
commitlint.config.js
Let's delve into the roles each plays.
Diving into the specifics, let's tackle an essential caveat first:
This situation could mess up our setup.
To avoid this problem, we make sure the root .lefthook/commit-msg/commitlint.sh
script calls the script inside the package directly:
This setup essentially ensures that our root .lefthook
's commitlint.sh
script activates the script defined in the package itself, thereby enforcing our commit guidelines directly from the package.
By doing so, we maintain control and clarity, using the root script to trigger the package-defined script, thereby unifying our commit linting process under one predictable mechanism.
Our primary aim is to uphold commit standards, necessitated by commitlint
, which avails the commitlint
command.
The question then becomes: how best to activate our commitlint
package?
Through the commitlint.sh
bash script, of course.
The naming conventions here are flexible; I opted for simplicity and consistency across the board, hence the recurrent use of "commitlint."
Let's break down this script bit by bit:
$1 and head -n1 $1: Fetches and echoes the first line of the commit message, preparing it for linting.
npx commitlint: Executes commitlint, applying our project's rules to the commit message in question.
Notice this part:
This configuration informs Lefthook about the commitlint.sh
script, ensuring it's triggered appropriately.
The brilliance and simplicity of extending this setup to other repositories lie in the lefthook.yml
file at the root, pointing to our configured lefthook.yml
within the lefthook-config
package:
You're essentially instructing Lefthook to fetch and merge the specified configurations, making project-wide guideline enforcement a breeze.
commitlint.config.js
Before we evaluate our setup's complexity, there is one last step that we have to follow up with.
After installing dependencies, we run Lefthook script:
However, there's a bit of uncertainty on my end about whether this setup is the way it's supposed to work.
The idea of a consumer package pulling its configuration from the lefthook-config
package, which then executes a script from the root of its own repository, works like a charm in practice.
Yet, it leaves me wondering if we've taken the simplicity of Lefthook's intended use and stretched it a bit too far.
Ultimately, our approach works seamlessly for now, but it raises the question:
Is this how it was meant to be used?
Only time will tell if adjustments or a nod of approval await us down the line.
has a way to fire off custom scripts when certain important actions occur.
Client-side hooks are triggered by operations such as and , while server-side hooks run on network operations like receiving pushed commits.
The first thing you'll likely find about git hooks is the official .
To name a few: , , , !
While there are several git hooks managers out there, my googling led me to a particular one that stood out—.
I've always worked with Husky, but ever since the , it became clear that, at the time, this was an unstable project that breached the trust.
When I gave a shot a few weeks ago, I'll be honest: I had no clue how to create a shareable configuration and was somewhat wary.
Add , commonly used with Husky, to the mix, and you get a sense that things could have been simpler in 2024.
If you visit at you will notice following banner at the very top:
is a tool designed to help developers create more consistent and structured commit messages following conventional commit guidelines.
checks if commit messages meet specified guidelines, playing a crucial role in our commit process.
If this is your first encounter with and how it's applied at exile.watch, I suggest giving Lerna - the hidden powerhouse of exile.watch a read.
Lefthook finds its place within the build tools , specifically under the package.
One more note: is also to make remotes work—more on that later.
This crucial detail explains why having the @exile-watch/lefthook-config
as a is vital—it empowers us to execute Lefthook within the same repository where the package resides:
And by invoking run_commitlint
, we kickstart the as defined within our package's commitlint.sh
script.
--edit, --color, --help-url: enhancing usability, like colored output and providing a URL for guideline assistance.
But how do we ensure Lefthook autonomously triggers this script at the? That's where lefthook.yml
within lefthook-config
comes into play
Check how and repos are consuming our .
Last component in our highlighted setup is file.
This sets our commit message rules, ensuring every message meets our project standards. It's key for keeping our git history clean and meaningful.
By Lefthook, we are synchronizing our git hooks with latest changes that are provided by our lefthook-config package.
.
Author: About exile.watch: Github: Visit to experience it first hand