Introduce claude-glm and claude-tk wrappers with secure keychain token lookup and backend model defaults, and replace cleanup.sh with the updated cleanup script entrypoint. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
913 lines
26 KiB
Bash
Executable File
913 lines
26 KiB
Bash
Executable File
#!/bin/bash
|
|
# cleanup.sh
|
|
# Script dọn dẹp các ứng dụng nặng và các bản cũ trên macOS
|
|
# Sử dụng: bash cleanup.sh
|
|
|
|
# -----------------------------
|
|
# Function: cleanup chung cho thư mục chứa version
|
|
# $1 = label (vd: "Edge", "Edge Updater")
|
|
# $2 = đường dẫn thư mục
|
|
# -----------------------------
|
|
cleanup_versions() {
|
|
local label="$1"
|
|
local target_dir="$2"
|
|
|
|
echo "--- [$label] ---"
|
|
echo " $target_dir"
|
|
|
|
if [ ! -d "$target_dir" ]; then
|
|
echo " ⚠ Thư mục không tồn tại."
|
|
return
|
|
fi
|
|
|
|
pushd "$target_dir" > /dev/null || return
|
|
|
|
# Lấy các thư mục version dạng x.y.z.w và sort theo version số.
|
|
local raw_dirs=()
|
|
local d
|
|
shopt -s nullglob
|
|
for d in [0-9]*.[0-9]*.[0-9]*.[0-9]*; do
|
|
[[ -d "$d" ]] && raw_dirs+=("$d")
|
|
done
|
|
shopt -u nullglob
|
|
|
|
if [ ${#raw_dirs[@]} -eq 0 ]; then
|
|
echo " ⚠ Thư mục rỗng, không có phiên bản nào."
|
|
popd > /dev/null
|
|
return
|
|
fi
|
|
|
|
local dirs=()
|
|
while IFS= read -r d; do
|
|
[[ -n "$d" ]] && dirs+=("$d")
|
|
done < <(
|
|
printf '%s\n' "${raw_dirs[@]}" |
|
|
awk -F'.' '{ printf "%05d.%05d.%05d.%05d\t%s\n", $1, $2, $3, $4, $0 }' |
|
|
sort |
|
|
cut -f2-
|
|
)
|
|
|
|
local last_index=$(( ${#dirs[@]} - 1 ))
|
|
local keep="${dirs[$last_index]}"
|
|
|
|
# Helper: lấy size thư mục
|
|
dir_size() { du -sh "$1" 2>/dev/null | cut -f1 | xargs; }
|
|
|
|
if [ ${#dirs[@]} -le 1 ]; then
|
|
echo " ✓ Phiên bản hiện tại: $keep ($(dir_size "$keep")) — không có bản cũ."
|
|
popd > /dev/null
|
|
return
|
|
fi
|
|
|
|
echo " ✓ Giữ lại: $keep ($(dir_size "$keep"))"
|
|
echo " ✗ Sẽ xóa:"
|
|
local i
|
|
for ((i = 0; i < ${#dirs[@]} - 1; i++)); do
|
|
echo " - ${dirs[$i]} ($(dir_size "${dirs[$i]}"))"
|
|
done
|
|
|
|
local confirm
|
|
read -r -p " Xác nhận xóa? (Y/n): " confirm
|
|
if [[ -n "$confirm" && ! "$confirm" =~ ^([yY]|[yY][eE][sS])$ ]]; then
|
|
echo " Đã hủy."
|
|
popd > /dev/null
|
|
return
|
|
fi
|
|
|
|
for ((i = 0; i < ${#dirs[@]} - 1; i++)); do
|
|
echo " Đã xóa: ${dirs[$i]}"
|
|
rm -rf -- "${dirs[$i]}"
|
|
done
|
|
|
|
popd > /dev/null
|
|
}
|
|
|
|
# -----------------------------
|
|
# Function: Cleanup Go mod cache
|
|
# -----------------------------
|
|
cleanup_go() {
|
|
echo "--- [Go Mod Cache] ---"
|
|
if ! command -v go &> /dev/null; then
|
|
echo " ⚠ go command not found."
|
|
return
|
|
fi
|
|
|
|
local GOMODCACHE
|
|
GOMODCACHE=$(go env GOMODCACHE)
|
|
|
|
if [ -d "$GOMODCACHE" ]; then
|
|
echo " $GOMODCACHE"
|
|
local size
|
|
size=$(du -sh "$GOMODCACHE" 2>/dev/null | cut -f1 | xargs)
|
|
echo " ✗ Sẽ xóa bộ nhớ cache: $size"
|
|
|
|
local confirm
|
|
read -r -p " Xác nhận xóa? (Y/n): " confirm
|
|
if [[ -n "$confirm" && ! "$confirm" =~ ^([yY]|[yY][eE][sS])$ ]]; then
|
|
echo " Đã hủy."
|
|
return
|
|
fi
|
|
|
|
go clean -modcache
|
|
echo " Đã xong."
|
|
else
|
|
echo " ✓ Bộ nhớ cache rỗng hoặc không tồn tại."
|
|
fi
|
|
}
|
|
|
|
# -----------------------------
|
|
# Function: Cleanup NPM cache
|
|
# -----------------------------
|
|
cleanup_npm() {
|
|
echo "--- [NPM Cache] ---"
|
|
if ! command -v npm &> /dev/null; then
|
|
echo " ⚠ npm command not found."
|
|
return
|
|
fi
|
|
|
|
local NPM_CACHE
|
|
NPM_CACHE=$(npm config get cache)
|
|
|
|
if [ -d "$NPM_CACHE" ]; then
|
|
echo " $NPM_CACHE"
|
|
local size
|
|
# Lưu ý: npm cache size thực tế thường lớn hơn kết quả du vì cấu trúc thư mục phức tạp
|
|
size=$(du -sh "$NPM_CACHE" 2>/dev/null | cut -f1 | xargs)
|
|
echo " ✗ Sẽ xóa bộ nhớ cache: $size"
|
|
|
|
local confirm
|
|
read -r -p " Xác nhận xóa? (Y/n): " confirm
|
|
if [[ -n "$confirm" && ! "$confirm" =~ ^([yY]|[yY][eE][sS])$ ]]; then
|
|
echo " Đã hủy."
|
|
return
|
|
fi
|
|
|
|
npm cache clean --force
|
|
echo " Đã xong."
|
|
else
|
|
echo " ✓ Bộ nhớ cache rỗng hoặc không tồn tại."
|
|
fi
|
|
}
|
|
|
|
# -----------------------------
|
|
# Function: Cleanup old VS Code extensions
|
|
# Giữ lại phiên bản mới nhất của mỗi extension
|
|
# -----------------------------
|
|
cleanup_vscode_extensions() {
|
|
echo "--- [VS Code Extensions] ---"
|
|
local EXT_DIR="$HOME/.vscode/extensions"
|
|
echo " $EXT_DIR"
|
|
|
|
if [ ! -d "$EXT_DIR" ]; then
|
|
echo " ⚠ Thư mục không tồn tại."
|
|
return
|
|
fi
|
|
|
|
# Build "ext_id<TAB>dirname" pairs
|
|
local entries=()
|
|
local basename ext_id
|
|
|
|
shopt -s nullglob
|
|
for dir in "$EXT_DIR"/*/; do
|
|
basename=$(command basename "$dir")
|
|
[[ "$basename" == .* ]] && continue
|
|
# Extract extension ID: everything before -<version>
|
|
ext_id=$(echo "$basename" | sed 's/-[0-9][0-9.]*.*$//')
|
|
[ -z "$ext_id" ] && continue
|
|
entries+=("${ext_id}"$'\t'"${basename}")
|
|
done
|
|
shopt -u nullglob
|
|
|
|
if [ ${#entries[@]} -eq 0 ]; then
|
|
echo " ⚠ Không có extension nào."
|
|
return
|
|
fi
|
|
|
|
# Sort by ext_id (field 1), then by dirname (field 2) for version ordering
|
|
local sorted
|
|
sorted=$(printf '%s\n' "${entries[@]}" | sort -t$'\t' -k1,1 -k2,2)
|
|
|
|
# Group by ext_id, find old versions
|
|
local to_delete=()
|
|
local to_keep=()
|
|
local group=()
|
|
local prev_id=""
|
|
|
|
while IFS=$'\t' read -r ext_id dirname; do
|
|
if [ -n "$prev_id" ] && [ "$ext_id" != "$prev_id" ]; then
|
|
# Process previous group
|
|
if [ ${#group[@]} -gt 1 ]; then
|
|
local last=$(( ${#group[@]} - 1 ))
|
|
to_keep+=("${group[$last]}")
|
|
local g
|
|
for ((g = 0; g < last; g++)); do
|
|
to_delete+=("${group[$g]}")
|
|
done
|
|
fi
|
|
group=()
|
|
fi
|
|
group+=("$dirname")
|
|
prev_id="$ext_id"
|
|
done <<< "$sorted"
|
|
|
|
# Process last group
|
|
if [ ${#group[@]} -gt 1 ]; then
|
|
local last=$(( ${#group[@]} - 1 ))
|
|
to_keep+=("${group[$last]}")
|
|
local g
|
|
for ((g = 0; g < last; g++)); do
|
|
to_delete+=("${group[$g]}")
|
|
done
|
|
fi
|
|
|
|
if [ ${#to_delete[@]} -eq 0 ]; then
|
|
echo " ✓ Không có extension cũ để xóa."
|
|
return
|
|
fi
|
|
|
|
echo " ✓ Giữ lại (mới nhất):"
|
|
local k
|
|
for k in "${to_keep[@]}"; do
|
|
local size
|
|
size=$(du -sh "$EXT_DIR/$k" 2>/dev/null | cut -f1 | xargs)
|
|
echo " + $k ($size)"
|
|
done
|
|
|
|
echo " ✗ Sẽ xóa (cũ):"
|
|
local d
|
|
for d in "${to_delete[@]}"; do
|
|
local size
|
|
size=$(du -sh "$EXT_DIR/$d" 2>/dev/null | cut -f1 | xargs)
|
|
echo " - $d ($size)"
|
|
done
|
|
|
|
local confirm
|
|
read -r -p " Xác nhận xóa? (Y/n): " confirm
|
|
if [[ -n "$confirm" && ! "$confirm" =~ ^([yY]|[yY][eE][sS])$ ]]; then
|
|
echo " Đã hủy."
|
|
return
|
|
fi
|
|
|
|
for d in "${to_delete[@]}"; do
|
|
echo " Đã xóa: $d"
|
|
rm -rf -- "$EXT_DIR/$d"
|
|
done
|
|
}
|
|
|
|
# -----------------------------
|
|
# Function: Cleanup old Antigravity browser recordings
|
|
# -----------------------------
|
|
cleanup_agy_recordings() {
|
|
local days="${1:-7}"
|
|
local rec_dir="$HOME/.gemini/antigravity/browser_recordings"
|
|
|
|
echo "--- [Antigravity Recordings] ---"
|
|
echo " $rec_dir"
|
|
|
|
if [ ! -d "$rec_dir" ]; then
|
|
echo " ⚠ Thư mục không tồn tại."
|
|
return
|
|
fi
|
|
|
|
local to_delete=()
|
|
local total_size=0
|
|
local dir
|
|
|
|
shopt -s nullglob
|
|
for dir in "$rec_dir"/*/; do
|
|
[ -d "$dir" ] || continue
|
|
local dirname
|
|
dirname=$(basename "$dir")
|
|
local size mod_date
|
|
size=$(du -sh "$dir" 2>/dev/null | cut -f1 | xargs)
|
|
mod_date=$(stat -f "%Sm" -t "%Y-%m-%d" "$dir")
|
|
|
|
if [ "$(find "$dir" -maxdepth 0 -mtime +"$days" 2>/dev/null)" ]; then
|
|
echo " - $dirname ($size, $mod_date)"
|
|
to_delete+=("$dir")
|
|
total_size=$((total_size + $(du -sk "$dir" | cut -f1)))
|
|
else
|
|
echo " + $dirname ($size, $mod_date) [KEEP]"
|
|
fi
|
|
done
|
|
shopt -u nullglob
|
|
|
|
if [ ${#to_delete[@]} -eq 0 ]; then
|
|
echo " ✓ Không có recording cũ (>${days} ngày) để xóa."
|
|
return
|
|
fi
|
|
|
|
local total_human
|
|
total_human=$(echo "scale=1; $total_size / 1024" | bc)
|
|
echo " ✗ Sẽ xóa ${#to_delete[@]} recording(s), tổng: ${total_human} MiB"
|
|
|
|
local confirm
|
|
read -r -p " Xác nhận xóa? (Y/n): " confirm
|
|
if [[ -n "$confirm" && ! "$confirm" =~ ^([yY]|[yY][eE][sS])$ ]]; then
|
|
echo " Đã hủy."
|
|
return
|
|
fi
|
|
|
|
for dir in "${to_delete[@]}"; do
|
|
echo " Đã xóa: $(basename "$dir")"
|
|
rm -rf -- "$dir"
|
|
done
|
|
}
|
|
|
|
# -----------------------------
|
|
# Function: Cleanup Homebrew
|
|
# -----------------------------
|
|
cleanup_homebrew() {
|
|
echo "--- [Homebrew] ---"
|
|
if ! command -v brew &> /dev/null; then
|
|
echo " ⚠ brew command not found."
|
|
return
|
|
fi
|
|
|
|
echo " Đang cập nhật Homebrew..."
|
|
brew update --quiet
|
|
|
|
local outdated
|
|
outdated=$(brew outdated 2>/dev/null)
|
|
if [ -n "$outdated" ]; then
|
|
echo " ✗ Các package cần upgrade:"
|
|
echo "$outdated" | sed 's/^/ - /'
|
|
local confirm
|
|
read -r -p " Upgrade tất cả? (Y/n): " confirm
|
|
if [[ -z "$confirm" || "$confirm" =~ ^([yY]|[yY][eE][sS])$ ]]; then
|
|
brew upgrade --quiet
|
|
echo " Đã upgrade xong."
|
|
else
|
|
echo " Bỏ qua upgrade."
|
|
fi
|
|
else
|
|
echo " ✓ Tất cả package đã cập nhật."
|
|
fi
|
|
|
|
# Cleanup cache
|
|
local cache_size
|
|
cache_size=$(brew --cache 2>/dev/null)
|
|
if [ -d "$cache_size" ]; then
|
|
local size
|
|
size=$(du -sh "$cache_size" 2>/dev/null | cut -f1 | xargs)
|
|
echo " ✗ Cache: $size ($cache_size)"
|
|
fi
|
|
|
|
echo " Đang dọn dẹp cache và autoremove..."
|
|
brew cleanup --prune=all -s 2>/dev/null
|
|
brew autoremove 2>/dev/null
|
|
echo " Đã xong."
|
|
}
|
|
|
|
# -----------------------------
|
|
# Function: Cleanup VS Code C++ Tools cache
|
|
# -----------------------------
|
|
cleanup_vscode_cpptools() {
|
|
local cache_dir="$HOME/Library/Caches/vscode-cpptools"
|
|
|
|
echo "--- [VS Code C++ Tools Cache] ---"
|
|
echo " $cache_dir"
|
|
|
|
if [ ! -d "$cache_dir" ]; then
|
|
echo " ⚠ Thư mục không tồn tại."
|
|
return
|
|
fi
|
|
|
|
local total_size
|
|
total_size=$(du -sh "$cache_dir" 2>/dev/null | cut -f1 | xargs)
|
|
echo " Tổng dung lượng: $total_size"
|
|
|
|
# ipch (IntelliSense cache) - thường chiếm nhiều nhất
|
|
if [ -d "$cache_dir/ipch" ]; then
|
|
local ipch_size
|
|
ipch_size=$(du -sh "$cache_dir/ipch" 2>/dev/null | cut -f1 | xargs)
|
|
echo " - ipch (IntelliSense cache): $ipch_size"
|
|
fi
|
|
|
|
# Các thư mục hash cache khác
|
|
local hash_count=0
|
|
local hash_size=0
|
|
local d
|
|
shopt -s nullglob
|
|
for d in "$cache_dir"/*/; do
|
|
local dirname
|
|
dirname=$(basename "$d")
|
|
[[ "$dirname" == "ipch" ]] && continue
|
|
hash_count=$((hash_count + 1))
|
|
hash_size=$((hash_size + $(du -sk "$d" | cut -f1)))
|
|
done
|
|
shopt -u nullglob
|
|
|
|
if [ $hash_count -gt 0 ]; then
|
|
local hash_human
|
|
hash_human=$(echo "scale=1; $hash_size / 1024" | bc)
|
|
echo " - $hash_count thư mục cache khác: ${hash_human} MiB"
|
|
fi
|
|
|
|
echo " ✗ Sẽ xóa toàn bộ cache"
|
|
local confirm
|
|
read -r -p " Xác nhận xóa? (Y/n): " confirm
|
|
if [[ -n "$confirm" && ! "$confirm" =~ ^([yY]|[yY][eE][sS])$ ]]; then
|
|
echo " Đã hủy."
|
|
return
|
|
fi
|
|
|
|
rm -rf -- "$cache_dir"/*
|
|
echo " Đã xóa toàn bộ cache. VS Code sẽ tạo lại khi cần."
|
|
}
|
|
|
|
# -----------------------------
|
|
# Function: Cleanup IDE logs (JetBrains, Google, etc.)
|
|
# Giữ lại phiên bản mới nhất của mỗi IDE
|
|
# $1 = label, $2 = đường dẫn thư mục logs
|
|
# -----------------------------
|
|
cleanup_ide_logs() {
|
|
local label="$1"
|
|
local logs_dir="$2"
|
|
|
|
echo "--- [$label Logs] ---"
|
|
echo " $logs_dir"
|
|
|
|
if [ ! -d "$logs_dir" ]; then
|
|
echo " ⚠ Thư mục không tồn tại."
|
|
return
|
|
fi
|
|
|
|
local to_delete=()
|
|
local all_dirs=()
|
|
|
|
shopt -s nullglob
|
|
for d in "$logs_dir"/*/; do
|
|
local dirname
|
|
dirname=$(basename "$d")
|
|
[[ "$dirname" == "Toolbox" || "$dirname" == ".DS_Store" ]] && continue
|
|
all_dirs+=("$dirname")
|
|
done
|
|
shopt -u nullglob
|
|
|
|
if [ ${#all_dirs[@]} -eq 0 ]; then
|
|
echo " ✓ Không có log nào."
|
|
return
|
|
fi
|
|
|
|
local sorted
|
|
sorted=$(printf '%s\n' "${all_dirs[@]}" | sort)
|
|
|
|
local prev_ide=""
|
|
local group=()
|
|
|
|
while IFS= read -r dirname; do
|
|
[ -z "$dirname" ] && continue
|
|
local ide_name
|
|
ide_name=$(echo "$dirname" | sed 's/[0-9].*$//')
|
|
[ -z "$ide_name" ] && continue
|
|
|
|
if [ -n "$prev_ide" ] && [ "$ide_name" != "$prev_ide" ]; then
|
|
if [ ${#group[@]} -gt 0 ]; then
|
|
local last_index=$(( ${#group[@]} - 1 ))
|
|
local keep="${group[$last_index]}"
|
|
local keep_size
|
|
keep_size=$(du -sh "$logs_dir/$keep" 2>/dev/null | cut -f1 | xargs)
|
|
echo " [$prev_ide]"
|
|
echo " + $keep ($keep_size) [KEEP]"
|
|
local i
|
|
for ((i = 0; i < last_index; i++)); do
|
|
local old="${group[$i]}"
|
|
local old_size
|
|
old_size=$(du -sh "$logs_dir/$old" 2>/dev/null | cut -f1 | xargs)
|
|
echo " - $old ($old_size)"
|
|
to_delete+=("$logs_dir/$old")
|
|
done
|
|
fi
|
|
group=()
|
|
fi
|
|
group+=("$dirname")
|
|
prev_ide="$ide_name"
|
|
done <<< "$sorted"
|
|
|
|
# Process the last group
|
|
if [ ${#group[@]} -gt 0 ]; then
|
|
local last_index=$(( ${#group[@]} - 1 ))
|
|
local keep="${group[$last_index]}"
|
|
local keep_size
|
|
keep_size=$(du -sh "$logs_dir/$keep" 2>/dev/null | cut -f1 | xargs)
|
|
echo " [$prev_ide]"
|
|
echo " + $keep ($keep_size) [KEEP]"
|
|
local i
|
|
for ((i = 0; i < last_index; i++)); do
|
|
local old="${group[$i]}"
|
|
local old_size
|
|
old_size=$(du -sh "$logs_dir/$old" 2>/dev/null | cut -f1 | xargs)
|
|
echo " - $old ($old_size)"
|
|
to_delete+=("$logs_dir/$old")
|
|
done
|
|
fi
|
|
|
|
if [ ${#to_delete[@]} -eq 0 ]; then
|
|
echo " ✓ Không có log cũ để xóa."
|
|
return
|
|
fi
|
|
|
|
local confirm
|
|
read -r -p " Xác nhận xóa ${#to_delete[@]} thư mục cũ? (Y/n): " confirm
|
|
if [[ -n "$confirm" && ! "$confirm" =~ ^([yY]|[yY][eE][sS])$ ]]; then
|
|
echo " Đã hủy."
|
|
return
|
|
fi
|
|
|
|
for d in "${to_delete[@]}"; do
|
|
echo " Đã xóa: $(basename "$d")"
|
|
rm -rf -- "$d"
|
|
done
|
|
}
|
|
|
|
# -----------------------------
|
|
# Function: Cleanup pip cache
|
|
# -----------------------------
|
|
cleanup_pip() {
|
|
echo "--- [Pip Cache] ---"
|
|
if ! command -v pip3 &> /dev/null; then
|
|
echo " ⚠ pip3 command not found."
|
|
return
|
|
fi
|
|
|
|
local cache_dir
|
|
cache_dir=$(pip3 cache dir 2>/dev/null)
|
|
if [ -d "$cache_dir" ]; then
|
|
local size
|
|
size=$(du -sh "$cache_dir" 2>/dev/null | cut -f1 | xargs)
|
|
echo " $cache_dir"
|
|
echo " ✗ Cache: $size"
|
|
local confirm
|
|
read -r -p " Xác nhận xóa? (Y/n): " confirm
|
|
if [[ -n "$confirm" && ! "$confirm" =~ ^([yY]|[yY][eE][sS])$ ]]; then
|
|
echo " Đã hủy."
|
|
return
|
|
fi
|
|
pip3 cache purge 2>/dev/null
|
|
echo " Đã xong."
|
|
else
|
|
echo " ✓ Cache rỗng."
|
|
fi
|
|
}
|
|
|
|
# -----------------------------
|
|
# Function: Cleanup deno cache
|
|
# -----------------------------
|
|
cleanup_deno() {
|
|
echo "--- [Deno Cache] ---"
|
|
local deno_cache="$HOME/Library/Caches/deno"
|
|
echo " $deno_cache"
|
|
|
|
if [ ! -d "$deno_cache" ]; then
|
|
echo " ⚠ Thư mục không tồn tại."
|
|
return
|
|
fi
|
|
|
|
local size
|
|
size=$(du -sh "$deno_cache" 2>/dev/null | cut -f1 | xargs)
|
|
echo " ✗ Cache: $size"
|
|
local confirm
|
|
read -r -p " Xác nhận xóa? (Y/n): " confirm
|
|
if [[ -n "$confirm" && ! "$confirm" =~ ^([yY]|[yY][eE][sS])$ ]]; then
|
|
echo " Đã hủy."
|
|
return
|
|
fi
|
|
rm -rf -- "$deno_cache"/*
|
|
echo " Đã xong."
|
|
}
|
|
|
|
# -----------------------------
|
|
# Function: Cleanup go build cache
|
|
# -----------------------------
|
|
cleanup_go_build() {
|
|
echo "--- [Go Build Cache] ---"
|
|
if ! command -v go &> /dev/null; then
|
|
echo " ⚠ go command not found."
|
|
return
|
|
fi
|
|
|
|
local cache_dir
|
|
cache_dir=$(go env GOCACHE)
|
|
if [ -d "$cache_dir" ]; then
|
|
local size
|
|
size=$(du -sh "$cache_dir" 2>/dev/null | cut -f1 | xargs)
|
|
echo " $cache_dir"
|
|
echo " ✗ Cache: $size"
|
|
local confirm
|
|
read -r -p " Xác nhận xóa? (Y/n): " confirm
|
|
if [[ -n "$confirm" && ! "$confirm" =~ ^([yY]|[yY][eE][sS])$ ]]; then
|
|
echo " Đã hủy."
|
|
return
|
|
fi
|
|
go clean -cache
|
|
echo " Đã xong."
|
|
else
|
|
echo " ✓ Cache rỗng."
|
|
fi
|
|
}
|
|
|
|
# -----------------------------
|
|
# Function: Cleanup generic cache directory
|
|
# $1 = label, $2 = đường dẫn thư mục
|
|
# -----------------------------
|
|
cleanup_cache_dir() {
|
|
local label="$1"
|
|
local cache_dir="$2"
|
|
|
|
echo "--- [$label Cache] ---"
|
|
echo " $cache_dir"
|
|
|
|
if [ ! -d "$cache_dir" ]; then
|
|
echo " ⚠ Thư mục không tồn tại."
|
|
return
|
|
fi
|
|
|
|
local size
|
|
size=$(du -sh "$cache_dir" 2>/dev/null | cut -f1 | xargs)
|
|
echo " ✗ Dung lượng: $size"
|
|
local confirm
|
|
read -r -p " Xác nhận xóa? (Y/n): " confirm
|
|
if [[ -n "$confirm" && ! "$confirm" =~ ^([yY]|[yY][eE][sS])$ ]]; then
|
|
echo " Đã hủy."
|
|
return
|
|
fi
|
|
rm -rf -- "$cache_dir"/*
|
|
echo " Đã xong."
|
|
}
|
|
|
|
# =============================================================================
|
|
# Interactive Menu System
|
|
# =============================================================================
|
|
|
|
print_welcome_banner() {
|
|
local use_color=0
|
|
local reset=""
|
|
local i=0
|
|
local line
|
|
local -a colors=()
|
|
local -a lines=(
|
|
' __ __ __ __ ________ __'
|
|
'| \ / \ | \ | \ | \| \'
|
|
'| $$\ / $$ ______ _______ _______ | $$ ______ ______ _______ __ __ ______ | $$____ __ __ \$$$$$$$$| $$'
|
|
'| $$$\ / $$$ | \ / \ / \| $$ / \ | \ | \ | \ | \ / \ | $$ \ | \ | \ | $$ | $$'
|
|
'| $$$$\ $$$$ \$$$$$$\| $$$$$$$ | $$$$$$$| $$| $$$$$$\ \$$$$$$\| $$$$$$$\| $$ | $$| $$$$$$\ | $$$$$$$\| $$ | $$ | $$ | $$'
|
|
'| $$\$$ $$ $$ / $$| $$ | $$ | $$| $$ $$ / $$| $$ | $$| $$ | $$| $$ | $$ | $$ | $$| $$ | $$ | $$ | $$'
|
|
'| $$ \$$$| $$| $$$$$$$| $$_____ | $$_____ | $$| $$$$$$$$| $$$$$$$| $$ | $$| $$__/ $$| $$__/ $$ | $$__/ $$| $$__/ $$ | $$ | $$_____'
|
|
'| $$ \$ | $$ \$$ $$ \$$ \ \$$ \| $$ \$$ \ \$$ $$| $$ | $$ \$$ $$| $$ $$ | $$ $$ \$$ $$ | $$ | $$ \'
|
|
' \$$ \$$ \$$$$$$$ \$$$$$$$ \$$$$$$$ \$$ \$$$$$$$ \$$$$$$$ \$$ \$$ \$$$$$$ | $$$$$$$ \$$$$$$$ _\$$$$$$$ \$$ \$$$$$$$$'
|
|
' | $$ | \__| $$'
|
|
' | $$ \$$ $$'
|
|
' \$$ \$$$$$$'
|
|
)
|
|
|
|
if [ -t 1 ]; then
|
|
use_color=1
|
|
reset=$'\033[0m'
|
|
colors=(
|
|
$'\033[1;38;5;51m'
|
|
$'\033[1;38;5;87m'
|
|
$'\033[1;38;5;123m'
|
|
$'\033[1;38;5;159m'
|
|
$'\033[1;38;5;193m'
|
|
$'\033[1;38;5;220m'
|
|
$'\033[1;38;5;214m'
|
|
$'\033[1;38;5;209m'
|
|
$'\033[1;38;5;204m'
|
|
$'\033[1;38;5;198m'
|
|
$'\033[1;38;5;177m'
|
|
$'\033[1;38;5;141m'
|
|
)
|
|
fi
|
|
|
|
echo ""
|
|
for line in "${lines[@]}"; do
|
|
if [ "$use_color" -eq 1 ]; then
|
|
printf '%b%s%b\n' "${colors[$i]}" "$line" "$reset"
|
|
else
|
|
printf '%s\n' "$line"
|
|
fi
|
|
i=$((i + 1))
|
|
done
|
|
echo ""
|
|
}
|
|
|
|
# Tên các nhóm cleanup
|
|
MENU_ITEMS=(
|
|
"All"
|
|
"Browsers (Edge, Mozilla, Playwright)"
|
|
"IDE (VS Code, AntiGravity)"
|
|
"Dev Caches (Go, NPM, Pip, Deno)"
|
|
"IDE Logs (JetBrains, Google)"
|
|
"Other Caches (Raspberry Pi)"
|
|
"Homebrew"
|
|
)
|
|
|
|
# Trạng thái chọn (0=off, 1=on), mặc định All=on
|
|
MENU_SELECTED=(1 1 1 1 1 1 1)
|
|
|
|
MENU_CURSOR=0
|
|
MENU_COUNT=${#MENU_ITEMS[@]}
|
|
|
|
# Vẽ menu lên terminal
|
|
draw_menu() {
|
|
# Di chuyển cursor lên đầu menu để vẽ lại
|
|
if [ "$1" = "redraw" ]; then
|
|
printf "\033[%dA" "$((MENU_COUNT + 2))"
|
|
fi
|
|
|
|
echo " Chọn mục cần dọn dẹp (↑↓: di chuyển | Space: chọn/bỏ chọn | Enter: chạy các mục có [✓]):"
|
|
echo ""
|
|
|
|
local i
|
|
for ((i = 0; i < MENU_COUNT; i++)); do
|
|
local prefix=" "
|
|
[ $i -eq $MENU_CURSOR ] && prefix="▸ "
|
|
|
|
local check="[ ]"
|
|
[ "${MENU_SELECTED[$i]}" -eq 1 ] && check="[✓]"
|
|
|
|
local label="${MENU_ITEMS[$i]}"
|
|
|
|
# In dòng, xóa hết ký tự cũ trên dòng
|
|
printf "\r\033[K%s%s %s\n" "$prefix" "$check" "$label"
|
|
done
|
|
}
|
|
|
|
# Toggle All: chọn hoặc bỏ tất cả
|
|
toggle_all() {
|
|
local new_val="${MENU_SELECTED[0]}"
|
|
# Nếu All đang bật → tắt hết, ngược lại → bật hết
|
|
if [ "$new_val" -eq 1 ]; then
|
|
new_val=0
|
|
else
|
|
new_val=1
|
|
fi
|
|
local i
|
|
for ((i = 0; i < MENU_COUNT; i++)); do
|
|
MENU_SELECTED[$i]=$new_val
|
|
done
|
|
}
|
|
|
|
# Cập nhật lại trạng thái All dựa trên các item con
|
|
sync_all() {
|
|
local all_on=1
|
|
local i
|
|
for ((i = 1; i < MENU_COUNT; i++)); do
|
|
if [ "${MENU_SELECTED[$i]}" -eq 0 ]; then
|
|
all_on=0
|
|
break
|
|
fi
|
|
done
|
|
MENU_SELECTED[0]=$all_on
|
|
}
|
|
|
|
# Chạy interactive menu
|
|
run_menu() {
|
|
print_welcome_banner
|
|
echo ""
|
|
echo "===== 🧹 Cleanup macOS ====="
|
|
echo ""
|
|
|
|
draw_menu
|
|
|
|
while true; do
|
|
# Đọc 1 ký tự (không echo, không buffer)
|
|
IFS= read -rsn1 key
|
|
|
|
case "$key" in
|
|
# ESC sequence (arrow keys)
|
|
$'\x1b')
|
|
read -rsn2 seq
|
|
case "$seq" in
|
|
'[A') # Up
|
|
MENU_CURSOR=$(( (MENU_CURSOR - 1 + MENU_COUNT) % MENU_COUNT ))
|
|
;;
|
|
'[B') # Down
|
|
MENU_CURSOR=$(( (MENU_CURSOR + 1) % MENU_COUNT ))
|
|
;;
|
|
esac
|
|
;;
|
|
# Space = toggle
|
|
' ')
|
|
if [ $MENU_CURSOR -eq 0 ]; then
|
|
toggle_all
|
|
else
|
|
if [ "${MENU_SELECTED[$MENU_CURSOR]}" -eq 1 ]; then
|
|
MENU_SELECTED[$MENU_CURSOR]=0
|
|
else
|
|
MENU_SELECTED[$MENU_CURSOR]=1
|
|
fi
|
|
sync_all
|
|
fi
|
|
;;
|
|
# Enter = confirm
|
|
'')
|
|
echo ""
|
|
break
|
|
;;
|
|
esac
|
|
|
|
draw_menu "redraw"
|
|
done
|
|
}
|
|
|
|
# =============================================================================
|
|
# Hàm chạy từng nhóm
|
|
# =============================================================================
|
|
|
|
run_browsers() {
|
|
cleanup_versions "Edge" "$HOME/Library/Application Support/Microsoft/EdgeUpdater/apps/msedge-stable"
|
|
echo ""
|
|
cleanup_versions "Edge Updater" "$HOME/Library/Application Support/Microsoft/EdgeUpdater/apps/msedge-updater"
|
|
echo ""
|
|
cleanup_cache_dir "Mozilla" "$HOME/Library/Caches/Mozilla"
|
|
echo ""
|
|
cleanup_cache_dir "Playwright" "$HOME/Library/Caches/ms-playwright"
|
|
echo ""
|
|
}
|
|
|
|
run_ide() {
|
|
cleanup_vscode_extensions
|
|
echo ""
|
|
cleanup_vscode_cpptools
|
|
echo ""
|
|
cleanup_cache_dir "VS Code ShipIt" "$HOME/Library/Caches/com.microsoft.VSCode.ShipIt"
|
|
echo ""
|
|
cleanup_agy_recordings
|
|
echo ""
|
|
}
|
|
|
|
run_dev_caches() {
|
|
cleanup_go
|
|
echo ""
|
|
cleanup_go_build
|
|
echo ""
|
|
cleanup_npm
|
|
echo ""
|
|
cleanup_pip
|
|
echo ""
|
|
cleanup_deno
|
|
echo ""
|
|
}
|
|
|
|
run_ide_logs() {
|
|
cleanup_ide_logs "JetBrains" "$HOME/Library/Logs/JetBrains"
|
|
echo ""
|
|
cleanup_ide_logs "Google" "$HOME/Library/Logs/Google"
|
|
echo ""
|
|
}
|
|
|
|
run_other_caches() {
|
|
cleanup_cache_dir "Raspberry Pi" "$HOME/Library/Caches/Raspberry Pi"
|
|
echo ""
|
|
}
|
|
|
|
run_homebrew() {
|
|
cleanup_homebrew
|
|
echo ""
|
|
}
|
|
|
|
# =============================================================================
|
|
# Main
|
|
# =============================================================================
|
|
|
|
# Map index → function
|
|
GROUP_FUNCS=(
|
|
"" # 0 = All (handled separately)
|
|
"run_browsers" # 1
|
|
"run_ide" # 2
|
|
"run_dev_caches" # 3
|
|
"run_ide_logs" # 4
|
|
"run_other_caches" # 5
|
|
"run_homebrew" # 6
|
|
)
|
|
|
|
run_menu
|
|
|
|
# Kiểm tra có mục nào được chọn không
|
|
has_selection=0
|
|
for ((i = 1; i < MENU_COUNT; i++)); do
|
|
if [ "${MENU_SELECTED[$i]}" -eq 1 ]; then
|
|
has_selection=1
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [ "$has_selection" -eq 0 ]; then
|
|
echo "Không có mục nào được chọn. Thoát."
|
|
exit 0
|
|
fi
|
|
|
|
# Chạy các nhóm đã chọn
|
|
echo "===== Bắt đầu cleanup ====="
|
|
echo ""
|
|
|
|
for ((i = 1; i < MENU_COUNT; i++)); do
|
|
if [ "${MENU_SELECTED[$i]}" -eq 1 ]; then
|
|
${GROUP_FUNCS[$i]}
|
|
fi
|
|
done
|
|
|
|
echo "===== ✅ Cleanup hoàn tất ====="
|