mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 19:07:04 +01:00
Some checks failed
Pre-commit checks / pre-commit (push) Waiting to run
Python tests / test (push) Waiting to run
Shell tests / shell-tests (push) Waiting to run
Shell tests / arch-tests (push) Waiting to run
billsplit CI / quality (push) Has been cancelled
reverse-survivors CI / check (push) Has been cancelled
The shellcheck hook ran with --severity=warning, so info- and style-level
findings never failed a commit. 222 had accumulated across 94 of 244 shell
files. They were not all style noise -- six were bugs:
- Five scripts in single_use/misc/testsAndMisc-bash/ sourced
$SCRIPT_DIR/../../lib/common.sh, one directory too shallow. Nothing exists
at that path, so all five failed at runtime.
- setup_personal_website.sh and setup_kcd2_dice_solver.sh called usage()
(which ends in `exit 0`) and then `exit 1` for an unknown command. The
exit 1 was unreachable, so a typo'd subcommand reported SUCCESS.
- Six i3blocks scripts had a `# shellcheck source=` directive missing the
scripts/periodic_background/ path segment. The stale path had silently
disabled source-following for all of them.
- diagnose_arch_performance.sh word-split a newline-joined file list; when
the find came back empty, awk got no file operands, fell through to
reading stdin, and the diagnostic hung.
- lib/common.sh's handle_arg_help_or_unknown ended in an unreachable
`return 0` while documenting "Returns: 0 if handled" -- a contract no code
path could satisfy.
- deploy.sh's SCRIPT_DIR is reassigned by config.sh, which it sources at top
level, and config.sh can repoint it at the on-device path
/data/local/tmp/focus_mode. Renamed to DEPLOY_DIR.
The SC1091 bulk (81 findings) was resolved rather than silenced: .shellcheckrc
sets external-sources + source-path=SCRIPTDIR so sourced files are actually
followed and analysed. That is what surfaced the i3blocks and deploy.sh bugs.
17 `# shellcheck disable=` directives remain, each with a written reason, and
only where the finding is provably wrong: injection-test payloads that must not
expand, literal source text being grepped for, POSIX sh scripts on the phone
where arrays do not exist, and source paths that only exist at runtime.
Everything else was fixed in code.
Verified in a CLEAN worktree, not just my own tree. config.sh sources the
gitignored config_secrets.sh, which exists locally but not in a fresh checkout;
a working-tree-only check passed while ci_mirror.sh -- which lints HEAD in a
clean worktree, exactly as CI does -- failed on it.
The gate is verified to bite: a file whose only finding is SC2016 (info) fails
the hook, while the old --severity=warning gate exits 0 on it.
third_party/ is excluded -- vendored upstream, matching the prettier hook.
Not fixed, reported: check_polling_antipatterns.sh carries a pre-existing
SC2034 disable that hides a real bug -- its `patterns` array is never used, so
the checker detects nothing. Confirmed unchanged by this commit.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
330 lines
9.6 KiB
Bash
Executable File
330 lines
9.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# disk_cleanup_check.sh — Analyze disk usage and suggest (or perform) cleanup.
|
|
#
|
|
# Usage:
|
|
# ./disk_cleanup_check.sh # Dry-run: report only
|
|
# ./disk_cleanup_check.sh --clean # Interactive: prompt before each action
|
|
#
|
|
# Safe by default: nothing is deleted without --clean AND user confirmation.
|
|
|
|
RED=$'\033[0;31m'
|
|
GREEN=$'\033[0;32m'
|
|
YELLOW=$'\033[1;33m'
|
|
CYAN=$'\033[0;36m'
|
|
BOLD=$'\033[1m'
|
|
RESET=$'\033[0m'
|
|
|
|
CLEAN=false
|
|
if [[ "${1:-}" == "--clean" ]]; then
|
|
CLEAN=true
|
|
fi
|
|
|
|
TOTAL_RECLAIMABLE=0
|
|
|
|
# ───────────────────── helpers ─────────────────────
|
|
|
|
human_readable() {
|
|
local kb=$1
|
|
if ((kb >= 1048576)); then
|
|
printf "%.1f GB" "$(echo "scale=1; $kb / 1048576" | bc)"
|
|
elif ((kb >= 1024)); then
|
|
printf "%.1f MB" "$(echo "scale=1; $kb / 1024" | bc)"
|
|
else
|
|
printf "%d KB" "$kb"
|
|
fi
|
|
}
|
|
|
|
dir_size_kb() {
|
|
local dir="$1"
|
|
if [[ -d "$dir" ]]; then
|
|
du -sk "$dir" 2>/dev/null | awk '{print $1}'
|
|
else
|
|
echo 0
|
|
fi
|
|
}
|
|
|
|
report() {
|
|
local label="$1" size_kb="$2" detail="${3:-}"
|
|
if ((size_kb > 0)); then
|
|
local hr
|
|
hr=$(human_readable "$size_kb")
|
|
printf "${YELLOW}%-40s${RESET} %10s" "$label" "$hr"
|
|
if [[ -n "$detail" ]]; then
|
|
printf " ${CYAN}(%s)${RESET}" "$detail"
|
|
fi
|
|
printf "\n"
|
|
TOTAL_RECLAIMABLE=$((TOTAL_RECLAIMABLE + size_kb))
|
|
fi
|
|
}
|
|
|
|
# Returns 0 if user confirms, 1 otherwise. Always 1 in dry-run.
|
|
confirm() {
|
|
local prompt="$1"
|
|
if $CLEAN; then
|
|
printf "${BOLD} → %s [y/N]: ${RESET}" "$prompt"
|
|
read -r ans
|
|
if [[ "$ans" =~ ^[Yy]$ ]]; then
|
|
return 0
|
|
fi
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
# Safe wrapper: confirm + action, never fails under errexit
|
|
try_clean() {
|
|
local prompt="$1"
|
|
shift
|
|
if confirm "$prompt"; then
|
|
"$@"
|
|
printf "%s ✓ Done%s\n" "${GREEN}" "${RESET}"
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
# ───────────────────── checks ─────────────────────
|
|
|
|
printf "\n%s=== Disk Cleanup Analysis ===%s\n\n" "${BOLD}" "${RESET}"
|
|
printf "${BOLD}%-40s %10s${RESET}\n" "Category" "Reclaimable"
|
|
printf '%s\n' "$(printf '%.0s─' {1..60})"
|
|
|
|
# 1. Trash
|
|
trash_dir="$HOME/.local/share/Trash"
|
|
size=$(dir_size_kb "$trash_dir")
|
|
report "Trash" "$size" "empty trash"
|
|
if ((size > 0)); then
|
|
try_clean "Empty trash?" \
|
|
rm -rf "${trash_dir}/files" "${trash_dir}/info" "${trash_dir}/expunged"
|
|
fi
|
|
|
|
# 2. Pacman cache
|
|
pacman_cache_size=$(dir_size_kb "/var/cache/pacman/pkg")
|
|
if ((pacman_cache_size > 0)); then
|
|
if command -v paccache &>/dev/null; then
|
|
pkg_count=$(find /var/cache/pacman/pkg/ -maxdepth 1 -type f -name '*.pkg.tar*' 2>/dev/null | wc -l)
|
|
report "Pacman cache ($pkg_count pkgs)" "$pacman_cache_size" \
|
|
"sudo paccache -rk2"
|
|
try_clean "Clean pacman cache (keep last 2 versions)?" \
|
|
sudo paccache -rk2
|
|
else
|
|
report "Pacman cache (install pacman-contrib)" "$pacman_cache_size" \
|
|
"pacman -S pacman-contrib && paccache -rk2"
|
|
fi
|
|
fi
|
|
|
|
# 3. Yay build cache
|
|
yay_cache="$HOME/.cache/yay"
|
|
size=$(dir_size_kb "$yay_cache")
|
|
report "Yay AUR build cache" "$size" "rm -rf ~/.cache/yay"
|
|
if ((size > 0)); then
|
|
try_clean "Clear yay build cache?" rm -rf "${yay_cache:?}"
|
|
fi
|
|
|
|
# 4. Pip cache
|
|
pip_cache="$HOME/.cache/pip"
|
|
size=$(dir_size_kb "$pip_cache")
|
|
report "Pip cache" "$size" "pip cache purge"
|
|
if ((size > 0)); then
|
|
try_clean "Clear pip cache?" rm -rf "${pip_cache:?}"
|
|
fi
|
|
|
|
# 5. uv cache
|
|
uv_cache="$HOME/.cache/uv"
|
|
size=$(dir_size_kb "$uv_cache")
|
|
report "uv cache" "$size" "uv cache clean"
|
|
if ((size > 0)); then
|
|
try_clean "Clear uv cache?" rm -rf "${uv_cache:?}"
|
|
fi
|
|
|
|
# 6. HuggingFace cache
|
|
hf_cache="$HOME/.cache/huggingface"
|
|
size=$(dir_size_kb "$hf_cache")
|
|
report "HuggingFace models" "$size" "manual review recommended"
|
|
|
|
# 7. Suno cache
|
|
suno_cache="$HOME/.cache/suno"
|
|
size=$(dir_size_kb "$suno_cache")
|
|
report "Suno AI cache" "$size" "manual review recommended"
|
|
|
|
# 8. Go build cache
|
|
go_cache="$HOME/.cache/go-build"
|
|
size=$(dir_size_kb "$go_cache")
|
|
report "Go build cache" "$size" "go clean -cache"
|
|
if ((size > 0)); then
|
|
try_clean "Clear Go build cache?" rm -rf "${go_cache:?}"
|
|
fi
|
|
|
|
# 9. Yarn cache
|
|
yarn_cache="$HOME/.cache/yarn"
|
|
size=$(dir_size_kb "$yarn_cache")
|
|
report "Yarn cache" "$size" "yarn cache clean"
|
|
if ((size > 0)); then
|
|
try_clean "Clear yarn cache?" rm -rf "${yarn_cache:?}"
|
|
fi
|
|
|
|
# 10. npm cache
|
|
npm_cache="$HOME/.npm/_cacache"
|
|
size=$(dir_size_kb "$npm_cache")
|
|
report "npm cache" "$size" "npm cache clean --force"
|
|
if ((size > 0)); then
|
|
try_clean "Clear npm cache?" rm -rf "${npm_cache:?}"
|
|
fi
|
|
|
|
# 11. Electron cache
|
|
electron_cache="$HOME/.cache/electron"
|
|
size=$(dir_size_kb "$electron_cache")
|
|
report "Electron cache" "$size"
|
|
if ((size > 0)); then
|
|
try_clean "Clear Electron cache?" rm -rf "${electron_cache:?}"
|
|
fi
|
|
|
|
# 12. Thumbnails
|
|
thumb_cache="$HOME/.cache/thumbnails"
|
|
size=$(dir_size_kb "$thumb_cache")
|
|
report "Thumbnails cache" "$size"
|
|
if ((size > 0)); then
|
|
try_clean "Clear thumbnails?" rm -rf "${thumb_cache:?}"
|
|
fi
|
|
|
|
# 13. vscode-cpptools cache
|
|
cpptools_cache="$HOME/.cache/vscode-cpptools"
|
|
size=$(dir_size_kb "$cpptools_cache")
|
|
if ((size > 102400)); then
|
|
report "VS Code cpptools cache" "$size"
|
|
try_clean "Clear cpptools cache?" rm -rf "${cpptools_cache:?}"
|
|
fi
|
|
|
|
# 14. Bazel cache
|
|
bazel_cache="$HOME/.cache/bazel"
|
|
size=$(dir_size_kb "$bazel_cache")
|
|
report "Bazel cache" "$size"
|
|
if ((size > 0)); then
|
|
try_clean "Clear bazel cache?" rm -rf "${bazel_cache:?}"
|
|
fi
|
|
|
|
# 15. Browser caches (report only)
|
|
for browser_cache in \
|
|
"$HOME/.cache/thorium" \
|
|
"$HOME/.cache/google-chrome" \
|
|
"$HOME/.cache/mozilla" \
|
|
"$HOME/.cache/BraveSoftware"; do
|
|
if [[ -d "$browser_cache" ]]; then
|
|
name=$(basename "$browser_cache")
|
|
size=$(dir_size_kb "$browser_cache")
|
|
if ((size > 102400)); then
|
|
report "Browser cache: $name" "$size" "close browser first"
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# 16. Docker
|
|
if command -v docker &>/dev/null; then
|
|
docker_dir="/var/lib/docker"
|
|
size=$(dir_size_kb "$docker_dir" || echo 0)
|
|
if ((size > 1048576)); then
|
|
report "Docker (images+build cache)" "$size" "docker system prune -a"
|
|
try_clean "Docker system prune (removes all unused)?" \
|
|
docker system prune -af --volumes
|
|
fi
|
|
fi
|
|
|
|
# 17. Orphan packages
|
|
orphans=$(pacman -Qdtq 2>/dev/null || true)
|
|
orphan_count=0
|
|
if [[ -n "$orphans" ]]; then
|
|
orphan_count=$(echo "$orphans" | wc -l)
|
|
fi
|
|
if ((orphan_count > 0)); then
|
|
orphan_size=$(echo "$orphans" | xargs pacman -Qi 2>/dev/null |
|
|
awk '/Installed Size/{
|
|
size=$4; unit=$5;
|
|
if (unit ~ /GiB/) total += size*1048576;
|
|
else if (unit ~ /MiB/) total += size*1024;
|
|
else if (unit ~ /KiB/) total += size;
|
|
} END{printf "%d", total}' || echo 0)
|
|
report "Orphan packages ($orphan_count)" "$orphan_size" \
|
|
"sudo pacman -Rns \$(pacman -Qdtq)"
|
|
if confirm "Remove $orphan_count orphan packages?"; then
|
|
# shellcheck disable=SC2046
|
|
sudo pacman -Rns $(pacman -Qdtq) --noconfirm
|
|
printf "%s ✓ Orphans removed%s\n" "${GREEN}" "${RESET}"
|
|
fi
|
|
fi
|
|
|
|
# 18. Journal logs — vacuum to 200M
|
|
journal_line=$(journalctl --disk-usage 2>&1 || true)
|
|
journal_kb=0
|
|
if [[ "$journal_line" =~ ([0-9.]+)G ]]; then
|
|
journal_kb=$(echo "${BASH_REMATCH[1]} * 1048576 / 1" | bc)
|
|
elif [[ "$journal_line" =~ ([0-9.]+)M ]]; then
|
|
journal_kb=$(echo "${BASH_REMATCH[1]} * 1024 / 1" | bc)
|
|
fi
|
|
if ((journal_kb > 204800)); then
|
|
excess=$((journal_kb - 204800))
|
|
report "Journal logs (excess over 200M)" "$excess" "journalctl --vacuum-size=200M"
|
|
try_clean "Vacuum journal to 200M?" \
|
|
sudo journalctl --vacuum-size=200M
|
|
fi
|
|
|
|
# 19. AUR source dirs — old archives (>30 days)
|
|
aur_dir="$HOME/aur"
|
|
if [[ -d "$aur_dir" ]]; then
|
|
old_archives_kb=$(find "$aur_dir" \
|
|
\( -name "*.pkg.tar.zst" -o -name "*.tar.gz" -o -name "*.tar.xz" \
|
|
-o -name "*.zip" -o -name "*.tar.bz2" \) \
|
|
-mtime +30 -printf '%k\n' 2>/dev/null |
|
|
awk '{t+=$1} END{print t+0}')
|
|
if ((old_archives_kb > 0)); then
|
|
report "AUR old archives (>30d)" "$old_archives_kb" \
|
|
"find ~/aur '*.pkg.tar.zst' -mtime +30 -delete"
|
|
if confirm "Delete old AUR archives (>30 days)?"; then
|
|
find "$aur_dir" \
|
|
\( -name "*.pkg.tar.zst" -o -name "*.tar.gz" -o -name "*.tar.xz" \
|
|
-o -name "*.zip" -o -name "*.tar.bz2" \) \
|
|
-mtime +30 -delete
|
|
printf "%s ✓ Old AUR archives deleted%s\n" "${GREEN}" "${RESET}"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# ───────────────────── report-only items ─────────────────────
|
|
|
|
printf "\n%s--- Report-only (manual review needed) ---%s\n" "${BOLD}" "${RESET}"
|
|
for manual_entry in \
|
|
"$HOME/Downloads/too_big:~/Downloads/too_big" \
|
|
"$HOME/Downloads:~/Downloads total" \
|
|
"$HOME/inne:~/inne" \
|
|
"/Games:/Games — still playing?"; do
|
|
dir="${manual_entry%%:*}"
|
|
label="${manual_entry#*:}"
|
|
size=$(dir_size_kb "$dir")
|
|
if ((size > 0)); then
|
|
hr=$(human_readable "$size")
|
|
printf "${RED}%-40s %10s${RESET} ${CYAN}(review manually)${RESET}\n" \
|
|
"$label" "$hr"
|
|
fi
|
|
done
|
|
|
|
# ───────────────────── summary ─────────────────────
|
|
|
|
printf "\n%s\n" "$(printf '%.0s─' {1..60})"
|
|
total_hr=$(human_readable "$TOTAL_RECLAIMABLE")
|
|
printf "${BOLD}Total auto-reclaimable: ${GREEN}%s${RESET}\n" "$total_hr"
|
|
|
|
read -r used_kb total_kb used_pct <<<"$(df -k / | awk 'NR==2{print $3, $2, $5}')"
|
|
used_pct="${used_pct%\%}"
|
|
printf "${BOLD}Current usage: %s / %s (%s%%)${RESET}\n" \
|
|
"$(human_readable "$used_kb")" "$(human_readable "$total_kb")" "$used_pct"
|
|
|
|
if ((TOTAL_RECLAIMABLE > 0)); then
|
|
new_used=$((used_kb - TOTAL_RECLAIMABLE))
|
|
if ((new_used < 0)); then new_used=0; fi
|
|
new_pct=$((new_used * 100 / total_kb))
|
|
printf "${BOLD}After cleanup: %s / %s (%d%%)${RESET}\n" \
|
|
"$(human_readable "$new_used")" "$(human_readable "$total_kb")" "$new_pct"
|
|
fi
|
|
|
|
if ! $CLEAN; then
|
|
printf "\n%sRun with --clean to interactively clean each category.%s\n" "${CYAN}" "${RESET}"
|
|
fi
|
|
printf "\n"
|