This guide provides practical examples for using husky-rs Git hooks in your Rust projects.
Create .husky/pre-commit:
#!/bin/sh
echo "Running tests before commit..."
cargo test --quiet || {
echo "❌ Tests failed! Commit aborted."
exit 1
}
echo "✓ Tests passed"Create .husky/pre-commit:
#!/bin/sh
echo "Checking code formatting..."
cargo fmt --check || {
echo "❌ Code is not formatted. Run 'cargo fmt' and try again."
exit 1
}
echo "✓ Code is properly formatted"Create .husky/pre-commit:
#!/bin/sh
echo "Running clippy..."
cargo clippy --all-targets --all-features -- -D warnings || {
echo "❌ Clippy found issues. Please fix them and try again."
exit 1
}
echo "✓ No clippy warnings"Create .husky/commit-msg:
#!/bin/sh
commit_msg_file="$1"
commit_msg=$(cat "$commit_msg_file")
# Check for conventional commit format
if ! echo "$commit_msg" | grep -qE "^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?:.+"; then
echo "❌ Invalid commit message format!"
echo ""
echo "Commit message must follow Conventional Commits:"
echo " type(scope): description"
echo ""
echo "Valid types: feat, fix, docs, style, refactor, test, chore, perf, ci, build, revert"
echo ""
echo "Examples:"
echo " feat(auth): add login functionality"
echo " fix(api): handle null pointer exception"
echo " docs: update README"
exit 1
fiCreate .husky/commit-msg:
#!/bin/sh
commit_msg=$(cat "$1")
msg_length=$(echo "$commit_msg" | head -n 1 | wc -c)
if [ "$msg_length" -lt 10 ]; then
echo "❌ Commit message is too short (minimum 10 characters)"
exit 1
fi
echo "✓ Commit message valid"Create .husky/pre-push:
#!/bin/sh
set -e
echo "🚀 Running pre-push checks..."
echo "1/3 Checking format..."
cargo fmt --check
echo "2/3 Running clippy..."
cargo clippy --all-targets --all-features -- -D warnings
echo "3/3 Running tests..."
cargo test
echo "✅ All checks passed! Pushing..."Create .husky/pre-push:
#!/bin/sh
protected_branch='main'
current_branch=$(git symbolic-ref HEAD | sed -e 's,.*/\(.*\),\1,')
if [ "$current_branch" = "$protected_branch" ]; then
echo "❌ Direct push to $protected_branch is not allowed!"
echo "Please create a feature branch and submit a PR."
exit 1
fi
echo "✓ Pushing to $current_branch"Create .husky/pre-commit:
#!/bin/sh
# Skip hook in CI environment
if [ -n "$CI" ]; then
echo "Running in CI, skipping local pre-commit hook"
exit 0
fi
echo "Running local pre-commit checks..."
cargo test --quietCreate .husky/pre-commit:
#!/bin/sh
set -e
# Check Rust code
if git diff --cached --name-only | grep -q "\.rs$"; then
echo "Checking Rust code..."
cargo fmt --check
cargo clippy -- -D warnings
fi
# Check Python code (if you have Python scripts)
if git diff --cached --name-only | grep -q "\.py$"; then
echo "Checking Python code..."
python -m black --check .
python -m pylint **/*.py
fi
echo "✓ All checks passed"Create .husky/pre-commit:
#!/bin/sh
# Check for common secret patterns
if git diff --cached | grep -iE "(password|api_key|secret|token|private_key)\s*=\s*[\"'][^\"']+[\"']"; then
echo "❌ Potential secret detected in commit!"
echo "Please remove sensitive data before committing."
exit 1
fi
# Check for common secret files
if git diff --cached --name-only | grep -E "\.(pem|key|env)$"; then
echo "⚠️ Warning: You're about to commit a file that might contain secrets"
echo "Files: $(git diff --cached --name-only | grep -E '\.(pem|key|env)$')"
echo "Press Ctrl+C to abort or Enter to continue"
read -r confirm
fi
echo "✓ No obvious secrets detected"Create .husky/pre-commit:
#!/bin/sh
# Generate docs if Rust code changed
if git diff --cached --name-only | grep -q "src/.*\.rs$"; then
echo "Rust source changed, updating documentation..."
cargo doc --no-deps
# Optionally add generated docs to commit
# git add target/doc
fiCreate .husky/pre-push:
#!/bin/sh
echo "Running performance benchmarks..."
# Run benchmarks and save results
cargo bench --no-run || {
echo "❌ Benchmarks failed to compile"
exit 1
}
echo "✓ Benchmarks compiled successfully"Create .husky/pre-commit:
#!/usr/bin/env python3
import subprocess
import sys
def run_command(cmd):
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
if result.returncode != 0:
print(f"❌ {cmd} failed")
print(result.stderr)
sys.exit(1)
print(f"✓ {cmd} passed")
if __name__ == "__main__":
print("Running Python-based pre-commit checks...")
run_command("cargo fmt --check")
run_command("cargo clippy -- -D warnings")
run_command("cargo test --quiet")
print("✅ All checks passed!")Hooks that take too long will slow down your workflow. Aim for:
- Pre-commit: < 10 seconds
- Commit-msg: < 1 second
- Pre-push: < 30 seconds
# Instead of:
cargo test
# Use:
cargo test --quiet # Less verbose outputAlways tell users what went wrong and how to fix it:
cargo fmt --check || {
echo "❌ Code formatting failed"
echo ""
echo "To fix: cargo fmt"
echo ""
exit 1
}Before committing your hooks, test them manually:
# Test pre-commit hook
.git/hooks/pre-commit
# Test commit-msg hook
echo "test message" | .git/hooks/commit-msgAdd set -e at the top of shell scripts to exit on any error:
#!/bin/sh
set -e # Exit immediately if any command fails
cargo fmt --check
cargo clippy -- -D warnings
cargo testYou can skip hooks with --no-verify:
# Skip pre-commit and commit-msg hooks
git commit --no-verify -m "WIP: temporary commit"
# Skip pre-push hook
git push --no-verify- Check hook file permissions (should be executable on Unix)
- Verify hook name matches Git's supported hooks
- Check for syntax errors:
sh -n .husky/pre-commit
This shouldn't happen with husky-rs. If it does, check that you don't have duplicate hooks in:
.git/hooks/.husky/
Profile your hook to find bottlenecks:
#!/bin/sh
time cargo test --quiet
time cargo clippy -- -D warnings