git-cleaner parity: add config/init, JSON output, and cleanup UX improvements#72
git-cleaner parity: add config/init, JSON output, and cleanup UX improvements#72markcallen wants to merge 1 commit intomainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR brings git-cleaner closer to feature parity with the other repo CLIs by adding optional YAML config support, a machine-readable JSON reporting mode, and cleanup UX improvements to better support automation and interactive use.
Changes:
- Add YAML config support (
--config,--init,--force) anddefaultScanPathfallback when--scanis omitted. - Add
--jsonreport output (including post-clean disk savings) and redirect cleanup subprocess output to stderr in JSON mode. - Add cleanup confirmation UX (
--yes+ interactive prompt) and optional.git-size percentage reporting (--show-pct).
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
git-cleaner/main.go |
Implements config IO, JSON reporting, confirmation prompt, and .git percent enrichment. |
git-cleaner/main_test.go |
Updates tests for signature changes and adds config/scan-path resolution tests. |
git-cleaner/README.md |
Documents new flags and adds examples for config init and JSON usage. |
git-cleaner/go.mod |
Adds YAML dependency. |
git-cleaner/go.sum |
Updates sums for added dependencies. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if *flagJSON { | ||
| if *flagClean { | ||
| if !*flagYes { | ||
| report.Warnings = append(report.Warnings, "cleanup skipped: confirmation required (use --yes with --clean in non-interactive mode)") |
There was a problem hiding this comment.
In --json + --clean mode when --yes is not provided, cleanup is skipped but the report still has DryRun: false (since --clean was set) and the program exits successfully. This makes the JSON output ambiguous/misleading for CI and automation. Consider either (a) treating --json --clean without --yes as an error (non-zero exit) or (b) reflecting the skip in the report (e.g., set DryRun back to true or add an explicit cleanup_performed field).
| report.Warnings = append(report.Warnings, "cleanup skipped: confirmation required (use --yes with --clean in non-interactive mode)") | |
| report.Warnings = append(report.Warnings, "cleanup skipped: confirmation required (use --yes with --clean in non-interactive mode)") | |
| // Reflect that no cleanup was actually performed, even though --clean was requested. | |
| report.DryRun = true |
| // Initial scan | ||
| findings := scanDirectory(scanPath) | ||
| if *flagShowPct { | ||
| _ = enrichWithRepoPercent(findings) |
There was a problem hiding this comment.
enrichWithRepoPercent returns warnings, but they’re currently discarded. This can silently produce missing/zero git_percent values without surfacing why. Consider appending these warnings to the JSON report (and/or printing them to stderr in table mode).
| _ = enrichWithRepoPercent(findings) | |
| if warnings := enrichWithRepoPercent(findings); len(warnings) > 0 { | |
| for _, w := range warnings { | |
| fmt.Fprintln(os.Stderr, "Warning:", w) | |
| } | |
| } |
| time.Sleep(100 * time.Millisecond) | ||
| afterFindings := scanDirectory(scanPath) | ||
| if *flagShowPct { | ||
| _ = enrichWithRepoPercent(afterFindings) |
There was a problem hiding this comment.
enrichWithRepoPercent warnings are also discarded in the non-JSON post-clean rescan. Even in table mode, users may want to know why the Git % column is missing/0 for some repos; consider printing these warnings to stderr.
| _ = enrichWithRepoPercent(afterFindings) | |
| warnings := enrichWithRepoPercent(afterFindings) | |
| for _, w := range warnings { | |
| fmt.Fprintln(os.Stderr, "Warning:", w) | |
| } |
| if *flagJSON { | ||
| if *flagClean { | ||
| if !*flagYes { |
There was a problem hiding this comment.
New --json behavior (including --json --clean disk savings reporting and the --yes gating) doesn’t appear to have automated tests. Adding a test that validates stdout is parseable JSON (and includes expected fields for both scan-only and clean runs) would help prevent regressions.
| } | ||
| os.Exit(1) | ||
| } | ||
|
|
There was a problem hiding this comment.
In non-JSON mode, the "Scanning ..." message is printed after scanDirectory(scanPath) has already completed, which makes the output misleading (and delays feedback for large scans). Consider printing the scanning header before starting the walk (and before any optional --show-pct enrichment).
| if !*flagJSON { | |
| fmt.Printf("Scanning %s ...\n", scanPath) | |
| } |
| if *flagShowPct { | ||
| _ = enrichWithRepoPercent(afterFindings) | ||
| } |
There was a problem hiding this comment.
enrichWithRepoPercent warnings are ignored again for the post-clean rescan. If repo size inspection fails for any repo, it would be helpful to carry those warnings into the JSON report / stderr so users understand missing git_percent values.
| b, _ := json.MarshalIndent(report, "", " ") | ||
| fmt.Println(string(b)) | ||
| return |
There was a problem hiding this comment.
Errors from json.MarshalIndent are ignored here as well. Please check the error and fail fast (non-zero exit) if JSON encoding fails, rather than printing an empty/invalid payload.
| b, _ := json.MarshalIndent(report, "", " ") | ||
| fmt.Println(string(b)) |
There was a problem hiding this comment.
Errors from json.MarshalIndent are ignored here too. This is the last output path in --json mode, so it’s especially important to handle the error and set a failing exit code if marshalling fails.
| ```bash | ||
| ./build/git-cleaner --init | ||
| ./build/git-cleaner | ||
| ``` |
There was a problem hiding this comment.
This example implies ./build/git-cleaner will work immediately after --init, but the starter config defaults to ~/src and the tool errors if the path doesn’t exist. Consider adjusting the docs to mention editing defaultScanPath (or choosing a more universally-existing default) so users don’t hit a confusing failure after init.
| fmt.Print("Proceed with git gc for all discovered repositories? [y/N]: ") | ||
| reader := bufio.NewReader(os.Stdin) | ||
| line, err := reader.ReadString('\n') | ||
| if err != nil { | ||
| return false | ||
| } | ||
| line = strings.TrimSpace(strings.ToLower(line)) | ||
| return line == "y" || line == "yes" |
There was a problem hiding this comment.
confirmCleanup returns false on any read error. With piped/non-interactive input that ends without a trailing newline, ReadString('\n') can return the user’s response plus io.EOF; in that case this would incorrectly cancel even if the input was "y"/"yes". Consider treating io.EOF as non-fatal and still evaluating the collected bytes.
Summary
git-cleaner:--configpath--initstarter config generation--forceoverwrite with backupoptions.defaultScanPathfallback when--scanis omitted--jsonmode with structured report output for scans and cleanup runs--yescleanup confirmation bypass and interactive confirmation prompt when--cleanis used without--yes--show-pctto include.gitsize percentage of full repository sizegit-cleanerREADME and testsIssues
Validation
go test ./...ingit-cleanergo test ./...indev-cachego test ./...inmac-cache-cleaner