package main import ( "flag" "fmt" "os" "strings" "time" "github.com/fatih/color" "github.com/briandowns/spinner" "upm/pkg_manager" ) var ( // Color definitions success = color.New(color.FgGreen, color.Bold) errorColor = color.New(color.FgRed, color.Bold) warning = color.New(color.FgYellow, color.Bold) info = color.New(color.FgCyan) header = color.New(color.FgMagenta, color.Bold) cmdColor = color.New(color.FgBlue, color.Bold) // Command line flags verbose bool ) func showSpinner(message string) func() { s := spinner.New(spinner.CharSets[14], 100*time.Millisecond) s.Suffix = " " + message s.Color("cyan") s.Start() return func() { s.Stop() } } func runWithSpinner(message string, needsSudo bool, fn func() error) error { var stop func() cmdStarted := make(chan struct{}) if needsSudo { // Start a goroutine to show spinner after sudo auth go func() { <-cmdStarted fmt.Print("\r\033[K") // Clear the line stop = showSpinner(message) }() } else { stop = showSpinner(message) } err := fn() if needsSudo { close(cmdStarted) // Give the spinner a moment to show progress time.Sleep(500 * time.Millisecond) } if stop != nil { stop() } return err } func main() { // Initialize universal package managers pkg_manager.InitializeUniversalManagers() // Create custom flag set flags := flag.NewFlagSet("upm", flag.ExitOnError) flags.Usage = printUsage // Define flags var universalPM string flags.StringVar(&universalPM, "u", "", "Use universal package manager (snap/flatpak)") flags.BoolVar(&verbose, "v", false, "Show verbose output") // Find where the command starts (after flags) commandStart := 1 for commandStart < len(os.Args) { if !strings.HasPrefix(os.Args[commandStart], "-") { break } commandStart++ } // Parse only the flags before the command if err := flags.Parse(os.Args[1:commandStart]); err != nil { fmt.Fprintf(os.Stderr, "Error parsing flags: %v\n", err) os.Exit(1) } // Get command and its arguments (everything after the flags) if commandStart >= len(os.Args) { printUsage() os.Exit(1) } args := os.Args[commandStart:] if universalPM != "" { if mgr, ok := pkg_manager.GetUniversalManager(universalPM); ok { handleCommand(&mgr.Manager, args) return } fmt.Fprintf(os.Stderr, "Universal package manager %s not available\n", universalPM) os.Exit(1) } pm, err := pkg_manager.DetectPackageManager() if err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) os.Exit(1) } handleCommand(pm, args) } func handleCommand(pm *pkg_manager.Manager, args []string) { // Set output mode based on verbose flag if verbose { pkg_manager.SetOutputMode(pkg_manager.OutputVerbose) } else { pkg_manager.SetOutputMode(pkg_manager.OutputQuiet) } if len(args) == 0 { printUsage() os.Exit(1) } command := args[0] // Filter out any arguments that look like flags var cmdArgs []string for _, arg := range args[1:] { if !strings.HasPrefix(arg, "-") { cmdArgs = append(cmdArgs, arg) } } switch command { case "install": if len(cmdArgs) == 0 { errorColor.Fprintln(os.Stderr, "Error: no packages specified") os.Exit(1) } err := runWithSpinner("Installing packages...", pm.NeedsSudo(), func() error { return pm.Install(cmdArgs...) }) if err != nil { errorColor.Fprintln(os.Stderr, err) os.Exit(1) } success.Printf("āœ“ Successfully installed: %s\n", strings.Join(cmdArgs, ", ")) case "remove": if len(cmdArgs) == 0 { errorColor.Fprintln(os.Stderr, "Error: no packages specified") os.Exit(1) } err := runWithSpinner("Removing packages...", pm.NeedsSudo(), func() error { return pm.Remove(cmdArgs...) }) if err != nil { errorColor.Fprintf(os.Stderr, "Failed to remove packages: %v\n", err) os.Exit(1) } success.Printf("āœ“ Successfully removed: %s\n", strings.Join(cmdArgs, ", ")) case "search": if len(cmdArgs) == 0 { fmt.Fprintln(os.Stderr, "Error: search term required") os.Exit(1) } if err := pm.Search(strings.Join(cmdArgs, " ")); err != nil { fmt.Fprintf(os.Stderr, "Search failed: %v\n", err) os.Exit(1) } case "update": if err := pm.Update(); err != nil { fmt.Fprintf(os.Stderr, "Failed to update package lists: %v\n", err) os.Exit(1) } case "upgrade": if err := pm.Upgrade(); err != nil { fmt.Fprintf(os.Stderr, "Failed to upgrade packages: %v\n", err) os.Exit(1) } case "list": if err := pm.ListInstalled(); err != nil { fmt.Fprintf(os.Stderr, "Failed to list packages: %v\n", err) os.Exit(1) } case "info": if len(cmdArgs) == 0 { fmt.Fprintln(os.Stderr, "Error: package name required") os.Exit(1) } if err := pm.ShowInfo(cmdArgs[0]); err != nil { fmt.Fprintf(os.Stderr, "Failed to show package info: %v\n", err) os.Exit(1) } case "repo": if len(cmdArgs) < 1 { fmt.Fprintln(os.Stderr, "Error: repo subcommand required (add/remove/list)") os.Exit(1) } switch cmdArgs[0] { case "add": if len(cmdArgs) < 2 { fmt.Fprintln(os.Stderr, "Error: repository URL required") os.Exit(1) } if err := pm.AddRepository(cmdArgs[1]); err != nil { fmt.Fprintf(os.Stderr, "Failed to add repository: %v\n", err) os.Exit(1) } case "remove": if len(cmdArgs) < 2 { fmt.Fprintln(os.Stderr, "Error: repository name required") os.Exit(1) } if err := pm.RemoveRepository(cmdArgs[1]); err != nil { fmt.Fprintf(os.Stderr, "Failed to remove repository: %v\n", err) os.Exit(1) } case "list": if err := pm.ListRepositories(); err != nil { fmt.Fprintf(os.Stderr, "Failed to list repositories: %v\n", err) os.Exit(1) } default: fmt.Fprintf(os.Stderr, "Unknown repo subcommand: %s\n", cmdArgs[0]) printUsage() os.Exit(1) } case "clean": if err := pm.CleanCache(); err != nil { fmt.Fprintf(os.Stderr, "Failed to clean cache: %v\n", err) os.Exit(1) } case "autoremove": if err := pm.AutoRemove(); err != nil { fmt.Fprintf(os.Stderr, "Failed to remove unused dependencies: %v\n", err) os.Exit(1) } case "check-deps": pkg := "" if len(cmdArgs) > 0 { pkg = cmdArgs[0] } if err := pm.CheckDependencies(pkg); err != nil { fmt.Fprintf(os.Stderr, "Dependency check failed: %v\n", err) os.Exit(1) } case "show-deps": if len(cmdArgs) == 0 { fmt.Fprintln(os.Stderr, "Error: package name required") os.Exit(1) } if err := pm.ShowDependencies(cmdArgs[0]); err != nil { fmt.Fprintf(os.Stderr, "Failed to show dependencies: %v\n", err) os.Exit(1) } case "source": if len(cmdArgs) == 0 { fmt.Fprintln(os.Stderr, "Error: package name required") os.Exit(1) } if err := pm.HandleSource(cmdArgs[0]); err != nil { fmt.Fprintf(os.Stderr, "Failed to search source: %v\n", err) os.Exit(1) } default: fmt.Fprintf(os.Stderr, "Unknown command: %s\n", command) printUsage() os.Exit(1) } } func printUsage() { header.Println("\nšŸ“¦ Universal Package Manager") fmt.Println("\nUsage: upm [-u snap|flatpak] [-v] [arguments]") info.Println("\nCommands:") cmdColor.Print(" install ") fmt.Println(" [package2[@version]...] - Install packages") cmdColor.Print(" remove ") fmt.Println(" [package2...] - Remove packages") cmdColor.Print(" search ") fmt.Println(" - Search for packages") cmdColor.Print(" update ") fmt.Println(" - Update package lists") cmdColor.Print(" upgrade ") fmt.Println(" - Upgrade installed packages") cmdColor.Print(" list ") fmt.Println(" - List installed packages") cmdColor.Print(" info ") fmt.Println(" - Show package information") info.Println("\nRepository Management:") cmdColor.Print(" repo add ") fmt.Println(" - Add repository") cmdColor.Print(" repo remove ") fmt.Println(" - Remove repository") cmdColor.Print(" repo list ") fmt.Println(" - List repositories") info.Println("\nMaintenance:") cmdColor.Print(" clean ") fmt.Println(" - Clean package cache") cmdColor.Print(" autoremove ") fmt.Println(" - Remove unused dependencies") info.Println("\nDependency Management:") cmdColor.Print(" check-deps ") fmt.Println("[package] - Check dependencies") cmdColor.Print(" show-deps ") fmt.Println(" - Show package dependencies") info.Println("\nFlags:") fmt.Println(" -u string Use universal package manager (snap/flatpak)") fmt.Println(" -v Show verbose output") warning.Println("\nExamples:") fmt.Println(" upm install nginx@1.18.0") fmt.Println(" upm -u snap install firefox") fmt.Println(" upm repo add https://repo.example.com") fmt.Println(" upm show-deps nginx") fmt.Println() }