mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-07-04 16:43:05 +02:00
- Move fresh-install/ → scripts/single_use/fresh-install/ - Move hosts/ → scripts/periodic_background/hosts/ - Move i3-configuration/ → scripts/periodic_background/i3-configuration/ - Delete linux_configuration/LaTeX/, nix-poc/, report/ (dead dirs) - Move repo-root scripts/ → meta/scripts/ - Update root .pre-commit-config.yaml: scripts/ → meta/scripts/ (9 entries) - Update run.sh ARTIFACT_INIT_SCRIPT to meta/scripts/ - Update fresh-install/main.sh: hosts/install.sh + i3-configuration/install.sh paths - Update check_python_location.sh: add meta/scripts/ to exception list - Fix midnight flakiness in test_recent_workout_returns_true: use timezone-aware local noon instead of now-1h to avoid SQL date() boundary issues
135 lines
4.4 KiB
Bash
Executable File
135 lines
4.4 KiB
Bash
Executable File
#!/bin/bash
|
||
# Pre-commit hook to detect polling script anti-patterns.
|
||
# Blocks commits of shell scripts that violate efficient polling best practices.
|
||
#
|
||
# Usage: check_polling_antipatterns.sh [files...]
|
||
# Exit codes: 0 = no issues, 1 = issues found
|
||
|
||
set -euo pipefail
|
||
|
||
errors=0
|
||
files_checked=0
|
||
|
||
# Patterns to detect (these are red flags for fork storms)
|
||
# shellcheck disable=SC2034
|
||
declare -A patterns=(
|
||
['while_true_with_sleep']='while\s+(true|:).*sleep'
|
||
['date_in_loop']='while.*date|for.*date'
|
||
['pgrep_in_loop']='while.*pgrep|for.*pgrep'
|
||
['xdotool_in_loop']='while.*xdotool|for.*xdotool'
|
||
['pipe_fork_chain']='|.*|.*|.*|'
|
||
['subshell_date']='$\(date\s+\+'
|
||
['backtick_date']='`date\s+\+'
|
||
['excessive_fork_chain']='(\$\(.*\|.*\|.*\)|`.*\|.*\|`)'
|
||
['sleep_less_than_one']='sleep\s+0\.[0-9]'
|
||
)
|
||
|
||
usage() {
|
||
echo "Usage: $(basename "$0") [files...]"
|
||
echo ""
|
||
echo "Checks shell scripts for polling anti-patterns that cause fork storms."
|
||
echo "Exit code 0 = no issues, 1 = issues found"
|
||
echo ""
|
||
echo "Patterns detected:"
|
||
echo " - while true + sleep (should use event-driven I/O)"
|
||
echo " - \$(date +...) in loops (forks process)"
|
||
echo " - pgrep/xdotool in loops (forks process)"
|
||
echo " - Multiple piped commands (each | forks)"
|
||
echo " - Aggressive polling (sleep < 1s)"
|
||
exit 0
|
||
}
|
||
|
||
check_file() {
|
||
local file="$1"
|
||
|
||
# Skip non-shell files
|
||
if ! grep -qE '^#!.*/(ba)?sh' "$file" 2>/dev/null; then
|
||
return 0
|
||
fi
|
||
|
||
files_checked=$((files_checked + 1))
|
||
|
||
# Single-pass awk analysis: C code, no bash loops, no per-line subshell forks.
|
||
# '\'' embeds a literal single quote inside a bash single-quoted string.
|
||
local findings
|
||
findings=$(awk '
|
||
/^[[:space:]]*([a-zA-Z0-9_]*_(loop|daemon)|poll[a-zA-Z0-9_]*)[[:space:]]*\(\)/ { in_poll=1 }
|
||
/^[[:space:]]*\}/ { in_poll=0 }
|
||
in_poll {
|
||
if (/while[[:space:]]+(true|:)/ && /sleep/)
|
||
print NR ": while true/: + sleep (use event-driven I/O)"
|
||
if (/\$\(date[[:space:]]/ || /`date[[:space:]]/)
|
||
print NR ": date fork in polling function"
|
||
if (/[^_a-zA-Z0-9]pgrep/ || /^[[:space:]]*pgrep/)
|
||
print NR ": pgrep in polling context"
|
||
if (/[^_a-zA-Z0-9]xdotool/ || /^[[:space:]]*xdotool/)
|
||
print NR ": xdotool in polling context"
|
||
if (/sleep[[:space:]]+0\.[0-9]/)
|
||
print NR ": Aggressive polling (sleep < 1s)"
|
||
}
|
||
!/^[[:space:]]*#/ && !/=/ && !/\)[[:space:]]*(#.*)?$/ && !/;;/ {
|
||
line = $0
|
||
gsub(/\|\|/, "", line)
|
||
while (match(line, /'\''[^'\'']*'\''/)) line = substr(line, 1, RSTART-1) substr(line, RSTART+RLENGTH)
|
||
while (match(line, /"[^"]*"/)) line = substr(line, 1, RSTART-1) substr(line, RSTART+RLENGTH)
|
||
n = gsub(/\|/, "", line)
|
||
if (n > 3) print NR ": Excessive pipes (" n " pipes = many forks)"
|
||
}
|
||
' "$file")
|
||
|
||
if [[ -n "$findings" ]]; then
|
||
echo " $file:"
|
||
while IFS= read -r finding; do
|
||
echo " Line $finding"
|
||
done <<< "$findings"
|
||
errors=$((errors + 1))
|
||
return 1
|
||
fi
|
||
return 0
|
||
}
|
||
provide_suggestions() {
|
||
echo ""
|
||
echo "📋 Optimization Tips:"
|
||
echo ""
|
||
echo "Replace polling loops with:"
|
||
echo " • inotifywait/fanotify for file system events"
|
||
echo " • timerfd for interval-based tasks"
|
||
echo " • select/poll for I/O multiplexing"
|
||
echo " • systemd timers for scheduled tasks"
|
||
echo " • dbus signals for system events"
|
||
echo ""
|
||
echo "Reduce fork overhead:"
|
||
echo " • Cache \$(date +%s) in variable, update periodically"
|
||
echo " • Use /proc filesystem instead of pgrep"
|
||
echo " • Consolidate commands with && instead of separate invocations"
|
||
echo ""
|
||
}
|
||
|
||
main() {
|
||
# Show usage if requested
|
||
[[ "$#" -eq 0 || "$1" == "-h" || "$1" == "--help" ]] && usage
|
||
|
||
# Check all provided files
|
||
for file in "$@"; do
|
||
check_file "$file" || true
|
||
done
|
||
|
||
# Report results
|
||
if [[ $files_checked -eq 0 ]]; then
|
||
echo "ℹ️ No shell scripts to check"
|
||
exit 0
|
||
fi
|
||
|
||
if [[ $errors -gt 0 ]]; then
|
||
echo ""
|
||
echo "❌ Found $errors issue(s) in shell scripts"
|
||
provide_suggestions
|
||
exit 1
|
||
else
|
||
echo "✓ No polling anti-patterns detected in $files_checked file(s)"
|
||
exit 0
|
||
fi
|
||
}
|
||
|
||
main "$@"
|