forked from Plemya-x/ALR
		
	fix: removeAlreadyInstalled before FindPkgs
This commit is contained in:
		@@ -65,6 +65,7 @@ import (
 | 
			
		||||
// Один содержит пути к собранным пакетам, другой - имена собранных пакетов.
 | 
			
		||||
func BuildPackage(ctx context.Context, opts types.BuildOpts) ([]string, []string, error) {
 | 
			
		||||
	log := loggerctx.From(ctx)
 | 
			
		||||
	reposInstance := repos.GetInstance(ctx)
 | 
			
		||||
 | 
			
		||||
	info, err := distro.ParseOSRelease(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -133,12 +134,12 @@ func BuildPackage(ctx context.Context, opts types.BuildOpts) ([]string, []string
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	buildDeps, err := installBuildDeps(ctx, vars, opts, installed) // Устанавливаем зависимости для сборки
 | 
			
		||||
	buildDeps, err := installBuildDeps(ctx, reposInstance, vars, opts) // Устанавливаем зависимости для сборки
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = installOptDeps(ctx, vars, opts, installed) // Устанавливаем опциональные зависимости
 | 
			
		||||
	err = installOptDeps(ctx, reposInstance, vars, opts) // Устанавливаем опциональные зависимости
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
@@ -329,18 +330,25 @@ func performChecks(ctx context.Context, vars *types.BuildVars, interactive bool,
 | 
			
		||||
	return true, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type PackageFinder interface {
 | 
			
		||||
	FindPkgs(ctx context.Context, pkgs []string) (map[string][]db.Package, []string, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Функция 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)
 | 
			
		||||
	var buildDeps []string
 | 
			
		||||
	if len(vars.BuildDepends) > 0 {
 | 
			
		||||
		found, notFound, err := repos.FindPkgs(ctx, vars.BuildDepends) // Находим пакеты-зависимости
 | 
			
		||||
		deps, err := removeAlreadyInstalled(opts, vars.BuildDepends)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			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() // Логгируем установку зависимостей
 | 
			
		||||
 | 
			
		||||
@@ -353,9 +361,13 @@ func installBuildDeps(ctx context.Context, vars *types.BuildVars, opts types.Bui
 | 
			
		||||
 | 
			
		||||
// Функция installOptDeps спрашивает у пользователя, какие, если таковые имеются, опциональные зависимости он хочет установить.
 | 
			
		||||
// Если пользователь решает установить какие-либо опциональные зависимости, выполняется их установка.
 | 
			
		||||
func installOptDeps(ctx context.Context, vars *types.BuildVars, opts types.BuildOpts, installed map[string]string) error {
 | 
			
		||||
	if len(vars.OptDepends) > 0 {
 | 
			
		||||
		optDeps, err := cliutils.ChooseOptDepends(ctx, vars.OptDepends, "install", opts.Interactive) // Пользователя просят выбрать опциональные зависимости
 | 
			
		||||
func installOptDeps(ctx context.Context, repos PackageFinder, vars *types.BuildVars, opts types.BuildOpts) error {
 | 
			
		||||
	optDeps, err := removeAlreadyInstalled(opts, vars.OptDepends)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if len(optDeps) > 0 {
 | 
			
		||||
		optDeps, err := cliutils.ChooseOptDepends(ctx, optDeps, "install", opts.Interactive) // Пользователя просят выбрать опциональные зависимости
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
@@ -369,7 +381,6 @@ func installOptDeps(ctx context.Context, vars *types.BuildVars, opts types.Build
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		found = removeAlreadyInstalled(found, installed) // Убираем уже установленные зависимости
 | 
			
		||||
		flattened := cliutils.FlattenPkgs(ctx, found, "install", opts.Interactive)
 | 
			
		||||
		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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Функция removeAlreadyInstalled возвращает карту без каких-либо зависимостей, которые уже установлены.
 | 
			
		||||
func removeAlreadyInstalled(found map[string][]db.Package, installed map[string]string) map[string][]db.Package {
 | 
			
		||||
	filteredPackages := make(map[string][]db.Package)
 | 
			
		||||
// Returns not installed dependencies
 | 
			
		||||
func removeAlreadyInstalled(opts types.BuildOpts, dependencies []string) ([]string, error) {
 | 
			
		||||
	filteredPackages := []string{}
 | 
			
		||||
 | 
			
		||||
	for name, pkgList := range found {
 | 
			
		||||
		filteredPkgList := []db.Package{}
 | 
			
		||||
		for _, pkg := range pkgList {
 | 
			
		||||
			if _, isInstalled := installed[pkg.Name]; !isInstalled {
 | 
			
		||||
				filteredPkgList = append(filteredPkgList, pkg)
 | 
			
		||||
			}
 | 
			
		||||
	for _, dep := range dependencies {
 | 
			
		||||
		installed, err := opts.Manager.IsInstalled(dep)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		filteredPackages[name] = filteredPkgList
 | 
			
		||||
		if installed {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		filteredPackages = append(filteredPackages, dep)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return filteredPackages
 | 
			
		||||
	return filteredPackages, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Функция 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)
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user