forked from Plemya-x/ALR
		
	fix: removeAlreadyInstalled before FindPkgs
This commit is contained in:
		
							
								
								
									
										3
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								go.mod
									
									
									
									
									
								
							@@ -56,6 +56,7 @@ require (
 | 
				
			|||||||
	github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
 | 
						github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
 | 
				
			||||||
	github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
 | 
						github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
 | 
				
			||||||
	github.com/cyphar/filepath-securejoin v0.2.4 // indirect
 | 
						github.com/cyphar/filepath-securejoin v0.2.4 // indirect
 | 
				
			||||||
 | 
						github.com/davecgh/go-spew v1.1.1 // indirect
 | 
				
			||||||
	github.com/dlclark/regexp2 v1.10.0 // indirect
 | 
						github.com/dlclark/regexp2 v1.10.0 // indirect
 | 
				
			||||||
	github.com/dsnet/compress v0.0.1 // indirect
 | 
						github.com/dsnet/compress v0.0.1 // indirect
 | 
				
			||||||
	github.com/dustin/go-humanize v1.0.1 // indirect
 | 
						github.com/dustin/go-humanize v1.0.1 // indirect
 | 
				
			||||||
@@ -92,6 +93,7 @@ require (
 | 
				
			|||||||
	github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect
 | 
						github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect
 | 
				
			||||||
	github.com/pierrec/lz4/v4 v4.1.15 // indirect
 | 
						github.com/pierrec/lz4/v4 v4.1.15 // indirect
 | 
				
			||||||
	github.com/pjbgf/sha1cd v0.3.0 // indirect
 | 
						github.com/pjbgf/sha1cd v0.3.0 // indirect
 | 
				
			||||||
 | 
						github.com/pmezard/go-difflib v1.0.0 // indirect
 | 
				
			||||||
	github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
 | 
						github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
 | 
				
			||||||
	github.com/rivo/uniseg v0.4.4 // indirect
 | 
						github.com/rivo/uniseg v0.4.4 // indirect
 | 
				
			||||||
	github.com/russross/blackfriday/v2 v2.1.0 // indirect
 | 
						github.com/russross/blackfriday/v2 v2.1.0 // indirect
 | 
				
			||||||
@@ -99,6 +101,7 @@ require (
 | 
				
			|||||||
	github.com/shopspring/decimal v1.2.0 // indirect
 | 
						github.com/shopspring/decimal v1.2.0 // indirect
 | 
				
			||||||
	github.com/skeema/knownhosts v1.2.2 // indirect
 | 
						github.com/skeema/knownhosts v1.2.2 // indirect
 | 
				
			||||||
	github.com/spf13/cast v1.6.0 // indirect
 | 
						github.com/spf13/cast v1.6.0 // indirect
 | 
				
			||||||
 | 
						github.com/stretchr/testify v1.10.0 // indirect
 | 
				
			||||||
	github.com/therootcompany/xz v1.0.1 // indirect
 | 
						github.com/therootcompany/xz v1.0.1 // indirect
 | 
				
			||||||
	github.com/ulikunitz/xz v0.5.12 // indirect
 | 
						github.com/ulikunitz/xz v0.5.12 // indirect
 | 
				
			||||||
	github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
 | 
						github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.sum
									
									
									
									
									
								
							@@ -327,6 +327,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
 | 
				
			|||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
 | 
					github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
 | 
				
			||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
 | 
					github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
 | 
				
			||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 | 
					github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 | 
				
			||||||
 | 
					github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
 | 
				
			||||||
 | 
					github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 | 
				
			||||||
github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
 | 
					github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
 | 
				
			||||||
github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
 | 
					github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
 | 
				
			||||||
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
 | 
					github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -65,6 +65,7 @@ import (
 | 
				
			|||||||
// Один содержит пути к собранным пакетам, другой - имена собранных пакетов.
 | 
					// Один содержит пути к собранным пакетам, другой - имена собранных пакетов.
 | 
				
			||||||
func BuildPackage(ctx context.Context, opts types.BuildOpts) ([]string, []string, error) {
 | 
					func BuildPackage(ctx context.Context, opts types.BuildOpts) ([]string, []string, error) {
 | 
				
			||||||
	log := loggerctx.From(ctx)
 | 
						log := loggerctx.From(ctx)
 | 
				
			||||||
 | 
						reposInstance := repos.GetInstance(ctx)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	info, err := distro.ParseOSRelease(ctx)
 | 
						info, err := distro.ParseOSRelease(ctx)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -133,12 +134,12 @@ func BuildPackage(ctx context.Context, opts types.BuildOpts) ([]string, []string
 | 
				
			|||||||
		return nil, nil, err
 | 
							return nil, nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	buildDeps, err := installBuildDeps(ctx, vars, opts, installed) // Устанавливаем зависимости для сборки
 | 
						buildDeps, err := installBuildDeps(ctx, reposInstance, vars, opts) // Устанавливаем зависимости для сборки
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, nil, err
 | 
							return nil, nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = installOptDeps(ctx, vars, opts, installed) // Устанавливаем опциональные зависимости
 | 
						err = installOptDeps(ctx, reposInstance, vars, opts) // Устанавливаем опциональные зависимости
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, nil, err
 | 
							return nil, nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -329,18 +330,25 @@ func performChecks(ctx context.Context, vars *types.BuildVars, interactive bool,
 | 
				
			|||||||
	return true, nil
 | 
						return true, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type PackageFinder interface {
 | 
				
			||||||
 | 
						FindPkgs(ctx context.Context, pkgs []string) (map[string][]db.Package, []string, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Функция installBuildDeps устанавливает все зависимости сборки, которые еще не установлены, и возвращает
 | 
					// Функция installBuildDeps устанавливает все зависимости сборки, которые еще не установлены, и возвращает
 | 
				
			||||||
// срез, содержащий имена всех установленных пакетов.
 | 
					// срез, содержащий имена всех установленных пакетов.
 | 
				
			||||||
func installBuildDeps(ctx context.Context, vars *types.BuildVars, opts types.BuildOpts, installed map[string]string) ([]string, error) {
 | 
					func installBuildDeps(ctx context.Context, repos PackageFinder, vars *types.BuildVars, opts types.BuildOpts) ([]string, error) {
 | 
				
			||||||
	log := loggerctx.From(ctx)
 | 
						log := loggerctx.From(ctx)
 | 
				
			||||||
	var buildDeps []string
 | 
						var buildDeps []string
 | 
				
			||||||
	if len(vars.BuildDepends) > 0 {
 | 
						if len(vars.BuildDepends) > 0 {
 | 
				
			||||||
		found, notFound, err := repos.FindPkgs(ctx, vars.BuildDepends) // Находим пакеты-зависимости
 | 
							deps, err := removeAlreadyInstalled(opts, vars.BuildDepends)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		found = removeAlreadyInstalled(found, installed) // Убираем уже установленные зависимости
 | 
							found, notFound, err := repos.FindPkgs(ctx, deps) // Находим пакеты-зависимости
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		log.Info("Installing build dependencies").Send() // Логгируем установку зависимостей
 | 
							log.Info("Installing build dependencies").Send() // Логгируем установку зависимостей
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -353,9 +361,13 @@ func installBuildDeps(ctx context.Context, vars *types.BuildVars, opts types.Bui
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Функция installOptDeps спрашивает у пользователя, какие, если таковые имеются, опциональные зависимости он хочет установить.
 | 
					// Функция installOptDeps спрашивает у пользователя, какие, если таковые имеются, опциональные зависимости он хочет установить.
 | 
				
			||||||
// Если пользователь решает установить какие-либо опциональные зависимости, выполняется их установка.
 | 
					// Если пользователь решает установить какие-либо опциональные зависимости, выполняется их установка.
 | 
				
			||||||
func installOptDeps(ctx context.Context, vars *types.BuildVars, opts types.BuildOpts, installed map[string]string) error {
 | 
					func installOptDeps(ctx context.Context, repos PackageFinder, vars *types.BuildVars, opts types.BuildOpts) error {
 | 
				
			||||||
	if len(vars.OptDepends) > 0 {
 | 
						optDeps, err := removeAlreadyInstalled(opts, vars.OptDepends)
 | 
				
			||||||
		optDeps, err := cliutils.ChooseOptDepends(ctx, vars.OptDepends, "install", opts.Interactive) // Пользователя просят выбрать опциональные зависимости
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(optDeps) > 0 {
 | 
				
			||||||
 | 
							optDeps, err := cliutils.ChooseOptDepends(ctx, optDeps, "install", opts.Interactive) // Пользователя просят выбрать опциональные зависимости
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -369,7 +381,6 @@ func installOptDeps(ctx context.Context, vars *types.BuildVars, opts types.Build
 | 
				
			|||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		found = removeAlreadyInstalled(found, installed) // Убираем уже установленные зависимости
 | 
					 | 
				
			||||||
		flattened := cliutils.FlattenPkgs(ctx, found, "install", opts.Interactive)
 | 
							flattened := cliutils.FlattenPkgs(ctx, found, "install", opts.Interactive)
 | 
				
			||||||
		InstallPkgs(ctx, flattened, notFound, opts) // Устанавливаем выбранные пакеты
 | 
							InstallPkgs(ctx, flattened, notFound, opts) // Устанавливаем выбранные пакеты
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -836,21 +847,22 @@ func setVersion(ctx context.Context, r *interp.Runner, to string) error {
 | 
				
			|||||||
	return r.Run(ctx, fl)
 | 
						return r.Run(ctx, fl)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Функция removeAlreadyInstalled возвращает карту без каких-либо зависимостей, которые уже установлены.
 | 
					// Returns not installed dependencies
 | 
				
			||||||
func removeAlreadyInstalled(found map[string][]db.Package, installed map[string]string) map[string][]db.Package {
 | 
					func removeAlreadyInstalled(opts types.BuildOpts, dependencies []string) ([]string, error) {
 | 
				
			||||||
	filteredPackages := make(map[string][]db.Package)
 | 
						filteredPackages := []string{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for name, pkgList := range found {
 | 
						for _, dep := range dependencies {
 | 
				
			||||||
		filteredPkgList := []db.Package{}
 | 
							installed, err := opts.Manager.IsInstalled(dep)
 | 
				
			||||||
		for _, pkg := range pkgList {
 | 
							if err != nil {
 | 
				
			||||||
			if _, isInstalled := installed[pkg.Name]; !isInstalled {
 | 
								return nil, err
 | 
				
			||||||
				filteredPkgList = append(filteredPkgList, pkg)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		filteredPackages[name] = filteredPkgList
 | 
							if installed {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							filteredPackages = append(filteredPackages, dep)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return filteredPackages
 | 
						return filteredPackages, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Функция packageNames возвращает имена всех предоставленных пакетов.
 | 
					// Функция packageNames возвращает имена всех предоставленных пакетов.
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										225
									
								
								pkg/build/build_internal_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										225
									
								
								pkg/build/build_internal_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,225 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 Евгений Храмов
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"plemya-x.ru/alr/internal/db"
 | 
				
			||||||
 | 
						"plemya-x.ru/alr/internal/types"
 | 
				
			||||||
 | 
						"plemya-x.ru/alr/pkg/manager"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type TestPackageFinder struct {
 | 
				
			||||||
 | 
						FindPkgsFunc func(ctx context.Context, pkgs []string) (map[string][]db.Package, []string, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (pf *TestPackageFinder) FindPkgs(ctx context.Context, pkgs []string) (map[string][]db.Package, []string, error) {
 | 
				
			||||||
 | 
						if pf.FindPkgsFunc != nil {
 | 
				
			||||||
 | 
							return pf.FindPkgsFunc(ctx, pkgs)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return map[string][]db.Package{}, []string{}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type TestManager struct {
 | 
				
			||||||
 | 
						NameFunc          func() string
 | 
				
			||||||
 | 
						FormatFunc        func() string
 | 
				
			||||||
 | 
						ExistsFunc        func() bool
 | 
				
			||||||
 | 
						SetRootCmdFunc    func(cmd string)
 | 
				
			||||||
 | 
						SyncFunc          func(opts *manager.Opts) error
 | 
				
			||||||
 | 
						InstallFunc       func(opts *manager.Opts, pkgs ...string) error
 | 
				
			||||||
 | 
						RemoveFunc        func(opts *manager.Opts, pkgs ...string) error
 | 
				
			||||||
 | 
						UpgradeFunc       func(opts *manager.Opts, pkgs ...string) error
 | 
				
			||||||
 | 
						InstallLocalFunc  func(opts *manager.Opts, files ...string) error
 | 
				
			||||||
 | 
						UpgradeAllFunc    func(opts *manager.Opts) error
 | 
				
			||||||
 | 
						ListInstalledFunc func(opts *manager.Opts) (map[string]string, error)
 | 
				
			||||||
 | 
						IsInstalledFunc   func(pkg string) (bool, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *TestManager) Name() string {
 | 
				
			||||||
 | 
						if m.NameFunc != nil {
 | 
				
			||||||
 | 
							return m.NameFunc()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "TestManager"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *TestManager) Format() string {
 | 
				
			||||||
 | 
						if m.FormatFunc != nil {
 | 
				
			||||||
 | 
							return m.FormatFunc()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "testpkg"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *TestManager) Exists() bool {
 | 
				
			||||||
 | 
						if m.ExistsFunc != nil {
 | 
				
			||||||
 | 
							return m.ExistsFunc()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *TestManager) SetRootCmd(cmd string) {
 | 
				
			||||||
 | 
						if m.SetRootCmdFunc != nil {
 | 
				
			||||||
 | 
							m.SetRootCmdFunc(cmd)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *TestManager) Sync(opts *manager.Opts) error {
 | 
				
			||||||
 | 
						if m.SyncFunc != nil {
 | 
				
			||||||
 | 
							return m.SyncFunc(opts)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *TestManager) Install(opts *manager.Opts, pkgs ...string) error {
 | 
				
			||||||
 | 
						if m.InstallFunc != nil {
 | 
				
			||||||
 | 
							return m.InstallFunc(opts, pkgs...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *TestManager) Remove(opts *manager.Opts, pkgs ...string) error {
 | 
				
			||||||
 | 
						if m.RemoveFunc != nil {
 | 
				
			||||||
 | 
							return m.RemoveFunc(opts, pkgs...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *TestManager) Upgrade(opts *manager.Opts, pkgs ...string) error {
 | 
				
			||||||
 | 
						if m.UpgradeFunc != nil {
 | 
				
			||||||
 | 
							return m.UpgradeFunc(opts, pkgs...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *TestManager) InstallLocal(opts *manager.Opts, files ...string) error {
 | 
				
			||||||
 | 
						if m.InstallLocalFunc != nil {
 | 
				
			||||||
 | 
							return m.InstallLocalFunc(opts, files...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *TestManager) UpgradeAll(opts *manager.Opts) error {
 | 
				
			||||||
 | 
						if m.UpgradeAllFunc != nil {
 | 
				
			||||||
 | 
							return m.UpgradeAllFunc(opts)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *TestManager) ListInstalled(opts *manager.Opts) (map[string]string, error) {
 | 
				
			||||||
 | 
						if m.ListInstalledFunc != nil {
 | 
				
			||||||
 | 
							return m.ListInstalledFunc(opts)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return map[string]string{}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *TestManager) IsInstalled(pkg string) (bool, error) {
 | 
				
			||||||
 | 
						if m.IsInstalledFunc != nil {
 | 
				
			||||||
 | 
							return m.IsInstalledFunc(pkg)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return true, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestInstallBuildDeps(t *testing.T) {
 | 
				
			||||||
 | 
						type testEnv struct {
 | 
				
			||||||
 | 
							pf   PackageFinder
 | 
				
			||||||
 | 
							vars *types.BuildVars
 | 
				
			||||||
 | 
							opts types.BuildOpts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Contains pkgs captured by FindPkgsFunc
 | 
				
			||||||
 | 
							capturedPkgs []string
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						type testCase struct {
 | 
				
			||||||
 | 
							Name     string
 | 
				
			||||||
 | 
							Prepare  func() *testEnv
 | 
				
			||||||
 | 
							Expected func(t *testing.T, e *testEnv, res []string, err error)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, tc := range []testCase{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Name: "install only needed deps",
 | 
				
			||||||
 | 
								Prepare: func() *testEnv {
 | 
				
			||||||
 | 
									pf := TestPackageFinder{}
 | 
				
			||||||
 | 
									vars := types.BuildVars{}
 | 
				
			||||||
 | 
									m := TestManager{}
 | 
				
			||||||
 | 
									opts := types.BuildOpts{
 | 
				
			||||||
 | 
										Manager:     &m,
 | 
				
			||||||
 | 
										Interactive: false,
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									env := &testEnv{
 | 
				
			||||||
 | 
										pf:           &pf,
 | 
				
			||||||
 | 
										vars:         &vars,
 | 
				
			||||||
 | 
										opts:         opts,
 | 
				
			||||||
 | 
										capturedPkgs: []string{},
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									pf.FindPkgsFunc = func(ctx context.Context, pkgs []string) (map[string][]db.Package, []string, error) {
 | 
				
			||||||
 | 
										env.capturedPkgs = append(env.capturedPkgs, pkgs...)
 | 
				
			||||||
 | 
										result := make(map[string][]db.Package)
 | 
				
			||||||
 | 
										result["bar"] = []db.Package{{
 | 
				
			||||||
 | 
											Name: "bar-pkg",
 | 
				
			||||||
 | 
										}}
 | 
				
			||||||
 | 
										result["buz"] = []db.Package{{
 | 
				
			||||||
 | 
											Name: "buz-pkg",
 | 
				
			||||||
 | 
										}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										return result, []string{}, nil
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									vars.BuildDepends = []string{
 | 
				
			||||||
 | 
										"foo",
 | 
				
			||||||
 | 
										"bar",
 | 
				
			||||||
 | 
										"buz",
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									m.IsInstalledFunc = func(pkg string) (bool, error) {
 | 
				
			||||||
 | 
										if pkg == "foo" {
 | 
				
			||||||
 | 
											return true, nil
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
 | 
											return false, nil
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									return env
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								Expected: func(t *testing.T, e *testEnv, res []string, err error) {
 | 
				
			||||||
 | 
									assert.NoError(t, err)
 | 
				
			||||||
 | 
									assert.Len(t, res, 2)
 | 
				
			||||||
 | 
									assert.ElementsMatch(t, res, []string{"bar-pkg", "buz-pkg"})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									assert.ElementsMatch(t, e.capturedPkgs, []string{"bar", "buz"})
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						} {
 | 
				
			||||||
 | 
							t.Run(tc.Name, func(tt *testing.T) {
 | 
				
			||||||
 | 
								ctx := context.Background()
 | 
				
			||||||
 | 
								env := tc.Prepare()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								result, err := installBuildDeps(
 | 
				
			||||||
 | 
									ctx,
 | 
				
			||||||
 | 
									env.pf,
 | 
				
			||||||
 | 
									env.vars,
 | 
				
			||||||
 | 
									env.opts,
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								tc.Expected(tt, env, result, err)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -149,6 +149,21 @@ func (a *APK) ListInstalled(opts *Opts) (map[string]string, error) {
 | 
				
			|||||||
	return out, nil
 | 
						return out, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a *APK) IsInstalled(pkg string) (bool, error) {
 | 
				
			||||||
 | 
						cmd := exec.Command("apk", "info", "--installed", pkg)
 | 
				
			||||||
 | 
						output, err := cmd.CombinedOutput()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if exitErr, ok := err.(*exec.ExitError); ok {
 | 
				
			||||||
 | 
								// Exit code 1 means the package is not installed
 | 
				
			||||||
 | 
								if exitErr.ExitCode() == 1 {
 | 
				
			||||||
 | 
									return false, nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return false, fmt.Errorf("apk: isinstalled: %w, output: %s", err, output)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return true, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (a *APK) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
 | 
					func (a *APK) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
 | 
				
			||||||
	var cmd *exec.Cmd
 | 
						var cmd *exec.Cmd
 | 
				
			||||||
	if opts.AsRoot {
 | 
						if opts.AsRoot {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -135,6 +135,21 @@ func (a *APT) ListInstalled(opts *Opts) (map[string]string, error) {
 | 
				
			|||||||
	return out, nil
 | 
						return out, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a *APT) IsInstalled(pkg string) (bool, error) {
 | 
				
			||||||
 | 
						cmd := exec.Command("dpkg-query", "-l", pkg)
 | 
				
			||||||
 | 
						output, err := cmd.CombinedOutput()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if exitErr, ok := err.(*exec.ExitError); ok {
 | 
				
			||||||
 | 
								// Exit code 1 means the package is not installed
 | 
				
			||||||
 | 
								if exitErr.ExitCode() == 1 {
 | 
				
			||||||
 | 
									return false, nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return false, fmt.Errorf("apt: isinstalled: %w, output: %s", err, output)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return true, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (a *APT) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
 | 
					func (a *APT) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
 | 
				
			||||||
	var cmd *exec.Cmd
 | 
						var cmd *exec.Cmd
 | 
				
			||||||
	if opts.AsRoot {
 | 
						if opts.AsRoot {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,7 +17,6 @@
 | 
				
			|||||||
package manager
 | 
					package manager
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bufio"
 | 
					 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"os/exec"
 | 
						"os/exec"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
@@ -25,6 +24,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// APTRpm represents the APT-RPM package manager
 | 
					// APTRpm represents the APT-RPM package manager
 | 
				
			||||||
type APTRpm struct {
 | 
					type APTRpm struct {
 | 
				
			||||||
 | 
						CommonRPM
 | 
				
			||||||
	rootCmd string
 | 
						rootCmd string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -106,38 +106,6 @@ func (a *APTRpm) UpgradeAll(opts *Opts) error {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (y *APTRpm) ListInstalled(opts *Opts) (map[string]string, error) {
 | 
					 | 
				
			||||||
	out := map[string]string{}
 | 
					 | 
				
			||||||
	cmd := exec.Command("rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	stdout, err := cmd.StdoutPipe()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	err = cmd.Start()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	scanner := bufio.NewScanner(stdout)
 | 
					 | 
				
			||||||
	for scanner.Scan() {
 | 
					 | 
				
			||||||
		name, version, ok := strings.Cut(scanner.Text(), "\u200b")
 | 
					 | 
				
			||||||
		if !ok {
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		version = strings.TrimPrefix(version, "0:")
 | 
					 | 
				
			||||||
		out[name] = version
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	err = scanner.Err()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return out, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (a *APTRpm) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
 | 
					func (a *APTRpm) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
 | 
				
			||||||
	var cmd *exec.Cmd
 | 
						var cmd *exec.Cmd
 | 
				
			||||||
	if opts.AsRoot {
 | 
						if opts.AsRoot {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										72
									
								
								pkg/manager/common_rpm.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								pkg/manager/common_rpm.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
				
			|||||||
 | 
					// ALR - Any Linux Repository
 | 
				
			||||||
 | 
					// Copyright (C) 2025 Евгений Храмов
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package manager
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bufio"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type CommonRPM struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *CommonRPM) ListInstalled(opts *Opts) (map[string]string, error) {
 | 
				
			||||||
 | 
						out := map[string]string{}
 | 
				
			||||||
 | 
						cmd := exec.Command("rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						stdout, err := cmd.StdoutPipe()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = cmd.Start()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						scanner := bufio.NewScanner(stdout)
 | 
				
			||||||
 | 
						for scanner.Scan() {
 | 
				
			||||||
 | 
							name, version, ok := strings.Cut(scanner.Text(), "\u200b")
 | 
				
			||||||
 | 
							if !ok {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							version = strings.TrimPrefix(version, "0:")
 | 
				
			||||||
 | 
							out[name] = version
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = scanner.Err()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return out, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a *CommonRPM) IsInstalled(pkg string) (bool, error) {
 | 
				
			||||||
 | 
						cmd := exec.Command("rpm", "-q", "--whatprovides", pkg)
 | 
				
			||||||
 | 
						output, err := cmd.CombinedOutput()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if exitErr, ok := err.(*exec.ExitError); ok {
 | 
				
			||||||
 | 
								if exitErr.ExitCode() == 1 {
 | 
				
			||||||
 | 
									return false, nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return false, fmt.Errorf("rpm: isinstalled: %w, output: %s", err, output)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return true, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -19,14 +19,13 @@
 | 
				
			|||||||
package manager
 | 
					package manager
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bufio"
 | 
					 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"os/exec"
 | 
						"os/exec"
 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DNF представляет менеджер пакетов DNF
 | 
					// DNF представляет менеджер пакетов DNF
 | 
				
			||||||
type DNF struct {
 | 
					type DNF struct {
 | 
				
			||||||
 | 
						CommonRPM
 | 
				
			||||||
	rootCmd string // rootCmd хранит команду, используемую для выполнения команд с правами root
 | 
						rootCmd string // rootCmd хранит команду, используемую для выполнения команд с правами root
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -120,39 +119,6 @@ func (d *DNF) UpgradeAll(opts *Opts) error {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ListInstalled возвращает список установленных пакетов и их версий
 | 
					 | 
				
			||||||
func (d *DNF) ListInstalled(opts *Opts) (map[string]string, error) {
 | 
					 | 
				
			||||||
	out := map[string]string{}
 | 
					 | 
				
			||||||
	cmd := exec.Command("rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	stdout, err := cmd.StdoutPipe()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	err = cmd.Start()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	scanner := bufio.NewScanner(stdout)
 | 
					 | 
				
			||||||
	for scanner.Scan() {
 | 
					 | 
				
			||||||
		name, version, ok := strings.Cut(scanner.Text(), "\u200b")
 | 
					 | 
				
			||||||
		if !ok {
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		version = strings.TrimPrefix(version, "0:")
 | 
					 | 
				
			||||||
		out[name] = version
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	err = scanner.Err()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return out, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// getCmd создает и возвращает команду exec.Cmd для менеджера пакетов DNF
 | 
					// getCmd создает и возвращает команду exec.Cmd для менеджера пакетов DNF
 | 
				
			||||||
func (d *DNF) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
 | 
					func (d *DNF) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
 | 
				
			||||||
	var cmd *exec.Cmd
 | 
						var cmd *exec.Cmd
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -80,6 +80,8 @@ type Manager interface {
 | 
				
			|||||||
	UpgradeAll(*Opts) error
 | 
						UpgradeAll(*Opts) error
 | 
				
			||||||
	// ListInstalled returns all installed packages mapped to their versions
 | 
						// ListInstalled returns all installed packages mapped to their versions
 | 
				
			||||||
	ListInstalled(*Opts) (map[string]string, error)
 | 
						ListInstalled(*Opts) (map[string]string, error)
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						IsInstalled(string) (bool, error)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Detect returns the package manager detected on the system
 | 
					// Detect returns the package manager detected on the system
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -142,6 +142,21 @@ func (p *Pacman) ListInstalled(opts *Opts) (map[string]string, error) {
 | 
				
			|||||||
	return out, nil
 | 
						return out, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *Pacman) IsInstalled(pkg string) (bool, error) {
 | 
				
			||||||
 | 
						cmd := exec.Command("pacman", "-Q", pkg)
 | 
				
			||||||
 | 
						output, err := cmd.CombinedOutput()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							// Pacman returns exit code 1 if the package is not found
 | 
				
			||||||
 | 
							if exitErr, ok := err.(*exec.ExitError); ok {
 | 
				
			||||||
 | 
								if exitErr.ExitCode() == 1 {
 | 
				
			||||||
 | 
									return false, nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return false, fmt.Errorf("pacman: isinstalled: %w, output: %s", err, output)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return true, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (p *Pacman) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
 | 
					func (p *Pacman) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
 | 
				
			||||||
	var cmd *exec.Cmd
 | 
						var cmd *exec.Cmd
 | 
				
			||||||
	if opts.AsRoot {
 | 
						if opts.AsRoot {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,14 +20,14 @@
 | 
				
			|||||||
package manager
 | 
					package manager
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bufio"
 | 
					 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"os/exec"
 | 
						"os/exec"
 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// YUM represents the YUM package manager
 | 
					// YUM represents the YUM package manager
 | 
				
			||||||
type YUM struct {
 | 
					type YUM struct {
 | 
				
			||||||
 | 
						CommonRPM
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rootCmd string
 | 
						rootCmd string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -111,38 +111,6 @@ func (y *YUM) UpgradeAll(opts *Opts) error {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (y *YUM) ListInstalled(opts *Opts) (map[string]string, error) {
 | 
					 | 
				
			||||||
	out := map[string]string{}
 | 
					 | 
				
			||||||
	cmd := exec.Command("rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	stdout, err := cmd.StdoutPipe()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	err = cmd.Start()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	scanner := bufio.NewScanner(stdout)
 | 
					 | 
				
			||||||
	for scanner.Scan() {
 | 
					 | 
				
			||||||
		name, version, ok := strings.Cut(scanner.Text(), "\u200b")
 | 
					 | 
				
			||||||
		if !ok {
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		version = strings.TrimPrefix(version, "0:")
 | 
					 | 
				
			||||||
		out[name] = version
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	err = scanner.Err()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return out, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (y *YUM) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
 | 
					func (y *YUM) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
 | 
				
			||||||
	var cmd *exec.Cmd
 | 
						var cmd *exec.Cmd
 | 
				
			||||||
	if opts.AsRoot {
 | 
						if opts.AsRoot {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,14 +20,13 @@
 | 
				
			|||||||
package manager
 | 
					package manager
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bufio"
 | 
					 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"os/exec"
 | 
						"os/exec"
 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Zypper represents the Zypper package manager
 | 
					// Zypper represents the Zypper package manager
 | 
				
			||||||
type Zypper struct {
 | 
					type Zypper struct {
 | 
				
			||||||
 | 
						CommonRPM
 | 
				
			||||||
	rootCmd string
 | 
						rootCmd string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -111,38 +110,6 @@ func (z *Zypper) UpgradeAll(opts *Opts) error {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (z *Zypper) ListInstalled(opts *Opts) (map[string]string, error) {
 | 
					 | 
				
			||||||
	out := map[string]string{}
 | 
					 | 
				
			||||||
	cmd := exec.Command("rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	stdout, err := cmd.StdoutPipe()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	err = cmd.Start()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	scanner := bufio.NewScanner(stdout)
 | 
					 | 
				
			||||||
	for scanner.Scan() {
 | 
					 | 
				
			||||||
		name, version, ok := strings.Cut(scanner.Text(), "\u200b")
 | 
					 | 
				
			||||||
		if !ok {
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		version = strings.TrimPrefix(version, "0:")
 | 
					 | 
				
			||||||
		out[name] = version
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	err = scanner.Err()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return out, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (z *Zypper) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
 | 
					func (z *Zypper) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
 | 
				
			||||||
	var cmd *exec.Cmd
 | 
						var cmd *exec.Cmd
 | 
				
			||||||
	if opts.AsRoot {
 | 
						if opts.AsRoot {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user