bash DevOps August 1, 2025

Pre-commit Hooks for Code Quality

A Husky + lint-staged setup that runs linting, formatting, and type checking on every commit — catching issues before they reach CI.

git-hookshuskylint-stagedpre-commitautomation

Description

An automated pre-commit hook setup using Husky and lint-staged that runs linters, formatters, and type checkers on staged files. Only processes changed files for fast feedback.

Features

  • Husky v9 for Git hook management
  • lint-staged to run tools only on staged files
  • ESLint for linting, Prettier for formatting
  • TypeScript type checking on commit
  • Conventional commit message validation

Setup Script

#!/bin/bash
# setup-hooks.sh — Initialize pre-commit hooks for a project

set -euo pipefail

echo "📦 Installing dependencies..."
npm install -D husky lint-staged @commitlint/cli @commitlint/config-conventional

echo "🐶 Initializing Husky..."
npx husky init

echo "📝 Creating pre-commit hook..."
cat > .husky/pre-commit << 'EOF'
npx lint-staged
EOF

echo "📝 Creating commit-msg hook..."
cat > .husky/commit-msg << 'EOF'
npx --no -- commitlint --edit "$1"
EOF

echo "✅ Hooks installed!"
echo "   - pre-commit: lint-staged (ESLint + Prettier + types)"
echo "   - commit-msg: conventional commits"

lint-staged Configuration

{
  "lint-staged": {
    "*.{ts,tsx}": [
      "eslint --fix --max-warnings=0",
      "prettier --write"
    ],
    "*.{js,jsx}": [
      "eslint --fix --max-warnings=0",
      "prettier --write"
    ],
    "*.{json,md,yml,yaml,css}": [
      "prettier --write"
    ]
  }
}

Commitlint Configuration

// commitlint.config.js
export default {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [2, 'always', [
      'feat', 'fix', 'docs', 'style', 'refactor',
      'perf', 'test', 'build', 'ci', 'chore', 'revert',
    ]],
    'subject-max-length': [2, 'always', 72],
    'body-max-line-length': [1, 'always', 100],
  },
};

Type Check Script

Add a pre-commit type check that only validates changed files:

#!/bin/bash
# .husky/pre-commit-typecheck.sh

# Get staged TypeScript files
STAGED_TS=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(ts|tsx)$' || true)

if [ -n "$STAGED_TS" ]; then
  echo "🔍 Type checking staged files..."
  npx tsc --noEmit --pretty
  echo "✅ Types OK"
fi

Usage

# Install hooks
bash setup-hooks.sh

# Hooks run automatically on commit
git add .
git commit -m "feat: add user profile page"
# → lint-staged runs ESLint + Prettier on staged files
# → commitlint validates the message format

# Skip hooks (emergency only)
git commit --no-verify -m "hotfix: emergency patch"