upm-universalpackagemanager/main.go

344 lines
9.0 KiB
Go

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] <command> [arguments]")
info.Println("\nCommands:")
cmdColor.Print(" install ")
fmt.Println("<package1[@version]> [package2[@version]...] - Install packages")
cmdColor.Print(" remove ")
fmt.Println("<package1> [package2...] - Remove packages")
cmdColor.Print(" search ")
fmt.Println("<term> - 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("<package> - Show package information")
info.Println("\nRepository Management:")
cmdColor.Print(" repo add ")
fmt.Println("<url> - Add repository")
cmdColor.Print(" repo remove ")
fmt.Println("<name> - 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("<package> - 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()
}