fixed spinner
This commit is contained in:
parent
326a2b79d2
commit
446c5343bd
127
main.go
127
main.go
@ -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:")
|
||||
|
@ -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
|
||||
@ -387,3 +419,8 @@ 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
|
||||
}
|
Loading…
Reference in New Issue
Block a user