Skip to content

Commit 325e736

Browse files
committed
feat: Add git hooks for TFO-Python-SDK
1 parent 224b1d2 commit 325e736

File tree

3 files changed

+279
-0
lines changed

3 files changed

+279
-0
lines changed

docs/githooks/commit-msg

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#!/bin/bash
2+
3+
# Commit-msg hook for TelemetryFlow projects
4+
# Enforces Conventional Commits format
5+
# https://www.conventionalcommits.org/
6+
7+
set -e
8+
9+
COMMIT_MSG_FILE=$1
10+
COMMIT_MSG=$(cat "$COMMIT_MSG_FILE")
11+
12+
# Colors for output
13+
RED='\033[0;31m'
14+
GREEN='\033[0;32m'
15+
YELLOW='\033[1;33m'
16+
NC='\033[0m' # No Color
17+
18+
# Skip merge commits
19+
if echo "$COMMIT_MSG" | grep -qE "^Merge"; then
20+
exit 0
21+
fi
22+
23+
# Conventional commit pattern
24+
# Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
25+
# Optional scope in parentheses
26+
# Optional breaking change indicator (!)
27+
# Colon and space, then description
28+
PATTERN="^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(.+\))?(!)?: .{1,}"
29+
30+
# Check if commit message follows conventional commits format
31+
if ! echo "$COMMIT_MSG" | head -1 | grep -qE "$PATTERN"; then
32+
echo -e "${RED}ERROR: Commit message does not follow Conventional Commits format.${NC}"
33+
echo ""
34+
echo -e "${YELLOW}Your commit message:${NC}"
35+
echo "$COMMIT_MSG" | head -1
36+
echo ""
37+
echo -e "${YELLOW}Expected format:${NC}"
38+
echo " <type>[optional scope][!]: <description>"
39+
echo ""
40+
echo -e "${YELLOW}Allowed types:${NC}"
41+
echo " feat: A new feature"
42+
echo " fix: A bug fix"
43+
echo " docs: Documentation only changes"
44+
echo " style: Code style changes (formatting, semicolons, etc)"
45+
echo " refactor: Code change that neither fixes a bug nor adds a feature"
46+
echo " perf: Performance improvement"
47+
echo " test: Adding or updating tests"
48+
echo " build: Build system or external dependency changes"
49+
echo " ci: CI configuration changes"
50+
echo " chore: Other changes that don't modify src or test files"
51+
echo " revert: Reverts a previous commit"
52+
echo ""
53+
echo -e "${YELLOW}Examples:${NC}"
54+
echo " feat: add user authentication"
55+
echo " fix(api): resolve null pointer in handler"
56+
echo " docs: update README with installation steps"
57+
echo " feat(auth)!: change token format (breaking change)"
58+
echo ""
59+
exit 1
60+
fi
61+
62+
# Check minimum description length (at least 10 characters after the type)
63+
DESCRIPTION=$(echo "$COMMIT_MSG" | head -1 | sed -E 's/^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(.+\))?(!)?: //')
64+
if [ ${#DESCRIPTION} -lt 10 ]; then
65+
echo -e "${RED}ERROR: Commit description is too short (minimum 10 characters).${NC}"
66+
echo -e "${YELLOW}Current description:${NC} $DESCRIPTION"
67+
exit 1
68+
fi
69+
70+
# Check that description doesn't start with capital letter (optional but recommended)
71+
if echo "$DESCRIPTION" | grep -qE "^[A-Z]"; then
72+
echo -e "${YELLOW}WARNING: Description should start with lowercase letter.${NC}"
73+
echo -e "${YELLOW}Current:${NC} $DESCRIPTION"
74+
# This is just a warning, not a failure
75+
fi
76+
77+
# Check commit message line length (first line should be <= 72 characters)
78+
FIRST_LINE_LENGTH=$(echo "$COMMIT_MSG" | head -1 | wc -c | tr -d ' ')
79+
if [ "$FIRST_LINE_LENGTH" -gt 72 ]; then
80+
echo -e "${YELLOW}WARNING: First line exceeds 72 characters (${FIRST_LINE_LENGTH} chars).${NC}"
81+
echo -e "${YELLOW}Consider shortening the commit message.${NC}"
82+
# This is just a warning, not a failure
83+
fi
84+
85+
echo -e "${GREEN}Commit message follows Conventional Commits format.${NC}"
86+
exit 0

docs/githooks/pre-commit

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
#!/bin/bash
2+
3+
# Pre-commit hook for TelemetryFlow Python SDK
4+
# Runs: black, flake8/ruff, mypy, and security scan (bandit)
5+
6+
set -e
7+
8+
echo "Running pre-commit checks..."
9+
10+
# Colors for output
11+
RED='\033[0;31m'
12+
GREEN='\033[0;32m'
13+
YELLOW='\033[1;33m'
14+
NC='\033[0m' # No Color
15+
16+
# Get staged Python files
17+
STAGED_PY_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep '\.py$' || true)
18+
19+
if [ -z "$STAGED_PY_FILES" ]; then
20+
echo -e "${YELLOW}No Python files staged for commit. Skipping Python checks.${NC}"
21+
exit 0
22+
fi
23+
24+
# Function to check if a command exists
25+
command_exists() {
26+
command -v "$1" >/dev/null 2>&1
27+
}
28+
29+
FAILED=0
30+
31+
# 1. Check black formatting
32+
echo -e "\n${YELLOW}[1/4] Running black (formatter)...${NC}"
33+
if command_exists black; then
34+
if ! black --check --quiet $STAGED_PY_FILES 2>&1; then
35+
echo -e "${RED}black found formatting issues:${NC}"
36+
black --diff $STAGED_PY_FILES 2>&1 || true
37+
echo -e "${YELLOW}Run 'black <file>' to fix${NC}"
38+
FAILED=1
39+
else
40+
echo -e "${GREEN}black passed${NC}"
41+
fi
42+
else
43+
echo -e "${YELLOW}black not installed. Skipping format check.${NC}"
44+
echo -e "${YELLOW}Install with: pip install black${NC}"
45+
fi
46+
47+
# 2. Run linter (ruff or flake8)
48+
echo -e "\n${YELLOW}[2/4] Running linter...${NC}"
49+
if command_exists ruff; then
50+
if ! ruff check $STAGED_PY_FILES 2>&1; then
51+
echo -e "${RED}ruff found issues${NC}"
52+
FAILED=1
53+
else
54+
echo -e "${GREEN}ruff passed${NC}"
55+
fi
56+
elif command_exists flake8; then
57+
if ! flake8 $STAGED_PY_FILES 2>&1; then
58+
echo -e "${RED}flake8 found issues${NC}"
59+
FAILED=1
60+
else
61+
echo -e "${GREEN}flake8 passed${NC}"
62+
fi
63+
else
64+
echo -e "${YELLOW}ruff/flake8 not installed. Skipping lint check.${NC}"
65+
echo -e "${YELLOW}Install with: pip install ruff${NC}"
66+
echo -e "${YELLOW}Or: pip install flake8${NC}"
67+
fi
68+
69+
# 3. Run type checker (mypy)
70+
echo -e "\n${YELLOW}[3/4] Running mypy (type checker)...${NC}"
71+
if command_exists mypy; then
72+
if ! mypy $STAGED_PY_FILES --ignore-missing-imports 2>&1; then
73+
echo -e "${RED}mypy found type issues${NC}"
74+
# Type issues are warnings, not failures by default
75+
echo -e "${YELLOW}(mypy issues are warnings only)${NC}"
76+
else
77+
echo -e "${GREEN}mypy passed${NC}"
78+
fi
79+
else
80+
echo -e "${YELLOW}mypy not installed. Skipping type check.${NC}"
81+
echo -e "${YELLOW}Install with: pip install mypy${NC}"
82+
fi
83+
84+
# 4. Run security scan (bandit)
85+
echo -e "\n${YELLOW}[4/4] Running security scan...${NC}"
86+
if command_exists bandit; then
87+
if ! bandit -r $STAGED_PY_FILES -ll -q 2>&1; then
88+
echo -e "${RED}bandit found security issues${NC}"
89+
FAILED=1
90+
else
91+
echo -e "${GREEN}bandit passed${NC}"
92+
fi
93+
elif command_exists safety; then
94+
echo -e "${YELLOW}Running safety check on dependencies...${NC}"
95+
if ! safety check --short-report 2>&1; then
96+
echo -e "${RED}safety found vulnerable dependencies${NC}"
97+
FAILED=1
98+
else
99+
echo -e "${GREEN}safety passed${NC}"
100+
fi
101+
else
102+
echo -e "${YELLOW}bandit/safety not installed. Skipping security scan.${NC}"
103+
echo -e "${YELLOW}Install with: pip install bandit${NC}"
104+
echo -e "${YELLOW}Or: pip install safety${NC}"
105+
fi
106+
107+
if [ $FAILED -ne 0 ]; then
108+
echo -e "\n${RED}Pre-commit checks failed. Please fix the issues above.${NC}"
109+
exit 1
110+
fi
111+
112+
echo -e "\n${GREEN}All pre-commit checks passed!${NC}"
113+
exit 0

docs/githooks/pre-push

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#!/bin/bash
2+
3+
# Pre-push hook for TelemetryFlow Python SDK
4+
# Runs tests before pushing to remote
5+
6+
set -e
7+
8+
echo "Running pre-push checks..."
9+
10+
# Colors for output
11+
RED='\033[0;31m'
12+
GREEN='\033[0;32m'
13+
YELLOW='\033[1;33m'
14+
NC='\033[0m' # No Color
15+
16+
REMOTE="$1"
17+
URL="$2"
18+
19+
# Read stdin for push info
20+
while read local_ref local_sha remote_ref remote_sha; do
21+
# Skip delete operations
22+
if [ "$local_sha" = "0000000000000000000000000000000000000000" ]; then
23+
continue
24+
fi
25+
26+
echo -e "${YELLOW}Pushing to: ${REMOTE} (${URL})${NC}"
27+
echo -e "${YELLOW}Branch: ${local_ref}${NC}"
28+
done
29+
30+
# Function to check if a command exists
31+
command_exists() {
32+
command -v "$1" >/dev/null 2>&1
33+
}
34+
35+
FAILED=0
36+
37+
# 1. Run tests
38+
echo -e "\n${YELLOW}[1/2] Running tests...${NC}"
39+
if command_exists pytest; then
40+
if pytest -v --tb=short 2>&1; then
41+
echo -e "${GREEN}All tests passed${NC}"
42+
else
43+
echo -e "${RED}Tests failed${NC}"
44+
FAILED=1
45+
fi
46+
elif command_exists python && python -c "import unittest" 2>/dev/null; then
47+
echo -e "${YELLOW}pytest not found, using unittest...${NC}"
48+
if python -m unittest discover -s tests -v 2>&1; then
49+
echo -e "${GREEN}All tests passed${NC}"
50+
else
51+
echo -e "${RED}Tests failed${NC}"
52+
FAILED=1
53+
fi
54+
else
55+
echo -e "${YELLOW}No test runner found. Skipping tests.${NC}"
56+
echo -e "${YELLOW}Install with: pip install pytest${NC}"
57+
fi
58+
59+
# 2. Check if package can be built/imported
60+
echo -e "\n${YELLOW}[2/2] Checking package import...${NC}"
61+
if command_exists python; then
62+
# Try to import the main package
63+
if python -c "import sys; sys.path.insert(0, 'src'); import telemetryflow" 2>&1; then
64+
echo -e "${GREEN}Package import successful${NC}"
65+
else
66+
echo -e "${RED}Package import failed${NC}"
67+
FAILED=1
68+
fi
69+
else
70+
echo -e "${YELLOW}Python not found. Skipping import check.${NC}"
71+
fi
72+
73+
if [ $FAILED -ne 0 ]; then
74+
echo -e "\n${RED}Pre-push checks failed. Push aborted.${NC}"
75+
echo -e "${YELLOW}Fix the issues above and try again.${NC}"
76+
exit 1
77+
fi
78+
79+
echo -e "\n${GREEN}All pre-push checks passed! Pushing...${NC}"
80+
exit 0

0 commit comments

Comments
 (0)