diff --git a/README.md b/README.md index 7bc7160..8c7e4be 100644 --- a/README.md +++ b/README.md @@ -25,11 +25,14 @@ backplane-tools offers an easy solution to install, remove, and upgrade a useful - [Upgrade a specific thing](#upgrade-a-specific-thing) - [Remove everything](#remove-everything) - [Remove a specific thing](#remove-a-specific-thing) + - [Cleanup everything](#cleanup-everything) + - [Cleanup a specific thing](#cleanup-a-specific-thing) - [Design](#design) - [Directory Structure](#directory-structure) - [Installing](#installing) - [Upgrading](#upgrading) - [Removing](#removing) + - [Cleaning up](#cleaning-up) ## Tools @@ -155,6 +158,16 @@ backplane-tools remove all backplane-tools remove ``` +### Cleanup everything +```shell +backplane-tools cleanup all +``` + +### Cleanup a specific thing +```shell +backplane-tools cleanup +``` + ## Design backplane-tools strives to be simplistic and non-invasive; it should not conflict with currently installed programs, nor should it require extensive research before operating. @@ -215,3 +228,10 @@ Users are able to remove individual tools or completely remove all files and dat `backplane-tools remove ...` allows users to remove a specific set of tools from their system. This is done by removing the tool-specific directory at `$HOME/.local/bin/backplane/`, as well as the tool's linked executable in `$HOME/.local/bin/backplane/latest/`. `backplane-tools remove all` allows users to remove everything managed by backplane-tools. This is done by completely removing `$HOME/.bin/local/backplane/`. Subsequent calls to `backplane-tools install` will cause the directory structure to be recreated from scratch. + +### Cleaning up +Users are able to remove older versions of individual or all tools managed by backplane-tools. + +`backplane-tools cleanup ...` allows users to cleanup older versions of a specific set of tools from their system. This is done by removing the tool-specific directory at `$HOME/.local/bin/backplane//`, keeping only the latest installed version as well as the tool's linked executable in `$HOME/.local/bin/backplane/latest/`. + +`backplane-tools cleanup all` allows users to remove older versions of everything managed by backplane-tools. This is done by removing `$HOME/.bin/local/backplane//`. \ No newline at end of file diff --git a/cmd/cleanup/cleanup.go b/cmd/cleanup/cleanup.go new file mode 100644 index 0000000..147ad01 --- /dev/null +++ b/cmd/cleanup/cleanup.go @@ -0,0 +1,61 @@ +package cleanup + +import ( + "fmt" + "strings" + + "github.com/openshift/backplane-tools/pkg/tools" + "github.com/openshift/backplane-tools/pkg/utils" + "github.com/spf13/cobra" +) + +// Cmd returns the Command used to invoke the upgrade logic +func Cmd() *cobra.Command { + toolNames := tools.Names() + cleanupCmd := &cobra.Command{ + Use: fmt.Sprintf("cleanup [all|%s]", strings.Join(toolNames, "|")), + Aliases: []string{"clean-up"}, + Args: cobra.OnlyValidArgs, + ValidArgs: append(toolNames, "all"), + Short: "Cleans up older versions of existing tool", + Long: "Cleans up older versions and keeps only the latest installed version of one or more tools from the provided list. It's valid to specify multiple tools: in this case, all tools provided will be cleaned up. If no specific tools are provided, all are cleaned up by default.", + RunE: func(_ *cobra.Command, args []string) error { + return Cleanup(args) + }, + } + return cleanupCmd +} + +// run cleanup the tool(s) specified by the provided positional args +func Cleanup(args []string) error { + var listTools []tools.Tool + if len(args) == 0 || utils.Contains(args, "all") { + // If user explicitly passes 'all' or doesn't specify which tools to clean up, + // everything that's been installed locally will be cleaned up + var err error + listTools, err = tools.ListInstalled() + if err != nil { + return err + } + } else { + // otherwise build the list verifying tool exist + toolMap := tools.GetMap() + + listTools = []tools.Tool{} + for _, toolName := range args { + t, found := toolMap[toolName] + if !found { + return fmt.Errorf("failed to locate '%s' in list of supported tools", toolName) + } + listTools = append(listTools, t) + } + } + + fmt.Println("Cleaning up the following tools: ") + + err := tools.Cleanup(listTools) + if err != nil { + return fmt.Errorf("failed to clean up tools: %w", err) + } + return nil +} diff --git a/main.go b/main.go index 908693d..4438f7a 100644 --- a/main.go +++ b/main.go @@ -3,6 +3,7 @@ package main import ( "log" + "github.com/openshift/backplane-tools/cmd/cleanup" "github.com/openshift/backplane-tools/cmd/install" "github.com/openshift/backplane-tools/cmd/list" "github.com/openshift/backplane-tools/cmd/remove" @@ -27,6 +28,7 @@ func init() { cmd.AddCommand(list.Cmd()) cmd.AddCommand(remove.Cmd()) cmd.AddCommand(upgrade.Cmd()) + cmd.AddCommand(cleanup.Cmd()) } func main() { diff --git a/pkg/tools/base/default.go b/pkg/tools/base/default.go index d4c132c..0f7896c 100644 --- a/pkg/tools/base/default.go +++ b/pkg/tools/base/default.go @@ -126,3 +126,46 @@ func (t *Default) InstalledVersion() (string, error) { } return t.installedVersion, nil } + +// Cleanup removes all previous versions of an installed tool except the currently linked version +func (t *Default) Cleanup() (string, error) { + toolDir := t.ToolDir() + + // Get the currently installed version (the one linked in latest/) + currentVersion, err := t.InstalledVersion() + if err != nil { + return "", fmt.Errorf("failed to get installed version: %w", err) + } + + // Read all version directories in the tool directory + entries, err := os.ReadDir(toolDir) + if err != nil { + return "", fmt.Errorf("failed to read tool directory %s: %w", toolDir, err) + } + + removedVersions := []string{} + for _, entry := range entries { + if !entry.IsDir() { + continue + } + + // Skip the current version + if entry.Name() == currentVersion { + continue + } + + // Remove old version directory + versionPath := filepath.Join(toolDir, entry.Name()) + err = os.RemoveAll(versionPath) + if err != nil { + return strings.Join(removedVersions, ", "), fmt.Errorf("failed to remove version %s: %w", entry.Name(), err) + } + removedVersions = append(removedVersions, entry.Name()) + } + + if len(removedVersions) == 0 { + return "no old versions found", nil + } + + return fmt.Sprintf("removed versions: %s", strings.Join(removedVersions, ", ")), nil +} diff --git a/pkg/tools/tools.go b/pkg/tools/tools.go index c0619ef..8e2b432 100644 --- a/pkg/tools/tools.go +++ b/pkg/tools/tools.go @@ -50,6 +50,9 @@ type Tool interface { // LatestVersion gets the latest version available on repo LatestVersion() (string, error) + + // Cleanup removes all previous versions of an installed tool + Cleanup() (string, error) } var toolMap map[string]Tool @@ -202,3 +205,18 @@ func ListInstalled() ([]Tool, error) { } return installedTools, nil } + +func Cleanup(tools []Tool) error { + for _, tool := range tools { + fmt.Println() + fmt.Printf("Removing %s\n", tool.Name()) + msg, err := tool.Cleanup() + if err != nil { + fmt.Printf("Encountered error while cleaning up %s: %v\n", tool.Name(), err) + fmt.Println("Skipping...") + } else { + fmt.Printf("Successfully cleaned up %s: %s\n", tool.Name(), msg) + } + } + return nil +}