Skip to content

Consider Using Pre-Commit #4100

@O957

Description

@O957

This issue covers considerations for using the tool https://pre-commit.com/.

Git hook scripts are useful for identifying simple issues before submission to code review. We run our hooks on every commit to automatically point out issues in code such as missing semicolons, trailing whitespace, and debug statements. By pointing these issues out before code review, this allows a code reviewer to focus on the architecture of a change while not wasting time with trivial style nitpicks.

As we created more libraries and projects we recognized that sharing our pre-commit hooks across projects is painful. We copied and pasted unwieldy bash scripts from project to project and had to manually change the hooks to work for different project structures.

There are many hooks supported by pre-commit. For this repository, the ruff Python linter and formatter (e.g. for automatically abiding by PEP-8 standards, https://peps.python.org/pep-0008/) might especially be useful.

Example .pre-commit-config.yaml File
repos:
-   repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v6.0.0
    hooks:
    -   id: check-added-large-files
        args: ["--maxkb=10000"]
    # simply check whether files parse as valid
    # python
    -   id: check-ast
    # check for files with names that would
    # conflict on a case-insensitive filesystem
    # like MacOS HFS+ or Windows FAT.
    -   id: check-case-conflict
    # checks for a common error of placing
    # code before the docstring.
    -   id: check-docstring-first
    # attempts to load all yaml files to
    # verify syntax.
    -   id: check-yaml
        # allow yaml files which use the
        # multi-document syntax
        args: ["--allow-multiple-documents"]
    # attempts to load all TOML files to
    # verify syntax.
    -   id: check-toml
    # makes sure files end in a newline and
    # only a newline.
    -   id: end-of-file-fixer
    # replaces or checks mixed line ending.
    -   id: mixed-line-ending
    # verifies that test files are named
    # correctly.
    -   id: name-tests-test
        # ensure tests match test_.*\.py
        args: ["--pytest-test-first"]
    # checks that all your JSON files are pretty.
    # "Pretty" here means that keys are sorted
    # and indented.
    -   id: pretty-format-json
        # automatically format json files;
        # when autofixing, retain the original
        # key ordering (instead of sorting
        # the keys)
        args: ["--autofix", "--no-sort-keys",  "--indent", "4"]
    # trims trailing whitespace.
    -   id: trailing-whitespace
    # checks that non-binary executables have
    # a proper shebang.
    -   id: check-executables-have-shebangs
        files: \.sh$
    # checks for the existence of private keys.
    -   id: detect-private-key
###############################################################################
# PYTHON
###############################################################################
-   repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.14.11
    hooks:
    -   id: ruff
        args: [
          "check",
          "--select",
          # isort
          "I",
          "--fix"
        ]
    # run ruff linter; the Ruff Linter is an extremely fast Python linter
    # designed as a drop-in replacement for Flake8 (plus dozens of plugins),
    # isort, pydocstyle, pyupgrade, autoflake, and more
    -   id: ruff
        args: [
          # ambiguous variable name: {name}
          "--ignore=E741",
          # do not assign a lambda expression, use a def
          "--ignore=E731",
          # indentation contains tabs (sorry, I like tabs)
          "--ignore=W191",
          # found useless expression. ignore since .qmd displays
          "--ignore=B018",
          # {name} is too complex ({complexity} > {max_complexity})
          # note: ignored on select repositories
          "--ignore=C901",
          # E and W: pycodestyle, standard PEP8 errors and pycodestyle warnings.
          # F: pyflakes warnings (e.g., unused variables, undefined names).,
          # B: flake8-bugbear (useful best practices).
          # SIM: flake8-simplify
          # C90: McCabe complexity (cyclomatic complexity).
          # UP: pyupgrade, Python version compatibility
          "--select=E,W,F,B,C90,UP,SIM",
          # linter checks for lines, but doesn't fix, default is 88
          "--line-length=79",
          # lint all files in the current directory, and fix any fixable errors.
          "--fix"
        ]
    # run the ruff-formatter; the Ruff formatter is an extremely fast
    # Python code formatter designed as a drop-in replacement for Black
    -   id: ruff-format
        args: [
          "--line-length=79",
        ]
###############################################################################
# XENON CODE COMPLEXITY
###############################################################################
-   repo: https://github.com/rubik/xenon
    rev: v0.9.3
    hooks:
    -   id: xenon
        args: [
            # threshold for the average complexity
            # (across all the codebase).
            "--max-absolute=B",
            # threshold for modules complexity.
            "--max-modules=B",
            # absolute threshold for block complexity.
            "--max-average=A"
        ]
###############################################################################
# UV PACKAGE AND DEPENDENCY MANAGEMENT
###############################################################################
-   repo: https://github.com/astral-sh/uv-pre-commit
    # uv version.
    rev: 0.9.24
    hooks:
    -   id: uv-lock
###############################################################################
# MAKE FILES
###############################################################################
-   repo: https://github.com/mrtazz/checkmake.git
    rev: v0.3.0
    hooks:
    -   id: checkmake
###############################################################################
# SECURITY
###############################################################################
-   repo: https://github.com/Yelp/detect-secrets
    rev: v1.5.0
    hooks:
    # must first run
    # detect-secrets scan > .secrets.baseline
    -   id: detect-secrets
        args: ["--baseline", ".secrets.baseline"]
        exclude: package.lock.json
###############################################################################
# GITHUB ACTIONS
###############################################################################
-   repo: https://github.com/rhysd/actionlint
    rev: v1.7.10
    hooks:
    -   id: actionlint
###############################################################################
# SPELLING
###############################################################################
-   repo: https://github.com/crate-ci/typos
    rev: v1
    hooks:
    -   id: typos
        args: ["--force-exclude"]
###############################################################################
# COMMIT MESSAGES
###############################################################################
-   repo: https://github.com/commitizen-tools/commitizen
    rev: v4.11.2
    hooks:
    -   id: commitizen
-   repo: https://github.com/jorisroovers/gitlint
    rev:  v0.19.1
    hooks:
    -   id: gitlint
###############################################################################
Example Pre-Commit Output (Local Run) Image

I'd be happy to create a PR for this. Feel free to assign me. If assigned, I will change the name of the issue to Add Pre-Commit And Default Hooks. The default hooks can be sorted out in the PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions