fixed spinner

This commit is contained in:
e 2025-01-21 17:56:15 -05:00
parent 326a2b79d2
commit 446c5343bd
2 changed files with 147 additions and 53 deletions

127
main.go
View File

@ -15,11 +15,11 @@ import (
var (
// Color definitions
success = color.New(color.FgGreen, color.Bold)
errorColor = color.New(color.FgRed, color.Bold)
warning = color.New(color.FgYellow, color.Bold)
error = color.New(color.FgRed, color.Bold)
info = color.New(color.FgCyan)
header = color.New(color.FgMagenta, color.Bold)
command = color.New(color.FgBlue, color.Bold)
cmdColor = color.New(color.FgBlue, color.Bold)
// Command line flags
verbose bool
)
@ -34,24 +34,75 @@ func showSpinner(message string) func() {
}
}
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()
if len(os.Args) < 2 {
// 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)
}
// Check for flags
var universalPM string
flag.StringVar(&universalPM, "u", "", "Use universal package manager (snap/flatpak)")
flag.BoolVar(&verbose, "v", false, "Show verbose output")
flag.Parse()
args := os.Args[commandStart:]
if universalPM != "" {
if mgr, ok := pkg_manager.GetUniversalManager(universalPM); ok {
handleCommand(&mgr.Manager, os.Args[1:])
handleCommand(&mgr.Manager, args)
return
}
fmt.Fprintf(os.Stderr, "Universal package manager %s not available\n", universalPM)
@ -64,7 +115,7 @@ func main() {
os.Exit(1)
}
handleCommand(pm, os.Args[1:])
handleCommand(pm, args)
}
func handleCommand(pm *pkg_manager.Manager, args []string) {
@ -81,33 +132,39 @@ func handleCommand(pm *pkg_manager.Manager, args []string) {
}
command := args[0]
cmdArgs := args[1:]
// 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 {
error.Fprintln(os.Stderr, "Error: no packages specified")
errorColor.Fprintln(os.Stderr, "Error: no packages specified")
os.Exit(1)
}
stop := showSpinner("Installing packages...")
err := pm.Install(cmdArgs...)
stop()
err := runWithSpinner("Installing packages...", pm.NeedsSudo(), func() error {
return pm.Install(cmdArgs...)
})
if err != nil {
error.Fprintf(os.Stderr, "Failed to install packages: %v\n", err)
errorColor.Fprintln(os.Stderr, err)
os.Exit(1)
}
success.Printf("✓ Successfully installed: %s\n", strings.Join(cmdArgs, ", "))
case "remove":
if len(cmdArgs) == 0 {
error.Fprintln(os.Stderr, "Error: no packages specified")
errorColor.Fprintln(os.Stderr, "Error: no packages specified")
os.Exit(1)
}
stop := showSpinner("Removing packages...")
err := pm.Remove(cmdArgs...)
stop()
err := runWithSpinner("Removing packages...", pm.NeedsSudo(), func() error {
return pm.Remove(cmdArgs...)
})
if err != nil {
error.Fprintf(os.Stderr, "Failed to remove packages: %v\n", err)
errorColor.Fprintf(os.Stderr, "Failed to remove packages: %v\n", err)
os.Exit(1)
}
success.Printf("✓ Successfully removed: %s\n", strings.Join(cmdArgs, ", "))
@ -229,39 +286,39 @@ func printUsage() {
fmt.Println("\nUsage: upm [-u snap|flatpak] [-v] <command> [arguments]")
info.Println("\nCommands:")
command.Print(" install ")
cmdColor.Print(" install ")
fmt.Println("<package1[@version]> [package2[@version]...] - Install packages")
command.Print(" remove ")
cmdColor.Print(" remove ")
fmt.Println("<package1> [package2...] - Remove packages")
command.Print(" search ")
cmdColor.Print(" search ")
fmt.Println("<term> - Search for packages")
command.Print(" update ")
cmdColor.Print(" update ")
fmt.Println(" - Update package lists")
command.Print(" upgrade ")
cmdColor.Print(" upgrade ")
fmt.Println(" - Upgrade installed packages")
command.Print(" list ")
cmdColor.Print(" list ")
fmt.Println(" - List installed packages")
command.Print(" info ")
cmdColor.Print(" info ")
fmt.Println("<package> - Show package information")
info.Println("\nRepository Management:")
command.Print(" repo add ")
cmdColor.Print(" repo add ")
fmt.Println("<url> - Add repository")
command.Print(" repo remove ")
cmdColor.Print(" repo remove ")
fmt.Println("<name> - Remove repository")
command.Print(" repo list ")
cmdColor.Print(" repo list ")
fmt.Println(" - List repositories")
info.Println("\nMaintenance:")
command.Print(" clean ")
cmdColor.Print(" clean ")
fmt.Println(" - Clean package cache")
command.Print(" autoremove ")
cmdColor.Print(" autoremove ")
fmt.Println(" - Remove unused dependencies")
info.Println("\nDependency Management:")
command.Print(" check-deps ")
cmdColor.Print(" check-deps ")
fmt.Println("[package] - Check dependencies")
command.Print(" show-deps ")
cmdColor.Print(" show-deps ")
fmt.Println("<package> - Show package dependencies")
info.Println("\nFlags:")

View File

@ -80,7 +80,7 @@ var universalManagers = map[string]*UniversalManager{
searchCmd: []string{"search"},
updateCmd: []string{"update", "-y"},
upgradeCmd: []string{"update", "-y"},
needsSudo: false,
needsSudo: true,
},
},
}
@ -232,12 +232,32 @@ func (m *Manager) formatPackageWithVersion(pv PackageVersion) string {
return fmt.Sprintf("%s%s%s", pv.Name, m.versionSep, pv.Version)
}
// CheckPackageExists verifies if a package exists in the repositories
func (m *Manager) CheckPackageExists(pkg string) bool {
cmd := exec.Command(m.command, append(m.searchCmd, pkg)...)
output, err := cmd.Output()
if err != nil {
return false
}
// Different package managers have different output formats
// This is a simple check that should work for most
return len(output) > 0
}
// Install packages with version support
func (m *Manager) Install(packages ...string) error {
if len(packages) == 0 {
return fmt.Errorf("no packages specified")
}
// Validate packages first
for _, pkg := range packages {
pv := ParsePackageVersion(pkg)
if !m.CheckPackageExists(pv.Name) {
return fmt.Errorf("package not found: %s", pv.Name)
}
}
formattedPkgs := make([]string, len(packages))
for i, pkg := range packages {
pv := ParsePackageVersion(pkg)
@ -277,31 +297,43 @@ func SetOutputMode(mode OutputMode) {
outputMode = mode
}
// execCommandWithSudo executes a command with sudo
func (m *Manager) execCommandWithSudo(baseCmd []string, args ...string) error {
cmdArgs := append(baseCmd, args...)
cmdArgs = append([]string{m.command}, cmdArgs...)
cmd := exec.Command("sudo", cmdArgs...)
// Always show sudo password prompt
cmd.Stdin = os.Stdin
if outputMode == OutputVerbose {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
} else {
cmd.Stdout = io.Discard
cmd.Stderr = io.Discard
}
return cmd.Run()
}
func (m *Manager) execCommand(baseCmd []string, args ...string) error {
cmdArgs := append(baseCmd, args...)
var cmd *exec.Cmd
if m.needsSudo && os.Geteuid() != 0 {
// Clear the line to prevent spinner interference with sudo prompt
fmt.Print("\r\033[K")
cmdArgs = append([]string{m.command}, cmdArgs...)
cmd = exec.Command("sudo", cmdArgs...)
return m.execCommandWithSudo(baseCmd, args...)
} else {
cmd = exec.Command(m.command, cmdArgs...)
if outputMode == OutputVerbose {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
} else {
cmd.Stdout = io.Discard
cmd.Stderr = io.Discard
}
cmd.Stdin = os.Stdin
return cmd.Run()
}
// Always capture output
if outputMode == OutputVerbose {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
} else {
// Discard output in quiet mode
cmd.Stdout = io.Discard
cmd.Stderr = io.Discard
}
cmd.Stdin = os.Stdin
return cmd.Run()
}
// Remove packages
@ -386,4 +418,9 @@ func DetectPackageManager() (*Manager, error) {
}
}
return nil, fmt.Errorf("no supported package manager found")
}
// NeedsSudo returns whether this package manager needs sudo
func (m *Manager) NeedsSudo() bool {
return m.needsSudo && os.Geteuid() != 0
}