mirror of
https://github.com/tw93/Mole.git
synced 2026-02-11 11:14:16 +00:00
✨ Add whitelist selection mechanism
This commit is contained in:
50
GUIDE.md
50
GUIDE.md
@@ -88,13 +88,21 @@ mole clean --dry-run
|
|||||||
|
|
||||||
这个命令只会**显示**哪些文件会被清理,**不会真的删除**。你可以先看看效果再决定。
|
这个命令只会**显示**哪些文件会被清理,**不会真的删除**。你可以先看看效果再决定。
|
||||||
|
|
||||||
|
**管理白名单(保护重要缓存):**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mole clean --whitelist
|
||||||
|
```
|
||||||
|
|
||||||
|
交互式选择哪些缓存不要删除,比如开发工具的大型缓存(Homebrew、Gradle 等)。
|
||||||
|
|
||||||
**正式清理:**
|
**正式清理:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mole clean
|
mole clean
|
||||||
```
|
```
|
||||||
|
|
||||||
会清理系统缓存、日志、临时文件等,释放磁盘空间。
|
会清理系统缓存、日志、临时文件等,释放磁盘空间。Mole 很安全,只删除可重新生成的文件。
|
||||||
|
|
||||||
### 卸载应用(彻底删除)
|
### 卸载应用(彻底删除)
|
||||||
|
|
||||||
@@ -144,36 +152,28 @@ mole analyze
|
|||||||
|
|
||||||
## 第五步:注意事项
|
## 第五步:注意事项
|
||||||
|
|
||||||
### 建议做的事
|
### 使用建议
|
||||||
|
|
||||||
- **第一次使用先用 `--dry-run` 预览**,看看会清理什么
|
**推荐:**
|
||||||
- **定期清理**,比如每个月或磁盘快满的时候
|
- 第一次使用先 `--dry-run` 预览
|
||||||
- **卸载应用前确认**,避免误删正在使用的软件
|
- 定期清理(每月一次或磁盘快满时)
|
||||||
|
- 有大型缓存可用 `--whitelist` 保护
|
||||||
|
|
||||||
### 不要做的事
|
**避免:**
|
||||||
|
- 频繁清理(一周一次就够了)
|
||||||
- 不要频繁清理(一周一次足够了)
|
- 运行重要程序时清理
|
||||||
- 不要删除系统应用(工具会自动保护,但还是要注意)
|
|
||||||
- 不要在运行重要程序时清理缓存
|
|
||||||
|
|
||||||
### 安全保障
|
### 安全保障
|
||||||
|
|
||||||
Mole 有智能保护机制:
|
**Mole 只删除可重新生成的缓存和日志,不会删除:**
|
||||||
|
- 应用配置文件(.plist)- 你的设置会保留
|
||||||
|
- 应用数据(Application Support)- 重要文档不受影响
|
||||||
|
- 系统关键文件、IDE 数据、数据库等
|
||||||
|
|
||||||
- 不会删除系统关键文件
|
**白名单保护:** 可以保护特定缓存不被删除
|
||||||
- 会跳过正在运行的应用
|
|
||||||
- 清理前会显示即将删除的内容
|
|
||||||
- 默认保护大型缓存(如 Playwright 浏览器、HuggingFace 模型等)
|
|
||||||
|
|
||||||
如果你有其他需要保护的文件,可以添加到白名单:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 查看默认保护的文件
|
mole clean --whitelist # 交互式选择要保护的缓存
|
||||||
mole clean --whitelist
|
|
||||||
|
|
||||||
# 添加自定义保护
|
|
||||||
mkdir -p ~/.config/mole
|
|
||||||
echo '~/我的重要缓存/*' >> ~/.config/mole/whitelist
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -192,7 +192,9 @@ echo '~/我的重要缓存/*' >> ~/.config/mole/whitelist
|
|||||||
|
|
||||||
### 清理后能恢复吗?
|
### 清理后能恢复吗?
|
||||||
|
|
||||||
一般的缓存文件清理后会自动重新生成,但应用卸载后无法恢复,请谨慎操作。
|
不需要恢复!Mole 只删除缓存和日志,应用会自动重新生成,不影响使用。
|
||||||
|
|
||||||
|
**注意:** 应用卸载后无法恢复(但配置文件会保留)
|
||||||
|
|
||||||
### 多久清理一次比较好?
|
### 多久清理一次比较好?
|
||||||
|
|
||||||
|
|||||||
32
README.md
32
README.md
@@ -44,6 +44,7 @@ brew install tw93/tap/mole
|
|||||||
mole # Interactive menu
|
mole # Interactive menu
|
||||||
mole clean # System cleanup
|
mole clean # System cleanup
|
||||||
mole clean --dry-run # Preview mode
|
mole clean --dry-run # Preview mode
|
||||||
|
mole clean --whitelist # Manage protected caches
|
||||||
mole uninstall # Uninstall apps
|
mole uninstall # Uninstall apps
|
||||||
mole analyze # Disk analyzer
|
mole analyze # Disk analyzer
|
||||||
mole update # Update Mole
|
mole update # Update Mole
|
||||||
@@ -51,11 +52,11 @@ mole --help # Show help
|
|||||||
```
|
```
|
||||||
|
|
||||||
> 💡 New to terminal? Check [小白使用指南](./GUIDE.md) · Homebrew users: `brew upgrade mole` to update
|
> 💡 New to terminal? Check [小白使用指南](./GUIDE.md) · Homebrew users: `brew upgrade mole` to update
|
||||||
> ⚠️ **Recommended:** Always run `mole clean --dry-run` first to preview changes before cleanup
|
> 💡 **Tip:** Run `mole clean --dry-run` to preview, or `mole clean --whitelist` to protect important caches before cleanup
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
### 🧹 Deep System Cleanup
|
### Deep System Cleanup
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ mole clean
|
$ mole clean
|
||||||
@@ -83,17 +84,16 @@ $ mole clean
|
|||||||
====================================================================
|
====================================================================
|
||||||
```
|
```
|
||||||
|
|
||||||
**Protect important files:**
|
**Whitelist Protection:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# View default whitelist (Playwright browsers, HuggingFace models, etc.)
|
mole clean --whitelist # Interactive - select caches to protect
|
||||||
mole clean --whitelist
|
|
||||||
|
|
||||||
# Add custom protection
|
# Default: Playwright browsers, HuggingFace models (always protected)
|
||||||
echo '~/my-important-cache/*' >> ~/.config/mole/whitelist
|
# Or edit: ~/.config/mole/whitelist
|
||||||
```
|
```
|
||||||
|
|
||||||
### 🗑️ Smart App Uninstaller
|
### Smart App Uninstaller
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ mole uninstall
|
$ mole uninstall
|
||||||
@@ -119,7 +119,7 @@ Space freed: 12.8GB
|
|||||||
====================================================================
|
====================================================================
|
||||||
```
|
```
|
||||||
|
|
||||||
### 📊 Disk Space Analyzer
|
### Disk Space Analyzer
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ mole analyze
|
$ mole analyze
|
||||||
@@ -146,15 +146,11 @@ Total: 156.8GB
|
|||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
1. **Will Mole delete important files?** - No. Mole has built-in protection for:
|
1. **Is Mole safe?** - Yes. Mole only deletes caches and logs (regenerable data). Never deletes app settings, user documents, or system files. Run `mole clean --dry-run` to preview before cleanup.
|
||||||
- System-critical files like Input Methods, Dock, and System Preferences
|
|
||||||
- User data from IDEs like JetBrains DataGrip and VS Code, plus database tools
|
2. **How often should I clean?** - Once a month, or when disk space is low.
|
||||||
- License data from paid apps like 1Password and Adobe products
|
|
||||||
- Large downloaded caches like Playwright browsers and HuggingFace models
|
3. **Can I protect specific caches?** - Yes. Use `mole clean --whitelist` to select which caches to keep (Playwright browsers, HuggingFace models are protected by default).
|
||||||
- App settings are only removed when you explicitly uninstall the app
|
|
||||||
2. **Can I undo cleanup operations?** - Cache files are safe to delete and will regenerate automatically. For important data protection, use the whitelist feature via `mole clean --whitelist`.
|
|
||||||
3. **How often should I run cleanup?** - Once a month is sufficient. Run when disk space is low.
|
|
||||||
4. **Is it safe to use?** - Yes. **Always run `mole clean --dry-run` first** to preview what will be deleted before any action.
|
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,12 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Prevent multiple sourcing
|
||||||
|
if [[ -n "${MOLE_COMMON_LOADED:-}" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
readonly MOLE_COMMON_LOADED=1
|
||||||
|
|
||||||
# Color definitions (readonly for safety)
|
# Color definitions (readonly for safety)
|
||||||
readonly ESC=$'\033'
|
readonly ESC=$'\033'
|
||||||
readonly GREEN="${ESC}[0;32m"
|
readonly GREEN="${ESC}[0;32m"
|
||||||
|
|||||||
@@ -31,6 +31,17 @@ paginated_multi_select() {
|
|||||||
selected[i]=false
|
selected[i]=false
|
||||||
done
|
done
|
||||||
|
|
||||||
|
if [[ -n "${MOLE_PRESELECTED_INDICES:-}" ]]; then
|
||||||
|
local cleaned_preselect="${MOLE_PRESELECTED_INDICES//[[:space:]]/}"
|
||||||
|
local -a initial_indices=()
|
||||||
|
IFS=',' read -ra initial_indices <<< "$cleaned_preselect"
|
||||||
|
for idx in "${initial_indices[@]}"; do
|
||||||
|
if [[ "$idx" =~ ^[0-9]+$ && $idx -ge 0 && $idx -lt $total_items ]]; then
|
||||||
|
selected[idx]=true
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
# Cleanup function
|
# Cleanup function
|
||||||
cleanup() {
|
cleanup() {
|
||||||
show_cursor
|
show_cursor
|
||||||
@@ -184,13 +195,18 @@ EOF
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Store result in global variable instead of returning via stdout
|
# Store result in global variable instead of returning via stdout
|
||||||
local result=""
|
local -a selected_indices=()
|
||||||
for ((i = 0; i < total_items; i++)); do
|
for ((i = 0; i < total_items; i++)); do
|
||||||
if [[ ${selected[i]} == true ]]; then
|
if [[ ${selected[i]} == true ]]; then
|
||||||
result="$result $i"
|
selected_indices+=("$i")
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
local final_result="${result# }"
|
|
||||||
|
local final_result=""
|
||||||
|
if [[ ${#selected_indices[@]} -gt 0 ]]; then
|
||||||
|
local IFS=','
|
||||||
|
final_result="${selected_indices[*]}"
|
||||||
|
fi
|
||||||
|
|
||||||
# Remove the trap to avoid cleanup on normal exit
|
# Remove the trap to avoid cleanup on normal exit
|
||||||
trap - EXIT INT TERM
|
trap - EXIT INT TERM
|
||||||
|
|||||||
441
lib/whitelist_manager.sh
Executable file
441
lib/whitelist_manager.sh
Executable file
@@ -0,0 +1,441 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Whitelist management functionality
|
||||||
|
# Shows actual files that would be deleted by dry-run
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Get script directory and source dependencies
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
source "$SCRIPT_DIR/common.sh"
|
||||||
|
source "$SCRIPT_DIR/paginated_menu.sh"
|
||||||
|
|
||||||
|
# Config file path
|
||||||
|
WHITELIST_CONFIG="$HOME/.config/mole/whitelist"
|
||||||
|
|
||||||
|
declare -a DEFAULT_WHITELIST_PATTERNS=(
|
||||||
|
"$HOME/Library/Caches/ms-playwright*"
|
||||||
|
"$HOME/.cache/huggingface*"
|
||||||
|
)
|
||||||
|
|
||||||
|
patterns_equivalent() {
|
||||||
|
local first="${1/#~/$HOME}"
|
||||||
|
local second="${2/#~/$HOME}"
|
||||||
|
|
||||||
|
# Only exact string match, no glob expansion
|
||||||
|
[[ "$first" == "$second" ]] && return 0
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
is_default_pattern() {
|
||||||
|
local candidate="$1"
|
||||||
|
local default_pat
|
||||||
|
for default_pat in "${DEFAULT_WHITELIST_PATTERNS[@]}"; do
|
||||||
|
if patterns_equivalent "$candidate" "$default_pat"; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run dry-run cleanup and collect what would be deleted
|
||||||
|
collect_files_to_be_cleaned() {
|
||||||
|
local clean_sh="$SCRIPT_DIR/../bin/clean.sh"
|
||||||
|
local -a items=()
|
||||||
|
|
||||||
|
echo -e "${BLUE}🔍${NC} Scanning cache files..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Run clean.sh in dry-run mode
|
||||||
|
local temp_output=$(mktemp)
|
||||||
|
echo "" | bash "$clean_sh" --dry-run 2>&1 > "$temp_output" || true
|
||||||
|
|
||||||
|
# Strip ANSI color codes for parsing
|
||||||
|
local temp_plain=$(mktemp)
|
||||||
|
sed $'s/\033\[[0-9;]*m//g' "$temp_output" > "$temp_plain"
|
||||||
|
|
||||||
|
# Parse output: " → Description (size, dry)"
|
||||||
|
local pattern='^[[:space:]]*→[[:space:]]+([^(]+)(\([^)]+\))?[[:space:]]*\(([^,)]+),.*dry\)$'
|
||||||
|
while IFS= read -r line; do
|
||||||
|
if [[ "$line" =~ $pattern ]]; then
|
||||||
|
local description="${BASH_REMATCH[1]}"
|
||||||
|
local size="${BASH_REMATCH[3]}"
|
||||||
|
|
||||||
|
description="${description#${description%%[![:space:]]*}}"
|
||||||
|
description="${description%${description##*[![:space:]]}}"
|
||||||
|
|
||||||
|
[[ "$description" =~ ^Orphaned ]] && continue
|
||||||
|
|
||||||
|
# Find corresponding path from clean.sh
|
||||||
|
local path=""
|
||||||
|
while IFS= read -r src_line; do
|
||||||
|
# Match: safe_clean <path> "<description>"
|
||||||
|
# Path may contain escaped spaces (\ )
|
||||||
|
if [[ "$src_line" =~ safe_clean[[:space:]]+(.+)[[:space:]]+\"$description\" ]]; then
|
||||||
|
path="${BASH_REMATCH[1]}"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done < "$clean_sh"
|
||||||
|
|
||||||
|
path="${path/#\~/$HOME}"
|
||||||
|
[[ -z "$path" || "$path" =~ \$ ]] && continue
|
||||||
|
|
||||||
|
items+=("$path|$description|$size")
|
||||||
|
fi
|
||||||
|
done < "$temp_plain"
|
||||||
|
|
||||||
|
rm -f "$temp_output" "$temp_plain"
|
||||||
|
|
||||||
|
# Return early if no items found
|
||||||
|
if [[ ${#items[@]} -eq 0 ]]; then
|
||||||
|
AVAILABLE_CACHE_ITEMS=()
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove duplicates
|
||||||
|
local -a unique_items=()
|
||||||
|
local -a seen_descriptions=()
|
||||||
|
|
||||||
|
for item in "${items[@]}"; do
|
||||||
|
IFS='|' read -r path desc size <<< "$item"
|
||||||
|
local is_duplicate=false
|
||||||
|
if [[ ${#seen_descriptions[@]} -gt 0 ]]; then
|
||||||
|
for seen in "${seen_descriptions[@]}"; do
|
||||||
|
[[ "$desc" == "$seen" ]] && is_duplicate=true && break
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$is_duplicate" == "false" ]]; then
|
||||||
|
unique_items+=("$item")
|
||||||
|
seen_descriptions+=("$desc")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Sort by size (largest first)
|
||||||
|
local -a sorted_items=()
|
||||||
|
if [[ ${#unique_items[@]} -gt 0 ]]; then
|
||||||
|
while IFS= read -r item; do
|
||||||
|
sorted_items+=("$item")
|
||||||
|
done < <(
|
||||||
|
for item in "${unique_items[@]}"; do
|
||||||
|
IFS='|' read -r path desc size <<< "$item"
|
||||||
|
local size_kb=0
|
||||||
|
if [[ "$size" =~ ([0-9.]+)GB ]]; then
|
||||||
|
size_kb=$(echo "${BASH_REMATCH[1]}" | awk '{printf "%d", $1 * 1024 * 1024}')
|
||||||
|
elif [[ "$size" =~ ([0-9.]+)MB ]]; then
|
||||||
|
size_kb=$(echo "${BASH_REMATCH[1]}" | awk '{printf "%d", $1 * 1024}')
|
||||||
|
elif [[ "$size" =~ ([0-9.]+)KB ]]; then
|
||||||
|
size_kb=$(echo "${BASH_REMATCH[1]}" | awk '{printf "%d", $1}')
|
||||||
|
fi
|
||||||
|
printf "%010d|%s\n" "$size_kb" "$item"
|
||||||
|
done | sort -rn | cut -d'|' -f2-
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Safe assignment for empty array
|
||||||
|
if [[ ${#sorted_items[@]} -gt 0 ]]; then
|
||||||
|
AVAILABLE_CACHE_ITEMS=("${sorted_items[@]}")
|
||||||
|
else
|
||||||
|
AVAILABLE_CACHE_ITEMS=()
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
declare -a AVAILABLE_CACHE_ITEMS=()
|
||||||
|
|
||||||
|
load_whitelist() {
|
||||||
|
local -a patterns=()
|
||||||
|
|
||||||
|
# Always include default patterns
|
||||||
|
patterns=("${DEFAULT_WHITELIST_PATTERNS[@]}")
|
||||||
|
|
||||||
|
# Add user-defined patterns from config file
|
||||||
|
if [[ -f "$WHITELIST_CONFIG" ]]; then
|
||||||
|
while IFS= read -r line; do
|
||||||
|
line="${line#${line%%[![:space:]]*}}"
|
||||||
|
line="${line%${line##*[![:space:]]}}"
|
||||||
|
[[ -z "$line" || "$line" =~ ^# ]] && continue
|
||||||
|
patterns+=("$line")
|
||||||
|
done < "$WHITELIST_CONFIG"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ${#patterns[@]} -gt 0 ]]; then
|
||||||
|
local -a unique_patterns=()
|
||||||
|
for pattern in "${patterns[@]}"; do
|
||||||
|
local duplicate="false"
|
||||||
|
if [[ ${#unique_patterns[@]} -gt 0 ]]; then
|
||||||
|
for existing in "${unique_patterns[@]}"; do
|
||||||
|
if patterns_equivalent "$pattern" "$existing"; then
|
||||||
|
duplicate="true"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
[[ "$duplicate" == "true" ]] && continue
|
||||||
|
unique_patterns+=("$pattern")
|
||||||
|
done
|
||||||
|
CURRENT_WHITELIST_PATTERNS=("${unique_patterns[@]}")
|
||||||
|
else
|
||||||
|
CURRENT_WHITELIST_PATTERNS=()
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
is_whitelisted() {
|
||||||
|
local pattern="$1"
|
||||||
|
local check_pattern="${pattern/#\~/$HOME}"
|
||||||
|
|
||||||
|
if [[ ${#CURRENT_WHITELIST_PATTERNS[@]} -eq 0 ]]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for existing in "${CURRENT_WHITELIST_PATTERNS[@]}"; do
|
||||||
|
local existing_expanded="${existing/#\~/$HOME}"
|
||||||
|
if [[ "$check_pattern" == "$existing_expanded" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [[ "$check_pattern" == $existing_expanded ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
format_whitelist_item() {
|
||||||
|
local description="$1" size="$2" is_protected="$3"
|
||||||
|
local desc_display="$description"
|
||||||
|
[[ ${#description} -gt 40 ]] && desc_display="${description:0:37}..."
|
||||||
|
local size_display=$(printf "%-15s" "$size")
|
||||||
|
local status=""
|
||||||
|
[[ "$is_protected" == "true" ]] && status=" ${GREEN}[Protected]${NC}"
|
||||||
|
printf "%-40s %s%s" "$desc_display" "$size_display" "$status"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get friendly description for a path pattern
|
||||||
|
get_description_for_pattern() {
|
||||||
|
local pattern="$1"
|
||||||
|
local desc=""
|
||||||
|
|
||||||
|
# Hardcoded descriptions for common patterns
|
||||||
|
case "$pattern" in
|
||||||
|
*"ms-playwright"*)
|
||||||
|
echo "Playwright Browser"
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
*"huggingface"*)
|
||||||
|
echo "HuggingFace Model"
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Try to match with safe_clean in clean.sh
|
||||||
|
# Use fuzzy matching by removing trailing /* or *
|
||||||
|
local pattern_base="${pattern%/\*}"
|
||||||
|
pattern_base="${pattern_base%\*}"
|
||||||
|
|
||||||
|
while IFS= read -r line; do
|
||||||
|
if [[ "$line" =~ safe_clean[[:space:]]+(.+)[[:space:]]+\"([^\"]+)\" ]]; then
|
||||||
|
local clean_path="${BASH_REMATCH[1]}"
|
||||||
|
local clean_desc="${BASH_REMATCH[2]}"
|
||||||
|
clean_path="${clean_path/#\~/$HOME}"
|
||||||
|
|
||||||
|
# Remove trailing /* or * for comparison
|
||||||
|
local clean_base="${clean_path%/\*}"
|
||||||
|
clean_base="${clean_base%\*}"
|
||||||
|
|
||||||
|
# Check if base paths match
|
||||||
|
if [[ "$pattern_base" == "$clean_base" || "$clean_path" == "$pattern" || "$pattern" == "$clean_path" ]]; then
|
||||||
|
echo "$clean_desc"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done < "$SCRIPT_DIR/../bin/clean.sh"
|
||||||
|
|
||||||
|
# If no match found, return short path
|
||||||
|
echo "${pattern/#$HOME/~}"
|
||||||
|
}
|
||||||
|
|
||||||
|
manage_whitelist() {
|
||||||
|
clear
|
||||||
|
echo ""
|
||||||
|
echo -e "${PURPLE}📋 Whitelist Manager${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Load user-defined whitelist
|
||||||
|
CURRENT_WHITELIST_PATTERNS=()
|
||||||
|
load_whitelist
|
||||||
|
|
||||||
|
echo "Select the cache files that need to be protected"
|
||||||
|
echo -e "${GRAY}Protected items are pre-selected. You can also edit ${WHITELIST_CONFIG} directly.${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
collect_files_to_be_cleaned
|
||||||
|
|
||||||
|
# Add items from config that are not in the scan results
|
||||||
|
local -a all_items=()
|
||||||
|
if [[ ${#AVAILABLE_CACHE_ITEMS[@]} -gt 0 ]]; then
|
||||||
|
all_items=("${AVAILABLE_CACHE_ITEMS[@]}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add saved patterns that are not in scan results
|
||||||
|
if [[ ${#CURRENT_WHITELIST_PATTERNS[@]} -gt 0 ]]; then
|
||||||
|
for pattern in "${CURRENT_WHITELIST_PATTERNS[@]}"; do
|
||||||
|
local pattern_expanded="${pattern/#\~/$HOME}"
|
||||||
|
local found="false"
|
||||||
|
|
||||||
|
if [[ ${#all_items[@]} -gt 0 ]]; then
|
||||||
|
for item in "${all_items[@]}"; do
|
||||||
|
IFS='|' read -r path _ _ <<< "$item"
|
||||||
|
if patterns_equivalent "$path" "$pattern_expanded"; then
|
||||||
|
found="true"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$found" == "false" ]]; then
|
||||||
|
local desc=$(get_description_for_pattern "$pattern_expanded")
|
||||||
|
all_items+=("$pattern_expanded|$desc|0B")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ${#all_items[@]} -eq 0 ]]; then
|
||||||
|
echo -e "${GREEN}✨${NC} No cache files found - system is clean!"
|
||||||
|
echo ""
|
||||||
|
echo "Press any key to exit..."
|
||||||
|
read -n 1 -s
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update global array with all items
|
||||||
|
AVAILABLE_CACHE_ITEMS=("${all_items[@]}")
|
||||||
|
|
||||||
|
echo -e "${GREEN}✓${NC} Found ${#AVAILABLE_CACHE_ITEMS[@]} items"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
local -a menu_options=()
|
||||||
|
local -a preselected_indices=()
|
||||||
|
local index=0
|
||||||
|
|
||||||
|
for item in "${AVAILABLE_CACHE_ITEMS[@]}"; do
|
||||||
|
IFS='|' read -r path description size <<< "$item"
|
||||||
|
local is_protected="false"
|
||||||
|
if is_whitelisted "$path"; then
|
||||||
|
is_protected="true"
|
||||||
|
preselected_indices+=("$index")
|
||||||
|
fi
|
||||||
|
menu_options+=("$(format_whitelist_item "$description" "$size" "$is_protected")")
|
||||||
|
((index++))
|
||||||
|
done
|
||||||
|
|
||||||
|
echo -e "${GRAY}↑↓ Navigate | Space Toggle | Enter Save | Q Quit${NC}"
|
||||||
|
|
||||||
|
if [[ ${#preselected_indices[@]} -gt 0 ]]; then
|
||||||
|
local IFS=','
|
||||||
|
MOLE_PRESELECTED_INDICES="${preselected_indices[*]}"
|
||||||
|
else
|
||||||
|
unset MOLE_PRESELECTED_INDICES
|
||||||
|
fi
|
||||||
|
|
||||||
|
MOLE_SELECTION_RESULT=""
|
||||||
|
paginated_multi_select "Select items to protect" "${menu_options[@]}"
|
||||||
|
unset MOLE_PRESELECTED_INDICES
|
||||||
|
local exit_code=$?
|
||||||
|
|
||||||
|
if [[ $exit_code -ne 0 ]]; then
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}Cancelled${NC} - No changes made"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local -a selected_indices=()
|
||||||
|
if [[ -n "$MOLE_SELECTION_RESULT" ]]; then
|
||||||
|
IFS=',' read -ra selected_indices <<< "$MOLE_SELECTION_RESULT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
save_whitelist "${selected_indices[@]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
save_whitelist() {
|
||||||
|
local -a selected_indices=("$@")
|
||||||
|
mkdir -p "$(dirname "$WHITELIST_CONFIG")"
|
||||||
|
|
||||||
|
local -a selected_patterns=()
|
||||||
|
local selected_default_count=0
|
||||||
|
local selected_custom_count=0
|
||||||
|
|
||||||
|
for idx in "${selected_indices[@]}"; do
|
||||||
|
if [[ $idx -ge 0 && $idx -lt ${#AVAILABLE_CACHE_ITEMS[@]} ]]; then
|
||||||
|
local item="${AVAILABLE_CACHE_ITEMS[$idx]}"
|
||||||
|
IFS='|' read -r path description size <<< "$item"
|
||||||
|
local portable_path="${path/#$HOME/~}"
|
||||||
|
|
||||||
|
local duplicate="false"
|
||||||
|
if [[ ${#selected_patterns[@]} -gt 0 ]]; then
|
||||||
|
for existing in "${selected_patterns[@]}"; do
|
||||||
|
if patterns_equivalent "$portable_path" "$existing"; then
|
||||||
|
duplicate="true"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
[[ "$duplicate" == "true" ]] && continue
|
||||||
|
|
||||||
|
if is_default_pattern "$portable_path"; then
|
||||||
|
((selected_default_count++))
|
||||||
|
else
|
||||||
|
((selected_custom_count++))
|
||||||
|
fi
|
||||||
|
|
||||||
|
selected_patterns+=("$portable_path")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
cat > "$WHITELIST_CONFIG" << 'EOF'
|
||||||
|
# Mole Whitelist - Protected paths won't be deleted
|
||||||
|
# Default: Playwright browsers, HuggingFace models
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Only save custom (non-default) patterns
|
||||||
|
local -a custom_patterns=()
|
||||||
|
for pattern in "${selected_patterns[@]}"; do
|
||||||
|
if ! is_default_pattern "$pattern"; then
|
||||||
|
custom_patterns+=("$pattern")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ ${#custom_patterns[@]} -gt 0 ]]; then
|
||||||
|
printf '\n' >> "$WHITELIST_CONFIG"
|
||||||
|
for pattern in "${custom_patterns[@]}"; do
|
||||||
|
echo "$pattern" >> "$WHITELIST_CONFIG"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
local total_count=${#selected_patterns[@]}
|
||||||
|
local -a summary_parts=()
|
||||||
|
if [[ $selected_default_count -gt 0 ]]; then
|
||||||
|
local default_label="default"
|
||||||
|
[[ $selected_default_count -ne 1 ]] && default_label+="s"
|
||||||
|
summary_parts+=("$selected_default_count $default_label")
|
||||||
|
fi
|
||||||
|
if [[ $selected_custom_count -gt 0 ]]; then
|
||||||
|
local custom_label="custom"
|
||||||
|
[[ $selected_custom_count -ne 1 ]] && custom_label+="s"
|
||||||
|
summary_parts+=("$selected_custom_count $custom_label")
|
||||||
|
fi
|
||||||
|
|
||||||
|
local summary=""
|
||||||
|
if [[ ${#summary_parts[@]} -gt 0 ]]; then
|
||||||
|
summary=" (${summary_parts[0]}"
|
||||||
|
for ((i = 1; i < ${#summary_parts[@]}; i++)); do
|
||||||
|
summary+=", ${summary_parts[$i]}"
|
||||||
|
done
|
||||||
|
summary+=")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}✓${NC} Protected $total_count items${summary}"
|
||||||
|
echo -e "${GRAY}Config: ${WHITELIST_CONFIG}${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
manage_whitelist
|
||||||
|
fi
|
||||||
1
mole
1
mole
@@ -130,6 +130,7 @@ show_help() {
|
|||||||
printf " %s%-28s%s %s\n" "$GREEN" "mole" "$NC" "Interactive main menu"
|
printf " %s%-28s%s %s\n" "$GREEN" "mole" "$NC" "Interactive main menu"
|
||||||
printf " %s%-28s%s %s\n" "$GREEN" "mole clean" "$NC" "Deeper system cleanup"
|
printf " %s%-28s%s %s\n" "$GREEN" "mole clean" "$NC" "Deeper system cleanup"
|
||||||
printf " %s%-28s%s %s\n" "$GREEN" "mole clean --dry-run" "$NC" "Preview cleanup (no deletions)"
|
printf " %s%-28s%s %s\n" "$GREEN" "mole clean --dry-run" "$NC" "Preview cleanup (no deletions)"
|
||||||
|
printf " %s%-28s%s %s\n" "$GREEN" "mole clean --whitelist" "$NC" "Manage protected caches"
|
||||||
printf " %s%-28s%s %s\n" "$GREEN" "mole uninstall" "$NC" "Remove applications completely"
|
printf " %s%-28s%s %s\n" "$GREEN" "mole uninstall" "$NC" "Remove applications completely"
|
||||||
printf " %s%-28s%s %s\n" "$GREEN" "mole analyze" "$NC" "Interactive disk space explorer"
|
printf " %s%-28s%s %s\n" "$GREEN" "mole analyze" "$NC" "Interactive disk space explorer"
|
||||||
printf " %s%-28s%s %s\n" "$GREEN" "mole update" "$NC" "Update Mole to the latest version"
|
printf " %s%-28s%s %s\n" "$GREEN" "mole update" "$NC" "Update Mole to the latest version"
|
||||||
|
|||||||
Reference in New Issue
Block a user