Ever accidentally push secrets to a repository? Or maybe you’ve pushed unformatted code, only to have to create a follow-up “linting” commit? We’ve all been there. These small mistakes can lead to failed CI/CD pipelines and frustrating delays, all because of checks that could have been run locally.
\ Git hooks are a powerful tool to prevent these issues by running scripts before you commit or push. However, sharing and standardizing these hooks across a team or project can be challenging.
\ This is where pre-commit comes in. Pre-commit is an easy-to-use framework for managing and sharing multi-language git hooks, helping you catch issues before they even leave your workstation.
Getting Started: The Right SetupBefore you can harness the power of pre-commit, you need to install it and configure it for your projects.
InstallationYou have several ways to install pre-commit:
This is the most common approach for enabling pre-commit in a specific project:
Create a Configuration File: In the root of your repository, create a file named .pre-commit-config.yaml. This file will define the hooks pre-commit should run. (We’ll cover how to populate this file in the next section).
\
Install Hooks: Navigate to your repository’s root directory in your terminal and run:
If you frequently start new projects or clone repositories and want pre-commit to be active by default (if a configuration exists), you can set it up globally. This method utilises Git’s init.templateDir feature.
\
A Configuration File: All your repository must have a .pre-commit-config.yaml. This file will define the hooks that pre-commit should run. The best would be to use a template repository with a minimal pre-commit file.
\
Configure Git’s Template Directory: Tell Git to use a specific directory as a template for new repositories:
(You can choose a different directory if you prefer, just ensure it’s consistent in the next step.)
\ 3. Initialize Pre-commit in the Template Directory: Run the following command:
pre-commit init-templatedir ~/.git-template(If you chose a different directory in the previous step, replace ~/.git-template accordingly.)
\ This has a major Benefit: With this global setup, any new repository you initialise (git init) or a clone will automatically have pre-commit’s hook installed. If a repository doesn’t have a .pre-commit-config.yaml file, pre-commit will simply do nothing, so it’s safe to enable globally.
\ Yet, I like to go one step further by adding a default hook ~/.git-template/hooks/pre-commit that would systematically fail if a repository doesn’t have a .pre-commit-config.yaml. Here is the content of the hook.
#!/usr/bin/env bash if [ -f .pre-commit-config.yaml ]; then echo 'pre-commit configuration detected, but `pre-commit install` was never run' 1>&2 exit 1 fi Building Your Configuration (.pre-commit-config.yaml)The heart of pre-commit is the .pre-commit-config.yaml file. This file, placed at the root of your repository, tells pre-commit which checks to run. Here is a small example of such a configuration.
# https://github.com/xNok/infra-bootstrap-tools/blob/main/.pre-commit-config.yaml repos: # Lint all yam files - repo: https://github.com/adrienverge/yamllint.git rev: v1.29.0 hooks: - id: yamllint # Runs Ansible lint - repo: https://github.com/ansible/ansible-lint rev: v24.7.0 hooks: - id: ansible-lint args: - "ansible" additional_dependencies: - ansible Core Structure ExplainedA typical configuration involves a list of repositories, each with specific hooks:
\
\
\
\
Here’s a basic .pre-commit-config.yaml to get you started. For this example, I advise you to head to Github and have a look at pre-commit/pre-commit-hooks/blob/main/.pre-commit-hooks.yaml. This is the list of simple hooks that the pre-commit team has implemented, and where you can find the id of each of the relevant hooks you may wanna use.
\ I’d say trailing-whitespace and end-of-file-fixer are really useful, thus the configuration would look like this.
repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.6.0 # Check for the latest stable version on the pre-commit-hooks repo! hooks: - id: trailing-whitespace - id: end-of-file-fixer # Add other repositories and their specific hooks below # - repo: ... # rev: ... # hooks: # - id: ...Note: Versions for hooks change over time. It’s good practice to occasionally check the pre-commit-hooks repository (or any other hook repositories you use) for the latest stable version tag and update your rev accordingly. Or have automation in place, like Renovate or Dependabot, to update those regularly.
\ You can find a large list of pre-existing hooks on the pre-commit website, see what’s there, and start validation. From my experience, this list is far from exhaustive; instead, I prefer checking the tools I like to use a .pre-commit-hooks.yaml and see if hooks are available.
Good to Know When Using Pre-CommitOnce you’re comfortable with the basics, pre-commit offers more advanced features for fine-tuning your workflow.
Running Hooks ManuallyWhile hooks run automatically on git commit, you might want to trigger them manually at other times:
\
(Replace
\ This is useful for an initial cleanup or when adding new hooks to an existing project.
Creating Your Own Local HooksSometimes, you might have project-specific scripts or checks that are not part of a public hook repository. Pre-commit allows you to define “local” hooks.
This tells pre-commit to run my_custom_script.sh for any changes in Python (.py) files. The language: script Type is very flexible; for specific environments like Python or Node, you can specify those to manage dependencies if needed. I have mostly experimented with bash hooks.
\ Still, pre-commit is very smart regarding working environments, as it creates an isolated runtime environment for the tools and required dependencies.
\ Not all hooks, sadly, have leveraged the dependency feature, and you may have to install the tools yourself to be able to run the hook (I’m thinking of terraform, for instance)
Pre-Commit in a Team and CI/CD EnvironmentWhile pre-commit shines on individual developer machines, its benefits multiply when integrated into team workflows and CI/CD pipelines. Even with pre-commit hooks installed locally, someone might accidentally commit without hooks (e.g., using git commit --no-verify) or have an outdated hook configuration. Your CI/CD pipeline can act as the ultimate gatekeeper.
\ By running pre-commit checks in your CI pipeline, you ensure that no code violating your project’s standards gets merged. The typical command for this is:
pre-commit run --all-files\ This command checks all files in the repository, not just the changed ones, ensuring comprehensive validation.
\ Conceptual CI Pipeline Step (e.g., GitHub Actions):
# Example for a GitHub Actions workflow # ... (other steps like checkout, setup python/node, etc.) - name: Install pre-commit and dependencies run: | pip install pre-commit # Install any other dependencies your hooks might need (e.g., linters) # This might be minimal if your hooks install their own dependencies (common). - name: Run pre-commit checks run: pre-commit run --all-files\ It is nice to have a conceptual pipeline that works with any CI system, but if you use GitHub actions, you don’t need to bother; use the official action.
jobs: pre-commit: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v3 - uses: pre-commit/[email protected]\ With the CI integration, the loop is completed, and the same validation is applied in developer environments as in the CI environment. If the pipeline fails, fix it locally by running pre-commit.
ConclusionRealizing there has to be a better way than manual checks and “oops” commits, we’ve explored how it pre-commit transforms your development workflow.
\ By automating checks for everything from whitespace errors and secret detection to code formatting and linting, pre-commit acts as your tireless local guardian of code quality that can even integrate “seamlessly” into CI/CD pipelines to serve as a final quality gate.
All Rights Reserved. Copyright , Central Coast Communications, Inc.