Skip to content
Latchkey

pre-commit local hooks: Run Your Own Scripts

A repo: local block defines hooks inline with your own entry command instead of pulling them from a remote repo.

When no published hook does what you need, define one locally. You supply the entry command, the language, and the file filters.

What it does

With repo: local, each hook needs an id, a name, an entry (the command), and a language. pre-commit runs entry against the matched files. language: system runs the command as-is on the runner; language: python builds a venv from additional_dependencies.

Common usage

.pre-commit-config.yaml
repos:
  - repo: local
    hooks:
      - id: no-print
        name: forbid print statements
        entry: grep -nE "print\("
        language: system
        types: [python]
        exclude: '^scripts/'

Required keys

KeyWhat it does
idUnique identifier for the hook
nameHuman-readable name shown in output
entryThe command pre-commit runs
languagesystem, python, node, script, etc.
files / typesWhich files to pass to entry
pass_filenamesWhether matched filenames are appended to entry

In CI

language: system assumes the tool is already installed on the runner; install it as a separate step or the hook fails with "Executable not found". For self-contained hooks, prefer language: python with additional_dependencies so pre-commit builds and caches the environment. Set pass_filenames: false for commands that should not receive a file list.

Common errors in CI

"[ERROR] Executable <cmd> not found" with language: system means entry points at a binary not installed on the runner. A local hook with no name or entry raises InvalidConfigError. If a grep-style hook exits 1 because it found a match, that correctly fails the run; invert the logic if you meant the opposite.

Related guides

Run this faster and cheaper on Latchkey managed runners. Start free →