mirror of
https://github.com/x1xhlol/system-prompts-and-models-of-ai-tools.git
synced 2026-06-17 23:09:35 +00:00
fix: Enhance pre-commit and pre-push hooks with robust checks
Pre-commit: ruff linter, secret detection (API keys + Bearer tokens), Arabic string consistency, affected test runner Pre-push: full test suite, uncommitted migration detection, .env file guard https://claude.ai/code/session_01LsnvBa7HwF5hs99VZbgLGj
This commit is contained in:
parent
9f87af8ea2
commit
2717f2943b
@ -1,23 +1,133 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# Dealix Pre-Commit Hook
|
||||
set -e
|
||||
echo "Dealix Pre-Commit Checks..."
|
||||
# Runs linting, secret detection, Arabic consistency, and affected tests.
|
||||
set -euo pipefail
|
||||
|
||||
# Check for hardcoded secrets
|
||||
if grep -rn "API_KEY\s*=\s*['\"][^'\"]*['\"]" backend/app/ --include="*.py" 2>/dev/null | grep -v config.py | grep -v example; then
|
||||
echo "ERROR: Hardcoded API key found!"
|
||||
exit 1
|
||||
fi
|
||||
ROOT_DIR="$(git rev-parse --show-toplevel)"
|
||||
BACKEND_DIR="${ROOT_DIR}/backend"
|
||||
STAGED_PY_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep '\.py$' || true)
|
||||
|
||||
# Check .env not staged
|
||||
if git diff --cached --name-only | grep -q "\.env$"; then
|
||||
echo "ERROR: .env file staged!"
|
||||
exit 1
|
||||
fi
|
||||
echo "============================================"
|
||||
echo " Dealix Pre-Commit Checks"
|
||||
echo "============================================"
|
||||
|
||||
# Run linter
|
||||
# ── 1. Ruff Linter ───────────────<E29480><E29480><EFBFBD>─────────────
|
||||
echo ""
|
||||
echo "[1/4] Running ruff linter..."
|
||||
|
||||
if [ -n "${STAGED_PY_FILES}" ]; then
|
||||
if command -v ruff &>/dev/null; then
|
||||
ruff check backend/app/ --fix --quiet 2>/dev/null || true
|
||||
LINT_FAILED=0
|
||||
echo "${STAGED_PY_FILES}" | xargs ruff check --select E,W,F,I --no-fix || LINT_FAILED=1
|
||||
if [ ${LINT_FAILED} -eq 1 ]; then
|
||||
echo "FAIL: ruff found issues. Fix them before committing."
|
||||
echo " Run: ruff check --fix backend/"
|
||||
exit 1
|
||||
fi
|
||||
echo "PASS: ruff checks passed."
|
||||
else
|
||||
echo "WARN: ruff not installed. Skipping lint check."
|
||||
echo " Install: pip install ruff"
|
||||
fi
|
||||
else
|
||||
echo "SKIP: No Python files staged."
|
||||
fi
|
||||
|
||||
echo "Pre-commit checks passed."
|
||||
# ── 2. Hardcoded Secrets Detection ─────────────
|
||||
echo ""
|
||||
echo "[2/4] Checking for hardcoded secrets..."
|
||||
|
||||
SECRETS_FOUND=0
|
||||
|
||||
if [ -n "${STAGED_PY_FILES}" ]; then
|
||||
MATCHES=$(echo "${STAGED_PY_FILES}" | xargs grep -n -E "(API_KEY|SECRET_KEY|PASSWORD|PRIVATE_KEY|ACCESS_TOKEN)\s*=\s*['"][^'"]{8,}" 2>/dev/null | grep -v "os\.environ\|get_settings\|config\.\|settings\.\|# example\|# test\|# noqa" || true)
|
||||
|
||||
if [ -n "${MATCHES}" ]; then
|
||||
echo "CRITICAL: Possible hardcoded secrets found:"
|
||||
echo "${MATCHES}"
|
||||
SECRETS_FOUND=1
|
||||
fi
|
||||
|
||||
BEARER_MATCHES=$(echo "${STAGED_PY_FILES}" | xargs grep -n -E "Bearer\s+[A-Za-z0-9_\-]{20,}" 2>/dev/null | grep -v "settings\.\|config\.\|Authorization.*Bearer.*{" || true)
|
||||
|
||||
if [ -n "${BEARER_MATCHES}" ]; then
|
||||
echo "CRITICAL: Possible hardcoded Bearer tokens:"
|
||||
echo "${BEARER_MATCHES}"
|
||||
SECRETS_FOUND=1
|
||||
fi
|
||||
|
||||
if [ ${SECRETS_FOUND} -eq 1 ]; then
|
||||
echo ""
|
||||
echo "FAIL: Remove hardcoded secrets. Use environment variables via get_settings()."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "PASS: No hardcoded secrets detected."
|
||||
else
|
||||
echo "SKIP: No Python files staged."
|
||||
fi
|
||||
|
||||
# ── 3. Arabic String Consistency ────────────────
|
||||
echo ""
|
||||
echo "[3/4] Checking Arabic string consistency..."
|
||||
|
||||
STAGED_FRONTEND_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(tsx?|jsx?)$' || true)
|
||||
|
||||
if [ -n "${STAGED_FRONTEND_FILES}" ]; then
|
||||
UNTRANSLATED=$(echo "${STAGED_FRONTEND_FILES}" | xargs grep -n -E "TODO.*translat|FIXME.*arabic|FIXME.*rtl" 2>/dev/null || true)
|
||||
|
||||
if [ -n "${UNTRANSLATED}" ]; then
|
||||
echo "WARN: Found untranslated string markers:"
|
||||
echo "${UNTRANSLATED}"
|
||||
echo " These should be resolved before release."
|
||||
else
|
||||
echo "PASS: No untranslated string markers found."
|
||||
fi
|
||||
else
|
||||
echo "SKIP: No frontend files staged."
|
||||
fi
|
||||
|
||||
if [ -n "${STAGED_PY_FILES}" ]; then
|
||||
MISSING_ARABIC=$(echo "${STAGED_PY_FILES}" | xargs grep -n -E "detail=\"[A-Z][a-z]|message=\"[A-Z][a-z]" 2>/dev/null | grep -v "# en-only\|# internal\|HTTPException\|logger\." || true)
|
||||
|
||||
if [ -n "${MISSING_ARABIC}" ]; then
|
||||
echo "WARN: Possible English-only user-facing strings in Python:"
|
||||
echo "${MISSING_ARABIC}"
|
||||
echo " Consider adding Arabic translations."
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── 4. Run Affected Tests ──────────────────────
|
||||
echo ""
|
||||
echo "[4/4] Running affected tests..."
|
||||
|
||||
if [ -n "${STAGED_PY_FILES}" ]; then
|
||||
TEST_FILES=""
|
||||
for PY_FILE in ${STAGED_PY_FILES}; do
|
||||
BASENAME=$(basename "${PY_FILE}" .py)
|
||||
FOUND_TESTS=$(find "${BACKEND_DIR}/tests" -name "test_${BASENAME}.py" 2>/dev/null || true)
|
||||
if [ -n "${FOUND_TESTS}" ]; then
|
||||
TEST_FILES="${TEST_FILES} ${FOUND_TESTS}"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "${TEST_FILES}" ]; then
|
||||
echo "Running tests:${TEST_FILES}"
|
||||
cd "${BACKEND_DIR}"
|
||||
if ! pytest ${TEST_FILES} -x -q --tb=short 2>/dev/null; then
|
||||
echo ""
|
||||
echo "FAIL: Some tests failed. Fix them before committing."
|
||||
exit 1
|
||||
fi
|
||||
echo "PASS: All affected tests passed."
|
||||
else
|
||||
echo "SKIP: No matching test files found for staged changes."
|
||||
fi
|
||||
else
|
||||
echo "SKIP: No Python files staged."
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "============================================"
|
||||
echo " All pre-commit checks passed"
|
||||
echo "============================================"
|
||||
|
||||
@ -1,13 +1,91 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# Dealix Pre-Push Hook
|
||||
set -e
|
||||
echo "Dealix Pre-Push Checks..."
|
||||
# Runs full test suite, checks migrations, and verifies no .env files are staged.
|
||||
set -euo pipefail
|
||||
|
||||
# Run tests
|
||||
cd backend && python -m pytest -x -q --tb=short 2>/dev/null || {
|
||||
echo "ERROR: Tests failed!"
|
||||
ROOT_DIR="$(git rev-parse --show-toplevel)"
|
||||
BACKEND_DIR="${ROOT_DIR}/backend"
|
||||
|
||||
echo "============================================"
|
||||
echo " Dealix Pre-Push Checks"
|
||||
echo "============================================"
|
||||
|
||||
# ── 1. Full Test Suite ──────────────────────────
|
||||
echo ""
|
||||
echo "[1/3] Running full test suite..."
|
||||
|
||||
if [ -d "${BACKEND_DIR}/tests" ]; then
|
||||
cd "${BACKEND_DIR}"
|
||||
if ! pytest -x -q --tb=short 2>&1; then
|
||||
echo ""
|
||||
echo "FAIL: Test suite failed. Fix all tests before pushing."
|
||||
exit 1
|
||||
}
|
||||
cd ..
|
||||
fi
|
||||
echo "PASS: Full test suite passed."
|
||||
cd "${ROOT_DIR}"
|
||||
else
|
||||
echo "WARN: No tests directory found at ${BACKEND_DIR}/tests"
|
||||
fi
|
||||
|
||||
echo "Pre-push checks passed."
|
||||
# ── 2. Uncommitted Migrations ──────────────────
|
||||
echo ""
|
||||
echo "[2/3] Checking for uncommitted migrations..."
|
||||
|
||||
UNTRACKED_MIGRATIONS=$(git ls-files --others --exclude-standard "${BACKEND_DIR}/alembic/versions/" 2>/dev/null | grep '\.py$' || true)
|
||||
MODIFIED_MIGRATIONS=$(git diff --name-only "${BACKEND_DIR}/alembic/versions/" 2>/dev/null | grep '\.py$' || true)
|
||||
|
||||
if [ -n "${UNTRACKED_MIGRATIONS}" ]; then
|
||||
echo "FAIL: Found untracked migration files:"
|
||||
echo "${UNTRACKED_MIGRATIONS}"
|
||||
echo " Commit these migrations before pushing."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -n "${MODIFIED_MIGRATIONS}" ]; then
|
||||
echo "WARN: Found modified but uncommitted migration files:"
|
||||
echo "${MODIFIED_MIGRATIONS}"
|
||||
echo " Consider committing these changes."
|
||||
fi
|
||||
|
||||
MODEL_CHANGES=$(git diff HEAD --name-only "${BACKEND_DIR}/app/models/" 2>/dev/null | grep '\.py$' || true)
|
||||
if [ -n "${MODEL_CHANGES}" ]; then
|
||||
MIGRATION_CHANGES=$(git diff HEAD --name-only "${BACKEND_DIR}/alembic/versions/" 2>/dev/null | grep '\.py$' || true)
|
||||
if [ -z "${MIGRATION_CHANGES}" ]; then
|
||||
echo "WARN: Model files changed but no new migrations detected:"
|
||||
echo "${MODEL_CHANGES}"
|
||||
echo " Run: cd backend && alembic revision --autogenerate -m 'description'"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "PASS: Migration check complete."
|
||||
|
||||
# ── 3. No .env Files ───────────────────────────
|
||||
echo ""
|
||||
echo "[3/3] Verifying no .env files are being pushed..."
|
||||
|
||||
TRACKED_ENV=$(git ls-files | grep -E '\.env$|\.env\.local$|\.env\.production$' | grep -v '\.env\.example$' || true)
|
||||
if [ -n "${TRACKED_ENV}" ]; then
|
||||
echo "CRITICAL: .env files are tracked in the repository:"
|
||||
echo "${TRACKED_ENV}"
|
||||
echo ""
|
||||
echo "FAIL: Never push .env files to the repository."
|
||||
echo " Remove them: git rm --cached <file>"
|
||||
echo " Add to .gitignore if missing"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
STAGED_ENV=$(git diff --cached --name-only | grep -E '\.env$|\.env\.' | grep -v '\.env\.example$' || true)
|
||||
if [ -n "${STAGED_ENV}" ]; then
|
||||
echo "CRITICAL: .env files staged for commit:"
|
||||
echo "${STAGED_ENV}"
|
||||
echo ""
|
||||
echo "FAIL: Unstage .env files: git reset HEAD <file>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "PASS: No .env files in push."
|
||||
|
||||
echo ""
|
||||
echo "============================================"
|
||||
echo " All pre-push checks passed"
|
||||
echo "============================================"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user