Skip to content

Pre-Commit Hooks

Overview

Pre-commit hooks automatically validate code before commits, catching syntax errors, formatting issues, and common mistakes. This prevents broken code from entering the repository.

Installation

One-Time Setup

# Install pre-commit (macOS)
brew install pre-commit

# Install in repository (creates .git/hooks/pre-commit)
pre-commit install

Tool Dependencies

Most tools are installed automatically by pre-commit. Optional tools:

# For manual linting outside pre-commit
brew install shellcheck yamllint
pip3 install --user ansible-lint black
npm install -g markdownlint-cli2  # Optional — pre-commit handles this

What Gets Checked

All Files

  • Trailing whitespace removal (except markdown line breaks)
  • End-of-file newline enforcement
  • Large files detection (>1MB)
  • Merge conflict markers
  • Line endings normalized to LF

Markdown Files (*.md)

  • markdownlint (via markdownlint-cli2) - style and consistency validation
  • Heading structure and blank lines
  • Code fence language identifiers (MD040)
  • List formatting and indentation
  • Config: .markdownlint.yml (rules), .markdownlint-cli2.jsonc (ignores)
  • Manual run: npx markdownlint-cli2 "**/*.md"
  • Auto-fix: npx markdownlint-cli2 "**/*.md" --fix

Shell Scripts (*.sh)

  • ShellCheck - catches common errors:
  • Unquoted variables
  • Missing error handling (cd without || exit)
  • Unused variables
  • Incorrect conditionals (== vs =)
  • And 300+ other issues

YAML Files

  • yamllint - syntax and style validation
  • Max line length: 120 characters
  • Consistent indentation
  • Proper quote usage

Ansible Files (cloud/ansible/*/.yml)

  • Basic YAML checks via check-yaml
  • ansible-lint disabled by default (run manually - see below)

Terraform Files (cloud/terraform/*/)

  • terraform fmt - automatic formatting
  • terraform validate - configuration validation

Python Files (*.py)

  • black - automatic PEP8 formatting

Usage

Hooks run automatically on git commit:

git add file.sh
git commit -m "Fix script"
# Pre-commit runs, auto-fixes some issues, blocks commit if errors found

If hooks fail:

  1. Review the errors
  2. Fix manually or let auto-fixers handle it
  3. Re-stage fixed files: git add <files>
  4. Commit again

Manual

Run on specific files:

pre-commit run --files scripts/opnsense-dev/build-golden-image.sh

Run on all files:

pre-commit run --all-files

Run specific hook:

pre-commit run shellcheck --all-files

Bypassing (Emergency Only)

git commit --no-verify -m "Emergency fix"

Only use for:

  • True emergencies (production down)
  • Committing intentionally broken code for collaboration
  • Working around known false positives (fix the config instead!)

Ansible Linting (Manual)

ansible-lint has dependency issues in pre-commit isolation. Run manually:

# From repo root
cd cloud/ansible
ansible-lint

# Check specific playbook
ansible-lint playbooks/bootstrap-dev-host.yml

# With profile
ansible-lint --profile=production

Maintenance

Update Hook Versions

pre-commit autoupdate

Clear Cache (if hooks misbehave)

pre-commit clean
pre-commit install --install-hooks

Common Issues

"command not found: shellcheck"

Install system package:

brew install shellcheck

"terraform: command not found"

Terraform validation needs terraform binary in PATH. Options:

  1. Install terraform: brew install terraform
  2. Disable terraform hooks in .pre-commit-config.yaml

Hooks Won't Run

# Reinstall hooks
pre-commit uninstall
pre-commit install

# Verify installation
ls -la .git/hooks/pre-commit

False Positives

ShellCheck warning you disagree with? Add directive:

# shellcheck disable=SC2164
cd /some/path  # Safe in this context

Or configure globally in .shellcheckrc (create if needed).

Configuration

Edit .pre-commit-config.yaml to:

  • Add/remove hooks
  • Adjust severity levels
  • Exclude files/patterns
  • Update versions

After config changes:

pre-commit install  # Re-install with new config

Benefits

Before pre-commit:

$ ./scripts/build-golden-image.sh
line 399: unexpected EOF while looking for matching `''`

With pre-commit:

$ git commit -m "Add feature"
shellcheck...........Failed
- Syntax error detected in build-golden-image.sh
# Fix BEFORE broken code enters git history

Result:

  • ✅ Catch errors in seconds, not minutes
  • ✅ No broken commits in history
  • ✅ Consistent code style across team
  • ✅ Fewer "it works on my machine" issues