Skip to content

Commit 5fb3a0e

Browse files
committed
feat: Complete Phase 2 with Docker and Java/Kotlin scanning support
Add comprehensive support for Docker and Java/Kotlin ecosystems: Scanner Implementation: - Create docker.go scanner with CLI integration (docker system df) - Create java.go scanner for Maven/Gradle caches and build artifacts - Handle Docker pseudo-paths (docker:images, docker:containers, etc.) - Integrate all Phase 1 + Phase 2 scanners in parallel execution Cleaner Enhancements: - Add cleanDocker() method using Docker CLI commands (prune operations) - Update safety validation to allow Docker pseudo-paths - Support special cleanup for Docker resources via CLI CLI Updates: - Add flags: --python, --rust, --go, --homebrew, --docker, --java - Update scan.go and clean.go with new ecosystem flags - Update help documentation with all 10 ecosystems TUI Improvements: - Add category detection for Python, Rust, Go, Homebrew, Docker, Java - Add color badges: Python (blue), Rust (orange), Go (cyan), Homebrew (yellow), Docker (blue), Java (orange) - Update rescanItems() to use DefaultScanOptions() for all ecosystems Total ecosystems supported: 10 (Xcode, Android, Node.js, Flutter, Python, Rust, Go, Homebrew, Docker, Java) Potential disk reclaim: 30-100+ GB per developer
1 parent 6ebbe89 commit 5fb3a0e

File tree

10 files changed

+582
-31
lines changed

10 files changed

+582
-31
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,7 @@ CLAUDE.md
8080
# docs
8181
design-mockups
8282
/plans/templates/
83+
/.idea/git_toolbox_prj.xml
84+
/.idea/mac-dev-cleaner-cli.iml
85+
/.idea/modules.xml
86+
/.idea/vcs.xml

cmd/root/clean.go

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,19 @@ import (
1616
)
1717

1818
var (
19-
dryRun bool
20-
confirmFlag bool
21-
cleanIOS bool
22-
cleanAndroid bool
23-
cleanNode bool
24-
cleanFlutter bool
25-
useTUI bool
19+
dryRun bool
20+
confirmFlag bool
21+
cleanIOS bool
22+
cleanAndroid bool
23+
cleanNode bool
24+
cleanFlutter bool
25+
cleanPython bool
26+
cleanRust bool
27+
cleanGo bool
28+
cleanHomebrew bool
29+
cleanDocker bool
30+
cleanJava bool
31+
useTUI bool
2632
)
2733

2834
// cleanCmd represents the clean command
@@ -60,6 +66,12 @@ Flags:
6066
--android Clean Android/Gradle artifacts only
6167
--node Clean Node.js artifacts only
6268
--flutter Clean Flutter/Dart artifacts only
69+
--python Clean Python caches
70+
--rust Clean Rust/Cargo caches
71+
--go Clean Go caches
72+
--homebrew Clean Homebrew caches
73+
--docker Clean Docker images, containers, volumes
74+
--java Clean Maven/Gradle caches
6375
--no-tui, -T Disable TUI, use simple text mode
6476
--tui Use interactive TUI mode (default: true)
6577
@@ -87,6 +99,12 @@ func init() {
8799
cleanCmd.Flags().BoolVar(&cleanAndroid, "android", false, "Clean Android/Gradle artifacts only")
88100
cleanCmd.Flags().BoolVar(&cleanNode, "node", false, "Clean Node.js artifacts only")
89101
cleanCmd.Flags().BoolVar(&cleanFlutter, "flutter", false, "Clean Flutter/Dart artifacts only")
102+
cleanCmd.Flags().BoolVar(&cleanPython, "python", false, "Clean Python caches")
103+
cleanCmd.Flags().BoolVar(&cleanRust, "rust", false, "Clean Rust/Cargo caches")
104+
cleanCmd.Flags().BoolVar(&cleanGo, "go", false, "Clean Go caches")
105+
cleanCmd.Flags().BoolVar(&cleanHomebrew, "homebrew", false, "Clean Homebrew caches")
106+
cleanCmd.Flags().BoolVar(&cleanDocker, "docker", false, "Clean Docker images, containers, volumes")
107+
cleanCmd.Flags().BoolVar(&cleanJava, "java", false, "Clean Maven/Gradle caches")
90108
cleanCmd.Flags().BoolVar(&useTUI, "tui", true, "Use interactive TUI mode (default)")
91109
cleanCmd.Flags().BoolP("no-tui", "T", false, "Disable TUI, use simple text mode")
92110
}
@@ -114,16 +132,23 @@ func runClean(cmd *cobra.Command, args []string) {
114132
MaxDepth: 3,
115133
}
116134

117-
if cleanIOS || cleanAndroid || cleanNode || cleanFlutter {
135+
specificFlagSet := cleanIOS || cleanAndroid || cleanNode || cleanFlutter ||
136+
cleanPython || cleanRust || cleanGo || cleanHomebrew ||
137+
cleanDocker || cleanJava
138+
139+
if specificFlagSet {
118140
opts.IncludeXcode = cleanIOS
119141
opts.IncludeAndroid = cleanAndroid
120142
opts.IncludeNode = cleanNode
121143
opts.IncludeFlutter = cleanFlutter
144+
opts.IncludePython = cleanPython
145+
opts.IncludeRust = cleanRust
146+
opts.IncludeGo = cleanGo
147+
opts.IncludeHomebrew = cleanHomebrew
148+
opts.IncludeDocker = cleanDocker
149+
opts.IncludeJava = cleanJava
122150
} else {
123-
opts.IncludeXcode = true
124-
opts.IncludeAndroid = true
125-
opts.IncludeNode = true
126-
opts.IncludeFlutter = true
151+
opts = types.DefaultScanOptions()
127152
}
128153

129154
ui.PrintHeader("Scanning for development artifacts...")

cmd/root/scan.go

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,18 @@ import (
1212
)
1313

1414
var (
15-
scanIOS bool
16-
scanAndroid bool
17-
scanNode bool
18-
scanFlutter bool
19-
scanAll bool
20-
scanTUI bool
15+
scanIOS bool
16+
scanAndroid bool
17+
scanNode bool
18+
scanFlutter bool
19+
scanPython bool
20+
scanRust bool
21+
scanGo bool
22+
scanHomebrew bool
23+
scanDocker bool
24+
scanJava bool
25+
scanAll bool
26+
scanTUI bool
2127
)
2228

2329
// scanCmd represents the scan command
@@ -35,20 +41,38 @@ Categories Scanned:
3541
• Android (Gradle caches, SDK system images)
3642
• Node.js (node_modules, npm/yarn/pnpm/bun caches)
3743
• Flutter (build artifacts, .pub-cache, .dart_tool)
44+
• Python (pip/poetry/uv caches, venv, __pycache__)
45+
• Rust (Cargo registry/git, target directories)
46+
• Go (build cache, module cache)
47+
• Homebrew (download caches)
48+
• Docker (unused images, containers, volumes, build cache)
49+
• Java/Kotlin (Maven .m2, Gradle caches, build directories)
3850
3951
Examples:
4052
dev-cleaner scan # Scan all, launch TUI (default)
4153
dev-cleaner scan --ios # Scan iOS/Xcode only
4254
dev-cleaner scan --android # Scan Android only
4355
dev-cleaner scan --node # Scan Node.js only
4456
dev-cleaner scan --flutter # Scan Flutter only
57+
dev-cleaner scan --python # Scan Python only
58+
dev-cleaner scan --rust # Scan Rust/Cargo only
59+
dev-cleaner scan --go # Scan Go only
60+
dev-cleaner scan --homebrew # Scan Homebrew only
61+
dev-cleaner scan --docker # Scan Docker only
62+
dev-cleaner scan --java # Scan Java/Maven/Gradle only
4563
dev-cleaner scan --no-tui # Text output without TUI
4664
4765
Flags:
4866
--ios Scan iOS/Xcode artifacts only
4967
--android Scan Android/Gradle artifacts only
5068
--node Scan Node.js artifacts only
5169
--flutter Scan Flutter/Dart artifacts only
70+
--python Scan Python caches and virtualenvs
71+
--rust Scan Rust/Cargo caches and targets
72+
--go Scan Go build and module caches
73+
--homebrew Scan Homebrew caches
74+
--docker Scan Docker images, containers, volumes
75+
--java Scan Maven/Gradle caches and build dirs
5276
--no-tui, -T Disable TUI, show simple text output
5377
--all Scan all categories (default: true)
5478
@@ -69,6 +93,12 @@ func init() {
6993
scanCmd.Flags().BoolVar(&scanAndroid, "android", false, "Scan Android/Gradle artifacts only")
7094
scanCmd.Flags().BoolVar(&scanNode, "node", false, "Scan Node.js artifacts only")
7195
scanCmd.Flags().BoolVar(&scanFlutter, "flutter", false, "Scan Flutter/Dart artifacts only")
96+
scanCmd.Flags().BoolVar(&scanPython, "python", false, "Scan Python caches (pip, poetry, venv, __pycache__)")
97+
scanCmd.Flags().BoolVar(&scanRust, "rust", false, "Scan Rust/Cargo caches and target directories")
98+
scanCmd.Flags().BoolVar(&scanGo, "go", false, "Scan Go build and module caches")
99+
scanCmd.Flags().BoolVar(&scanHomebrew, "homebrew", false, "Scan Homebrew caches")
100+
scanCmd.Flags().BoolVar(&scanDocker, "docker", false, "Scan Docker images, containers, volumes")
101+
scanCmd.Flags().BoolVar(&scanJava, "java", false, "Scan Maven/Gradle caches and build dirs")
72102
scanCmd.Flags().BoolVar(&scanAll, "all", true, "Scan all categories (default)")
73103
scanCmd.Flags().BoolVar(&scanTUI, "tui", true, "Launch interactive TUI (default)")
74104
scanCmd.Flags().BoolP("no-tui", "T", false, "Disable TUI, show text output")
@@ -87,17 +117,24 @@ func runScan(cmd *cobra.Command, args []string) {
87117
}
88118

89119
// If any specific flag is set, use only those
90-
if scanIOS || scanAndroid || scanNode || scanFlutter {
120+
specificFlagSet := scanIOS || scanAndroid || scanNode || scanFlutter ||
121+
scanPython || scanRust || scanGo || scanHomebrew ||
122+
scanDocker || scanJava
123+
124+
if specificFlagSet {
91125
opts.IncludeXcode = scanIOS
92126
opts.IncludeAndroid = scanAndroid
93127
opts.IncludeNode = scanNode
94128
opts.IncludeFlutter = scanFlutter
129+
opts.IncludePython = scanPython
130+
opts.IncludeRust = scanRust
131+
opts.IncludeGo = scanGo
132+
opts.IncludeHomebrew = scanHomebrew
133+
opts.IncludeDocker = scanDocker
134+
opts.IncludeJava = scanJava
95135
} else {
96136
// Default: scan all
97-
opts.IncludeXcode = true
98-
opts.IncludeAndroid = true
99-
opts.IncludeNode = true
100-
opts.IncludeFlutter = true
137+
opts = types.DefaultScanOptions()
101138
}
102139

103140
ui.PrintHeader("Scanning for development artifacts...")

dev-cleaner

557 KB
Binary file not shown.

internal/cleaner/cleaner.go

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import (
55
"fmt"
66
"log"
77
"os"
8+
"os/exec"
89
"path/filepath"
10+
"strings"
911
"time"
1012

1113
"github.com/thanhdevapp/dev-cleaner/pkg/types"
@@ -72,6 +74,13 @@ func (c *Cleaner) Clean(results []types.ScanResult) ([]CleanResult, error) {
7274
var cleanResults []CleanResult
7375

7476
for _, result := range results {
77+
// Handle Docker paths specially
78+
if strings.HasPrefix(result.Path, "docker:") {
79+
cleanResult := c.cleanDocker(result)
80+
cleanResults = append(cleanResults, cleanResult)
81+
continue
82+
}
83+
7584
// Validate path safety
7685
if err := ValidatePath(result.Path); err != nil {
7786
cleanResults = append(cleanResults, CleanResult{
@@ -93,7 +102,7 @@ func (c *Cleaner) Clean(results []types.ScanResult) ([]CleanResult, error) {
93102
})
94103
} else {
95104
c.logger.Printf("[DELETE] Removing: %s (%.2f MB)\n", result.Path, float64(result.Size)/(1024*1024))
96-
105+
97106
if err := os.RemoveAll(result.Path); err != nil {
98107
c.logger.Printf("[ERROR] Failed to delete %s: %v\n", result.Path, err)
99108
cleanResults = append(cleanResults, CleanResult{
@@ -116,6 +125,59 @@ func (c *Cleaner) Clean(results []types.ScanResult) ([]CleanResult, error) {
116125
return cleanResults, nil
117126
}
118127

128+
// cleanDocker handles Docker resource cleanup via CLI
129+
func (c *Cleaner) cleanDocker(result types.ScanResult) CleanResult {
130+
resourceType := strings.TrimPrefix(result.Path, "docker:")
131+
132+
if c.dryRun {
133+
c.logger.Printf("[DRY-RUN] Would clean Docker %s (%.2f MB)\n", resourceType, float64(result.Size)/(1024*1024))
134+
return CleanResult{
135+
Path: result.Path,
136+
Size: result.Size,
137+
Success: true,
138+
WasDryRun: true,
139+
}
140+
}
141+
142+
var cmd *exec.Cmd
143+
switch resourceType {
144+
case "images":
145+
cmd = exec.Command("docker", "image", "prune", "-a", "-f")
146+
case "containers":
147+
cmd = exec.Command("docker", "container", "prune", "-f")
148+
case "local-volumes":
149+
cmd = exec.Command("docker", "volume", "prune", "-f")
150+
case "build-cache":
151+
cmd = exec.Command("docker", "builder", "prune", "-a", "-f")
152+
default:
153+
return CleanResult{
154+
Path: result.Path,
155+
Size: result.Size,
156+
Success: false,
157+
Error: fmt.Errorf("unknown docker resource type: %s", resourceType),
158+
}
159+
}
160+
161+
c.logger.Printf("[DELETE] Running: %s\n", strings.Join(cmd.Args, " "))
162+
163+
if err := cmd.Run(); err != nil {
164+
c.logger.Printf("[ERROR] Docker cleanup failed: %v\n", err)
165+
return CleanResult{
166+
Path: result.Path,
167+
Size: result.Size,
168+
Success: false,
169+
Error: err,
170+
}
171+
}
172+
173+
c.logger.Printf("[SUCCESS] Docker %s cleaned at %s\n", resourceType, time.Now().Format(time.RFC3339))
174+
return CleanResult{
175+
Path: result.Path,
176+
Size: result.Size,
177+
Success: true,
178+
}
179+
}
180+
119181
// TotalSize calculates total size from results
120182
func TotalSize(results []types.ScanResult) int64 {
121183
var total int64

internal/cleaner/safety.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ var protectedPatterns = []string{
3232

3333
// ValidatePath checks if a path is safe to delete
3434
func ValidatePath(path string) error {
35+
// Allow Docker pseudo-paths
36+
if strings.HasPrefix(path, "docker:") {
37+
return nil
38+
}
39+
3540
// Must be an absolute path
3641
if !strings.HasPrefix(path, "/") {
3742
return fmt.Errorf("path must be absolute: %s", path)

0 commit comments

Comments
 (0)