fixed spinner
This commit is contained in:
parent
326a2b79d2
commit
446c5343bd
127
main.go
127
main.go
@ -15,11 +15,11 @@ import (
|
|||||||
var (
|
var (
|
||||||
// Color definitions
|
// Color definitions
|
||||||
success = color.New(color.FgGreen, color.Bold)
|
success = color.New(color.FgGreen, color.Bold)
|
||||||
|
errorColor = color.New(color.FgRed, color.Bold)
|
||||||
warning = color.New(color.FgYellow, color.Bold)
|
warning = color.New(color.FgYellow, color.Bold)
|
||||||
error = color.New(color.FgRed, color.Bold)
|
|
||||||
info = color.New(color.FgCyan)
|
info = color.New(color.FgCyan)
|
||||||
header = color.New(color.FgMagenta, color.Bold)
|
header = color.New(color.FgMagenta, color.Bold)
|
||||||
command = color.New(color.FgBlue, color.Bold)
|
cmdColor = color.New(color.FgBlue, color.Bold)
|
||||||
// Command line flags
|
// Command line flags
|
||||||
verbose bool
|
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() {
|
func main() {
|
||||||
// Initialize universal package managers
|
// Initialize universal package managers
|
||||||
pkg_manager.InitializeUniversalManagers()
|
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()
|
printUsage()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for flags
|
args := os.Args[commandStart:]
|
||||||
var universalPM string
|
|
||||||
flag.StringVar(&universalPM, "u", "", "Use universal package manager (snap/flatpak)")
|
|
||||||
flag.BoolVar(&verbose, "v", false, "Show verbose output")
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
if universalPM != "" {
|
if universalPM != "" {
|
||||||
if mgr, ok := pkg_manager.GetUniversalManager(universalPM); ok {
|
if mgr, ok := pkg_manager.GetUniversalManager(universalPM); ok {
|
||||||
handleCommand(&mgr.Manager, os.Args[1:])
|
handleCommand(&mgr.Manager, args)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Fprintf(os.Stderr, "Universal package manager %s not available\n", universalPM)
|
fmt.Fprintf(os.Stderr, "Universal package manager %s not available\n", universalPM)
|
||||||
@ -64,7 +115,7 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCommand(pm, os.Args[1:])
|
handleCommand(pm, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleCommand(pm *pkg_manager.Manager, args []string) {
|
func handleCommand(pm *pkg_manager.Manager, args []string) {
|
||||||
@ -81,33 +132,39 @@ func handleCommand(pm *pkg_manager.Manager, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
command := args[0]
|
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 {
|
switch command {
|
||||||
case "install":
|
case "install":
|
||||||
if len(cmdArgs) == 0 {
|
if len(cmdArgs) == 0 {
|
||||||
error.Fprintln(os.Stderr, "Error: no packages specified")
|
errorColor.Fprintln(os.Stderr, "Error: no packages specified")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
stop := showSpinner("Installing packages...")
|
err := runWithSpinner("Installing packages...", pm.NeedsSudo(), func() error {
|
||||||
err := pm.Install(cmdArgs...)
|
return pm.Install(cmdArgs...)
|
||||||
stop()
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
error.Fprintf(os.Stderr, "Failed to install packages: %v\n", err)
|
errorColor.Fprintln(os.Stderr, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
success.Printf("✓ Successfully installed: %s\n", strings.Join(cmdArgs, ", "))
|
success.Printf("✓ Successfully installed: %s\n", strings.Join(cmdArgs, ", "))
|
||||||
|
|
||||||
case "remove":
|
case "remove":
|
||||||
if len(cmdArgs) == 0 {
|
if len(cmdArgs) == 0 {
|
||||||
error.Fprintln(os.Stderr, "Error: no packages specified")
|
errorColor.Fprintln(os.Stderr, "Error: no packages specified")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
stop := showSpinner("Removing packages...")
|
err := runWithSpinner("Removing packages...", pm.NeedsSudo(), func() error {
|
||||||
err := pm.Remove(cmdArgs...)
|
return pm.Remove(cmdArgs...)
|
||||||
stop()
|
})
|
||||||
if err != nil {
|
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)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
success.Printf("✓ Successfully removed: %s\n", strings.Join(cmdArgs, ", "))
|
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]")
|
fmt.Println("\nUsage: upm [-u snap|flatpak] [-v] <command> [arguments]")
|
||||||
|
|
||||||
info.Println("\nCommands:")
|
info.Println("\nCommands:")
|
||||||
command.Print(" install ")
|
cmdColor.Print(" install ")
|
||||||
fmt.Println("<package1[@version]> [package2[@version]...] - Install packages")
|
fmt.Println("<package1[@version]> [package2[@version]...] - Install packages")
|
||||||
command.Print(" remove ")
|
cmdColor.Print(" remove ")
|
||||||
fmt.Println("<package1> [package2...] - Remove packages")
|
fmt.Println("<package1> [package2...] - Remove packages")
|
||||||
command.Print(" search ")
|
cmdColor.Print(" search ")
|
||||||
fmt.Println("<term> - Search for packages")
|
fmt.Println("<term> - Search for packages")
|
||||||
command.Print(" update ")
|
cmdColor.Print(" update ")
|
||||||
fmt.Println(" - Update package lists")
|
fmt.Println(" - Update package lists")
|
||||||
command.Print(" upgrade ")
|
cmdColor.Print(" upgrade ")
|
||||||
fmt.Println(" - Upgrade installed packages")
|
fmt.Println(" - Upgrade installed packages")
|
||||||
command.Print(" list ")
|
cmdColor.Print(" list ")
|
||||||
fmt.Println(" - List installed packages")
|
fmt.Println(" - List installed packages")
|
||||||
command.Print(" info ")
|
cmdColor.Print(" info ")
|
||||||
fmt.Println("<package> - Show package information")
|
fmt.Println("<package> - Show package information")
|
||||||
|
|
||||||
info.Println("\nRepository Management:")
|
info.Println("\nRepository Management:")
|
||||||
command.Print(" repo add ")
|
cmdColor.Print(" repo add ")
|
||||||
fmt.Println("<url> - Add repository")
|
fmt.Println("<url> - Add repository")
|
||||||
command.Print(" repo remove ")
|
cmdColor.Print(" repo remove ")
|
||||||
fmt.Println("<name> - Remove repository")
|
fmt.Println("<name> - Remove repository")
|
||||||
command.Print(" repo list ")
|
cmdColor.Print(" repo list ")
|
||||||
fmt.Println(" - List repositories")
|
fmt.Println(" - List repositories")
|
||||||
|
|
||||||
info.Println("\nMaintenance:")
|
info.Println("\nMaintenance:")
|
||||||
command.Print(" clean ")
|
cmdColor.Print(" clean ")
|
||||||
fmt.Println(" - Clean package cache")
|
fmt.Println(" - Clean package cache")
|
||||||
command.Print(" autoremove ")
|
cmdColor.Print(" autoremove ")
|
||||||
fmt.Println(" - Remove unused dependencies")
|
fmt.Println(" - Remove unused dependencies")
|
||||||
|
|
||||||
info.Println("\nDependency Management:")
|
info.Println("\nDependency Management:")
|
||||||
command.Print(" check-deps ")
|
cmdColor.Print(" check-deps ")
|
||||||
fmt.Println("[package] - Check dependencies")
|
fmt.Println("[package] - Check dependencies")
|
||||||
command.Print(" show-deps ")
|
cmdColor.Print(" show-deps ")
|
||||||
fmt.Println("<package> - Show package dependencies")
|
fmt.Println("<package> - Show package dependencies")
|
||||||
|
|
||||||
info.Println("\nFlags:")
|
info.Println("\nFlags:")
|
||||||
|
@ -80,7 +80,7 @@ var universalManagers = map[string]*UniversalManager{
|
|||||||
searchCmd: []string{"search"},
|
searchCmd: []string{"search"},
|
||||||
updateCmd: []string{"update", "-y"},
|
updateCmd: []string{"update", "-y"},
|
||||||
upgradeCmd: []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)
|
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
|
// Install packages with version support
|
||||||
func (m *Manager) Install(packages ...string) error {
|
func (m *Manager) Install(packages ...string) error {
|
||||||
if len(packages) == 0 {
|
if len(packages) == 0 {
|
||||||
return fmt.Errorf("no packages specified")
|
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))
|
formattedPkgs := make([]string, len(packages))
|
||||||
for i, pkg := range packages {
|
for i, pkg := range packages {
|
||||||
pv := ParsePackageVersion(pkg)
|
pv := ParsePackageVersion(pkg)
|
||||||
@ -277,32 +297,44 @@ func SetOutputMode(mode OutputMode) {
|
|||||||
outputMode = mode
|
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 {
|
func (m *Manager) execCommand(baseCmd []string, args ...string) error {
|
||||||
cmdArgs := append(baseCmd, args...)
|
cmdArgs := append(baseCmd, args...)
|
||||||
|
|
||||||
var cmd *exec.Cmd
|
var cmd *exec.Cmd
|
||||||
if m.needsSudo && os.Geteuid() != 0 {
|
if m.needsSudo && os.Geteuid() != 0 {
|
||||||
// Clear the line to prevent spinner interference with sudo prompt
|
return m.execCommandWithSudo(baseCmd, args...)
|
||||||
fmt.Print("\r\033[K")
|
|
||||||
cmdArgs = append([]string{m.command}, cmdArgs...)
|
|
||||||
cmd = exec.Command("sudo", cmdArgs...)
|
|
||||||
} else {
|
} else {
|
||||||
cmd = exec.Command(m.command, cmdArgs...)
|
cmd = exec.Command(m.command, cmdArgs...)
|
||||||
}
|
|
||||||
|
|
||||||
// Always capture output
|
|
||||||
if outputMode == OutputVerbose {
|
if outputMode == OutputVerbose {
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
} else {
|
} else {
|
||||||
// Discard output in quiet mode
|
|
||||||
cmd.Stdout = io.Discard
|
cmd.Stdout = io.Discard
|
||||||
cmd.Stderr = io.Discard
|
cmd.Stderr = io.Discard
|
||||||
}
|
}
|
||||||
cmd.Stdin = os.Stdin
|
cmd.Stdin = os.Stdin
|
||||||
|
|
||||||
return cmd.Run()
|
return cmd.Run()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Remove packages
|
// Remove packages
|
||||||
func (m *Manager) Remove(packages ...string) error {
|
func (m *Manager) Remove(packages ...string) error {
|
||||||
@ -387,3 +419,8 @@ func DetectPackageManager() (*Manager, error) {
|
|||||||
}
|
}
|
||||||
return nil, fmt.Errorf("no supported package manager found")
|
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