diff --git a/README.md b/README.md index b2943af..6e16453 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,7 @@ For more examples and real use cases, see the [cmd/syspkg/](cmd/syspkg/) directo | Package Manager | Install | Remove | Search | Upgrade | List Installed | List Upgradable | Get Package Info | | --------------- | ------- | ------ | ------ | ------- | -------------- | --------------- | ---------------- | | APT | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| YUM | ❓ | ❓ | ✅ | ❓ | ✅ | ❓ | ✅ | | SNAP | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | Flatpak | ❓ | ❓ | ✅ | ✅ | ✅ | ✅ | ✅ | | Your favorite package manager here! | 🚀 | 🚀 | 🚀 | 🚀 | 🚀 | 🚀 | 🚀 | diff --git a/cmd/syspkg/main.go b/cmd/syspkg/main.go index 29e768e..2fda25d 100644 --- a/cmd/syspkg/main.go +++ b/cmd/syspkg/main.go @@ -293,12 +293,12 @@ func main() { &cli.BoolFlag{ Name: "apt", Usage: "Use apt package manager", - // Hidden: true, + Hidden: false, }, &cli.BoolFlag{ Name: "yum", Usage: "Use yum package manager", - Hidden: true, + Hidden: false, }, &cli.BoolFlag{ Name: "dnf", @@ -323,12 +323,12 @@ func main() { &cli.BoolFlag{ Name: "flatpak", Usage: "Use flatpak package manager", - // Hidden: true, + Hidden: false, }, &cli.BoolFlag{ Name: "snap", Usage: "Use snap package manager", - Hidden: true, + Hidden: false, }, }, } diff --git a/interface.go b/interface.go index 29359be..407e78f 100644 --- a/interface.go +++ b/interface.go @@ -53,7 +53,7 @@ type SysPkg interface { // If the name is empty, the first available package manager will be returned. // If no suitable package manager is found, an error is returned. // Note: only package managers that are specified in the IncludeOptions when creating the SysPkg instance (with New() method) will be returned. If you want to use package managers that are not specified in the IncludeOptions, you should use the FindPackageManagers() method to get a list of all available package managers, or use RefreshPackageManagers() with the IncludeOptions parameter to refresh the package manager list. - GetPackageManager(name string) PackageManager + GetPackageManager(name string) (PackageManager, error) // Install(pkgs []string, opts *manager.Options) ([]manager.PackageInfo, error) // Delete(pkgs []string, opts *manager.Options) ([]manager.PackageInfo, error) diff --git a/manager/yum/utils.go b/manager/yum/utils.go new file mode 100644 index 0000000..b202fd8 --- /dev/null +++ b/manager/yum/utils.go @@ -0,0 +1,149 @@ +// Package yum provides a package manager implementation for RedHat-based systems using +// YUM as the underlying package management tool. +package yum + +import ( + "regexp" + "strings" + + "github.com/bluet/syspkg/manager" +) + +// ParseFindOutput parses the output of `yum search packageName` command +// and returns a list of available packages that match the search query. It extracts package +// information such as name, architecture from the +// output, and stores them in a list of manager.PackageInfo objects. +// +// The output format is expected to be similar to the following example: +// +//Last metadata expiration check: 0:26:09 ago on Thu 22 May 2025 04:30:18 PM UTC. +// ==================================================Name Exactly Matched: nginx ==================================================== +//nginx.x86_64 : A high performance web server and reverse proxy server +//====================================================Name & Summary Matched: nginx================================================== +//nginx-all-modules.noarch : A meta package that installs all available Nginx modules +//nginx-core.x86_64 : nginx minimal core + +// The function first removes the "Last Metadata..." and the "=========" +// lines, and then processes each package entry line to extract relevant +// information. +func ParseFindOutput(msg string, opts *manager.Options) []manager.PackageInfo { + var packages []manager.PackageInfo + + // remove the last empty line + msg = strings.TrimSuffix(msg, "\n") + + // split output by empty lines + var lines []string = strings.Split(msg, "\n") + var packageLineRegex = regexp.MustCompile(`^[\w\d-]+\.[\w\d_]+`) + + for _, line := range lines { + if strings.HasPrefix(line, "=======") { + continue + } + if strings.HasPrefix(line, "Last metadata") { + continue + } + if packageLineRegex.MatchString(line) { + parts := strings.Fields(line) + + // if name is empty, it might be not what we want + if parts[0] == "" { + continue + } + name_arch := strings.Split(parts[0], ".") + if len(name_arch) != 2 { + continue + } + + packageInfo := manager.PackageInfo{ + Name: name_arch[0], + Arch: name_arch[1], + PackageManager: pm, + } + + packages = append(packages, packageInfo) + } + } + + return packages +} + +// ParseListInstalledOutput parses the output of `yum list --installed` command +// and returns a list of installed packages. It extracts the package name, version, +// and architecture from the output and stores them in a list of manager.PackageInfo objects. +func ParseListInstalledOutput(msg string, opts *manager.Options) []manager.PackageInfo { + var packages []manager.PackageInfo + + // remove the last empty line + msg = strings.TrimSuffix(msg, "\n") + lines := strings.Split(string(msg), "\n") + + for _, line := range lines { + if strings.HasPrefix(line, "Installed Packages") { + continue + } + + if len(line) > 0 { + parts := strings.Fields(line) + + // if it doesn't split correctly, or the name is empty, it might be not what we want + if len(parts) < 2 || parts[0] == "" { + continue + } + name_arch := strings.Split(parts[0], ".") + if len(name_arch) != 2 { + continue + } + name := name_arch[0] + arch := name_arch[1] + + packageInfo := manager.PackageInfo{ + Name: name, + Version: parts[1], + Status: manager.PackageStatusInstalled, + Arch: arch, + PackageManager: "yum", + } + packages = append(packages, packageInfo) + } + } + + return packages +} + +// ParsePackageInfoOutput parses the output of `yum info packageName` command +// and returns a manager.PackageInfo object containing package information such as name, version, +// architecture, and category. This function is useful for getting detailed package information. +func ParsePackageInfoOutput(msg string, opts *manager.Options) manager.PackageInfo { + var pkg manager.PackageInfo + + // remove the last empty line + msg = strings.TrimSuffix(msg, "\n") + lines := strings.Split(string(msg), "\n") + + for _, line := range lines { + if len(line) > 0 { + parts := strings.SplitN(line, ":", 2) + + if len(parts) != 2 { + continue + } + + key := strings.TrimSpace(parts[0]) + value := strings.TrimSpace(parts[1]) + + switch key { + case "Name": + pkg.Name = value + case "Version": + pkg.Version = value + case "Architecture": + pkg.Arch = value + } + } + } + + pkg.PackageManager = "yum" + + return pkg +} diff --git a/manager/yum/yum.go b/manager/yum/yum.go new file mode 100644 index 0000000..48c0bf0 --- /dev/null +++ b/manager/yum/yum.go @@ -0,0 +1,133 @@ +// Package yum provides an implementation of the syspkg manager interface for the yum package manager. +// It provides an Go (golang) API interface for interacting with the YUM package manager. +// This package is a wrapper around the yum command line tool. +// +// YUM was the default package manager on RedHat-based systems such as Centos, it has been recently superseded by DNF (Dandified YUM) +// +// This package is part of the syspkg library. +package yum + +import ( + "errors" + "log" + "os" + "os/exec" + + "github.com/bluet/syspkg/manager" +) + +var pm string = "yum" + +// Constants used for yum commands +const ( + ArgsAssumeYes string = "-y" + ArgsAssumeNo string = "--assumeno" + ArgsQuiet string = "-q" + ArgsDryRun string = "" + ArgsFixBroken string = "" + ArgsPurge string = "" + ArgsAutoRemove string = "" + ArgsShowProgress string = "" +) + +// PackageManager implements the manager.PackageManager interface for the yum package manager. +type PackageManager struct{} + +// IsAvailable checks if the yum package manager is available on the system. +func (a *PackageManager) IsAvailable() bool { + _, err := exec.LookPath(pm) + return err == nil +} + +// GetPackageManager returns the name of the yum package manager. +func (a *PackageManager) GetPackageManager() string { + return pm +} + +func (a *PackageManager) Install(pkgs []string, opts *manager.Options) ([]manager.PackageInfo, error) { + return nil, errors.New("not implemented") +} + +func (a *PackageManager) Delete(pkgs []string, opts *manager.Options) ([]manager.PackageInfo, error) { + return nil, errors.New("not implemented") +} + +// Refresh updates the package list using the yum package manager. +func (a *PackageManager) Refresh(opts *manager.Options) error { + cmd := exec.Command(pm, "clean", "expire-cache") + + if opts == nil { + opts = &manager.Options{ + DryRun: false, + Interactive: false, + Verbose: false, + } + } + if opts.Interactive { + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Stdin = os.Stdin + err := cmd.Run() + return err + } else { + out, err := cmd.Output() + if err != nil { + return err + } + if opts.Verbose { + log.Println(string(out)) + } + return nil + } +} + +// Find searches for packages matching the provided keywords using the yum package manager. +func (a *PackageManager) Find(keywords []string, opts *manager.Options) ([]manager.PackageInfo, error) { + args := append([]string{"search"}, keywords...) + cmd := exec.Command(pm, args...) + + out, err := cmd.Output() + if err != nil { + return nil, err + } + + return ParseFindOutput(string(out), opts), nil +} + +// ListInstalled lists all installed packages using the yum package manager. +func (a *PackageManager) ListInstalled(opts *manager.Options) ([]manager.PackageInfo, error) { + args := []string{"list", "--installed"} + cmd := exec.Command(pm, args...) + out, err := cmd.Output() + if err != nil { + return nil, err + } + return ParseListInstalledOutput(string(out), opts), nil +} + +func (a *PackageManager) ListUpgradable(opts *manager.Options) ([]manager.PackageInfo, error) { + return nil, errors.New("not implemented") +} +func (a *PackageManager) Upgrade(pkgs []string, opts *manager.Options) ([]manager.PackageInfo, error) { + return nil, errors.New("not implemented") +} +func (a *PackageManager) UpgradeAll(opts *manager.Options) ([]manager.PackageInfo, error) { + return nil, errors.New("not implemented") +} +func (a *PackageManager) Clean(opts *manager.Options) error { + return a.Refresh(nil) +} + +// GetPackageInfo retrieves package information for the specified package using the yum package manager. +func (a *PackageManager) GetPackageInfo(pkg string, opts *manager.Options) (manager.PackageInfo, error) { + cmd := exec.Command(pm, "info", pkg) + out, err := cmd.Output() + if err != nil { + return manager.PackageInfo{}, err + } + return ParsePackageInfoOutput(string(out), opts), nil +} + +func (a *PackageManager) AutoRemove(opts *manager.Options) ([]manager.PackageInfo, error) { + return nil, errors.New("not implemented") +} diff --git a/manager/yum/yum_test.go b/manager/yum/yum_test.go new file mode 100644 index 0000000..ec6a217 --- /dev/null +++ b/manager/yum/yum_test.go @@ -0,0 +1,167 @@ +// yum/yum_test.go +package yum_test + +import ( + "testing" + + "github.com/bluet/syspkg/manager" + "github.com/bluet/syspkg/manager/yum" +) + +func TestYumPackageManagerNotAvailable(t *testing.T) { + yumManager := yum.PackageManager{} + opts := manager.Options{} + packages := []string{"nginx"} + + _, erri := yumManager.Install(packages, nil) + if erri == nil { + t.Fatal("YumPackageManager should not support installation") + } + _, errd := yumManager.Delete(packages, nil) + if errd == nil { + t.Fatal("YumPackageManager should not support removal") + } + _, errlu := yumManager.ListUpgradable(&opts) + if errlu == nil { + t.Fatal("YumPackageManager should not support list-upgadeable") + } + _, erru := yumManager.Upgrade(packages, nil) + if erru == nil { + t.Fatal("YumPackageManager should not support upgrade") + } + _, errua := yumManager.UpgradeAll(&opts) + if errua == nil { + t.Fatal("YumPackageManager should not support upgrade-all") + } + _, errar := yumManager.AutoRemove(&opts) + if errar == nil { + t.Fatal("YumPackageManager should not support autoremove") + } +} + +/* +// these e2e tests work only under a RHEL derived Linux distro that yum installed + +func TestYumPackageManagerIsAvailable(t *testing.T) { + yumManager := yum.PackageManager{} + if !yumManager.IsAvailable() { + t.Fatal("YumPackageManager is not available") + } +} + +func TestYumPackageManagerListPackages(t *testing.T) { + yumManager := yum.PackageManager{} + opts:=manager.Options{} + result,err:=yumManager.ListInstalled(&opts) + if err!=nil{ + t.Errorf("Should have been able to list correctly, %s",err) + } + if len(result)==0{ + t.Fatal("Zero packages detected, there should have been at least one") + } +} + +func TestYumPackageManagerGetPackageInfo(t *testing.T) { + yumManager := yum.PackageManager{} + opts:=manager.Options{} + packages:="rpm" + + result,err:=yumManager.GetPackageInfo(packages, &opts) + if err!=nil{ + t.Errorf("Should have been able to get info correctly, %s",err) + } + if result.Name != packages { + + t.Errorf("rpm should be present, found %+v", result) + } +} + +func TestYumPackageManagerFind(t *testing.T) { + yumManager := yum.PackageManager{} + opts:=manager.Options{} + packages:=[]string{"nginx"} + + result,err:=yumManager.Find(packages, &opts) + if err!=nil{ + t.Errorf("Should have been able to search correctly, %s",err) + } + if len(result)==0 { + t.Errorf("nginx should be present, found %+v", result) + } + if result[0].Name != packages[0] { + t.Errorf("nginx should be available, found %+v", result) + } +} +*/ + +func TestParseFindOutput(t *testing.T) { + msg := ` +Last metadata expiration check: 4:56:00 ago on Thu 22 May 2025 04:30:18 PM UTC. +============================================================================= Name Exactly Matched: nginx ============================================================================== +nginx.x86_64 : A high performance web server and reverse proxy server +============================================================================ Name & Summary Matched: nginx ============================================================================= +nginx-all-modules.noarch : A meta package that installs all available Nginx modules +nginx-core.x86_64 : nginx minimal core +nginx-filesystem.noarch : The basic directory layout for the Nginx server +nginx-mod-http-image-filter.x86_64 : Nginx HTTP image filter module +nginx-mod-http-perl.x86_64 : Nginx HTTP perl module +nginx-mod-http-xslt-filter.x86_64 : Nginx XSLT module +nginx-mod-mail.x86_64 : Nginx mail modules +nginx-mod-stream.x86_64 : Nginx stream modules +pcp-pmda-nginx.x86_64 : Performance Co-Pilot (PCP) metrics for the Nginx Webserver +` + packages := yum.ParseFindOutput(msg, nil) + if packages[0].Name != "nginx" || packages[0].Arch != "x86_64" { + t.Errorf("Expected to find nginx, found %+v", packages[0]) + } +} + +func TestParseListInstalledOutput(t *testing.T) { + msg := ` + Installed Packages +NetworkManager.x86_64 1:1.48.10-2.el9_5 @baseos +rocky-release.noarch 9.5-1.2.el9 @baseos +rpm.x86_64 4.16.1.3-34.el9.0.1 @baseos +rsync.x86_64 3.2.3-20.el9 @baseos + +` + packages := yum.ParseListInstalledOutput(msg, nil) + found := false + for _, pack := range packages { + if pack.Name == "rpm" || pack.Arch == "x86_64" || pack.Version == "4.16.1.3-34.el9.0.1" { + found = true + break + } + } + if !found { + t.Errorf("Expected to find rpm, but not found. Found instead %+v", packages) + } +} + +func TestParsePackageInfoOutput(t *testing.T) { + msg := ` + Last metadata expiration check: 5:16:10 ago on Thu 22 May 2025 04:30:18 PM UTC. +Available Packages +Name : nginx +Epoch : 2 +Version : 1.20.1 +Release : 20.el9.0.1 +Architecture : x86_64 +Size : 36 k +Source : nginx-1.20.1-20.el9.0.1.src.rpm +Repository : appstream +Summary : A high performance web server and reverse proxy server +URL : https://nginx.org +License : BSD +Description : Nginx is a web server and a reverse proxy server for HTTP, SMTP, POP3 and + : IMAP protocols, with a strong focus on high concurrency, performance and low + : memory usage. + + + +` + packages := yum.ParsePackageInfoOutput(msg, nil) + if packages.Name != "nginx" || packages.Arch != "x86_64" || packages.Version != "1.20.1" { + t.Errorf("Expected to find nginx, found %+v", packages) + } +} diff --git a/osinfo/osinfo.go b/osinfo/osinfo.go index 1c11d93..ec6acd3 100644 --- a/osinfo/osinfo.go +++ b/osinfo/osinfo.go @@ -89,8 +89,10 @@ func getLinuxDistribution() (string, string, error) { line := scanner.Text() if strings.HasPrefix(line, "ID=") { dist = strings.TrimPrefix(line, "ID=") + dist = strings.Trim(dist, "\"") } else if strings.HasPrefix(line, "VERSION_ID=") { - distVersion = strings.Trim(strings.TrimPrefix(line, "VERSION_ID="), "\"") + distVersion = strings.TrimPrefix(line, "VERSION_ID=") + distVersion = strings.Trim(distVersion, "\"") } } diff --git a/syspkg.go b/syspkg.go index bd47df4..cdde621 100644 --- a/syspkg.go +++ b/syspkg.go @@ -14,17 +14,19 @@ // if err != nil { // log.Fatal(err) // } -// aptManager := sysPkg.GetPackageManager("apt") +// aptManager,err := sysPkg.GetPackageManager("apt") package syspkg import ( "errors" "log" + "sort" "github.com/bluet/syspkg/manager" "github.com/bluet/syspkg/manager/apt" "github.com/bluet/syspkg/manager/flatpak" "github.com/bluet/syspkg/manager/snap" + "github.com/bluet/syspkg/manager/yum" // "github.com/bluet/syspkg/zypper" // "github.com/bluet/syspkg/dnf" // "github.com/bluet/syspkg/apk" @@ -41,6 +43,7 @@ type IncludeOptions struct { Dnf bool Flatpak bool Snap bool + Yum bool Zypper bool } @@ -75,6 +78,7 @@ func (s *sysPkgImpl) FindPackageManagers(include IncludeOptions) (map[string]Pac {"apt", &apt.PackageManager{}, include.Apt}, {"flatpak", &flatpak.PackageManager{}, include.Flatpak}, {"snap", &snap.PackageManager{}, include.Snap}, + {"yum", &yum.PackageManager{}, include.Yum}, // {"apk", &apk.PackageManager{}, include.Apk}, // {"dnf", &dnf.PackageManager{}, include.Dnf}, // {"zypper", &zypper.PackageManager{}, include.Zypper}, @@ -97,8 +101,30 @@ func (s *sysPkgImpl) FindPackageManagers(include IncludeOptions) (map[string]Pac } // GetPackageManager returns a PackageManager instance by its name (e.g., "apt", "snap", "flatpak", etc.). -func (s *sysPkgImpl) GetPackageManager(name string) PackageManager { - return s.pms[name] +// if name is empty, return the first available +func (s *sysPkgImpl) GetPackageManager(name string) (PackageManager,error) { + var pm PackageManager + + // if there are no package managers, return before accessing non existing properties + if len(s.pms) == 0 { + return nil, errors.New("no supported package manager detected") + } + + if name == "" { + // get first pm available, lexicographically sorted + keys := make([]string, 0, len(s.pms)) + for k := range s.pms { + keys = append(keys, k) + } + sort.Strings(keys) + pm = s.pms[keys[0]] + } else { + pm,found := s.pms[name] + if !found { + return pm, errors.New("no such package manager") + } + } + return pm, nil } // RefreshPackageManagers refreshes the internal list of available package managers, and returns the new list. diff --git a/syspkg_test.go b/syspkg_test.go index 4aac5d1..03adea1 100644 --- a/syspkg_test.go +++ b/syspkg_test.go @@ -30,7 +30,7 @@ func TestNewPackageManager(t *testing.T) { AllAvailable: true, }) if err != nil { - t.Fatalf("GetPackageManagers() error: %+v", err) + t.Fatalf("FindPackageManagers() error: %+v", err) } log.Printf("pms: %+v", pms) @@ -53,73 +53,96 @@ func TestNewPackageManager(t *testing.T) { // if we are on any other distro, we should have nothing if OSInfo.Distribution == "ubuntu" || OSInfo.Distribution == "debian" || OSInfo.Distribution == "mint" || OSInfo.Distribution == "PopOS" || OSInfo.Distribution == "elementary" || OSInfo.Distribution == "Zorin" || OSInfo.Distribution == "ChromeOS" { - if _, ok := pms["apt"]; !ok && s.GetPackageManager("apt") == nil { - if _, ok := pms["snap"]; !ok && s.GetPackageManager("snap") == nil { - if _, ok := pms["flatpak"]; !ok && s.GetPackageManager("flatpak") == nil { + pm,err:=s.GetPackageManager("apt") + + if err != nil && pm == nil { + pm,err:=s.GetPackageManager("snap") + + if err!=nil && pm == nil { + pm,err:=s.GetPackageManager("flatpak") + + if err!=nil && pm == nil { t.Fatalf("apt, snap, or flatpak package manager not found") } } } - } else if OSInfo.Distribution == "fedora" || OSInfo.Distribution == "centos" || OSInfo.Distribution == "rhel" || OSInfo.Distribution == "rockylinux" || OSInfo.Distribution == "almalinux" || OSInfo.Distribution == "amazon linux" || OSInfo.Distribution == "oracle linux" || OSInfo.Distribution == "scientific linux" || OSInfo.Distribution == "cloudlinux" { - if _, ok := pms["dnf"]; !ok && s.GetPackageManager("dnf") == nil { - if _, ok := pms["yum"]; !ok && s.GetPackageManager("yum") == nil { + } else if OSInfo.Distribution == "fedora" || OSInfo.Distribution == "centos" || OSInfo.Distribution == "rhel" || OSInfo.Distribution == "rockylinux" || OSInfo.Distribution == "rocky" || OSInfo.Distribution == "almalinux" || OSInfo.Distribution == "amazon linux" || OSInfo.Distribution == "oracle linux" || OSInfo.Distribution == "scientific linux" || OSInfo.Distribution == "cloudlinux" { + pm,err:=s.GetPackageManager("dnf") + if err != nil && pm == nil { + pm,err:=s.GetPackageManager("yum") + if err != nil && pm == nil { t.Fatalf("dnf or yum package manager not found") } } } else if OSInfo.Distribution == "opensuse" { - if _, ok := pms["zypper"]; !ok && s.GetPackageManager("zypper") == nil { + pm,err:=s.GetPackageManager("zypper") + if err != nil && pm == nil { t.Fatalf("zypper package manager not found") } } else if OSInfo.Distribution == "alpine" { - if _, ok := pms["apk"]; !ok && s.GetPackageManager("apk") == nil { + pm,err:=s.GetPackageManager("apk") + if err != nil && pm == nil { t.Fatalf("apk package manager not found") } } else if OSInfo.Distribution == "arch" { - if _, ok := pms["pacman"]; !ok && s.GetPackageManager("pacman") == nil { + pm,err:=s.GetPackageManager("pacman") + if err != nil && pm == nil { t.Fatalf("pacman package manager not found") } } else if OSInfo.Distribution == "gentoo" { - if _, ok := pms["emerge"]; !ok && s.GetPackageManager("emerge") == nil { + pm,err:=s.GetPackageManager("emerge") + if err != nil && pm == nil { t.Fatalf("emerge package manager not found") } } else if OSInfo.Distribution == "slackware" { - if _, ok := pms["slackpkg"]; !ok && s.GetPackageManager("slackpkg") == nil { + pm,err:=s.GetPackageManager("slackpkg") + if err != nil && pm == nil { t.Fatalf("slackpkg package manager not found") } } else if OSInfo.Distribution == "void" { - if _, ok := pms["xbps"]; !ok && s.GetPackageManager("xbps") == nil { + pm,err:=s.GetPackageManager("xbps") + if err != nil && pm == nil { t.Fatalf("xbps package manager not found") } } else if OSInfo.Distribution == "solus" { - if _, ok := pms["eopkg"]; !ok && s.GetPackageManager("eopkg") == nil { + pm,err:=s.GetPackageManager("eopkg") + if err != nil && pm == nil { t.Fatalf("eopkg package manager not found") } } else if OSInfo.Distribution == "freebsd" || OSInfo.Distribution == "dragonfly" || OSInfo.Distribution == "termux" { - if _, ok := pms["pkg"]; !ok && s.GetPackageManager("pkg") == nil { + pm,err:=s.GetPackageManager("pkg") + if err != nil && pm == nil { t.Fatalf("pkg package manager not found") } } else if OSInfo.Distribution == "openbsd" || OSInfo.Distribution == "netbsd" { - if _, ok := pms["pkg_add"]; !ok && s.GetPackageManager("pkg_add") == nil { + pm,err:=s.GetPackageManager("pkg_add") + if err != nil && pm == nil { t.Fatalf("pkg_add package manager not found") } } else if OSInfo.Distribution == "macos" { - if _, ok := pms["brew"]; !ok && s.GetPackageManager("brew") == nil { + pm,err:=s.GetPackageManager("brew") + if err != nil && pm == nil { t.Fatalf("brew package manager not found") } } else if OSInfo.Distribution == "windows" { - if _, ok := pms["chocolatey"]; !ok && s.GetPackageManager("chocolatey") == nil { - if _, ok := pms["scoop"]; !ok && s.GetPackageManager("scoop") == nil { - if _, ok := pms["winget"]; !ok && s.GetPackageManager("winget") == nil { + pm,err:=s.GetPackageManager("chocolatey") + if err != nil && pm == nil { + pm,err:=s.GetPackageManager("scoop") + if err != nil && pm == nil { + pm,err:=s.GetPackageManager("winget") + if err != nil && pm == nil { t.Fatalf("chocolatey, scoop, or winget package manager not found") } } } } else if OSInfo.Distribution == "android" { - if _, ok := pms["f-droid"]; !ok && s.GetPackageManager("f-droid") == nil { + pm,err:=s.GetPackageManager("f-droid") + if err != nil && pm == nil { t.Fatalf("f-droid package manager not found") } } else if OSInfo.Distribution == "ios" { - if _, ok := pms["cydia"]; !ok && s.GetPackageManager("cydia") == nil { + pm,err:=s.GetPackageManager("cydia") + if err != nil && pm == nil { t.Fatalf("cydia package manager not found") } } else {