-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpre_commit_check.sh
More file actions
executable file
·358 lines (313 loc) · 11.5 KB
/
pre_commit_check.sh
File metadata and controls
executable file
·358 lines (313 loc) · 11.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
#!/bin/bash
# Pre-commit validation script
# Run this before making any commits to ensure code quality
# This script mirrors the CI pipeline checks to catch issues early
echo "🔍 Running comprehensive pre-commit checks..."
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Track if any checks fail
CHECKS_PASSED=true
AUTO_FIXES_APPLIED=false
# Function to print colored output
print_status() {
local status=$1
local message=$2
if [ "$status" = "pass" ]; then
echo -e "${GREEN}✅ $message${NC}"
elif [ "$status" = "fail" ]; then
echo -e "${RED}❌ $message${NC}"
CHECKS_PASSED=false
elif [ "$status" = "warn" ]; then
echo -e "${YELLOW}⚠️ $message${NC}"
elif [ "$status" = "info" ]; then
echo -e "${BLUE}ℹ️ $message${NC}"
elif [ "$status" = "fix" ]; then
echo -e "${YELLOW}🔧 $message${NC}"
AUTO_FIXES_APPLIED=true
fi
}
# Function to check if a command exists
check_command() {
if ! command -v "$1" &> /dev/null; then
print_status "fail" "$1 is not installed. Run: pip install $1"
return 1
fi
return 0
}
# Function to ask for user confirmation
ask_user() {
local message=$1
echo -e "${YELLOW}❓ $message (y/N): ${NC}"
read -r response
case "$response" in
[yY][eE][sS]|[yY])
return 0
;;
*)
return 1
;;
esac
}
echo "📋 Step 1: Checking required tools..."
# Check if required tools are installed
REQUIRED_TOOLS=("black" "isort" "flake8" "bandit" "safety")
for tool in "${REQUIRED_TOOLS[@]}"; do
if check_command "$tool"; then
print_status "pass" "$tool is installed"
fi
done
# Install missing tools if any
if [ "$CHECKS_PASSED" = false ]; then
echo ""
if ask_user "Install missing tools automatically?"; then
print_status "fix" "Installing missing tools..."
pip install black isort flake8 bandit safety autopep8
echo ""
CHECKS_PASSED=true # Reset for the rest of the checks
else
print_status "info" "Please install missing tools manually: pip install black isort flake8 bandit safety autopep8"
echo ""
fi
fi
echo ""
echo "🏗️ Step 2: Validating project structure..."
# Check core files exist
REQUIRED_FILES=(
"upload_files.py"
"alttext_ai.py"
"setup.py"
"requirements.txt"
"process_csv.sh"
".env.example"
"README.md"
"CONTRIBUTING.md"
"LICENSE"
"PROJECT_RULES.md"
)
for file in "${REQUIRED_FILES[@]}"; do
if [ -f "$file" ]; then
print_status "pass" "$file exists"
else
print_status "fail" "Required file $file is missing"
fi
done
# Check directories exist and create if missing
if [ -d "data" ]; then
print_status "pass" "data directory exists"
else
print_status "fix" "Creating data directory..."
mkdir -p data
fi
# Check data subdirectories exist and create if missing
DATA_DIRS=("input" "output" "local_images" "examples")
for dir in "${DATA_DIRS[@]}"; do
if [ -d "data/$dir" ]; then
print_status "pass" "data/$dir directory exists"
else
print_status "fix" "Creating data/$dir directory..."
mkdir -p "data/$dir"
# Add .gitkeep files only if directory is empty (to ensure directories are tracked)
if [ -z "$(ls -A "data/$dir" 2>/dev/null)" ]; then
touch "data/$dir/.gitkeep"
fi
fi
done
# Check shell script is executable and fix if needed
if [ -x "process_csv.sh" ]; then
print_status "pass" "process_csv.sh is executable"
else
print_status "fix" "Making process_csv.sh executable..."
chmod +x process_csv.sh
fi
echo ""
echo "📝 Step 3: Code formatting..."
print_status "info" "Formatting code with black..."
if black .; then
print_status "pass" "Code formatting completed"
else
print_status "fail" "Black formatting failed"
fi
print_status "info" "Sorting imports with isort..."
if isort .; then
print_status "pass" "Import sorting completed"
else
print_status "fail" "Import sorting failed"
fi
echo ""
echo "🔍 Step 4: Code quality checks..."
print_status "info" "Running flake8 linting..."
if flake8 --exclude=venv --max-line-length=127 --extend-ignore=E203,W503 .; then
print_status "pass" "All linting checks passed"
else
print_status "warn" "Linting errors found. Attempting to auto-fix..."
# Try to auto-fix with autopep8
if command -v autopep8 &> /dev/null; then
print_status "fix" "Auto-fixing with autopep8..."
autopep8 --in-place --aggressive --aggressive --max-line-length=127 *.py
# Re-run flake8 to check if issues are resolved
if flake8 --exclude=venv --max-line-length=127 --extend-ignore=E203,W503 .; then
print_status "pass" "Auto-fix successful - all linting checks now pass"
else
print_status "fail" "Some linting errors could not be auto-fixed. Manual review needed."
fi
else
if ask_user "Install autopep8 to auto-fix linting issues?"; then
pip install autopep8
print_status "fix" "Auto-fixing with autopep8..."
autopep8 --in-place --aggressive --aggressive --max-line-length=127 *.py
# Re-run flake8 to check if issues are resolved
if flake8 --exclude=venv --max-line-length=127 --extend-ignore=E203,W503 .; then
print_status "pass" "Auto-fix successful - all linting checks now pass"
else
print_status "fail" "Some linting errors could not be auto-fixed. Manual review needed."
fi
else
print_status "fail" "Linting errors found and auto-fix declined"
fi
fi
fi
echo ""
echo "🔒 Step 5: Security checks..."
print_status "info" "Running bandit security check..."
if bandit -r . -x ./tests/,./venv/ --severity-level medium --quiet; then
print_status "pass" "No security issues found by bandit"
else
print_status "fail" "Security issues found by bandit"
print_status "info" "💡 Run 'bandit -r . -x ./tests/,./venv/' for detailed security report"
fi
print_status "info" "Checking for known vulnerabilities with safety..."
if safety scan --short-report --output text 2>/dev/null; then
print_status "pass" "No known vulnerabilities found"
else
print_status "warn" "Known vulnerabilities found in dependencies"
if ask_user "Would you like to see vulnerable packages and suggested fixes?"; then
echo ""
print_status "info" "Vulnerability details:"
safety scan --output text
echo ""
if ask_user "Attempt to update vulnerable packages automatically?"; then
print_status "fix" "Updating vulnerable packages..."
# Get list of vulnerable packages and try to update them
vulnerable_packages=$(safety scan --output json 2>/dev/null | python3 -c "
import json, sys
try:
data = json.load(sys.stdin)
if 'vulnerabilities' in data:
packages = set()
for vuln in data['vulnerabilities']:
if 'package_name' in vuln:
packages.add(vuln['package_name'])
print(' '.join(packages))
except:
pass
" 2>/dev/null)
if [ -n "$vulnerable_packages" ]; then
print_status "fix" "Updating packages: $vulnerable_packages"
pip install --upgrade $vulnerable_packages
print_status "info" "Re-running security scan..."
if safety scan --short-report --output text 2>/dev/null; then
print_status "pass" "Vulnerabilities resolved!"
else
print_status "warn" "Some vulnerabilities may still remain. Manual review recommended."
fi
else
print_status "info" "Could not determine specific packages to update"
fi
fi
fi
fi
echo ""
echo "🧪 Step 6: Import and functionality tests..."
print_status "info" "Testing Python imports..."
if python -c "import upload_files; print('upload_files imports successfully')" 2>/dev/null; then
print_status "pass" "upload_files imports successfully"
else
print_status "fail" "upload_files import failed"
fi
if python -c "import alttext_ai; print('alttext_ai imports successfully')" 2>/dev/null; then
print_status "pass" "alttext_ai imports successfully"
else
print_status "fail" "alttext_ai import failed"
fi
print_status "info" "Testing core functionality..."
if python -c "from upload_files import optimize_image, get_best_format; from alttext_ai import AltTextAI; print('Core functions available')" 2>/dev/null; then
print_status "pass" "Core functions import successfully"
else
print_status "fail" "Core function imports failed"
fi
echo ""
echo "📚 Step 7: Documentation checks..."
# Check README has basic sections
if grep -q "## 🚀 Features" README.md 2>/dev/null; then
print_status "pass" "README has Features section"
else
print_status "fail" "README missing Features section"
fi
if grep -q "## 🛠 Installation" README.md 2>/dev/null; then
print_status "pass" "README has Installation section"
else
print_status "fail" "README missing Installation section"
fi
# Check .env.example has required variables
REQUIRED_ENV_VARS=(
"ALTTEXT_AI_API_KEY"
"ALTTEXT_AI_KEYWORDS"
"ALTTEXT_AI_WEBHOOK_URL"
)
for var in "${REQUIRED_ENV_VARS[@]}"; do
if grep -q "$var" .env.example 2>/dev/null; then
print_status "pass" "$var found in .env.example"
else
print_status "fail" "$var missing from .env.example"
fi
done
echo ""
echo "📄 Step 8: Git status check..."
# Check if there are any uncommitted changes after formatting
if [[ -n $(git status --porcelain) ]]; then
print_status "warn" "Code formatting/fixes created changes. Please review:"
git status --short
echo ""
if [ "$AUTO_FIXES_APPLIED" = true ]; then
print_status "info" "🔧 Auto-fixes were applied during this run"
if ask_user "Commit the auto-fixes automatically?"; then
git add .
git commit -m "style: apply automated pre-commit fixes
- Auto-format code with black and isort
- Fix linting issues with autopep8
- Create missing directories and .gitkeep files
- Fix file permissions"
print_status "fix" "Auto-fixes committed successfully"
else
print_status "info" "💡 Tip: Run 'git add . && git commit -m \"style: apply automated fixes\"'"
fi
else
print_status "info" "💡 Tip: Run 'git add . && git commit -m \"style: auto-format code\"'"
fi
else
print_status "pass" "No formatting changes needed"
fi
echo ""
echo "============================================================"
# Final status
if [ "$CHECKS_PASSED" = true ]; then
print_status "pass" "🎉 All pre-commit checks passed! Code is ready for commit."
echo ""
if [ "$AUTO_FIXES_APPLIED" = true ]; then
print_status "info" "🔧 Auto-fixes were applied to resolve issues automatically."
fi
print_status "info" "Your code meets all CI pipeline requirements and should pass all GitHub Actions checks."
exit 0
else
print_status "fail" "❌ Some pre-commit checks failed. Please fix the remaining issues above."
echo ""
if [ "$AUTO_FIXES_APPLIED" = true ]; then
print_status "info" "🔧 Some issues were auto-fixed, but manual intervention is needed for the rest."
fi
print_status "info" "💡 These are the same checks that run in the CI pipeline - fixing them now will save time later."
exit 1
fi