mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-07-04 15:03:01 +02:00
- Move 7 loose top-level Markdown reports under docs/cleanup-2026-05/. - Relocate batch3_bloatware_uninstall.sh into phone_focus_mode/ where its ADB/phone wiring belongs. - Delete tracked out.json (empty puzzle_solver fixture). - Remove untracked clutter (mp4/wav/lcov/log/txt) from the working tree.
289 lines
7.6 KiB
Markdown
289 lines
7.6 KiB
Markdown
# Shell Script Quality & Efficiency Guidelines
|
||
|
||
## Overview
|
||
|
||
This repository uses **three layers of shell script quality control**:
|
||
|
||
1. **shellcheck** - Syntax and common errors (pre-commit)
|
||
2. **polling antipatterns detector** - Fork-storm prevention (pre-commit, NEW)
|
||
3. **shell.instructions** - Best practices (in-editor, via Copilot)
|
||
|
||
## What Changed
|
||
|
||
### New: Polling Antipatterns Pre-commit Hook
|
||
|
||
**File**: `scripts/check_polling_antipatterns.sh`
|
||
**Hook ID**: `no-polling-antipatterns`
|
||
**When**: Automatically runs on `.sh` files during `pre-commit run` or `git commit`
|
||
|
||
The hook detects and **blocks commits** of shell scripts with these anti-patterns:
|
||
|
||
| Anti-pattern | Why bad | Detector |
|
||
| --------------------------------------- | ------------------ | -------------------- |
|
||
| `while true; do [check]; sleep 1; done` | 60k forks/hour | Loop + sleep pattern |
|
||
| `$(date +...)` in monitoring | 10ms fork per call | Subprocess date |
|
||
| `pgrep/xdotool` in polling | 5ms fork per call | Process inspection |
|
||
| `\| awk \| grep \| tr` chains | Fork per pipe | Heavy piping |
|
||
| `sleep 0.5` aggressive | Fork storm | Sub-second polling |
|
||
|
||
### Updated: Shell Instructions
|
||
|
||
**File**: `/home/kuhy/.copilot/instructions/shell.instructions.md`
|
||
**New section**: "⚡ Efficient Polling & Monitoring Scripts"
|
||
|
||
Explains the **R1-R8 rules** for writing zero-fork polling scripts:
|
||
|
||
- R1: Zero forks in hot path
|
||
- R2: Use /proc and /sys directly
|
||
- R3: Event-driven over polling
|
||
- R4: i3blocks `interval=persist`
|
||
- R5: Increase polling intervals
|
||
- R6: Cache expensive values
|
||
- R7: Profile before deployment
|
||
- R8: Recognize fork-storm signatures
|
||
|
||
## Usage
|
||
|
||
### For Developers
|
||
|
||
#### 1. Write compliant polling scripts
|
||
|
||
Follow the patterns in `.copilot/instructions/shell.instructions.md`:
|
||
|
||
```bash
|
||
#!/bin/bash
|
||
# ✅ Zero-fork polling script example
|
||
|
||
set -u
|
||
|
||
emit() {
|
||
printf ' %s\n' "$1"
|
||
}
|
||
|
||
# Read from /proc directly (no fork)
|
||
read -r uptime_s _ < /proc/uptime
|
||
current_time=${uptime_s%%.*}
|
||
|
||
# Event-driven if possible, else increase interval
|
||
emit "Time: $current_time"
|
||
```
|
||
|
||
#### 2. Pre-commit runs automatically
|
||
|
||
```bash
|
||
# Commits that violate anti-patterns are blocked:
|
||
git commit -m "Add new polling script"
|
||
# ❌ BLOCKED if script violates rules
|
||
|
||
# Fix the script:
|
||
# - Replace $(date) with /proc reads
|
||
# - Replace while true + sleep with event-driven I/O
|
||
# - Remove aggressive sleep intervals
|
||
# - Reduce piped commands
|
||
|
||
git commit -m "Add new polling script"
|
||
# ✅ PASSES
|
||
|
||
# Or run manually:
|
||
pre-commit run no-polling-antipatterns --files my_script.sh
|
||
```
|
||
|
||
#### 3. Use diagnostic tools
|
||
|
||
```bash
|
||
cd /home/kuhy/testsAndMisc
|
||
|
||
# Find all polling anti-patterns in repo
|
||
./run.sh --diagnose
|
||
|
||
# Profile for 30s to find active fork storms
|
||
./run.sh --profile 30
|
||
|
||
# Generate resource report
|
||
./run.sh
|
||
```
|
||
|
||
### For Code Reviewers
|
||
|
||
When reviewing shell scripts:
|
||
|
||
1. **Check if hook ran**: Pre-commit output should show `no-polling-antipatterns` passed
|
||
2. **Look for**:
|
||
- `while true` + `sleep` → suggest event-driven
|
||
- `$(date ...)` → suggest `/proc/uptime`
|
||
- Multiple pipes → suggest bash builtins
|
||
- `pgrep` in loops → suggest caching
|
||
3. **Reference**: Point to shell.instructions section "⚡ Efficient Polling & Monitoring Scripts"
|
||
|
||
## Examples
|
||
|
||
### Example 1: Polling Loop ❌ → ✅
|
||
|
||
```bash
|
||
# ❌ FAILS pre-commit check
|
||
#!/bin/bash
|
||
while true; do
|
||
now=$(date +%s)
|
||
echo "Current: $now"
|
||
sleep 1
|
||
done
|
||
|
||
# ✅ PASSES - uses /proc/uptime, adaptive sleep
|
||
#!/bin/bash
|
||
emit() { printf ' %s\n' "$1"; }
|
||
|
||
emit "$(initial_value)"
|
||
while true; do
|
||
read -r uptime_s _ < /proc/uptime
|
||
emit "Current: ${uptime_s%%.*}"
|
||
|
||
if is_active; then
|
||
sleep 0.5
|
||
else
|
||
sleep 3 # Adaptive polling
|
||
fi
|
||
done
|
||
```
|
||
|
||
### Example 2: i3blocks Status Script ❌ → ✅
|
||
|
||
```bash
|
||
# ❌ INEFFICIENT - forked every 5 seconds
|
||
#!/bin/bash
|
||
# battery.sh
|
||
cap=$(cat /sys/class/power_supply/BAT0/capacity)
|
||
echo " $cap%"
|
||
|
||
# i3blocks config:
|
||
# [battery]
|
||
# interval=5
|
||
# Result: 720 checks/hour × 1 fork = 720 forks/hour
|
||
|
||
# ✅ OPTIMIZED - zero fork when idle
|
||
#!/bin/bash
|
||
# battery.sh
|
||
set -u
|
||
emit() { printf ' %s%%\n' "$1"; }
|
||
|
||
read -r cap < /sys/class/power_supply/BAT0/capacity
|
||
emit "$cap"
|
||
|
||
# Watch for power supply changes (blocks when idle)
|
||
udevadm monitor --udev --property --subsystem-match=power_supply |
|
||
while IFS='=' read -r key value || true; do
|
||
[[ $key == POWER_SUPPLY_CAPACITY ]] || continue
|
||
read -r cap < /sys/class/power_supply/BAT0/capacity
|
||
emit "$cap"
|
||
done
|
||
|
||
# i3blocks config:
|
||
# [battery]
|
||
# interval=persist
|
||
# Result: Zero CPU when plugged in, one fork per cable plug/unplug
|
||
```
|
||
|
||
### Example 3: Process Monitoring ❌ → ✅
|
||
|
||
```bash
|
||
# ❌ FAILS - pgrep in loop = fork per second × N processes
|
||
while true; do
|
||
if pgrep -f "python" > /dev/null; then
|
||
echo "Python running"
|
||
fi
|
||
sleep 1
|
||
done
|
||
|
||
# ✅ PASSES - adaptive polling with cached check
|
||
focus_running=0
|
||
while true; do
|
||
if is_focus_app_running; then
|
||
focus_running=1
|
||
else
|
||
if ((focus_running)); then
|
||
echo "Focus ended"
|
||
focus_running=0
|
||
fi
|
||
fi
|
||
|
||
if ((focus_running)); then
|
||
sleep 0.5 # Active
|
||
else
|
||
sleep 3 # Idle
|
||
fi
|
||
done
|
||
```
|
||
|
||
## Integration with Existing Tools
|
||
|
||
### With shellcheck
|
||
|
||
Pre-commit runs both:
|
||
|
||
```bash
|
||
pre-commit run shellcheck --files my_script.sh
|
||
pre-commit run no-polling-antipatterns --files my_script.sh
|
||
```
|
||
|
||
### With formatting
|
||
|
||
Polling linter runs alongside code formatters:
|
||
|
||
```bash
|
||
pre-commit run --all-files
|
||
# → trailing-whitespace, shellcheck, no-polling-antipatterns, ruff, etc.
|
||
```
|
||
|
||
### With CI/CD
|
||
|
||
Pre-commit hooks are required before:
|
||
|
||
- `git commit` (pre-commit hook)
|
||
- `git push` (pre-push hook, includes slower tests)
|
||
|
||
Scripts that fail the polling detector must be fixed before pushing.
|
||
|
||
## Exemptions
|
||
|
||
Some scripts are exempted (e.g., C/CPP test utilities):
|
||
|
||
```yaml
|
||
# .pre-commit-config.yaml
|
||
- id: no-polling-antipatterns
|
||
exclude: ^(\.git/|C/|CPP/|phone_focus_mode/lib/tests/)
|
||
```
|
||
|
||
To add an exemption, modify `.pre-commit-config.yaml` and explain why in a comment.
|
||
|
||
## Common Questions
|
||
|
||
**Q: My status-bar script is trivial—do I need to optimize it?**
|
||
A: Yes! A 100-byte script that forks once per second still costs ~30 CPU-seconds per day. Use `/proc` reads instead.
|
||
|
||
**Q: Can I suppress the antipatterns check?**
|
||
A: No, by design. (See `.git/hooks/` and `userMemory: lint-rules.md` for why). Instead, fix the underlying issue—it's usually a 2-line change.
|
||
|
||
**Q: What if my script MUST call `date`?**
|
||
A: Use bash builtin: `printf -v now '%(%Y-%m-%d)T' -1` (no fork).
|
||
|
||
**Q: Why block on every commit?**
|
||
A: Polling fork-storms cause system-wide slowdown. This saves hundreds of CPU-hours per year per developer machine.
|
||
|
||
## Resources
|
||
|
||
- **Shell Instructions**: `.copilot/instructions/shell.instructions.md` (in-editor, Copilot knowledge)
|
||
- **Efficiency Skill**: `.github/skills/efficient-polling-scripts/SKILL.md` (detailed patterns)
|
||
- **Live Tools**:
|
||
- `./run.sh --diagnose` - Audit repo for patterns
|
||
- `./run.sh --profile 30` - Profile live system
|
||
- `scripts/check_polling_antipatterns.sh` - Manual check
|
||
|
||
## Summary
|
||
|
||
| Layer | Tool | Purpose |
|
||
| --------------------- | ----------------------------- | -------------------------------------------- |
|
||
| **1. Syntax** | shellcheck | Catch bugs, unused variables, quoting issues |
|
||
| **2. Efficiency** | check_polling_antipatterns.sh | Block fork-storm patterns |
|
||
| **3. Best Practices** | shell.instructions.md | Guide developers toward optimal patterns |
|
||
|
||
All three work together to ensure shell scripts are **safe, efficient, and maintainable**.
|