Module 11: Git Hooks and Automation
08. Mini-Project: Complete Automation Setup
Project description
In this mini-project you will create a production-ready automation system with Husky, lint-staged, Commitizen, custom hooks, and comprehensive quality gates. You will implement linting, testing, secret detection, conventional commits, and branch protection. This is the setup professional teams use.
This project is your template for any future project.
Project objectives
By completing this project, you will have:
✅ Configured Husky for shared hooks
✅ Integrated lint-staged for fast linting
✅ Implemented Commitizen for conventional commits
✅ Created custom hooks (secrets, tickets, branch naming)
✅ Configured commitlint for validation
✅ Added pre-commit checks (lint, format, secrets)
✅ Added pre-push checks (tests, branch protection)
✅ Documented the setup for the team
✅ Tested the whole system end-to-end
Estimated time: 60-90 minutes
Initial setup (10 min)
Step 1: Create project
mkdir automation-project
cd automation-project
npm init -y
git init
Step 2: Install dependencies
npm install --save-dev \
husky \
lint-staged \
commitizen \
cz-conventional-changelog \
@commitlint/cli \
@commitlint/config-conventional \
eslint \
prettier
Step 3: Initialize tools
# Husky
npx husky install
npm set-script prepare "husky install"
# Commitizen
npx commitizen init cz-conventional-changelog --save-dev --save-exact
# ESLint
npx eslint --init
# Choose: problems, esm, none, no, node, JSON
# Prettier
cat > .prettierrc << 'EOF'
{
"semi": true,
"singleQuote": true,
"trailingComma": "es5"
}
EOF
Phase 1: Lint-staged setup (10 min)
Configure lint-staged:
// .lintstagedrc.js
module.exports = {
'*.js': [
'eslint --fix',
'prettier --write'
],
'*.{json,md,yml}': [
'prettier --write'
]
};
Add pre-commit hook:
npx husky add .husky/pre-commit "npx lint-staged"
Test:
# Create test file
echo "const x=1" > test.js
# Stage and commit
git add test.js
npm run commit
# Should auto-fix formatting
Phase 2: Commitizen + commitlint (10 min)
Configure commitlint:
// commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
[
'feat',
'fix',
'docs',
'style',
'refactor',
'test',
'chore',
'perf',
'ci',
'build',
'revert'
]
],
'subject-case': [2, 'always', 'sentence-case'],
'header-max-length': [2, 'always', 100]
}
};
Add commit-msg hook:
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'
Test:
# Valid commit
npm run commit
# Follow prompts
# Invalid commit (should fail)
git commit -m "bad commit" --no-verify
git commit -m "update readme"
# Should be blocked by commit-msg hook
Phase 3: Secret detection (10 min)
Create secret detection script:
#!/bin/bash
# scripts/check-secrets.sh
echo "🔍 Scanning for secrets..."
# Patterns
patterns=(
"api[_-]?key"
"password"
"secret"
"token"
"private[_-]?key"
"bearer"
)
# Build regex
regex=$(IFS="|"; echo "${patterns[*]}")
# Check staged files
if git diff --cached --name-only | xargs grep -iE "$regex" 2>/dev/null; then
echo ""
echo "❌ Potential secrets detected!"
echo ""
echo "Remove secrets before committing."
echo "Use environment variables instead."
echo ""
echo "If false positive: git commit --no-verify"
exit 1
fi
echo "✅ No secrets detected"
chmod +x scripts/check-secrets.sh
Update pre-commit hook:
# .husky/pre-commit
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
# 1. Lint staged files
npx lint-staged
# 2. Check for secrets
./scripts/check-secrets.sh
Test:
# Should fail
echo "API_KEY=abc123" > .env
git add .env
git commit -m "test"
# Should pass
echo "# Configuration" > config.txt
git add config.txt
npm run commit
Phase 4: Branch naming (10 min)
Create branch validation script:
#!/bin/bash
# scripts/check-branch-name.sh
branch=$(git symbolic-ref --short HEAD)
# Valid pattern
valid="^(feature|fix|hotfix|release|chore)/[a-z0-9-]+$"
if [[ ! $branch =~ $valid ]]; then
echo "❌ Invalid branch name: $branch"
echo ""
echo "Branch naming convention:"
echo " feature/description"
echo " fix/description"
echo " hotfix/description"
echo " release/version"
echo " chore/description"
echo ""
echo "Examples:"
echo " feature/user-authentication"
echo " fix/login-validation"
echo ""
exit 1
fi
echo "✅ Branch name valid: $branch"
chmod +x scripts/check-branch-name.sh
Add pre-push hook:
#!/usr/bin/env sh
# .husky/pre-push
. "$(dirname -- "$0")/_/husky.sh"
# Check branch name
./scripts/check-branch-name.sh
Test:
# Should fail
git checkout -b invalid
git push # Blocked
# Should pass
git checkout -b feature/automation-setup
git push # Allowed
Phase 5: Ticket validation (10 min)
Create ticket validation:
#!/bin/bash
# scripts/check-ticket.sh
commit_msg_file=$1
commit_msg=$(cat "$commit_msg_file")
# Pattern: [PROJ-123] or #123
if ! echo "$commit_msg" | grep -qE "(\[[A-Z]+-[0-9]+\]|#[0-9]+)"; then
echo "❌ Commit must include ticket reference"
echo ""
echo "Valid formats:"
echo " [PROJ-123] Your message"
echo " Fixes #123: Your message"
echo ""
exit 1
fi
echo "✅ Ticket reference found"
chmod +x scripts/check-ticket.sh
Update commit-msg hook:
#!/usr/bin/env sh
# .husky/commit-msg
. "$(dirname -- "$0")/_/husky.sh"
# 1. Validate conventional commits
npx --no -- commitlint --edit "$1"
# 2. Validate ticket reference
./scripts/check-ticket.sh "$1"
Update Commitizen config:
// .cz-config.js (if using cz-customizable)
module.exports = {
types: [
{ value: 'feat', name: 'feat: New feature' },
{ value: 'fix', name: 'fix: Bug fix' },
{ value: 'docs', name: 'docs: Documentation' },
{ value: 'style', name: 'style: Code style' },
{ value: 'refactor', name: 'refactor: Refactor' },
{ value: 'test', name: 'test: Tests' },
{ value: 'chore', name: 'chore: Chores' },
],
messages: {
type: 'Select the type of change:',
scope: 'Scope (optional):',
customScope: 'Custom scope:',
subject: 'Short description:',
body: 'Longer description (optional):',
breaking: 'Breaking changes (optional):',
footer: 'Issues (e.g. #123, [PROJ-456]):',
confirmCommit: 'Confirm commit?',
},
allowBreakingChanges: ['feat', 'fix'],
};
Phase 6: Testing integration (10 min)
Add test script:
// tests/example.test.js
const sum = (a, b) => a + b;
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
Install Jest:
npm install --save-dev jest
# Add script
npm set-script test "jest"
Update pre-push hook:
#!/usr/bin/env sh
# .husky/pre-push
. "$(dirname -- "$0")/_/husky.sh"
echo "🚀 Pre-push checks..."
# 1. Branch name
./scripts/check-branch-name.sh
# 2. Run tests
echo "\n🧪 Running tests..."
npm test
echo "\n✅ All pre-push checks passed!"
Phase 7: Documentation (10 min)
Create CONTRIBUTING.md:
# Contributing Guide
## Setup
1. Clone repository
2. Install dependencies: `npm install`
3. Hooks will install automatically via Husky
## Development Workflow
### Committing
Use Commitizen for consistent commits:
```bash
npm run commit
Or:
git cz
This opens an interactive prompt.
Commit Format
Commits must follow Conventional Commits:
type(scope): Short description
Longer description (optional)
Closes #123
Types: feat, fix, docs, style, refactor, test, chore
Ticket: All commits must reference a ticket: [PROJ-123] or #123
Branch Naming
Branches must follow:
feature/description
fix/description
hotfix/description
release/version
chore/description
Examples:
feature/user-authenticationfix/login-validation
Pre-commit Checks
Automatically run:
- ESLint (auto-fix)
- Prettier (auto-format)
- Secret detection
Pre-push Checks
Automatically run:
- Branch name validation
- Full test suite
Bypassing Hooks
In emergencies only:
git commit --no-verify -m "hotfix: Critical fix"
Document why in commit message.
Quality Gates
- ✅ Code style (ESLint + Prettier)
- ✅ Conventional commits
- ✅ Ticket references
- ✅ Branch naming
- ✅ No secrets
- ✅ Tests passing
---
### **Update README.md:**
```markdown
# Automation Project
Production-ready Git automation setup.
## Quick Start
```bash
# Install
npm install
# Commit
npm run commit
# Push
git push
Features
- ✅ Husky - Shared Git hooks
- ✅ lint-staged - Fast linting (staged files only)
- ✅ Commitizen - Interactive conventional commits
- ✅ commitlint - Commit message validation
- ✅ ESLint + Prettier - Code quality
- ✅ Secret detection - Prevent credential leaks
- ✅ Branch naming - Enforce naming conventions
- ✅ Ticket validation - Require ticket references
- ✅ Automated tests - Pre-push testing
Contributing
See CONTRIBUTING.md
---
## Phase 8: Complete testing (10 min)
### **Test checklist:**
```bash
# ✅ 1. Lint auto-fix
echo "const x=1" > src/index.js
git add src/index.js
npm run commit
# Should format code
# ✅ 2. Secret detection
echo "API_KEY=secret" > .env
git add .env
git commit -m "test"
# Should block
# ✅ 3. Conventional commits
git commit -m "bad format"
# Should block
npm run commit
# Should work
# ✅ 4. Ticket validation
# (Will be enforced by commit-msg)
# ✅ 5. Branch naming
git checkout -b invalid-branch
git push
# Should block
git checkout -b feature/test
git push
# Should work
# ✅ 6. Tests
# Write failing test
echo "test('fail', () => expect(1).toBe(2));" > tests/fail.test.js
git add tests/
git commit -m "[PROJ-1] test: Add failing test"
git push
# Should block
Final project structure
automation-project/
├── .husky/
│ ├── _/
│ ├── pre-commit # lint-staged + secrets
│ ├── commit-msg # commitlint + ticket
│ └── pre-push # branch + tests
├── scripts/
│ ├── check-secrets.sh
│ ├── check-branch-name.sh
│ └── check-ticket.sh
├── tests/
│ └── example.test.js
├── src/
│ └── index.js
├── .eslintrc.json
├── .prettierrc
├── .lintstagedrc.js
├── commitlint.config.js
├── package.json
├── CONTRIBUTING.md
└── README.md
Final package.json
{
"name": "automation-project",
"scripts": {
"prepare": "husky install",
"commit": "cz",
"lint": "eslint .",
"format": "prettier --write .",
"test": "jest"
},
"devDependencies": {
"@commitlint/cli": "^17.0.0",
"@commitlint/config-conventional": "^17.0.0",
"commitizen": "^4.2.0",
"cz-conventional-changelog": "^3.3.0",
"eslint": "^8.0.0",
"husky": "^8.0.0",
"jest": "^29.0.0",
"lint-staged": "^13.0.0",
"prettier": "^2.8.0"
},
"config": {
"commitizen": {
"path": "cz-conventional-changelog"
}
}
}
Final validation
Complete checklist:
- Husky installed and working
- lint-staged configured
- Commitizen working (
npm run commit) - commitlint validating messages
- Secret detection blocking secrets
- Branch naming enforced
- Ticket validation working
- Tests running pre-push
- Documentation complete
- Team can clone and use immediately
Optional extensions
1. Add TypeScript:
npm install --save-dev typescript @typescript-eslint/parser @typescript-eslint/eslint-plugin
# Update .lintstagedrc.js
'*.{js,ts}': ['eslint --fix', 'prettier --write']
2. Add test coverage threshold:
// jest.config.js
module.exports = {
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80
}
}
};
3. Add Docker check:
#!/bin/bash
# scripts/check-docker.sh
if git diff --cached --name-only | grep -q "Dockerfile"; then
echo "🐳 Dockerfile changed, validating..."
docker build -t test . --dry-run
fi
4. Add changelog generation:
npm install --save-dev standard-version
# Add script
npm set-script release "standard-version"
# Generate changelog
npm run release
Success criteria
- Husky hooks shared via
.husky/ - lint-staged speeds up pre-commit
- Commitizen enforces format
- commitlint validates messages
- Secrets blocked automatically
- Branch naming enforced
- Tickets required in commits
- Tests run before push
- Documentation complete
- Team onboarding < 5 minutes
Project summary
You completed:
✅ Complete automation stack
✅ Husky + lint-staged + Commitizen
✅ Custom validation hooks
✅ Secret detection
✅ Branch + ticket validation
✅ Pre-commit + pre-push gates
✅ Production-ready setup
✅ Team documentation
Skills demonstrated:
- Hook integration mastery
- Automation setup expertise
- Team workflow design
- Quality gate implementation
Next steps
This setup is your template for future projects:
- Fork this repo as a template
- Customize it for your stack (TS, React, etc.)
- Share it with your team
- Iterate based on feedback
Module 11 summary
You completed the full Git Hooks and Automation module:
✅ Native Git hooks (pre-commit, commit-msg, pre-push)
✅ Husky for shared hooks
✅ lint-staged for performance
✅ Commitizen for conventional commits
✅ Advanced custom hooks
✅ Bypass strategies and troubleshooting
✅ Complete production setup
Next module: Module 12: GitHub Actions CI/CD
Additional resources
- Husky Best Practices - Official guide
- lint-staged Examples - Real-world configs
- Git Hooks Collection - Community hooks
Total project time: 60-90 minutes
Congratulations! You have completed Module 11 - Git Hooks and Automation.