Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
f0a4eeb
Fix YUM package manager implementation issues
bluet May 30, 2025
787cb5d
Complete comprehensive multi-OS testing and documentation
bluet May 30, 2025
c07a743
Add visual diagrams to explain testing architecture and workflows
bluet May 30, 2025
9c8e73e
Fix CI/CD workflow failures by disabling unimplemented package managers
bluet May 30, 2025
77eaefe
Fix critical YUM parsing issue for packages with multiple dots
bluet May 30, 2025
c8c0327
Address additional review comments from bots
bluet May 30, 2025
35cb01f
Fix package manager detection logic for Rocky/AlmaLinux/CentOS
bluet May 30, 2025
9813297
Improve fixture path construction robustness
bluet May 30, 2025
d046c1c
Remove unconditional failure suppression from CI tests
bluet May 30, 2025
49bd41c
Remove redundant AlmaLinux CI test to optimize build time
bluet May 30, 2025
9c73da4
Fix YUM integration test import cycle and package references
bluet May 30, 2025
46912d7
Fix find command to work without root privileges
bluet May 30, 2025
ac16e49
Fix APT package search parsing and environment issues
bluet May 30, 2025
4650cbd
Improve root privilege detection and fix APT version parsing
bluet May 30, 2025
5158a13
Fix APT find output format to match original design specification
bluet May 30, 2025
58d764c
Update documentation to reflect current project status
bluet May 30, 2025
5d7dae6
Fix GitHub workflow failures and improve package manager operations
bluet May 31, 2025
6e9d25b
Add test fixtures for Rocky Linux and additional Ubuntu APT formats
bluet May 31, 2025
620e320
Normalize config-files status to available for cross-package manager …
bluet May 31, 2025
aa3449d
Fix Go version download URL in OS detection tests
bluet May 31, 2025
2bf9e62
Fix Alpine Go version compatibility in OS detection tests
bluet May 31, 2025
de78a97
Update documentation to reflect current behaviors and recent improvem…
bluet May 31, 2025
d3c178d
Add completed documentation work to project roadmap
bluet May 31, 2025
12c4f2a
Add YUM fixture analysis and cleanup tasks to project roadmap
bluet May 31, 2025
6ebe9a1
Fix Alpine Dockerfile to use Go 1.23.4
bluet May 31, 2025
68102b4
Fix language and grammar issues in documentation
bluet May 31, 2025
0c64ca3
Improve APT ParseDeletedOutput robustness for line endings and whites…
bluet May 31, 2025
27ae993
Fix misleading getPackageStatus documentation about unknown status ha…
bluet May 31, 2025
7ea13c3
Fix ignored error handling in testenv documentation examples
bluet May 31, 2025
4651591
Optimize regex compilation in ParseDeletedOutput for better performance
bluet May 31, 2025
fe255a1
Final documentation updates reflecting all PR #14 achievements
bluet May 31, 2025
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
30 changes: 19 additions & 11 deletions manager/yum/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import (
"github.com/bluet/syspkg/manager"
)

// packageLineRegex matches package lines in yum search output (name.arch format)
var packageLineRegex = regexp.MustCompile(`^[\w\d-]+\.[\w\d_]+`)

// 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
Expand All @@ -26,15 +29,16 @@ import (
// The function first removes the "Last Metadata..." and the "========="
// lines, and then processes each package entry line to extract relevant
// information.
//
// The opts parameter is reserved for future parsing options and is currently unused.
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_]+`)
// split output by lines
lines := strings.Split(msg, "\n")

for _, line := range lines {
if strings.HasPrefix(line, "=======") {
Expand All @@ -50,14 +54,14 @@ func ParseFindOutput(msg string, opts *manager.Options) []manager.PackageInfo {
if parts[0] == "" {
continue
}
name_arch := strings.Split(parts[0], ".")
if len(name_arch) != 2 {
nameArch := strings.Split(parts[0], ".")
if len(nameArch) < 2 {
continue
}

packageInfo := manager.PackageInfo{
Name: name_arch[0],
Arch: name_arch[1],
Name: nameArch[0],
Arch: nameArch[1],
PackageManager: pm,
}
Comment thread
bluet marked this conversation as resolved.
Outdated

Expand All @@ -71,6 +75,8 @@ func ParseFindOutput(msg string, opts *manager.Options) []manager.PackageInfo {
// 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.
//
// The opts parameter is reserved for future parsing options and is currently unused.
func ParseListInstalledOutput(msg string, opts *manager.Options) []manager.PackageInfo {
var packages []manager.PackageInfo

Expand All @@ -90,12 +96,12 @@ func ParseListInstalledOutput(msg string, opts *manager.Options) []manager.Packa
if len(parts) < 2 || parts[0] == "" {
continue
}
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]
Comment thread
bluet marked this conversation as resolved.
Outdated

packageInfo := manager.PackageInfo{
Name: name,
Expand All @@ -114,6 +120,8 @@ func ParseListInstalledOutput(msg string, opts *manager.Options) []manager.Packa
// 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.
//
// The opts parameter is reserved for future parsing options and is currently unused.
func ParsePackageInfoOutput(msg string, opts *manager.Options) manager.PackageInfo {
var pkg manager.PackageInfo

Expand Down
77 changes: 65 additions & 12 deletions manager/yum/yum.go
Original file line number Diff line number Diff line change
@@ -1,33 +1,41 @@
// 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.
// It provides a 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)
// 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 (
"context"
"errors"
"log"
"os"
"os/exec"
"time"

"github.com/bluet/syspkg/manager"
)

// Timeouts for different YUM operations
const (
readTimeout = 3 * time.Minute // For search, list, info operations
cleanTimeout = 5 * time.Minute // For clean operations
)

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 = ""
ArgsDryRun string = "--setopt=tsflags=test" // Test transaction without executing
ArgsFixBroken string = "check" // Check for broken dependencies
ArgsPurge string = "" // YUM doesn't distinguish remove vs purge
ArgsAutoRemove string = "autoremove" // Remove unneeded dependencies
ArgsShowProgress string = "-v" // Verbose output shows progress
)

// PackageManager implements the manager.PackageManager interface for the yum package manager.
Expand All @@ -53,8 +61,14 @@ func (a *PackageManager) Delete(pkgs []string, opts *manager.Options) ([]manager
}

// Refresh updates the package list using the yum package manager.
// Uses 'yum clean expire-cache' which efficiently refreshes metadata without
// aggressive cache clearing. This preserves valid cache files while ensuring
// up-to-date repository information.
func (a *PackageManager) Refresh(opts *manager.Options) error {
cmd := exec.Command(pm, "clean", "expire-cache")
ctx, cancel := context.WithTimeout(context.Background(), cleanTimeout)
defer cancel()

cmd := exec.CommandContext(ctx, pm, "clean", "expire-cache")

if opts == nil {
opts = &manager.Options{
Expand Down Expand Up @@ -83,8 +97,11 @@ func (a *PackageManager) Refresh(opts *manager.Options) error {

// 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) {
ctx, cancel := context.WithTimeout(context.Background(), readTimeout)
defer cancel()

args := append([]string{"search"}, keywords...)
cmd := exec.Command(pm, args...)
cmd := exec.CommandContext(ctx, pm, args...)

out, err := cmd.Output()
if err != nil {
Expand All @@ -96,8 +113,11 @@ func (a *PackageManager) Find(keywords []string, opts *manager.Options) ([]manag

// ListInstalled lists all installed packages using the yum package manager.
func (a *PackageManager) ListInstalled(opts *manager.Options) ([]manager.PackageInfo, error) {
ctx, cancel := context.WithTimeout(context.Background(), readTimeout)
defer cancel()

args := []string{"list", "--installed"}
cmd := exec.Command(pm, args...)
cmd := exec.CommandContext(ctx, pm, args...)
out, err := cmd.Output()
if err != nil {
return nil, err
Expand All @@ -114,13 +134,46 @@ func (a *PackageManager) Upgrade(pkgs []string, opts *manager.Options) ([]manage
func (a *PackageManager) UpgradeAll(opts *manager.Options) ([]manager.PackageInfo, error) {
return nil, errors.New("not implemented")
}

// Clean performs comprehensive cleanup of YUM caches.
// Uses 'yum clean all' which removes all cached packages, metadata, and headers.
// This is what administrators typically expect from a clean operation.
func (a *PackageManager) Clean(opts *manager.Options) error {
return a.Refresh(nil)
ctx, cancel := context.WithTimeout(context.Background(), cleanTimeout)
defer cancel()

cmd := exec.CommandContext(ctx, pm, "clean", "all")

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
return cmd.Run()
}

out, err := cmd.Output()
if err != nil {
return err
}
if opts.Verbose {
log.Println(string(out))
}
return 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)
ctx, cancel := context.WithTimeout(context.Background(), readTimeout)
defer cancel()

cmd := exec.CommandContext(ctx, pm, "info", pkg)
out, err := cmd.Output()
if err != nil {
return manager.PackageInfo{}, err
Expand Down
2 changes: 1 addition & 1 deletion manager/yum/yum_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func TestYumPackageManagerNotAvailable(t *testing.T) {
}
_, errlu := yumManager.ListUpgradable(&opts)
if errlu == nil {
t.Fatal("YumPackageManager should not support list-upgadeable")
t.Fatal("YumPackageManager should not support list-upgradable")
}
_, erru := yumManager.Upgrade(packages, nil)
if erru == nil {
Expand Down