Stop Letting Crappy Code Into Your Repos

Utilizing git hooks and pre-commit to improve your repo quality

Stop Letting Crappy Code Into Your Repos
Photo by Sigmund on Unsplash

I’ve seen this happen in all manner of companies. From Fortune 500 to small startups everyone seems to be missing the first step needed to ensure your codebase doesn’t get tainted by bad and broken commits. That is git commit hooks.


What are Git Hooks?

Git hooks allow you to trigger custom code on different git events. A common use case for this would be running your linter before you allow a commit to pass. You can view some sample hooks in your repo by going to .git/hooks/ and look at all the ones that end in “.sample”.

There are 2 categories of hooks. Local and server-side. We will just be covering the local hooks as that is where I feel the most unrealized value is.

Below I have a screenshot of what the pre-commit.sample looks like. If you are like me (not a fan of shell scripts) then the code below is pretty scary looking… So we are not going to use it!

.git/hooks/pre-commit.sample

There are 3 reasons we aren’t going to use the built-in shell scripting for our hooks. First, they aren’t able to be checked into version control. There is a workaround to allow git hooks to be checked in but that involves modifying your global core.hooksPath which isn’t sustainable. Most repos won’t agree on where the hooks should be so it’s better to not have to constantly modify your hooks' path.

The second reason is shell scripting is not something that most people want to learn. You already have enough complicated languages to know. Shell scripting is not often one that people want to pick up.

Finally, and this is the big one. You can’t share or distribute hooks. You would literally need to copy your hooks from one project to another. That is a lot of code duplication. That would make updating hooks for all your repositories a nightmare. This is where pre-commit comes in to save the day.

Pre-Commit — A sustainable solution

Pre-Commit is a library for allowing us to utilize and distribute git hooks. While it is written in Python it is able to call any executable program which means you can configure all of your hooks from one place.

Using pre-commit is as simple as this:python -m pip install pre-commit
pre-commit install

Then you simply need to create a .pre-commit-hooks.yaml file in the root of your repository. This hooks file describes to pre-commit where it can find the commit hooks it needs to run. Below is a sample of what this looks like.repos:
-   repo: https://github.com/pre-commit/pre-commit-hooks
   rev: v2.3.0
   hooks:
   -   id: check-yaml
   -   id: end-of-file-fixer
   -   id: trailing-whitespace-   repo: https://github.com/Yelp/detect-secrets
   rev: v1.1.0
   hooks:
   -   id: detect-secrets
       args: ["scan"]

- repo: https://github.com/dtaivpp/commit-msg-regex-hook 
  rev: v0.1.0 
  hooks: 
    - id: commit-msg-hook 
      args: ["--pattern='[A-Z]{3,4}-[0-9]{3,6} \\| [\\w\\s]*'" 
      stages: [commit-msg]

Here is how it works; on the first run pre-commit downloads the hooks repos into their own virtual environments with the tagged version. Then it will run them against the files that have changed and are being checked in. If you update the ‘rev’ to bump the version, on the next run it will download the new version for checking your code.

The first repo in the config will validate YAML to ensure it is formatted correctly. It will then fix the end of the file and remove any unnecessary trailing whitespaces. These are just code cleanliness steps.

Next, Yelp detect-secrets will be run and check to ensure you are not checking in any keys or secrets to your repo. It is familiar with many different token types and can even detect passwords by looking for high entropy strings. It is really important to find secrets before they are ever entered into a commit as once they are committed it is difficult to scrub your git history.

Finally, commit-msg-regex-hook will check and verify the commit message I have specified matches the regex pattern provided. Especially proud of this step as I crafted this plugin.

Note: to be able to run this one you need to run pre-commit install --hook-type commit-msg . This hook is a commit message hook and it gets installed in a different hook file from the regular pre-commit hooks.

Pre-Commit — Advanced Usage

Any number of plugins can be added as well. If it has an executable pre-commit is able to use it.

Linting Hooks

A common use case for pre-commit is automatically linting your files. For python, you could use the below to automatically run pylint and have it fail your commit if your linter scores your code under 8. It will give the output of the linting and tell you areas that need to be improved to score higher.-    repo: git://github.com/pycqa/pylint
    rev: pylint-2.6.0
    hooks:
    -   id: pylint
       args: ["--fail-under=8"]

Type Checking/Config Validation

If you are using some tool that heavily relies on config files like Kubernetes, CircleCI, Ansible, etc. you have probably experienced the frustration of checking in your code only to have it fail because you miss typed a variable name or your indentation was off.

Pre-Commit can help here. Many of these tools provide utilities for checking these file types. For example, with CircleCI you can use the command circleci config validate. This can be added in as a pre-commit hook so that before every commit you can know that your config files are valid. Below is a pre-commit hook that can help validate CircleCI files. Credits to KoBoldMetals for this commit hook.-   repo: http://github.com/KoBoldMetals/pre-commit-circleci
   rev: v0.0.4
   hooks:
   -   id: circleci-validate

With that, you are now ready to elevate your developer experience and your company will thank you as your repo will now have better security, fewer failed commits, and overall better quality code.