Skip to content

Commit 3c91e76

Browse files
feat(cli/clean): display and remove extra taps
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
1 parent cc4689b commit 3c91e76

2 files changed

Lines changed: 156 additions & 3 deletions

File tree

internal/cli/clean.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ func runClean(cmd *cobra.Command) error {
7777
showCleanPreview(result)
7878

7979
if !dryRun {
80-
proceed, err := ui.Confirm(fmt.Sprintf("Remove %d packages?", result.TotalExtra()), false)
80+
proceed, err := ui.Confirm(fmt.Sprintf("Remove %d item(s)?", result.TotalExtra()), false)
8181
if err != nil {
8282
return err
8383
}
@@ -130,7 +130,7 @@ func cleanFromRemote(userSlug string) (*cleaner.CleanResult, error) {
130130
return nil, fmt.Errorf("failed to fetch remote config: %w", err)
131131
}
132132

133-
return cleaner.DiffFromLists(rc.Packages, rc.Casks, rc.Npm)
133+
return cleaner.DiffFromLists(rc.Packages, rc.Casks, rc.Npm, rc.Taps)
134134
}
135135

136136
func cleanFromLocalSnapshot() (*cleaner.CleanResult, error) {
@@ -158,6 +158,9 @@ func showCleanSummary(result *cleaner.CleanResult) {
158158
if len(result.RemovedNpm) > 0 {
159159
fmt.Printf(" npm: %s\n", strings.Join(result.RemovedNpm, ", "))
160160
}
161+
if len(result.RemovedTaps) > 0 {
162+
fmt.Printf(" taps: %s\n", strings.Join(result.RemovedTaps, ", "))
163+
}
161164
}
162165
if result.TotalFailed() > 0 {
163166
ui.Warn(fmt.Sprintf("Failed to remove %d package(s):", result.TotalFailed()))
@@ -170,11 +173,14 @@ func showCleanSummary(result *cleaner.CleanResult) {
170173
if len(result.FailedNpm) > 0 {
171174
fmt.Printf(" npm: %s\n", strings.Join(result.FailedNpm, ", "))
172175
}
176+
if len(result.FailedTaps) > 0 {
177+
fmt.Printf(" taps: %s\n", strings.Join(result.FailedTaps, ", "))
178+
}
173179
}
174180
}
175181

176182
func showCleanPreview(result *cleaner.CleanResult) {
177-
ui.Info(fmt.Sprintf("Found %d extra packages not in your config:", result.TotalExtra()))
183+
ui.Info(fmt.Sprintf("Found %d extra item(s) not in your config:", result.TotalExtra()))
178184
fmt.Println()
179185

180186
if len(result.ExtraFormulae) > 0 {
@@ -189,5 +195,9 @@ func showCleanPreview(result *cleaner.CleanResult) {
189195
ui.Info(fmt.Sprintf(" NPM (%d):", len(result.ExtraNpm)))
190196
fmt.Printf(" %s\n", strings.Join(result.ExtraNpm, ", "))
191197
}
198+
if len(result.ExtraTaps) > 0 {
199+
ui.Info(fmt.Sprintf(" Taps (%d):", len(result.ExtraTaps)))
200+
fmt.Printf(" %s\n", strings.Join(result.ExtraTaps, ", "))
201+
}
192202
fmt.Println()
193203
}
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
//go:build integration
2+
3+
package integration
4+
5+
import (
6+
"testing"
7+
8+
"github.com/openbootdotdev/openboot/internal/brew"
9+
"github.com/openbootdotdev/openboot/internal/cleaner"
10+
"github.com/openbootdotdev/openboot/internal/snapshot"
11+
"github.com/stretchr/testify/assert"
12+
"github.com/stretchr/testify/require"
13+
)
14+
15+
func TestIntegration_Cleaner_DiffFromLists_AllDesiredInstalled(t *testing.T) {
16+
// Given: brew is installed and we know what's installed
17+
require.True(t, brew.IsInstalled(), "brew must be installed")
18+
formulae, casks, err := brew.GetInstalledPackages()
19+
require.NoError(t, err)
20+
21+
desiredFormulae := make([]string, 0, len(formulae))
22+
for name := range formulae {
23+
desiredFormulae = append(desiredFormulae, name)
24+
}
25+
desiredCasks := make([]string, 0, len(casks))
26+
for name := range casks {
27+
desiredCasks = append(desiredCasks, name)
28+
}
29+
30+
// When: desired == installed
31+
result, err := cleaner.DiffFromLists(desiredFormulae, desiredCasks, nil, nil)
32+
33+
// Then: no extras detected
34+
require.NoError(t, err)
35+
assert.Empty(t, result.ExtraFormulae, "no extra formulae when desired matches installed")
36+
assert.Empty(t, result.ExtraCasks, "no extra casks when desired matches installed")
37+
}
38+
39+
func TestIntegration_Cleaner_DiffFromLists_EmptyDesired(t *testing.T) {
40+
// Given: brew is installed with at least one package
41+
require.True(t, brew.IsInstalled(), "brew must be installed")
42+
formulae, casks, err := brew.GetInstalledPackages()
43+
require.NoError(t, err)
44+
45+
// When: desired lists are empty (want nothing installed)
46+
result, err := cleaner.DiffFromLists(nil, nil, nil, nil)
47+
48+
// Then: everything installed shows up as extra
49+
require.NoError(t, err)
50+
assert.Equal(t, len(formulae), len(result.ExtraFormulae),
51+
"all installed formulae should appear as extra when desired is empty")
52+
assert.Equal(t, len(casks), len(result.ExtraCasks),
53+
"all installed casks should appear as extra when desired is empty")
54+
t.Logf("Extra formulae: %d, extra casks: %d", len(result.ExtraFormulae), len(result.ExtraCasks))
55+
}
56+
57+
func TestIntegration_Cleaner_DiffFromLists_SubsetDesired(t *testing.T) {
58+
// Given: brew is installed with multiple packages
59+
require.True(t, brew.IsInstalled(), "brew must be installed")
60+
formulae, _, err := brew.GetInstalledPackages()
61+
require.NoError(t, err)
62+
if len(formulae) < 2 {
63+
t.Skip("need at least 2 installed formulae for subset test")
64+
}
65+
66+
// When: desired is exactly one installed formula
67+
var oneFormula string
68+
for name := range formulae {
69+
oneFormula = name
70+
break
71+
}
72+
result, err := cleaner.DiffFromLists([]string{oneFormula}, nil, nil, nil)
73+
74+
// Then: all other installed formulae appear as extras
75+
require.NoError(t, err)
76+
assert.NotContains(t, result.ExtraFormulae, oneFormula, "desired package should not appear as extra")
77+
assert.Equal(t, len(formulae)-1, len(result.ExtraFormulae),
78+
"all formulae except the desired one should be extra")
79+
}
80+
81+
func TestIntegration_Cleaner_DiffFromSnapshot_CurrentState(t *testing.T) {
82+
// Given: brew is installed; we build a snapshot from current installed state
83+
require.True(t, brew.IsInstalled(), "brew must be installed")
84+
formulae, casks, err := brew.GetInstalledPackages()
85+
require.NoError(t, err)
86+
87+
installedFormulae := make([]string, 0, len(formulae))
88+
for name := range formulae {
89+
installedFormulae = append(installedFormulae, name)
90+
}
91+
installedCasks := make([]string, 0, len(casks))
92+
for name := range casks {
93+
installedCasks = append(installedCasks, name)
94+
}
95+
96+
snap := &snapshot.Snapshot{
97+
Packages: snapshot.PackageSnapshot{
98+
Formulae: installedFormulae,
99+
Casks: installedCasks,
100+
},
101+
}
102+
103+
// When: we diff against a snapshot that matches current state
104+
result, err := cleaner.DiffFromSnapshot(snap)
105+
106+
// Then: no extras
107+
require.NoError(t, err)
108+
assert.Empty(t, result.ExtraFormulae, "no extra formulae when snapshot matches installed")
109+
assert.Empty(t, result.ExtraCasks, "no extra casks when snapshot matches installed")
110+
}
111+
112+
func TestIntegration_Cleaner_Execute_DryRun_NoChanges(t *testing.T) {
113+
// Given: brew is installed; build a result with formulae we know exist but won't actually remove
114+
require.True(t, brew.IsInstalled(), "brew must be installed")
115+
116+
result := &cleaner.CleanResult{
117+
ExtraFormulae: []string{"wget"},
118+
ExtraCasks: []string{},
119+
}
120+
121+
// When: Execute in dry-run mode
122+
err := cleaner.Execute(result, true)
123+
124+
// Then: no error, nothing removed from the system
125+
assert.NoError(t, err)
126+
t.Logf("Dry-run removed: %d formulae, %d casks", len(result.RemovedFormulae), len(result.RemovedCasks))
127+
}
128+
129+
func TestIntegration_Cleaner_CleanResult_TotalMethods(t *testing.T) {
130+
// Given: a real diff result from the current system
131+
require.True(t, brew.IsInstalled(), "brew must be installed")
132+
133+
result, err := cleaner.DiffFromLists(nil, nil, nil, nil)
134+
require.NoError(t, err)
135+
136+
// When: we query totals
137+
total := result.TotalExtra()
138+
139+
// Then: total matches component counts
140+
assert.Equal(t, len(result.ExtraFormulae)+len(result.ExtraCasks)+len(result.ExtraNpm)+len(result.ExtraTaps), total)
141+
assert.Equal(t, 0, result.TotalRemoved(), "nothing removed yet")
142+
assert.Equal(t, 0, result.TotalFailed(), "nothing failed yet")
143+
}

0 commit comments

Comments
 (0)