mirror of
https://github.com/89luca89/distrobox.git
synced 2026-08-19 01:14:49 -05:00
fix(rm): propagate verbose flag to generate-entry cleanup (#2109)
This commit is contained in:
committed by
Alessio Biancalana
parent
d28032c066
commit
7fa2ed9ef1
@@ -63,6 +63,7 @@ func rmAction(ctx context.Context, cmd *cli.Command, cfg *config.Values) error {
|
||||
Force: cmd.Bool("force"),
|
||||
All: cmd.Bool("all"),
|
||||
RemoveHome: cmd.Bool("rm-home"),
|
||||
Verbose: cmd.Bool("verbose"),
|
||||
ContainerNames: names,
|
||||
}
|
||||
|
||||
|
||||
+6
-5
@@ -32,6 +32,7 @@ type RmOptions struct {
|
||||
Force bool
|
||||
All bool
|
||||
RemoveHome bool
|
||||
Verbose bool
|
||||
ContainerNames []string
|
||||
}
|
||||
|
||||
@@ -68,7 +69,7 @@ func (c *RmCommand) Execute(ctx context.Context, options RmOptions) (*RmResult,
|
||||
|
||||
var removedDistroboxes []containermanager.Container
|
||||
for _, currentDistrobox := range distroboxesToRemove {
|
||||
err := c.removeContainer(ctx, currentDistrobox, options.Force, options.NoTTY, userHome)
|
||||
err := c.removeContainer(ctx, currentDistrobox, options.Force, options.NoTTY, options.Verbose, userHome)
|
||||
if err != nil {
|
||||
//nolint:forbidigo // waiting for the logger implementation
|
||||
fmt.Printf("error deleting %s: %s", currentDistrobox.Name, err)
|
||||
@@ -84,6 +85,7 @@ func (c *RmCommand) removeContainer(
|
||||
container containermanager.Container,
|
||||
force bool,
|
||||
noTTY bool,
|
||||
verbose bool,
|
||||
userHome string,
|
||||
) error {
|
||||
forceRemove := force
|
||||
@@ -121,12 +123,12 @@ func (c *RmCommand) removeContainer(
|
||||
return fmt.Errorf("failed to remove container: %w", err)
|
||||
}
|
||||
|
||||
c.cleanup(ctx, userHome, container.Name)
|
||||
c.cleanup(ctx, userHome, container.Name, verbose)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *RmCommand) cleanup(ctx context.Context, userHome, containerName string) {
|
||||
func (c *RmCommand) cleanup(ctx context.Context, userHome, containerName string, verbose bool) {
|
||||
bins := findExportedBinaries(userHome, containerName)
|
||||
desktopApps := findExportedDesktopApps(userHome, containerName)
|
||||
|
||||
@@ -144,8 +146,7 @@ func (c *RmCommand) cleanup(ctx context.Context, userHome, containerName string)
|
||||
&GenerateEntryOptions{
|
||||
ContainerName: containerName,
|
||||
Delete: true,
|
||||
// TODO: handle verbose
|
||||
Verbose: false,
|
||||
Verbose: verbose,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
package commands_test
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/89luca89/distrobox/pkg/commands"
|
||||
"github.com/89luca89/distrobox/pkg/config"
|
||||
"github.com/89luca89/distrobox/pkg/containermanager"
|
||||
"github.com/89luca89/distrobox/pkg/internal/testutil"
|
||||
"github.com/89luca89/distrobox/pkg/ui"
|
||||
)
|
||||
|
||||
func newTestRmCommand(mock *testutil.MockContainerManager) *commands.RmCommand {
|
||||
prompter := ui.NewPrompter(*bufio.NewReader(strings.NewReader("")), io.Discard)
|
||||
return commands.NewRmCommand(&config.Values{}, mock, prompter)
|
||||
}
|
||||
|
||||
// writeExportedDesktopApp writes a minimal desktop file that
|
||||
// findExportedDesktopApps will match for the given containerName (i.e.
|
||||
// the per-app desktop entries created by distrobox-export, not the
|
||||
// one created by `distrobox generate-entry`).
|
||||
func writeExportedDesktopApp(t *testing.T, userHome, containerName string) string {
|
||||
t.Helper()
|
||||
appsDir := filepath.Join(userHome, ".local", "share", "applications")
|
||||
require.NoError(t, os.MkdirAll(appsDir, 0o755))
|
||||
desktopFile := filepath.Join(appsDir, containerName+"-app.desktop")
|
||||
content := "[Desktop Entry]\nExec=/usr/bin/distrobox enter " + containerName + " -- some-app\n"
|
||||
require.NoError(t, os.WriteFile(desktopFile, []byte(content), 0o644))
|
||||
return desktopFile
|
||||
}
|
||||
|
||||
// writeGenerateEntryDesktop writes the desktop file produced by
|
||||
// `distrobox generate-entry <name>` so the generate-entry delete
|
||||
// branch (invoked from RmCommand.cleanup) can be observed.
|
||||
func writeGenerateEntryDesktop(t *testing.T, userHome, containerName string) string {
|
||||
t.Helper()
|
||||
appsDir := filepath.Join(userHome, ".local", "share", "applications")
|
||||
require.NoError(t, os.MkdirAll(appsDir, 0o755))
|
||||
entryFile := filepath.Join(appsDir, containerName+".desktop")
|
||||
require.NoError(t, os.WriteFile(entryFile, []byte("[Desktop Entry]\nName="+containerName+"\n"), 0o644))
|
||||
return entryFile
|
||||
}
|
||||
|
||||
func TestRmCommand_Execute_CleanupRemovesExportedDesktopApp(t *testing.T) {
|
||||
tempHome := t.TempDir()
|
||||
t.Setenv("HOME", tempHome)
|
||||
t.Setenv("XDG_DATA_HOME", filepath.Join(tempHome, ".local", "share"))
|
||||
|
||||
containerName := "test-rm-cleanup"
|
||||
exportedDesktopFile := writeExportedDesktopApp(t, tempHome, containerName)
|
||||
|
||||
mock := &testutil.MockContainerManager{
|
||||
ListContainersResult: []containermanager.Container{
|
||||
{
|
||||
Name: containerName,
|
||||
Status: "Exited",
|
||||
Labels: map[string]string{"manager": "distrobox"},
|
||||
},
|
||||
},
|
||||
InspectContainerResult: &containermanager.InspectResult{
|
||||
ContainerHome: tempHome,
|
||||
},
|
||||
}
|
||||
cmd := newTestRmCommand(mock)
|
||||
|
||||
_, err := cmd.Execute(context.Background(), commands.RmOptions{
|
||||
ContainerNames: []string{containerName},
|
||||
Force: true,
|
||||
NoTTY: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.NoFileExists(t, exportedDesktopFile, "cleanup should remove the per-app exported desktop file")
|
||||
}
|
||||
|
||||
func TestRmCommand_Execute_CleanupRemovesGenerateEntryDesktop(t *testing.T) {
|
||||
tempHome := t.TempDir()
|
||||
t.Setenv("HOME", tempHome)
|
||||
t.Setenv("XDG_DATA_HOME", filepath.Join(tempHome, ".local", "share"))
|
||||
|
||||
containerName := "test-rm-genentry"
|
||||
generateEntryFile := writeGenerateEntryDesktop(t, tempHome, containerName)
|
||||
|
||||
mock := &testutil.MockContainerManager{
|
||||
ListContainersResult: []containermanager.Container{
|
||||
{
|
||||
Name: containerName,
|
||||
Status: "Exited",
|
||||
Labels: map[string]string{"manager": "distrobox"},
|
||||
},
|
||||
},
|
||||
InspectContainerResult: &containermanager.InspectResult{
|
||||
ContainerHome: tempHome,
|
||||
},
|
||||
}
|
||||
cmd := newTestRmCommand(mock)
|
||||
|
||||
_, err := cmd.Execute(context.Background(), commands.RmOptions{
|
||||
ContainerNames: []string{containerName},
|
||||
Force: true,
|
||||
NoTTY: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.NoFileExists(t, generateEntryFile, "cleanup should invoke GenerateEntry delete to remove the <name>.desktop file")
|
||||
}
|
||||
@@ -34,11 +34,17 @@ type ContainerManagerSpy struct {
|
||||
// ExistsFn, when non-nil, overrides the default Exists behavior (which
|
||||
// always returns false). Tests can use it to simulate name collisions
|
||||
// or other lookup scenarios.
|
||||
//
|
||||
// ListContainersResult and InspectContainerResult, when non-nil, override
|
||||
// the default zero-value return values of ListContainers and
|
||||
// InspectContainer respectively.
|
||||
type MockContainerManager struct {
|
||||
Spy ContainerManagerSpy
|
||||
Root bool
|
||||
RootClone *MockContainerManager
|
||||
ExistsFn func(containerName string) bool
|
||||
Spy ContainerManagerSpy
|
||||
Root bool
|
||||
RootClone *MockContainerManager
|
||||
ExistsFn func(containerName string) bool
|
||||
ListContainersResult []containermanager.Container
|
||||
InspectContainerResult *containermanager.InspectResult
|
||||
}
|
||||
|
||||
func (m *MockContainerManager) Name() string {
|
||||
@@ -49,11 +55,14 @@ func (m *MockContainerManager) Name() string {
|
||||
func (m *MockContainerManager) CloneAsRoot() containermanager.ContainerManager {
|
||||
m.Spy.CloneAsRoot = append(m.Spy.CloneAsRoot, []any{})
|
||||
if m.RootClone == nil {
|
||||
// Propagate behavior hooks (e.g. ExistsFn) to the clone so tests
|
||||
// see consistent results between the rootless and root variants.
|
||||
// Propagate override fields so tests that set them on the base
|
||||
// mock see consistent behavior when code paths run on the root
|
||||
// clone (real providers preserve fields across CloneAsRoot).
|
||||
m.RootClone = &MockContainerManager{
|
||||
Root: true,
|
||||
ExistsFn: m.ExistsFn,
|
||||
Root: true,
|
||||
ExistsFn: m.ExistsFn,
|
||||
ListContainersResult: m.ListContainersResult,
|
||||
InspectContainerResult: m.InspectContainerResult,
|
||||
}
|
||||
}
|
||||
return m.RootClone
|
||||
@@ -66,6 +75,9 @@ func (m *MockContainerManager) Enter(_ context.Context, options containermanager
|
||||
|
||||
func (m *MockContainerManager) ListContainers(_ context.Context) ([]containermanager.Container, error) {
|
||||
m.Spy.ListContainers = append(m.Spy.ListContainers, []any{})
|
||||
if m.ListContainersResult != nil {
|
||||
return m.ListContainersResult, nil
|
||||
}
|
||||
return []containermanager.Container{}, nil
|
||||
}
|
||||
|
||||
@@ -94,6 +106,9 @@ func (m *MockContainerManager) Stop(_ context.Context, containerNames []string)
|
||||
|
||||
func (m *MockContainerManager) InspectContainer(_ context.Context, containerName string) (*containermanager.InspectResult, error) {
|
||||
m.Spy.InspectContainer = append(m.Spy.InspectContainer, []any{containerName})
|
||||
if m.InspectContainerResult != nil {
|
||||
return m.InspectContainerResult, nil
|
||||
}
|
||||
return &containermanager.InspectResult{}, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user