From 132740b7a14600bb8c139d59147e3dd0868c8d26 Mon Sep 17 00:00:00 2001 From: VetheonGames Date: Sun, 7 Jan 2024 17:30:30 -0700 Subject: [PATCH] Major Update 6 (Pre-Release Binary 0.8) Changes to main.go: ``` - make needed changes to remove the flag package - refactor functions to use the preferred command syntax ``` changes to logger.go: ``` - make logger be quiet and stop outputting to STDOUT now that we are in user testing ``` changes to install.go: ``` - setup some extra logic in InstallPAckageSnap to install snaps in "classic confinement mode" if the user wishes ``` changes to updater_utils.go: ``` - create the file to contain some helper methods (namely one to update packages by name from a more central interface) ``` changes to cli_utils.go: ``` - create the file to contain some helper methods for the CLI (namely just a method to handle errors in the update process) ``` --- cmd/cli_utils.go | 13 +++ cmd/main.go | 163 ++++++++++++++++------------ pkg/logger/logger.go | 2 - pkg/packagemanager/install.go | 30 ++++- pkg/packagemanager/updater_utils.go | 31 ++++++ 5 files changed, 162 insertions(+), 77 deletions(-) create mode 100644 cmd/cli_utils.go create mode 100644 pkg/packagemanager/updater_utils.go diff --git a/cmd/cli_utils.go b/cmd/cli_utils.go new file mode 100644 index 0000000..005d17c --- /dev/null +++ b/cmd/cli_utils.go @@ -0,0 +1,13 @@ +package main + +import ( + "fmt" +) + +func handleUpdateError(updateOption string, err error) { + if err != nil { + fmt.Printf("Error occurred during '%s' update: %v\n", updateOption, err) + } else { + fmt.Printf("Update '%s' completed successfully.\n", updateOption) + } +} diff --git a/cmd/main.go b/cmd/main.go index 7db06c8..2792ad5 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -3,7 +3,6 @@ package main // This file is our main entrypoint, and build point for AllPac import ( - "flag" "fmt" "os" "strings" @@ -21,40 +20,40 @@ func main() { logger.Errorf("Failed to initialize logger: %v", err) } - // Define flag sets for different commands - commandHandlers := map[string]func(*flag.FlagSet){ - "update": handleUpdate, - "install": handleInstall, - "uninstall": handleUninstall, - "search": func(cmd *flag.FlagSet) { handleSearch(cmd, os.Args[2:]) }, - "rebuild": handleRebuild, - "clean-aur": handleCleanAur, - "toolcheck": handleToolCheck, - } - if len(os.Args) < 2 { fmt.Println("Expected 'update', 'install', 'uninstall', 'search', 'rebuild', 'clean-aur', or 'toolcheck' subcommands") os.Exit(1) } - if handler, ok := commandHandlers[os.Args[1]]; ok { - cmd := flag.NewFlagSet(os.Args[1], flag.ExitOnError) - handler(cmd) - } else { - fmt.Printf("Unknown subcommand: %s\n", os.Args[1]) + command := os.Args[1] + args := os.Args[2:] + + switch command { + case "update": + handleUpdate(args) + case "install": + handleInstall(args) + case "uninstall": + handleUninstall(args) + case "search": + handleSearch(args) + case "rebuild": + handleRebuild(args) + case "clean-aur": + handleCleanAur(args) + case "toolcheck": + handleToolCheck(args) + default: + fmt.Printf("Unknown subcommand: %s\n", command) os.Exit(1) } } -func handleUpdate(cmd *flag.FlagSet) { - updateFlags := map[string]*bool{ - "everything": cmd.Bool("everything", false, "Update all packages on the system"), - "snap": cmd.Bool("snap", false, "Update all Snap packages"), - "aur": cmd.Bool("aur", false, "Update all AUR packages"), - "arch": cmd.Bool("arch", false, "Update all Arch packages"), - "flats": cmd.Bool("flats", false, "Update all Flatpak packages"), +func handleUpdate(args []string) { + if len(args) == 0 { + fmt.Println("You must specify an update option: 'everything', 'snap', 'aur', 'arch', 'flats', or a specific package name.") + return } - cmd.Parse(os.Args[2:]) updateFuncs := map[string]func() error{ "everything": packagemanager.UpdateAllPackages, @@ -64,33 +63,33 @@ func handleUpdate(cmd *flag.FlagSet) { "flats": func() error { return packagemanager.UpdateFlatpakPackages() }, } - for flagName, flagValue := range updateFlags { - if *flagValue { - if updateFunc, ok := updateFuncs[flagName]; ok { - if err := updateFunc(); err != nil { - fmt.Printf("Error occurred during '%s' update: %v\n", flagName, err) - } - return - } - } + updateOption := args[0] + if updateFunc, ok := updateFuncs[updateOption]; ok { + err := updateFunc() + handleUpdateError(updateOption, err) + } else { + err := packagemanager.UpdatePackageByName(updateOption) + handleUpdateError(updateOption, err) } - - fmt.Println("No update option specified or unrecognized option") } // handles the install command for packages -func handleInstall(cmd *flag.FlagSet) { - packageNames := cmd.String("packages", "", "Comma-separated list of packages to install") - cmd.Parse(os.Args[2:]) - - if *packageNames == "" { +func handleInstall(args []string) { + if len(args) == 0 { fmt.Println("You must specify at least one package name.") - cmd.Usage() return } - packages := strings.Split(*packageNames, ",") - searchResults, err := packagemanager.SearchAllSources(packages) + // Join the args to form a single string, then split by comma + packagesInput := strings.Join(args, " ") + packageNames := strings.Split(packagesInput, ",") + + // Trim whitespace from each package name + for i, pkg := range packageNames { + packageNames[i] = strings.TrimSpace(pkg) + } + + searchResults, err := packagemanager.SearchAllSources(packageNames) if err != nil { fmt.Printf("Error searching for packages: %v\n", err) return @@ -120,6 +119,16 @@ func handleInstall(cmd *flag.FlagSet) { continue } + // Display the actual package name that will be installed + fmt.Printf("Available package(s) for installation from %s:\n", selectedSource) + for _, match := range exactMatches { + if match.Source == selectedSource { + for _, pkg := range match.Results { + fmt.Println(pkg) + } + } + } + fmt.Printf("Installing %s from %s...\n", result.PackageName, selectedSource) if installFunc, ok := installFuncs[selectedSource]; ok { if err := installFunc(result.PackageName); err != nil { @@ -171,21 +180,23 @@ func isExactMatch(packageName, result string) bool { } // handles the uninstall command for packages -func handleUninstall(cmd *flag.FlagSet) { - packageNames := cmd.String("packages", "", "Comma-separated list of packages to uninstall") - - cmd.Parse(os.Args[2:]) - - if *packageNames == "" { +func handleUninstall(args []string) { + if len(args) == 0 { fmt.Println("You must specify at least one package name.") - cmd.Usage() return } - packages := strings.Split(*packageNames, ",") + // Join the args to form a single string, then split by comma + packagesInput := strings.Join(args, " ") + packageNames := strings.Split(packagesInput, ",") + + // Trim whitespace from each package name + for i, pkg := range packageNames { + packageNames[i] = strings.TrimSpace(pkg) + } // Call the function to uninstall the packages - err := packagemanager.UninstallPackages(packages) + err := packagemanager.UninstallPackages(packageNames) if err != nil { fmt.Printf("Error uninstalling packages: %v\n", err) } else { @@ -194,10 +205,9 @@ func handleUninstall(cmd *flag.FlagSet) { } // handles the search command for packages across different package managers -func handleSearch(cmd *flag.FlagSet, args []string) { +func handleSearch(args []string) { if len(args) < 1 { fmt.Println("You must specify a package name.") - cmd.Usage() return } @@ -232,30 +242,41 @@ func handleSearch(cmd *flag.FlagSet, args []string) { } // handles the rebuild command for an AUR package -func handleRebuild(cmd *flag.FlagSet) { - packageName := cmd.String("package", "", "Name of the AUR package to rebuild") - - cmd.Parse(os.Args[2:]) - - if *packageName == "" { - fmt.Println("You must specify a package name.") - cmd.Usage() +func handleRebuild(args []string) { + if len(args) == 0 { + fmt.Println("You must specify the name of an AUR package to rebuild.") return } - err := packagemanager.RebuildAndReinstallAURPackage(*packageName) + packageName := args[0] + + pkgList, err := packagemanager.ReadPackageList() if err != nil { - fmt.Printf("Error rebuilding package %s: %v\n", *packageName, err) + fmt.Printf("Error reading package list: %v\n", err) + return + } + + if _, exists := pkgList[packageName]; !exists { + fmt.Printf("Package %s is not managed by AllPac or not installed.\n", packageName) + return + } + + cacheDir := filepath.Join(os.Getenv("HOME"), ".allpac", "cache", packageName) + if err := os.RemoveAll(cacheDir); err != nil { + fmt.Printf("Error removing old build directory: %v\n", err) + return + } + + err = packagemanager.RebuildAndReinstallAURPackage(packageName) + if err != nil { + fmt.Printf("Error rebuilding package %s: %v\n", packageName, err) } else { - fmt.Printf("Package %s rebuilt and reinstalled successfully.\n", *packageName) + fmt.Printf("Package %s rebuilt and reinstalled successfully.\n", packageName) } } // handles the cleaning of AUR cache -func handleCleanAur(cmd *flag.FlagSet) { - // Parse the command flags if needed - cmd.Parse(os.Args[2:]) - +func handleCleanAur(args []string) { // Call the function to clear the AUR cache err := packagemanager.ClearAllPacCache() if err != nil { @@ -277,7 +298,7 @@ func promptUserForSource(sources []packagemanager.SourceResult) int { return choice } -func handleToolCheck(cmd *flag.FlagSet) { +func handleToolCheck(args []string) { checks := []struct { Name string Func func() error diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go index 657ea56..e4e8fbf 100644 --- a/pkg/logger/logger.go +++ b/pkg/logger/logger.go @@ -1,7 +1,6 @@ package logger import ( - "io" "log" "os" "path/filepath" @@ -21,7 +20,6 @@ func Init(logFilePath string) error { } Logger = log.New(logFile, "AllPac: ", log.Ldate|log.Ltime|log.Lshortfile) - Logger.SetOutput(io.MultiWriter(os.Stderr, logFile)) return nil } diff --git a/pkg/packagemanager/install.go b/pkg/packagemanager/install.go index 0c9f06b..ccc9f57 100644 --- a/pkg/packagemanager/install.go +++ b/pkg/packagemanager/install.go @@ -9,6 +9,7 @@ import ( "os" "os/exec" "os/user" + "strings" "path/filepath" "pixelridgesoftworks.com/AllPac/pkg/logger" ) @@ -37,14 +38,35 @@ func InstallPackagePacman(packageName string) error { // InstallPackageSnap installs a package using Snap and logs the installation func InstallPackageSnap(packageName string) error { cmd := exec.Command("sudo", "snap", "install", packageName) - if output, err := cmd.CombinedOutput(); err != nil { - logger.Errorf("error installing package with Snap: %s, %v", output, err) - return fmt.Errorf("error installing package with Snap: %s, %v", output, err) + output, err := cmd.CombinedOutput() + + if err != nil { + outputStr := string(output) + logger.Errorf("error installing package with Snap: %s, %v", outputStr, err) + + // Check if the error is due to the need for classic confinement + if strings.Contains(outputStr, "using classic") { + fmt.Println("This package requires installation in classic mode, which may perform arbitrary system changes outside of the security sandbox. Do you want to proceed? (yes/no)") + var response string + fmt.Scanln(&response) + if strings.ToLower(response) == "yes" { + // Retry installation with --classic flag + classicCmd := exec.Command("sudo", "snap", "install", "--classic", packageName) + if classicOutput, classicErr := classicCmd.CombinedOutput(); classicErr != nil { + logger.Errorf("error installing package with Snap in classic mode: %s, %v", classicOutput, classicErr) + return fmt.Errorf("error installing package with Snap in classic mode: %s, %v", classicOutput, classicErr) + } + } else { + return fmt.Errorf("installation aborted by user") + } + } else { + return fmt.Errorf("error installing package with Snap: %s, %v", outputStr, err) + } } version, err := GetVersionFromSnap(packageName) if err != nil { - logger.Errorf("An error has occured:", err) + logger.Errorf("An error has occurred:", err) return err } diff --git a/pkg/packagemanager/updater_utils.go b/pkg/packagemanager/updater_utils.go new file mode 100644 index 0000000..1e4752d --- /dev/null +++ b/pkg/packagemanager/updater_utils.go @@ -0,0 +1,31 @@ +package packagemanager + +import ( + "fmt" +) + +// UpdatePackageByName updates a specific package by its name +func UpdatePackageByName(packageName string) error { + pkgList, err := ReadPackageList() + if err != nil { + return fmt.Errorf("error reading package list: %v", err) + } + + pkgInfo, exists := pkgList[packageName] + if !exists { + return fmt.Errorf("package %s not found in package list", packageName) + } + + switch pkgInfo.Source { + case "pacman": + return UpdatePacmanPackages(packageName) + case "aur": + return UpdateAURPackages(packageName) + case "snap": + return UpdateSnapPackages(packageName) + case "flatpak": + return UpdateFlatpakPackages(packageName) + default: + return fmt.Errorf("unknown source for package %s", packageName) + } +}