Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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! | 🚀 | 🚀 | 🚀 | 🚀 | 🚀 | 🚀 | 🚀 |
Expand Down
8 changes: 4 additions & 4 deletions cmd/syspkg/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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,
},
},
}
Expand Down
2 changes: 1 addition & 1 deletion interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
149 changes: 149 additions & 0 deletions manager/yum/utils.go
Original file line number Diff line number Diff line change
@@ -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,
Comment thread
aijanai marked this conversation as resolved.
}

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]
Comment on lines +93 to +98
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Fix variable naming convention.

Variable name_arch should use camelCase per Go conventions:

-			name_arch := strings.Split(parts[0], ".")
-			if len(name_arch) != 2 {
+			nameArch := strings.Split(parts[0], ".")
+			if len(nameArch) != 2 {
 				continue
 			}
-			name := name_arch[0]
-			arch := name_arch[1]
+			name := nameArch[0]
+			arch := nameArch[1]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
name_arch := strings.Split(parts[0], ".")
if len(name_arch) != 2 {
continue
}
name := name_arch[0]
arch := name_arch[1]
nameArch := strings.Split(parts[0], ".")
if len(nameArch) != 2 {
continue
}
name := nameArch[0]
arch := nameArch[1]
🤖 Prompt for AI Agents
In manager/yum/utils.go between lines 93 and 98, the variable name `name_arch`
does not follow Go naming conventions. Rename `name_arch` to `nameArch` to
adhere to camelCase style. Update all references to this variable accordingly.


packageInfo := manager.PackageInfo{
Name: name,
Version: parts[1],
Status: manager.PackageStatusInstalled,
Comment thread
aijanai marked this conversation as resolved.
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
}
133 changes: 133 additions & 0 deletions manager/yum/yum.go
Original file line number Diff line number Diff line change
@@ -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)
Comment thread
aijanai marked this conversation as resolved.
}
Comment thread
aijanai marked this conversation as resolved.

// 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")
}
Loading