Init commit
This commit is contained in:
commit
981dc3d2ff
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
# Binaries
|
||||
upm
|
||||
*.exe
|
||||
|
||||
# IDE specific files
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# OS specific files
|
||||
.DS_Store
|
||||
Thumbs.db
|
16
Makefile
Normal file
16
Makefile
Normal file
@ -0,0 +1,16 @@
|
||||
.PHONY: build clean install
|
||||
|
||||
build:
|
||||
go build -o upm
|
||||
|
||||
clean:
|
||||
rm -f upm
|
||||
|
||||
install: build
|
||||
sudo mv upm /usr/local/bin/
|
||||
|
||||
test:
|
||||
go test ./...
|
||||
|
||||
deps:
|
||||
go mod tidy
|
145
README.md
Normal file
145
README.md
Normal file
@ -0,0 +1,145 @@
|
||||
# Universal Package Manager (UPM)
|
||||
|
||||
A unified command-line interface for managing packages across different Linux distributions. UPM provides a consistent interface for package management operations regardless of the underlying package manager (apt, dnf, pacman, etc.).
|
||||
|
||||
## Features
|
||||
|
||||
- 🔄 Universal syntax across package managers
|
||||
- 📦 Support for multiple package managers:
|
||||
- apt (Debian/Ubuntu)
|
||||
- dnf/yum (RHEL/Fedora)
|
||||
- pacman (Arch)
|
||||
- zypper (openSUSE)
|
||||
- apk (Alpine)
|
||||
- xbps (Void Linux)
|
||||
- emerge (Gentoo)
|
||||
- 🚀 Support for universal package managers:
|
||||
- snap
|
||||
- flatpak
|
||||
- 🔍 Version-specific package installation
|
||||
- 📋 Repository management
|
||||
- 🧹 Cache cleaning
|
||||
- 🔗 Dependency handling
|
||||
|
||||
## Installation
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Go 1.22 or newer
|
||||
- Git
|
||||
|
||||
### Building from Source
|
||||
|
||||
1. Clone the repository and enter the directory:
|
||||
|
||||
git clone https://git.supernets.org/e/managerofmanagers
|
||||
cd upm
|
||||
|
||||
2. Install dependencies:
|
||||
|
||||
go mod download
|
||||
|
||||
3. Build the binary:
|
||||
|
||||
go build -o upm
|
||||
|
||||
4. (Optional) Install system-wide:
|
||||
|
||||
sudo mv upm /usr/local/bin/
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Commands
|
||||
|
||||
Install packages:
|
||||
upm install nginx
|
||||
upm install nginx@1.18.0 # Install specific version
|
||||
|
||||
Remove packages:
|
||||
upm remove nginx
|
||||
|
||||
Search for packages:
|
||||
upm search nginx
|
||||
|
||||
Update package lists:
|
||||
upm update
|
||||
|
||||
Upgrade installed packages:
|
||||
upm upgrade
|
||||
|
||||
List installed packages:
|
||||
upm list
|
||||
|
||||
Show package information:
|
||||
upm info nginx
|
||||
|
||||
### Repository Management
|
||||
|
||||
Add a repository:
|
||||
upm repo add https://repo.example.com
|
||||
|
||||
Remove a repository:
|
||||
upm repo remove example-repo
|
||||
|
||||
List repositories:
|
||||
upm repo list
|
||||
|
||||
### Maintenance
|
||||
|
||||
Clean package cache:
|
||||
upm clean
|
||||
|
||||
Remove unused dependencies:
|
||||
upm autoremove
|
||||
|
||||
### Dependency Management
|
||||
|
||||
Check dependencies:
|
||||
upm check-deps
|
||||
|
||||
Check dependencies for specific package:
|
||||
upm check-deps nginx
|
||||
|
||||
Show package dependencies:
|
||||
upm show-deps nginx
|
||||
|
||||
### Using Universal Package Managers
|
||||
|
||||
Install using snap:
|
||||
upm -u snap install firefox
|
||||
|
||||
Install using flatpak:
|
||||
upm -u flatpak install org.mozilla.firefox
|
||||
|
||||
## Project Structure
|
||||
|
||||
upm/
|
||||
├── main.go # Main application entry point
|
||||
├── pkg_manager/
|
||||
│ └── pkg_manager.go # Core package manager implementation
|
||||
├── go.mod
|
||||
├── go.sum
|
||||
└── README.md
|
||||
|
||||
## Dependencies
|
||||
|
||||
Required Go packages:
|
||||
- github.com/fatih/color - For colorized terminal output
|
||||
- github.com/briandowns/spinner - For progress indicators
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Please feel free to submit a Pull Request.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License - see the LICENSE file for details.
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
- Thanks to all the Linux distribution maintainers for their package managers
|
||||
- The Go community for the excellent standard library and third-party packages
|
||||
|
||||
## Support
|
||||
|
||||
If you encounter any issues or have questions, please file an issue on the GitHub repository.
|
15
go.mod
Normal file
15
go.mod
Normal file
@ -0,0 +1,15 @@
|
||||
module git.supernets.org/e/managerofmanagers
|
||||
|
||||
go 1.22
|
||||
|
||||
require (
|
||||
github.com/briandowns/spinner v1.23.0
|
||||
github.com/fatih/color v1.16.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
golang.org/x/sys v0.14.0 // indirect
|
||||
golang.org/x/term v0.1.0 // indirect
|
||||
)
|
15
go.sum
Normal file
15
go.sum
Normal file
@ -0,0 +1,15 @@
|
||||
github.com/briandowns/spinner v1.23.0 h1:alDF2guRWqa/FOZZYWjlMIx2L6H0wyewPxo/CH4Pt2A=
|
||||
github.com/briandowns/spinner v1.23.0/go.mod h1:rPG4gmXeN3wQV/TsAY4w8lPdIM6RX3yqeBQJSrbXjuE=
|
||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
|
||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
277
main.go
Normal file
277
main.go
Normal file
@ -0,0 +1,277 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/briandowns/spinner"
|
||||
"git.supernets.org/e/managerofmanagers/pkg_manager"
|
||||
)
|
||||
|
||||
var (
|
||||
// Color definitions
|
||||
success = color.New(color.FgGreen, 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)
|
||||
// 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 main() {
|
||||
// Initialize universal package managers
|
||||
pkg_manager.InitializeUniversalManagers()
|
||||
|
||||
if len(os.Args) < 2 {
|
||||
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()
|
||||
|
||||
if universalPM != "" {
|
||||
if mgr, ok := pkg_manager.GetUniversalManager(universalPM); ok {
|
||||
handleCommand(&mgr.Manager, os.Args[1:])
|
||||
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, os.Args[1:])
|
||||
}
|
||||
|
||||
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]
|
||||
cmdArgs := args[1:]
|
||||
|
||||
switch command {
|
||||
case "install":
|
||||
if len(cmdArgs) == 0 {
|
||||
error.Fprintln(os.Stderr, "Error: no packages specified")
|
||||
os.Exit(1)
|
||||
}
|
||||
stop := showSpinner("Installing packages...")
|
||||
err := pm.Install(cmdArgs...)
|
||||
stop()
|
||||
if err != nil {
|
||||
error.Fprintf(os.Stderr, "Failed to install packages: %v\n", 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")
|
||||
os.Exit(1)
|
||||
}
|
||||
stop := showSpinner("Removing packages...")
|
||||
err := pm.Remove(cmdArgs...)
|
||||
stop()
|
||||
if err != nil {
|
||||
error.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)
|
||||
}
|
||||
|
||||
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:")
|
||||
command.Print(" install ")
|
||||
fmt.Println("<package1[@version]> [package2[@version]...] - Install packages")
|
||||
command.Print(" remove ")
|
||||
fmt.Println("<package1> [package2...] - Remove packages")
|
||||
command.Print(" search ")
|
||||
fmt.Println("<term> - Search for packages")
|
||||
command.Print(" update ")
|
||||
fmt.Println(" - Update package lists")
|
||||
command.Print(" upgrade ")
|
||||
fmt.Println(" - Upgrade installed packages")
|
||||
command.Print(" list ")
|
||||
fmt.Println(" - List installed packages")
|
||||
command.Print(" info ")
|
||||
fmt.Println("<package> - Show package information")
|
||||
|
||||
info.Println("\nRepository Management:")
|
||||
command.Print(" repo add ")
|
||||
fmt.Println("<url> - Add repository")
|
||||
command.Print(" repo remove ")
|
||||
fmt.Println("<name> - Remove repository")
|
||||
command.Print(" repo list ")
|
||||
fmt.Println(" - List repositories")
|
||||
|
||||
info.Println("\nMaintenance:")
|
||||
command.Print(" clean ")
|
||||
fmt.Println(" - Clean package cache")
|
||||
command.Print(" autoremove ")
|
||||
fmt.Println(" - Remove unused dependencies")
|
||||
|
||||
info.Println("\nDependency Management:")
|
||||
command.Print(" check-deps ")
|
||||
fmt.Println("[package] - Check dependencies")
|
||||
command.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()
|
||||
}
|
388
pkg_manager/pkg_manager.go
Normal file
388
pkg_manager/pkg_manager.go
Normal file
@ -0,0 +1,388 @@
|
||||
package pkg_manager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// PackageManager interface defines common operations
|
||||
type PackageManager interface {
|
||||
Install(packages ...string) error
|
||||
Remove(packages ...string) error
|
||||
Search(term string) error
|
||||
Update() error
|
||||
Upgrade() error
|
||||
ListInstalled() error
|
||||
ShowInfo(pkg string) error
|
||||
AddRepository(repo string) error
|
||||
RemoveRepository(repo string) error
|
||||
ListRepositories() error
|
||||
CleanCache() error
|
||||
AutoRemove() error
|
||||
CheckDependencies(pkg string) error
|
||||
ShowDependencies(pkg string) error
|
||||
}
|
||||
|
||||
// Manager represents a concrete package manager
|
||||
type Manager struct {
|
||||
command string
|
||||
installCmd []string
|
||||
removeCmd []string
|
||||
searchCmd []string
|
||||
updateCmd []string
|
||||
upgradeCmd []string
|
||||
listCmd []string
|
||||
infoCmd []string
|
||||
addRepoCmd []string
|
||||
removeRepoCmd []string
|
||||
listRepoCmd []string
|
||||
cleanCacheCmd []string
|
||||
autoRemoveCmd []string
|
||||
checkDependsCmd []string
|
||||
showDependsCmd []string
|
||||
needsSudo bool
|
||||
versionSep string
|
||||
cacheDir string
|
||||
}
|
||||
|
||||
// UniversalManager represents universal package managers like snap/flatpak
|
||||
type UniversalManager struct {
|
||||
Manager
|
||||
isAvailable bool
|
||||
}
|
||||
|
||||
// PackageVersion represents a package with optional version
|
||||
type PackageVersion struct {
|
||||
Name string
|
||||
Version string
|
||||
}
|
||||
|
||||
var universalManagers = map[string]*UniversalManager{
|
||||
"snap": {
|
||||
Manager: Manager{
|
||||
command: "snap",
|
||||
installCmd: []string{"install"},
|
||||
removeCmd: []string{"remove"},
|
||||
searchCmd: []string{"find"},
|
||||
updateCmd: []string{"refresh"},
|
||||
upgradeCmd: []string{"refresh"},
|
||||
needsSudo: true,
|
||||
},
|
||||
},
|
||||
"flatpak": {
|
||||
Manager: Manager{
|
||||
command: "flatpak",
|
||||
installCmd: []string{"install", "-y"},
|
||||
removeCmd: []string{"uninstall", "-y"},
|
||||
searchCmd: []string{"search"},
|
||||
updateCmd: []string{"update", "-y"},
|
||||
upgradeCmd: []string{"update", "-y"},
|
||||
needsSudo: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var managers = map[string]Manager{
|
||||
"apt": {
|
||||
command: "apt",
|
||||
installCmd: []string{"install", "-y"},
|
||||
removeCmd: []string{"remove", "-y"},
|
||||
searchCmd: []string{"search"},
|
||||
updateCmd: []string{"update"},
|
||||
upgradeCmd: []string{"upgrade", "-y"},
|
||||
listCmd: []string{"list", "--installed"},
|
||||
infoCmd: []string{"show"},
|
||||
addRepoCmd: []string{"add-apt-repository", "-y"},
|
||||
removeRepoCmd: []string{"add-apt-repository", "-r", "-y"},
|
||||
listRepoCmd: []string{"sources"},
|
||||
cleanCacheCmd: []string{"clean"},
|
||||
autoRemoveCmd: []string{"autoremove", "-y"},
|
||||
checkDependsCmd: []string{"check"},
|
||||
showDependsCmd: []string{"depends"},
|
||||
needsSudo: true,
|
||||
versionSep: "=",
|
||||
cacheDir: "/var/cache/apt",
|
||||
},
|
||||
"dnf": {
|
||||
command: "dnf",
|
||||
installCmd: []string{"install", "-y"},
|
||||
removeCmd: []string{"remove", "-y"},
|
||||
searchCmd: []string{"search"},
|
||||
updateCmd: []string{"check-update"},
|
||||
upgradeCmd: []string{"upgrade", "-y"},
|
||||
listCmd: []string{"list", "installed"},
|
||||
infoCmd: []string{"info"},
|
||||
addRepoCmd: []string{"config-manager", "--add-repo"},
|
||||
removeRepoCmd: []string{"config-manager", "--disable"},
|
||||
listRepoCmd: []string{"repolist"},
|
||||
cleanCacheCmd: []string{"clean", "all"},
|
||||
autoRemoveCmd: []string{"autoremove", "-y"},
|
||||
checkDependsCmd: []string{"check-dependencies"},
|
||||
showDependsCmd: []string{"repoquery", "--requires"},
|
||||
needsSudo: true,
|
||||
versionSep: "-",
|
||||
cacheDir: "/var/cache/dnf",
|
||||
},
|
||||
"yum": {
|
||||
command: "yum",
|
||||
installCmd: []string{"install", "-y"},
|
||||
removeCmd: []string{"remove", "-y"},
|
||||
searchCmd: []string{"search"},
|
||||
updateCmd: []string{"check-update"},
|
||||
upgradeCmd: []string{"upgrade", "-y"},
|
||||
needsSudo: true,
|
||||
versionSep: "-",
|
||||
cacheDir: "/var/cache/yum",
|
||||
},
|
||||
"pacman": {
|
||||
command: "pacman",
|
||||
installCmd: []string{"-S", "--noconfirm"},
|
||||
removeCmd: []string{"-R", "--noconfirm"},
|
||||
searchCmd: []string{"-Ss"},
|
||||
updateCmd: []string{"-Sy"},
|
||||
upgradeCmd: []string{"-Syu", "--noconfirm"},
|
||||
needsSudo: true,
|
||||
versionSep: "=",
|
||||
cacheDir: "/var/cache/pacman",
|
||||
},
|
||||
"zypper": {
|
||||
command: "zypper",
|
||||
installCmd: []string{"install", "-y"},
|
||||
removeCmd: []string{"remove", "-y"},
|
||||
searchCmd: []string{"search"},
|
||||
updateCmd: []string{"refresh"},
|
||||
upgradeCmd: []string{"update", "-y"},
|
||||
needsSudo: true,
|
||||
versionSep: "=",
|
||||
cacheDir: "/var/cache/zypp",
|
||||
},
|
||||
"apk": {
|
||||
command: "apk",
|
||||
installCmd: []string{"add"},
|
||||
removeCmd: []string{"del"},
|
||||
searchCmd: []string{"search"},
|
||||
updateCmd: []string{"update"},
|
||||
upgradeCmd: []string{"upgrade"},
|
||||
needsSudo: true,
|
||||
versionSep: "=",
|
||||
cacheDir: "/var/cache/apk",
|
||||
},
|
||||
"xbps": {
|
||||
command: "xbps-install",
|
||||
installCmd: []string{"-y"},
|
||||
removeCmd: []string{"-R"},
|
||||
searchCmd: []string{"-Rs"},
|
||||
updateCmd: []string{"-S"},
|
||||
upgradeCmd: []string{"-Su"},
|
||||
listCmd: []string{"-l"},
|
||||
infoCmd: []string{"-S"},
|
||||
addRepoCmd: []string{"-R", "add"},
|
||||
removeRepoCmd: []string{"-R", "remove"},
|
||||
listRepoCmd: []string{"-R", "list"},
|
||||
cleanCacheCmd: []string{"-Op"},
|
||||
autoRemoveCmd: []string{"-Oo"},
|
||||
checkDependsCmd: []string{"-C"},
|
||||
showDependsCmd: []string{"-x"},
|
||||
needsSudo: true,
|
||||
versionSep: "-",
|
||||
cacheDir: "/var/cache/xbps",
|
||||
},
|
||||
"emerge": {
|
||||
command: "emerge",
|
||||
installCmd: []string{"--ask", "n"},
|
||||
removeCmd: []string{"--unmerge", "--ask", "n"},
|
||||
searchCmd: []string{"--search"},
|
||||
updateCmd: []string{"--sync"},
|
||||
upgradeCmd: []string{"--update", "--deep", "--ask", "n", "@world"},
|
||||
listCmd: []string{"--list-sets"},
|
||||
infoCmd: []string{"--info"},
|
||||
addRepoCmd: []string{"--repos-conf", "add"},
|
||||
removeRepoCmd: []string{"--repos-conf", "remove"},
|
||||
listRepoCmd: []string{"--repos-conf", "list"},
|
||||
cleanCacheCmd: []string{"--depclean"},
|
||||
autoRemoveCmd: []string{"--depclean"},
|
||||
checkDependsCmd: []string{"--depclean", "-p"},
|
||||
showDependsCmd: []string{"--deps"},
|
||||
needsSudo: true,
|
||||
versionSep: "-",
|
||||
cacheDir: "/var/cache/portage",
|
||||
},
|
||||
}
|
||||
|
||||
// ParsePackageVersion parses package name and version
|
||||
func ParsePackageVersion(pkg string) PackageVersion {
|
||||
parts := strings.Split(pkg, "@")
|
||||
if len(parts) == 2 {
|
||||
return PackageVersion{
|
||||
Name: parts[0],
|
||||
Version: parts[1],
|
||||
}
|
||||
}
|
||||
return PackageVersion{Name: pkg}
|
||||
}
|
||||
|
||||
// formatPackageWithVersion formats package with version using manager's separator
|
||||
func (m *Manager) formatPackageWithVersion(pv PackageVersion) string {
|
||||
if pv.Version == "" {
|
||||
return pv.Name
|
||||
}
|
||||
return fmt.Sprintf("%s%s%s", pv.Name, m.versionSep, pv.Version)
|
||||
}
|
||||
|
||||
// Install packages with version support
|
||||
func (m *Manager) Install(packages ...string) error {
|
||||
if len(packages) == 0 {
|
||||
return fmt.Errorf("no packages specified")
|
||||
}
|
||||
|
||||
formattedPkgs := make([]string, len(packages))
|
||||
for i, pkg := range packages {
|
||||
pv := ParsePackageVersion(pkg)
|
||||
formattedPkgs[i] = m.formatPackageWithVersion(pv)
|
||||
}
|
||||
|
||||
return m.execCommand(m.installCmd, formattedPkgs...)
|
||||
}
|
||||
|
||||
// InitializeUniversalManagers checks which universal package managers are available
|
||||
func InitializeUniversalManagers() {
|
||||
for _, mgr := range universalManagers {
|
||||
_, err := exec.LookPath(mgr.command)
|
||||
mgr.isAvailable = err == nil
|
||||
}
|
||||
}
|
||||
|
||||
// GetUniversalManager returns a universal package manager if available
|
||||
func GetUniversalManager(name string) (*UniversalManager, bool) {
|
||||
if mgr, exists := universalManagers[name]; exists && mgr.isAvailable {
|
||||
return mgr, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
type OutputMode int
|
||||
|
||||
const (
|
||||
OutputQuiet OutputMode = iota
|
||||
OutputVerbose
|
||||
)
|
||||
|
||||
var outputMode = OutputQuiet
|
||||
|
||||
// SetOutputMode sets the output verbosity
|
||||
func SetOutputMode(mode OutputMode) {
|
||||
outputMode = mode
|
||||
}
|
||||
|
||||
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...)
|
||||
} else {
|
||||
cmd = exec.Command(m.command, cmdArgs...)
|
||||
}
|
||||
|
||||
// Always capture output
|
||||
if outputMode == OutputVerbose {
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
} else {
|
||||
// Capture but don't display output in quiet mode
|
||||
cmd.Stdout = nil
|
||||
cmd.Stderr = nil
|
||||
}
|
||||
cmd.Stdin = os.Stdin
|
||||
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// Remove packages
|
||||
func (m *Manager) Remove(packages ...string) error {
|
||||
if len(packages) == 0 {
|
||||
return fmt.Errorf("no packages specified")
|
||||
}
|
||||
return m.execCommand(m.removeCmd, packages...)
|
||||
}
|
||||
|
||||
// Search for packages
|
||||
func (m *Manager) Search(term string) error {
|
||||
if term == "" {
|
||||
return fmt.Errorf("no search term specified")
|
||||
}
|
||||
return m.execCommand(m.searchCmd, term)
|
||||
}
|
||||
|
||||
// Update package lists
|
||||
func (m *Manager) Update() error {
|
||||
return m.execCommand(m.updateCmd)
|
||||
}
|
||||
|
||||
// Upgrade installed packages
|
||||
func (m *Manager) Upgrade() error {
|
||||
return m.execCommand(m.upgradeCmd)
|
||||
}
|
||||
|
||||
// ListInstalled lists all installed packages
|
||||
func (m *Manager) ListInstalled() error {
|
||||
return m.execCommand(m.listCmd)
|
||||
}
|
||||
|
||||
// ShowInfo shows detailed information about a package
|
||||
func (m *Manager) ShowInfo(pkg string) error {
|
||||
return m.execCommand(m.infoCmd, pkg)
|
||||
}
|
||||
|
||||
// AddRepository adds a new repository
|
||||
func (m *Manager) AddRepository(repo string) error {
|
||||
return m.execCommand(m.addRepoCmd, repo)
|
||||
}
|
||||
|
||||
// RemoveRepository removes a repository
|
||||
func (m *Manager) RemoveRepository(repo string) error {
|
||||
return m.execCommand(m.removeRepoCmd, repo)
|
||||
}
|
||||
|
||||
// ListRepositories lists all configured repositories
|
||||
func (m *Manager) ListRepositories() error {
|
||||
return m.execCommand(m.listRepoCmd)
|
||||
}
|
||||
|
||||
// CleanCache cleans the package manager cache
|
||||
func (m *Manager) CleanCache() error {
|
||||
return m.execCommand(m.cleanCacheCmd)
|
||||
}
|
||||
|
||||
// AutoRemove removes unused dependencies
|
||||
func (m *Manager) AutoRemove() error {
|
||||
return m.execCommand(m.autoRemoveCmd)
|
||||
}
|
||||
|
||||
// CheckDependencies checks for dependency issues
|
||||
func (m *Manager) CheckDependencies(pkg string) error {
|
||||
if pkg == "" {
|
||||
return m.execCommand(m.checkDependsCmd)
|
||||
}
|
||||
return m.execCommand(m.checkDependsCmd, pkg)
|
||||
}
|
||||
|
||||
// ShowDependencies shows dependencies for a package
|
||||
func (m *Manager) ShowDependencies(pkg string) error {
|
||||
return m.execCommand(m.showDependsCmd, pkg)
|
||||
}
|
||||
|
||||
// DetectPackageManager identifies the system's package manager
|
||||
func DetectPackageManager() (*Manager, error) {
|
||||
for _, mgr := range managers {
|
||||
if _, err := exec.LookPath(mgr.command); err == nil {
|
||||
return &mgr, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("no supported package manager found")
|
||||
}
|
Loading…
Reference in New Issue
Block a user