From ab6e690195946caa40a45c10f8d2022b7c4b7551 Mon Sep 17 00:00:00 2001 From: Bruno De Assis Marques Date: Thu, 25 Sep 2025 09:40:10 +1000 Subject: [PATCH] feat: add APT purge support via CustomCommandArgs - Add isPurgeRequested function to check for --purge flag in CustomCommandArgs - Modify Delete method to conditionally add --purge flag when requested - Add comprehensive tests for both with and without purge scenarios - Maintain backward compatibility by keeping --purge optional Signed-off-by: Bruno De Assis Marques --- manager/apt/apt.go | 22 +++++++- manager/apt/apt_commandrunner_test.go | 74 +++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 2 deletions(-) diff --git a/manager/apt/apt.go b/manager/apt/apt.go index 0ba9053..18a7071 100644 --- a/manager/apt/apt.go +++ b/manager/apt/apt.go @@ -196,8 +196,6 @@ func (a *PackageManager) Delete(pkgs []string, opts *manager.Options) ([]manager return nil, err } - // args := append([]string{"remove", ArgsFixBroken, ArgsPurge, ArgsAutoRemove}, pkgs...) - args := append([]string{"remove", ArgsFixBroken, ArgsAutoRemove}, pkgs...) if opts == nil { opts = &manager.Options{ DryRun: false, @@ -206,13 +204,33 @@ func (a *PackageManager) Delete(pkgs []string, opts *manager.Options) ([]manager } } + // Start with base remove command + args := []string{"remove"} + + // always fix broken and auto-remove unused dependencies + args = append(args, ArgsFixBroken, ArgsAutoRemove) + + // Add dry-run if requested if opts.DryRun { args = append(args, ArgsDryRun) } + + // assume yes if not interactive, to avoid hanging if !opts.Interactive { args = append(args, ArgsAssumeYes) } + // Check if purge is requested in CustomCommandArgs + for _, arg := range opts.CustomCommandArgs { + if arg == ArgsPurge { + args = append(args, ArgsPurge) + break + } + } + + // Append package names to the command arguments + args = append(args, pkgs...) + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) defer cancel() diff --git a/manager/apt/apt_commandrunner_test.go b/manager/apt/apt_commandrunner_test.go index 77b193c..36e6978 100644 --- a/manager/apt/apt_commandrunner_test.go +++ b/manager/apt/apt_commandrunner_test.go @@ -214,6 +214,80 @@ curl 7.81.0-1ubuntu1.4 t.Errorf("Expected %d environment variables, got %d", len(expectedEnv), len(env)) } }) + + t.Run("Delete with purge flag", func(t *testing.T) { + // Create mock command runner + mockRunner := manager.NewMockCommandRunner() + + removeOutputWithPurge := `Reading package lists... Done +Building dependency tree... Done +Reading state information... Done +The following packages will be REMOVED: + ebtables* +0 upgraded, 0 newly installed, 1 to remove and 0 not upgraded. +After this operation, 123 kB disk space will be freed. +Do you want to continue? [Y/n] +(Reading database ... 1234 files and directories currently installed.) +Removing ebtables (2.0.11-4ubuntu1) ... +Purging configuration files for ebtables (2.0.11-4ubuntu1) ... +` + expectedArgs := []string{"remove", "-f", "--autoremove", "-y", "--purge", "ebtables"} + mockRunner.AddCommand("apt", expectedArgs, []byte(removeOutputWithPurge), nil) + + // Create APT package manager with mocked runner + pm := apt.NewPackageManagerWithCustomRunner(mockRunner) + + // Execute Delete operation with purge flag + opts := &manager.Options{ + AssumeYes: true, + CustomCommandArgs: []string{apt.ArgsPurge}, + } + packages, err := pm.Delete([]string{"ebtables"}, opts) + if err != nil { + t.Fatalf("Delete with purge flag failed: %v", err) + } + + // Verify return value + if packages == nil { + t.Error("Delete should return package info, got nil") + } + }) + + t.Run("Delete without purge flag", func(t *testing.T) { + // Create mock command runner + mockRunner := manager.NewMockCommandRunner() + + removeOutputWithoutPurge := `Reading package lists... Done +Building dependency tree... Done +Reading state information... Done +The following packages will be REMOVED: + ebtables +0 upgraded, 0 newly installed, 1 to remove and 0 not upgraded. +After this operation, 123 kB disk space will be freed. +Do you want to continue? [Y/n] +(Reading database ... 1234 files and directories currently installed.) +Removing ebtables (2.0.11-4ubuntu1) ... +` + expectedArgs := []string{"remove", "-f", "--autoremove", "-y", "ebtables"} + mockRunner.AddCommand("apt", expectedArgs, []byte(removeOutputWithoutPurge), nil) + + // Create APT package manager with mocked runner + pm := apt.NewPackageManagerWithCustomRunner(mockRunner) + + // Execute Delete operation without purge flag + opts := &manager.Options{ + AssumeYes: true, + } + packages, err := pm.Delete([]string{"ebtables"}, opts) + if err != nil { + t.Fatalf("Delete without purge flag failed: %v", err) + } + + // Verify return value + if packages == nil { + t.Error("Delete should return package info, got nil") + } + }) } // TestAPTCommandRunnerMigration verifies that the migration from CommandBuilder to CommandRunner works correctly