Repository navigation
Expand file tree
/
Copy pathvalidate-patches.sh
More file actions
executable file
·245 lines (198 loc) · 7.56 KB
/
Copy pathvalidate-patches.sh
File metadata and controls
executable file
·245 lines (198 loc) · 7.56 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
#!/bin/bash
#
# GameHub Lite Patch Validator
# Validates patch files for common issues before committing
#
# Usage: ./validate-patches.sh
#
# Optional dependency: pip install unidiff (for syntax validation)
#
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PATCHES_DIR="$SCRIPT_DIR/patches"
errors=0
warnings=0
print_header() {
echo ""
echo "====================================="
echo " GameHub Lite Patch Validator"
echo "====================================="
echo ""
}
print_check() {
echo -e "${BLUE}==>${NC} $1"
}
print_ok() {
echo -e " ${GREEN}[OK]${NC} $1"
}
print_warn() {
echo -e " ${YELLOW}[WARN]${NC} $1"
warnings=$((warnings + 1))
}
print_err() {
echo -e " ${RED}[ERROR]${NC} $1"
errors=$((errors + 1))
}
# ── Check 1: Patch file encoding ────────────────────────────────────────────
check_encoding() {
print_check "Checking patch file encodings..."
local count=0
while IFS= read -r -d '' patch_file; do
local encoding
encoding=$(file -b --mime-encoding "$patch_file")
if [[ "$encoding" != "us-ascii" && "$encoding" != "utf-8" ]]; then
print_err "$patch_file is encoded as $encoding (must be UTF-8 or ASCII)"
fi
# Check for UTF-8 BOM
if [ "$(head -c 3 "$patch_file" | xxd -p)" = "efbbbf" ]; then
print_err "$patch_file contains UTF-8 BOM (byte order mark)"
fi
# Check for CRLF line endings
if grep -qP '\r\n' "$patch_file" 2>/dev/null; then
print_warn "$patch_file has CRLF line endings (Windows) - may cause patch failures"
fi
count=$((count + 1))
done < <(find "$PATCHES_DIR/diffs" -name '*.patch' -print0)
if [ $count -eq 0 ]; then
print_warn "No patch files found"
else
print_ok "Checked $count patch files"
fi
}
# ── Check 2: Trailing newlines on manifest files ────────────────────────────
check_trailing_newlines() {
print_check "Checking trailing newlines on manifest files..."
for f in "$PATCHES_DIR"/files_to_patch.txt "$PATCHES_DIR"/files_to_add.txt "$PATCHES_DIR"/files_to_delete.txt; do
if [ -f "$f" ] && [ -s "$f" ]; then
if [ "$(tail -c 1 "$f" | wc -l)" -eq 0 ]; then
print_err "$(basename "$f") is missing a trailing newline - last entry may be silently skipped"
else
print_ok "$(basename "$f")"
fi
fi
done
}
# ── Check 3: Orphaned patches ───────────────────────────────────────────────
check_orphaned_patches() {
print_check "Checking for orphaned patches..."
local orphaned=0
local missing=0
# Patch files not listed in files_to_patch.txt
while IFS= read -r -d '' patch_file; do
local rel_path="${patch_file#$PATCHES_DIR/diffs/}"
local target_file="${rel_path%.patch}"
if ! grep -qxF "$target_file" "$PATCHES_DIR/files_to_patch.txt"; then
print_err "Orphaned patch: $rel_path (not listed in files_to_patch.txt)"
orphaned=$((orphaned + 1))
fi
done < <(find "$PATCHES_DIR/diffs" -name '*.patch' -print0)
# Entries in files_to_patch.txt without a matching patch or binary replacement
while IFS= read -r target_file; do
[ -z "$target_file" ] && continue
local patch_path="$PATCHES_DIR/diffs/${target_file}.patch"
local binary_path="$PATCHES_DIR/binary_replacements/${target_file}"
if [ ! -f "$patch_path" ] && [ ! -f "$binary_path" ]; then
print_err "Missing patch for: $target_file (listed in files_to_patch.txt but no .patch file exists)"
missing=$((missing + 1))
fi
done < "$PATCHES_DIR/files_to_patch.txt"
if [ $orphaned -eq 0 ] && [ $missing -eq 0 ]; then
print_ok "All patches are properly tracked"
fi
}
# ── Check 4: Patch syntax validation (requires python3 + unidiff) ───────────
check_patch_syntax() {
print_check "Validating patch syntax..."
if ! python3 -c "import unidiff" 2>/dev/null; then
print_warn "Skipping syntax validation - install with: pip install unidiff"
return
fi
python3 << 'PYEOF'
import os, sys
patches_dir = os.environ.get('PATCHES_DIR', 'patches')
diffs_dir = os.path.join(patches_dir, 'diffs')
from unidiff import PatchSet
from unidiff.errors import UnidiffParseError
errs = 0
checked = 0
for root, dirs, files in os.walk(diffs_dir):
for fname in sorted(files):
if not fname.endswith('.patch'):
continue
fpath = os.path.join(root, fname)
checked += 1
try:
patch = PatchSet.from_filename(fpath, encoding='utf-8')
if len(patch) == 0:
print(f' \033[1;33m[WARN]\033[0m {fpath}: empty patch (no hunks)')
except UnidiffParseError as e:
print(f' \033[0;31m[ERROR]\033[0m {fpath}: invalid syntax: {e}')
errs += 1
except UnicodeDecodeError as e:
print(f' \033[0;31m[ERROR]\033[0m {fpath}: encoding error: {e}')
errs += 1
if errs == 0:
print(f' \033[0;32m[OK]\033[0m All {checked} patches have valid unified diff syntax')
else:
print(f' \033[0;31m[ERROR]\033[0m {errs} of {checked} patches have invalid syntax')
sys.exit(errs)
PYEOF
local exit_code=$?
errors=$((errors + exit_code))
}
# ── Check 5: lsdiff structural validation ───────────────────────────────────
check_patch_structure() {
print_check "Validating patch structure..."
if ! command -v lsdiff &>/dev/null; then
print_warn "Skipping structural validation - install patchutils (brew install patchutils / apt install patchutils)"
return
fi
local checked=0
local failed=0
while IFS= read -r -d '' patch_file; do
if ! lsdiff "$patch_file" > /dev/null 2>&1; then
print_err "lsdiff cannot parse: ${patch_file#$PATCHES_DIR/}"
failed=$((failed + 1))
fi
checked=$((checked + 1))
done < <(find "$PATCHES_DIR/diffs" -name '*.patch' -print0)
if [ $failed -eq 0 ]; then
print_ok "All $checked patches pass structural validation"
fi
}
# ── Main ─────────────────────────────────────────────────────────────────────
main() {
print_header
if [ ! -d "$PATCHES_DIR/diffs" ]; then
echo -e "${RED}No patches directory found at: $PATCHES_DIR/diffs${NC}"
exit 1
fi
export PATCHES_DIR
check_encoding
check_trailing_newlines
check_orphaned_patches
check_patch_syntax
check_patch_structure
echo ""
echo "====================================="
if [ $errors -gt 0 ]; then
echo -e " ${RED}FAILED: $errors error(s), $warnings warning(s)${NC}"
echo "====================================="
exit 1
elif [ $warnings -gt 0 ]; then
echo -e " ${YELLOW}PASSED with $warnings warning(s)${NC}"
echo "====================================="
exit 0
else
echo -e " ${GREEN}All checks passed${NC}"
echo "====================================="
exit 0
fi
}
main "$@"