Home Machine Learning Customized pre-commit hooks for safer code modifications | by Thierry Jean | Mar, 2024

Customized pre-commit hooks for safer code modifications | by Thierry Jean | Mar, 2024

0
Customized pre-commit hooks for safer code modifications | by Thierry Jean | Mar, 2024

[ad_1]

A step-by-step information on writing your first pre-commit hook

pre-commit run outcomes, together with our Hamilton hook!

Most software program is developed utilizing the git model management system to replace and distribute code. One problem of writing code collaboratively is guaranteeing particular requirements whereas every contributor has their type and opinion about what constitutes clear code.

pre-commit hooks are scripts or instructions to execute robotically earlier than committing code modifications. They’ll implement styling guidelines and catch errors earlier than they’re dedicated and additional distributed. Notable hooks embody checking recordsdata for syntax errors, sorting imports, and normalizing citation marks. They’re an important instrument for any venture, particularly open-source ones with many contributors.

I wished to create pre-commit hooks to validate dataflow definitions for the Python library Hamilton, however I discovered most on-line assets scattered and restricted to fundamental use.

On this put up, you’ll discover:

  1. begin utilizing pre-commit hooks in your venture
  2. A step-by-step tutorial to develop customized pre-commit hooks

To floor the dialogue, I’ll undergo this GitHub repository containing the pre-commit hooks I developed for Hamilton.

Hooks are a mechanism constructed straight into the git model management system. You’ll find your venture’s hooks underneath the .git/hooks listing (it is perhaps hidden by default). Though they’re colloquially referred to as “pre-commit hooks”, git hooks cowl the entire git lifecycle. As an illustration, you may have hooks set off simply after a commit or earlier than a push. Additionally, hooks will be written in any programming language. Notably, the Ruff library reimplemented many Python-based hooks in Rust for efficiency enchancment.

In comparison with software program testing, which focuses on code conduct, you may consider hooks as light-weight checks you’ll do on every file save. When you can anticipate exams to vary and evolve together with your codebase, your code-writing tips and pre-commit hooks will probably be fixed.

Venture setup

Let’s fake we’re beginning a brand new Python venture (or utilizing an current one) within the listing /my-project. The popular approach of working with pre-commit hooks is thru the pre-commit Python library. We will set it up with the next steps:

  1. Create a git repository on your venture with git init
  2. Set up the pre-commit library with pip set up pre-commit
  3. Add a .pre-commit-config.yaml to your repository. Right here’s an instance:
# .pre-commit-config.yaml
repos:
# repository with hook definitions
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.3.0 # launch model of the repo
hooks: # record of hooks from the repo to incorporate on this venture
- id: end-of-file-fixer
- id: trailing-whitespace
- id: check-yaml
args: ['--unsafe'] # add arguments to `check-yaml`

# obtain one other repository with hooks
- repo: https://github.com/psf/black
rev: 22.10.0
hooks:
- id: black

4. Set up the hooks with pre-commit set up. It would learn directions from .pre-commit-config.yaml and set up hooks regionally underneath .git/hooks/pre-commit

5. Make a commit or manually run hooks with pre-commit run --all-files to set off the hooks

Neighborhood-maintained hooks present flexibility and will be tailor-made to satisfy your most well-liked coding tips. They need to meet your wants 98% of the time. Nevertheless, off-the-shelf options don’t know concerning the particular instruments you’re utilizing or your staff’s inside conventions. For instance, you would possibly wish to validate inside configurations or implement a listing construction on your initiatives.

In our case, we wish to create a hook to validate the Python code for his or her Hamilton dataflow definition. Our hook script will leverage the hamilton CLI instrument to conduct the validation, leaving us with a easy code instance to comply with.

1. Organising your pre-commit hook repository

As launched within the Venture setup part, pre-commit hooks must exist in a public repository to permit initiatives to reference them in .pre-commit-config.yaml and set up them regionally with pre-commit set up.

Beforehand, we have been in our venture listing /my-project the place we outlined a .pre-commit-config.yaml and put in hooks. Now, we’ll create a /my-hooks listing the place we’ll outline our customized hooks. You’ll be able to confer with our hamilton-pre-commit repository to view the overall construction.

Screenshot of the hamilton-pre-commit repository

2. Writing the hook’s logic

Beneath hooks/, we’ve a file __init__.py to make the listing a discoverable Python module and our script cli_command.py. It comprises a single operate principal(), which reads an inventory of hamilton CLI instructions from sys.argv. Then, it executes them one after the other as a subprocess wrapped in a attempt/besides clause.

# hooks/cli_command.py
import sys
import json
import subprocess

PASS = 0
FAIL = 1

def principal() -> int:
"""Execute an inventory of instructions utilizing the Hamilton CLI"""
instructions = sys.argv[1:]

if len(instructions) == 0:
return PASS

exit_code = PASS
for command in instructions:
attempt:
args = command.cut up(" ")
# insert `--json-out` for correct stdout parsing
args.insert(1, "--json-out")
end result = subprocess.run(args, stdout=subprocess.PIPE, textual content=True)
response = json.masses(end result.stdout)

if response["success"] is False:
increase ValueError

besides Exception:
exit_code |= FAIL

return exit_code

if __name__ == "__main__":
increase SystemExit(principal())

Originally, we set exit_code = PASS, however any exception or unsuccessful instructions will set exit_code = FAIL. The principal() operate returns the exit code to the SystemExit exception. For the pre-commit hook to succeed, we have to return PASS in spite of everything instructions succeeded. It is perhaps counterintuitive to have PASS=0 and FAIL=1 however these values confer with the usual system’s exit code.

We used Python for comfort, however this easy logic might be in a lighter scripting language like Bash. You’ll be able to go to the hooks maintained by the pre-commit staff for extra examples.

3. Defining the hook entry level

Now, your hooks repository (/my-hooks) should embody a .pre-commit-hooks.yaml file that specifies the accessible hooks and learn how to execute them as soon as put in.

- id: cli-command
identify: Execute `hamilton` CLI instructions
description: This hook executes a command utilizing the `hamilton` CLI.
entry: cli-command
language: python
varieties: [python]
levels: [pre-commit, pre-merge-commit, manual]
pass_filenames: false

In our case, we set id: cli-command and entry: cli-command, add some metadata, and specify the programming language as Python. Importantly, the recordsdata attribute wasn’t set to have our hook run as soon as per commit. In your case, you would possibly wish to set recordsdata: "*.py" to run your hook on every edited Python file for instance (study accessible choices).

To date, we created a Python script underneath hooks/cli_command.py and added to .pre-commit-hooks.yaml a hook with the entry level cli-command. Nevertheless, that you must hyperlink the 2 explicitly in your Python venture file pyproject.toml.

[project.scripts]
cli-command = "hooks.cli_command:principal"

This line reads “the entry level cli-command refers back to the operate principal in hooks.cli_command”.

see this instance for those who’re utilizing setup.cfgon your Python venture

4. Testing your hook regionally

First, it’s best to validate your hook’s logic with unit exams. Nevertheless, we gained’t dive into testing because it deserves its personal put up. Our hamilton-pre-commit repository presently doesn’t have exams because the underlying CLI is examined underneath the principle Hamilton repository. You’ll be able to go to the formally maintained pre-commit hooks for check examples.

Second, it’s best to confirm that the .pre-commit-hooks.yaml and entry factors are correctly configured by attempting your pre-commit hook regionally. Ideally, you’d wish to keep away from including a decide to set off the hook every time you wish to check modifications. The pre-commit library offers utilities to facilitate this course of, nevertheless it requires a number of handbook steps detailed in pre-commit GitHub points.

  1. Go to your listing /my-project the place you’d like to check your hook.
  2. Execute pre-commit try-repo ../LOCAL/PATH/TO/my-hooks then, it’s best to see a neighborhood initialization message.

One limitation is you can’t straight go args to your hook by way of this command.

3. Copy the configuration discovered underneath Utilizing config: to a neighborhood file and add the args part. We created .local-pre-commit-config.yaml however you need to use any identify.

# my-project/.local-pre-commit-config.yaml
repos:
- repo: ../../dagworks/hamilton-pre-commit
rev: e4b77a499ba0ff3446a86ebbe4c2cbca82eb54f8
hooks:
- id: cli-command
args: [
hamilton build my_func2.py
]

4. Use your native hook by way of pre-commit run --config .local-pre-commit-config.yaml --all-files. The --all-files flag will apply the hook to all recordsdata in your repository as an alternative of these presently staged.

When including a check, at all times begin by making it fail. You wouldn’t wish to add a check that at all times succeeds :^)

5. Publishing your pre-commit hook

You’re virtually there! You’ve got a working hook script that’s examined and packaged in a git repository. Now, you simply must make it accessible on-line. We are going to present the steps for GitHub-hosted initiatives, however your pre-commit hook can reside anyplace accessible by way of git clone.

  1. Out of your GitHub repository, go to the Releases part
Foremost web page of a GitHub repository.

2. Click on Draft a brand new launch

Releases part of a GitHub repository

3. On the brand new launch web page, that you must add a model tag, a title, and an outline. If it’s your first launch, I recommend setting the tag as v0.1.0 to comply with semantic versioning, as really useful by GitHub.

While you’re making modifications and wish to distribute experimental variations, you may set your model as v0.1.1-rc (for “launch candidate”) and mark it as a pre-release utilizing the checkbox.

New launch kind on GitHub.

The rev worth in your .pre-commit-config.yaml file might want to match the model tag you set.

repos:
- repo: https://github.com/DAGWorks-Inc/hamilton-pre-commit
rev: v0.1.3rc
hooks:
- id: cli-command
# ...

Congrats! You made it by way of this put up! You at the moment are ready to make use of pre-commit hooks to enhance code high quality in your initiatives. Outfitted with an understanding of their internals, you can begin writing your individual hooks!

Don’t neglect to check out the various hooks maintained by the group earlier than reinventing the wheel: https://pre-commit.com/hooks.html

[ad_2]