forked from Plemya-x/ALR
		
	Комментирование кода, добавление возможности сборки нескольких пакетов package_* из одного alr.sh
This commit is contained in:
		
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -4,4 +4,5 @@ | |||||||
| /dist/ | /dist/ | ||||||
| /internal/config/version.txt | /internal/config/version.txt | ||||||
| .fleet | .fleet | ||||||
| .idea | .idea | ||||||
|  | .gigaide | ||||||
| @@ -1,19 +1,29 @@ | |||||||
| /* | /* | ||||||
|  * ALR - Any Linux Repository |  * ALR - Any Linux Repository | ||||||
|  |  * Любая Linux репозитория | ||||||
|  * Copyright (C) 2024 Евгений Храмов |  * Copyright (C) 2024 Евгений Храмов | ||||||
|  * |  * | ||||||
|  * This program is free software: you can redistribute it and/or modify |  * 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 |  * it under the terms of the GNU General Public License as published by | ||||||
|  * the Free Software Foundation, either version 3 of the License, or |  * the Free Software Foundation, either version 3 of the License, or | ||||||
|  * (at your option) any later version. |  * (at your option) any later version. | ||||||
|  |  * Это программное обеспечение свободно: вы можете распространять его и/или изменять | ||||||
|  |  * на условиях GNU General Public License, опубликованной Free Software Foundation, | ||||||
|  |  * либо версии 3 лицензии, либо (на ваш выбор) любой более поздней версии. | ||||||
|  * |  * | ||||||
|  * This program is distributed in the hope that it will be useful, |  * This program is distributed in the hope that it will be useful, | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * GNU General Public License for more details. |  * GNU General Public License for more details. | ||||||
|  |  * Это программное обеспечение распространяется в надежде, что оно будет полезным, | ||||||
|  |  * но БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ; даже без подразумеваемой гарантии | ||||||
|  |  * КОММЕРЧЕСКОЙ ПРИГОДНОСТИ или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННОЙ ЦЕЛИ. См. | ||||||
|  |  * GNU General Public License для более подробной информации. | ||||||
|  * |  * | ||||||
|  * You should have received a copy of the GNU General Public License |  * You should have received a copy of the GNU General Public License | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. |  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * Вы должны были получить копию GNU General Public License | ||||||
|  |  * вместе с этой программой. Если нет, см. <http://www.gnu.org/licenses/>. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| package build | package build | ||||||
| @@ -32,6 +42,7 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
|  |     // Импортируем пакеты для поддержки различных форматов пакетов (APK, DEB, RPM и ARCH). | ||||||
| 	_ "github.com/goreleaser/nfpm/v2/apk" | 	_ "github.com/goreleaser/nfpm/v2/apk" | ||||||
| 	_ "github.com/goreleaser/nfpm/v2/arch" | 	_ "github.com/goreleaser/nfpm/v2/arch" | ||||||
| 	_ "github.com/goreleaser/nfpm/v2/deb" | 	_ "github.com/goreleaser/nfpm/v2/deb" | ||||||
| @@ -57,8 +68,8 @@ import ( | |||||||
| 	"mvdan.cc/sh/v3/syntax" | 	"mvdan.cc/sh/v3/syntax" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // BuildPackage builds the script at the given path. It returns two slices. One contains the paths | // Функция BuildPackage выполняет сборку скрипта по указанному пути. Возвращает два среза. | ||||||
| // to the built package(s), the other contains the names of the built package(s). | // Один содержит пути к собранным пакетам, другой - имена собранных пакетов. | ||||||
| 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) | ||||||
|  |  | ||||||
| @@ -72,9 +83,8 @@ func BuildPackage(ctx context.Context, opts types.BuildOpts) ([]string, []string | |||||||
| 		return nil, nil, err | 		return nil, nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// The first pass is just used to get variable values and runs before |     // Первый проход предназначен для получения значений переменных и выполняется | ||||||
| 	// the script is displayed, so it's restricted so as to prevent malicious |     // до отображения скрипта, чтобы предотвратить выполнение вредоносного кода. | ||||||
| 	// code from executing. |  | ||||||
| 	vars, err := executeFirstPass(ctx, info, fl, opts.Script) | 	vars, err := executeFirstPass(ctx, info, fl, opts.Script) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, nil, err | 		return nil, nil, err | ||||||
| @@ -82,8 +92,8 @@ func BuildPackage(ctx context.Context, opts types.BuildOpts) ([]string, []string | |||||||
|  |  | ||||||
| 	dirs := getDirs(ctx, vars, opts.Script) | 	dirs := getDirs(ctx, vars, opts.Script) | ||||||
|  |  | ||||||
| 	// If opts.Clean isn't set and we find the package already built, |     // Если флаг opts.Clean не установлен, и пакет уже собран, | ||||||
| 	// just return it rather than rebuilding |     // возвращаем его, а не собираем заново. | ||||||
| 	if !opts.Clean { | 	if !opts.Clean { | ||||||
| 		builtPkgPath, ok, err := checkForBuiltPackage(opts.Manager, vars, getPkgFormat(opts.Manager), dirs.BaseDir) | 		builtPkgPath, ok, err := checkForBuiltPackage(opts.Manager, vars, getPkgFormat(opts.Manager), dirs.BaseDir) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| @@ -95,7 +105,7 @@ func BuildPackage(ctx context.Context, opts types.BuildOpts) ([]string, []string | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Ask the user if they'd like to see the build script |     // Спрашиваем у пользователя, хочет ли он увидеть скрипт сборки. | ||||||
| 	err = cliutils.PromptViewScript(ctx, opts.Script, vars.Name, config.Config(ctx).PagerStyle, opts.Interactive) | 	err = cliutils.PromptViewScript(ctx, opts.Script, vars.Name, config.Config(ctx).PagerStyle, opts.Interactive) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatal("Failed to prompt user to view build script").Err(err).Send() | 		log.Fatal("Failed to prompt user to view build script").Err(err).Send() | ||||||
| @@ -103,161 +113,161 @@ func BuildPackage(ctx context.Context, opts types.BuildOpts) ([]string, []string | |||||||
|  |  | ||||||
| 	log.Info("Building package").Str("name", vars.Name).Str("version", vars.Version).Send() | 	log.Info("Building package").Str("name", vars.Name).Str("version", vars.Version).Send() | ||||||
|  |  | ||||||
| 	// The second pass will be used to execute the actual code, |     // Второй проход будет использоваться для выполнения реального кода, | ||||||
| 	// so it's unrestricted. The script has already been displayed |     // поэтому он не ограничен. Скрипт уже был показан | ||||||
| 	// to the user by this point, so it should be safe |     // пользователю к этому моменту, так что это должно быть безопасно. | ||||||
| 	dec, err := executeSecondPass(ctx, info, fl, dirs) | 	dec, err := executeSecondPass(ctx, info, fl, dirs) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, nil, err | 		return nil, nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Get the installed packages on the system | 	// Получаем список установленных пакетов в системе | ||||||
| 	installed, err := opts.Manager.ListInstalled(nil) | 	installed, err := opts.Manager.ListInstalled(nil) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, nil, err | 		return nil, nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	cont, err := performChecks(ctx, vars, opts.Interactive, installed) | 	cont, err := performChecks(ctx, vars, opts.Interactive, installed) // Выполняем различные проверки | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, nil, err | 		return nil, nil, err | ||||||
| 	} else if !cont { | 	} else if !cont { | ||||||
| 		os.Exit(1) | 		os.Exit(1) // Если проверки не пройдены, выходим из программы | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Prepare the directories for building |     // Подготавливаем директории для сборки | ||||||
| 	err = prepareDirs(dirs) | 	err = prepareDirs(dirs) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, nil, err | 		return nil, nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	buildDeps, err := installBuildDeps(ctx, vars, opts, installed) | 	buildDeps, err := installBuildDeps(ctx, vars, opts, installed) // Устанавливаем зависимости для сборки | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, nil, err | 		return nil, nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	err = installOptDeps(ctx, vars, opts, installed) | 	err = installOptDeps(ctx, vars, opts, installed) // Устанавливаем опциональные зависимости | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, nil, err | 		return nil, nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	builtPaths, builtNames, repoDeps, err := buildALRDeps(ctx, opts, vars) | 	builtPaths, builtNames, repoDeps, err := buildALRDeps(ctx, opts, vars) // Собираем зависимости | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, nil, err | 		return nil, nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	log.Info("Downloading sources").Send() | 	log.Info("Downloading sources").Send() // Записываем в лог загрузку источников | ||||||
|  |  | ||||||
| 	err = getSources(ctx, dirs, vars) | 	err = getSources(ctx, dirs, vars) // Загружаем исходники | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, nil, err | 		return nil, nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	err = executeFunctions(ctx, dec, dirs, vars) | 	err = executeFunctions(ctx, dec, dirs, vars) // Выполняем специальные функции | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, nil, err | 		return nil, nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	log.Info("Building package metadata").Str("name", vars.Name).Send() | 	log.Info("Building package metadata").Str("name", vars.Name).Send() // Логгируем сборку метаданных пакета | ||||||
|  |  | ||||||
| 	pkgFormat := getPkgFormat(opts.Manager) | 	pkgFormat := getPkgFormat(opts.Manager) // Получаем формат пакета | ||||||
|  |  | ||||||
| 	pkgInfo, err := buildPkgMetadata(vars, dirs, pkgFormat, append(repoDeps, builtNames...)) | 	pkgInfo, err := buildPkgMetadata(vars, dirs, pkgFormat, append(repoDeps, builtNames...)) // Собираем метаданные пакета | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, nil, err | 		return nil, nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	packager, err := nfpm.Get(pkgFormat) | 	packager, err := nfpm.Get(pkgFormat) // Получаем упаковщик для формата пакета | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, nil, err | 		return nil, nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	pkgName := packager.ConventionalFileName(pkgInfo) | 	pkgName := packager.ConventionalFileName(pkgInfo) // Получаем имя файла пакета | ||||||
| 	pkgPath := filepath.Join(dirs.BaseDir, pkgName) | 	pkgPath := filepath.Join(dirs.BaseDir, pkgName) // Определяем путь к пакету | ||||||
|  |  | ||||||
| 	pkgFile, err := os.Create(pkgPath) | 	pkgFile, err := os.Create(pkgPath) // Создаём файл пакета | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, nil, err | 		return nil, nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	log.Info("Compressing package").Str("name", pkgName).Send() | 	log.Info("Compressing package").Str("name", pkgName).Send() // Логгируем сжатие пакета | ||||||
|  |  | ||||||
| 	err = packager.Package(pkgInfo, pkgFile) | 	err = packager.Package(pkgInfo, pkgFile) // Упаковываем пакет | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, nil, err | 		return nil, nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	err = removeBuildDeps(ctx, buildDeps, opts) | 	err = removeBuildDeps(ctx, buildDeps, opts) // Удаляем зависимости для сборки | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, nil, err | 		return nil, nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Add the path and name of the package we just built to the |     // Добавляем путь и имя только что собранного пакета в | ||||||
| 	// appropriate slices |     // соответствующие срезы | ||||||
| 	pkgPaths := append(builtPaths, pkgPath) | 	pkgPaths := append(builtPaths, pkgPath) | ||||||
| 	pkgNames := append(builtNames, vars.Name) | 	pkgNames := append(builtNames, vars.Name) | ||||||
|  |  | ||||||
| 	// Remove any duplicates from the pkgPaths and pkgNames. |     // Удаляем дубликаты из pkgPaths и pkgNames. | ||||||
| 	// Duplicates can be introduced if several of the dependencies |     // Дубликаты могут появиться, если несколько зависимостей | ||||||
| 	// depend on the same packages. |     // зависят от одних и тех же пакетов. | ||||||
| 	pkgPaths = removeDuplicates(pkgPaths) | 	pkgPaths = removeDuplicates(pkgPaths) | ||||||
| 	pkgNames = removeDuplicates(pkgNames) | 	pkgNames = removeDuplicates(pkgNames) | ||||||
|  |  | ||||||
| 	return pkgPaths, pkgNames, nil | 	return pkgPaths, pkgNames, nil // Возвращаем пути и имена пакетов | ||||||
| } | } | ||||||
|  |  | ||||||
| // parseScript parses the build script using the built-in bash implementation | // Функция parseScript анализирует скрипт сборки с использованием встроенной реализации bash | ||||||
| func parseScript(info *distro.OSRelease, script string) (*syntax.File, error) { | func parseScript(info *distro.OSRelease, script string) (*syntax.File, error) { | ||||||
| 	fl, err := os.Open(script) | 	fl, err := os.Open(script) // Открываем файл скрипта | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 	defer fl.Close() | 	defer fl.Close() // Закрываем файл после выполнения | ||||||
|  |  | ||||||
| 	file, err := syntax.NewParser().Parse(fl, "alr.sh") | 	file, err := syntax.NewParser().Parse(fl, "alr.sh") // Парсим скрипт с помощью синтаксического анализатора | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return file, nil | 	return file, nil // Возвращаем синтаксическое дерево | ||||||
| } | } | ||||||
|  |  | ||||||
| // executeFirstPass executes the parsed script in a restricted environment | // Функция executeFirstPass выполняет парсированный скрипт в ограниченной среде, | ||||||
| // to extract the build variables without executing any actual code. | // чтобы извлечь переменные сборки без выполнения реального кода. | ||||||
| func executeFirstPass(ctx context.Context, info *distro.OSRelease, fl *syntax.File, script string) (*types.BuildVars, error) { | func executeFirstPass(ctx context.Context, info *distro.OSRelease, fl *syntax.File, script string) (*types.BuildVars, error) { | ||||||
| 	scriptDir := filepath.Dir(script) | 	scriptDir := filepath.Dir(script) // Получаем директорию скрипта | ||||||
| 	env := createBuildEnvVars(info, types.Directories{ScriptDir: scriptDir}) | 	env := createBuildEnvVars(info, types.Directories{ScriptDir: scriptDir}) // Создаём переменные окружения для сборки | ||||||
|  |  | ||||||
| 	runner, err := interp.New( | 	runner, err := interp.New( | ||||||
| 		interp.Env(expand.ListEnviron(env...)), | 		interp.Env(expand.ListEnviron(env...)), // Устанавливаем окружение | ||||||
| 		interp.StdIO(os.Stdin, os.Stdout, os.Stderr), | 		interp.StdIO(os.Stdin, os.Stdout, os.Stderr), // Устанавливаем стандартный ввод-вывод | ||||||
| 		interp.ExecHandler(helpers.Restricted.ExecHandler(handlers.NopExec)), | 		interp.ExecHandler(helpers.Restricted.ExecHandler(handlers.NopExec)), // Ограничиваем выполнение | ||||||
| 		interp.ReadDirHandler(handlers.RestrictedReadDir(scriptDir)), | 		interp.ReadDirHandler(handlers.RestrictedReadDir(scriptDir)), // Ограничиваем чтение директорий | ||||||
| 		interp.StatHandler(handlers.RestrictedStat(scriptDir)), | 		interp.StatHandler(handlers.RestrictedStat(scriptDir)), // Ограничиваем доступ к статистике файлов | ||||||
| 		interp.OpenHandler(handlers.RestrictedOpen(scriptDir)), | 		interp.OpenHandler(handlers.RestrictedOpen(scriptDir)), // Ограничиваем открытие файлов | ||||||
| 	) | 	) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	err = runner.Run(ctx, fl) | 	err = runner.Run(ctx, fl) // Запускаем скрипт | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	dec := decoder.New(info, runner) | 	dec := decoder.New(info, runner) // Создаём новый декодер | ||||||
|  |  | ||||||
| 	var vars types.BuildVars | 	var vars types.BuildVars | ||||||
| 	err = dec.DecodeVars(&vars) | 	err = dec.DecodeVars(&vars) // Декодируем переменные | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return &vars, nil | 	return &vars, nil // Возвращаем переменные сборки | ||||||
| } | } | ||||||
|  |  | ||||||
| // getDirs returns the appropriate directories for the script | // Функция getDirs возвращает соответствующие директории для скрипта | ||||||
| func getDirs(ctx context.Context, vars *types.BuildVars, script string) types.Directories { | func getDirs(ctx context.Context, vars *types.BuildVars, script string) types.Directories { | ||||||
| 	baseDir := filepath.Join(config.GetPaths(ctx).PkgsDir, vars.Name) | 	baseDir := filepath.Join(config.GetPaths(ctx).PkgsDir, vars.Name) // Определяем базовую директорию | ||||||
| 	return types.Directories{ | 	return types.Directories{ | ||||||
| 		BaseDir:   baseDir, | 		BaseDir:   baseDir, | ||||||
| 		SrcDir:    filepath.Join(baseDir, "src"), | 		SrcDir:    filepath.Join(baseDir, "src"), | ||||||
| @@ -266,46 +276,46 @@ func getDirs(ctx context.Context, vars *types.BuildVars, script string) types.Di | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // executeSecondPass executes the build script for the second time, this time without any restrictions. | // Функция executeSecondPass выполняет скрипт сборки второй раз без каких-либо ограничений. Возвращается декодер, | ||||||
| // It returns a decoder that can be used to retrieve functions and variables from the script. | // который может быть использован для получения функций и переменных из скрипта. | ||||||
| func executeSecondPass(ctx context.Context, info *distro.OSRelease, fl *syntax.File, dirs types.Directories) (*decoder.Decoder, error) { | func executeSecondPass(ctx context.Context, info *distro.OSRelease, fl *syntax.File, dirs types.Directories) (*decoder.Decoder, error) { | ||||||
| 	env := createBuildEnvVars(info, dirs) | 	env := createBuildEnvVars(info, dirs) // Создаём переменные окружения для сборки | ||||||
|  |  | ||||||
| 	fakeroot := handlers.FakerootExecHandler(2 * time.Second) | 	fakeroot := handlers.FakerootExecHandler(2 * time.Second) // Настраиваем "fakeroot" для выполнения | ||||||
| 	runner, err := interp.New( | 	runner, err := interp.New( | ||||||
| 		interp.Env(expand.ListEnviron(env...)), | 		interp.Env(expand.ListEnviron(env...)), // Устанавливаем окружение | ||||||
| 		interp.StdIO(os.Stdin, os.Stdout, os.Stderr), | 		interp.StdIO(os.Stdin, os.Stdout, os.Stderr), // Устанавливаем стандартный ввод-вывод | ||||||
| 		interp.ExecHandler(helpers.Helpers.ExecHandler(fakeroot)), | 		interp.ExecHandler(helpers.Helpers.ExecHandler(fakeroot)), // Обрабатываем выполнение через fakeroot | ||||||
| 	) | 	) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	err = runner.Run(ctx, fl) | 	err = runner.Run(ctx, fl) // Запускаем скрипт | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return decoder.New(info, runner), nil | 	return decoder.New(info, runner), nil // Возвращаем новый декодер | ||||||
| } | } | ||||||
|  |  | ||||||
| // prepareDirs prepares the directories for building. | // Функция prepareDirs подготавливает директории для сборки. | ||||||
| func prepareDirs(dirs types.Directories) error { | func prepareDirs(dirs types.Directories) error { | ||||||
| 	err := os.RemoveAll(dirs.BaseDir) | 	err := os.RemoveAll(dirs.BaseDir) // Удаляем базовую директорию, если она существует | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	err = os.MkdirAll(dirs.SrcDir, 0o755) | 	err = os.MkdirAll(dirs.SrcDir, 0o755) // Создаем директорию для источников | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	return os.MkdirAll(dirs.PkgDir, 0o755) | 	return os.MkdirAll(dirs.PkgDir, 0o755) // Создаем директорию для пакетов | ||||||
| } | } | ||||||
|  |  | ||||||
| // performChecks checks various things on the system to ensure that the package can be installed. | // Функция performChecks проверяет различные аспекты в системе, чтобы убедиться, что пакет может быть установлен. | ||||||
| func performChecks(ctx context.Context, vars *types.BuildVars, interactive bool, installed map[string]string) (bool, error) { | func performChecks(ctx context.Context, vars *types.BuildVars, interactive bool, installed map[string]string) (bool, error) { | ||||||
| 	log := loggerctx.From(ctx) | 	log := loggerctx.From(ctx) | ||||||
| 	if !cpu.IsCompatibleWith(cpu.Arch(), vars.Architectures) { | 	if !cpu.IsCompatibleWith(cpu.Arch(), vars.Architectures) { // Проверяем совместимость архитектуры | ||||||
| 		cont, err := cliutils.YesNoPrompt(ctx, "Your system's CPU architecture doesn't match this package. Do you want to build anyway?", interactive, true) | 		cont, err := cliutils.YesNoPrompt(ctx, "Your system's CPU architecture doesn't match this package. Do you want to build anyway?", interactive, true) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return false, err | 			return false, err | ||||||
| @@ -316,7 +326,7 @@ func performChecks(ctx context.Context, vars *types.BuildVars, interactive bool, | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if instVer, ok := installed[vars.Name]; ok { | 	if instVer, ok := installed[vars.Name]; ok { // Если пакет уже установлен, выводим предупреждение | ||||||
| 		log.Warn("This package is already installed"). | 		log.Warn("This package is already installed"). | ||||||
| 			Str("name", vars.Name). | 			Str("name", vars.Name). | ||||||
| 			Str("version", instVer). | 			Str("version", instVer). | ||||||
| @@ -326,33 +336,33 @@ func performChecks(ctx context.Context, vars *types.BuildVars, interactive bool, | |||||||
| 	return true, nil | 	return true, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // installBuildDeps installs any build dependencies that aren't already installed and returns | // Функция installBuildDeps устанавливает все зависимости сборки, которые еще не установлены, и возвращает | ||||||
| // a slice containing the names of all the packages it installed. | // срез, содержащий имена всех установленных пакетов. | ||||||
| func installBuildDeps(ctx context.Context, vars *types.BuildVars, opts types.BuildOpts, installed map[string]string) ([]string, error) { | func installBuildDeps(ctx context.Context, vars *types.BuildVars, opts types.BuildOpts, installed map[string]string) ([]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) | 		found, notFound, err := repos.FindPkgs(ctx, vars.BuildDepends) // Находим пакеты-зависимости | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return nil, err | 			return nil, err | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		found = removeAlreadyInstalled(found, installed) | 		found = removeAlreadyInstalled(found, installed) // Убираем уже установленные зависимости | ||||||
|  |  | ||||||
| 		log.Info("Installing build dependencies").Send() | 		log.Info("Installing build dependencies").Send() // Логгируем установку зависимостей | ||||||
|  |  | ||||||
| 		flattened := cliutils.FlattenPkgs(ctx, found, "install", opts.Interactive) | 		flattened := cliutils.FlattenPkgs(ctx, found, "install", opts.Interactive) // Уплощаем список зависимостей | ||||||
| 		buildDeps = packageNames(flattened) | 		buildDeps = packageNames(flattened) | ||||||
| 		InstallPkgs(ctx, flattened, notFound, opts) | 		InstallPkgs(ctx, flattened, notFound, opts) // Устанавливаем пакеты | ||||||
| 	} | 	} | ||||||
| 	return buildDeps, nil | 	return buildDeps, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // installOptDeps asks the user which, if any, optional dependencies they want to install. | // Функция installOptDeps спрашивает у пользователя, какие, если таковые имеются, опциональные зависимости он хочет установить. | ||||||
| // If the user chooses to install any optional dependencies, it performs the installation. | // Если пользователь решает установить какие-либо опциональные зависимости, выполняется их установка. | ||||||
| func installOptDeps(ctx context.Context, vars *types.BuildVars, opts types.BuildOpts, installed map[string]string) error { | func installOptDeps(ctx context.Context, vars *types.BuildVars, opts types.BuildOpts, installed map[string]string) error { | ||||||
| 	if len(vars.OptDepends) > 0 { | 	if len(vars.OptDepends) > 0 { | ||||||
| 		optDeps, err := cliutils.ChooseOptDepends(ctx, vars.OptDepends, "install", opts.Interactive) | 		optDeps, err := cliutils.ChooseOptDepends(ctx, vars.OptDepends, "install", opts.Interactive) // Пользователя просят выбрать опциональные зависимости | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| @@ -361,63 +371,63 @@ func installOptDeps(ctx context.Context, vars *types.BuildVars, opts types.Build | |||||||
| 			return nil | 			return nil | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		found, notFound, err := repos.FindPkgs(ctx, optDeps) | 		found, notFound, err := repos.FindPkgs(ctx, optDeps) // Находим опциональные зависимости | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		found = removeAlreadyInstalled(found, installed) | 		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) // Устанавливаем выбранные пакеты | ||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // buildALRDeps builds all the ALR dependencies of the package. It returns the paths and names | // Функция buildALRDeps собирает все ALR зависимости пакета. Возвращает пути и имена | ||||||
| // of the packages it built, as well as all the dependencies it didn't find in the ALR repo so | // пакетов, которые она собрала, а также все зависимости, которые не были найдены в ALR репозитории, | ||||||
| // they can be installed from the system repos. | // чтобы они могли быть установлены из системных репозиториев. | ||||||
| func buildALRDeps(ctx context.Context, opts types.BuildOpts, vars *types.BuildVars) (builtPaths, builtNames, repoDeps []string, err error) { | func buildALRDeps(ctx context.Context, opts types.BuildOpts, vars *types.BuildVars) (builtPaths, builtNames, repoDeps []string, err error) { | ||||||
| 	log := loggerctx.From(ctx) | 	log := loggerctx.From(ctx) | ||||||
| 	if len(vars.Depends) > 0 { | 	if len(vars.Depends) > 0 { | ||||||
| 		log.Info("Installing dependencies").Send() | 		log.Info("Installing dependencies").Send() | ||||||
|  |  | ||||||
| 		found, notFound, err := repos.FindPkgs(ctx, vars.Depends) | 		found, notFound, err := repos.FindPkgs(ctx, vars.Depends) // Поиск зависимостей | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return nil, nil, nil, err | 			return nil, nil, nil, err | ||||||
| 		} | 		} | ||||||
| 		repoDeps = notFound | 		repoDeps = notFound | ||||||
|  |  | ||||||
| 		// If there are multiple options for some packages, flatten them all into a single slice |         // Если для некоторых пакетов есть несколько опций, упрощаем их все в один срез | ||||||
| 		pkgs := cliutils.FlattenPkgs(ctx, found, "install", opts.Interactive) | 		pkgs := cliutils.FlattenPkgs(ctx, found, "install", opts.Interactive) | ||||||
| 		scripts := GetScriptPaths(ctx, pkgs) | 		scripts := GetScriptPaths(ctx, pkgs) | ||||||
| 		for _, script := range scripts { | 		for _, script := range scripts { | ||||||
| 			newOpts := opts | 			newOpts := opts | ||||||
| 			newOpts.Script = script | 			newOpts.Script = script | ||||||
|  |  | ||||||
| 			// Build the dependency |             // Собираем зависимости | ||||||
| 			pkgPaths, pkgNames, err := BuildPackage(ctx, newOpts) | 			pkgPaths, pkgNames, err := BuildPackage(ctx, newOpts) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return nil, nil, nil, err | 				return nil, nil, nil, err | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			// Append the paths of all the built packages to builtPaths |             // Добавляем пути всех собранных пакетов в builtPaths | ||||||
| 			builtPaths = append(builtPaths, pkgPaths...) | 			builtPaths = append(builtPaths, pkgPaths...) | ||||||
| 			// Append the names of all the built packages to builtNames |             // Добавляем пути всех собранных пакетов в builtPaths | ||||||
| 			builtNames = append(builtNames, pkgNames...) | 			builtNames = append(builtNames, pkgNames...) | ||||||
| 			// Append the name of the current package to builtNames |             // Добавляем имя текущего пакета в builtNames | ||||||
| 			builtNames = append(builtNames, filepath.Base(filepath.Dir(script))) | 			builtNames = append(builtNames, filepath.Base(filepath.Dir(script))) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Remove any potential duplicates, which can be introduced if |     // Удаляем возможные дубликаты, которые могут быть введены, если | ||||||
| 	// several of the dependencies depend on the same packages. |     // несколько зависимостей зависят от одних и тех же пакетов. | ||||||
| 	repoDeps = removeDuplicates(repoDeps) | 	repoDeps = removeDuplicates(repoDeps) | ||||||
| 	builtPaths = removeDuplicates(builtPaths) | 	builtPaths = removeDuplicates(builtPaths) | ||||||
| 	builtNames = removeDuplicates(builtNames) | 	builtNames = removeDuplicates(builtNames) | ||||||
| 	return builtPaths, builtNames, repoDeps, nil | 	return builtPaths, builtNames, repoDeps, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // executeFunctions executes the special ALR functions, such as version(), prepare(), etc. | // Функция executeFunctions выполняет специальные функции ALR, такие как version(), prepare() и т.д. | ||||||
| func executeFunctions(ctx context.Context, dec *decoder.Decoder, dirs types.Directories, vars *types.BuildVars) (err error) { | func executeFunctions(ctx context.Context, dec *decoder.Decoder, dirs types.Directories, vars *types.BuildVars) (err error) { | ||||||
| 	log := loggerctx.From(ctx) | 	log := loggerctx.From(ctx) | ||||||
| 	version, ok := dec.GetFunc("version") | 	version, ok := dec.GetFunc("version") | ||||||
| @@ -465,22 +475,34 @@ func executeFunctions(ctx context.Context, dec *decoder.Decoder, dirs types.Dire | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	packageFn, ok := dec.GetFunc("package") |     // Выполнение всех функций, начинающихся с package_ | ||||||
| 	if ok { |     for { | ||||||
| 		log.Info("Executing package()").Send() |         packageFn, ok := dec.GetFunc("package") | ||||||
|  |         if ok { | ||||||
|  |             log.Info("Executing package()").Send() | ||||||
|  |             err = packageFn(ctx, interp.Dir(dirs.SrcDir)) | ||||||
|  |             if err != nil { | ||||||
|  |                 return err | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
| 		err = packageFn(ctx, interp.Dir(dirs.SrcDir)) |         // Проверка на наличие дополнительных функций package_* | ||||||
| 		if err != nil { |         packageFuncName := "package_" | ||||||
| 			return err |         if packageFunc, ok := dec.GetFunc(packageFuncName); ok { | ||||||
| 		} |             log.Info("Executing " + packageFuncName).Send() | ||||||
| 	} else { |             err = packageFunc(ctx, interp.Dir(dirs.SrcDir)) | ||||||
| 		log.Fatal("The package() function is required").Send() |             if err != nil { | ||||||
| 	} |                 return err | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             break // Если больше нет функций package_*, выходим из цикла | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // buildPkgMetadata builds the metadata for the package that's going to be built. | // Функция buildPkgMetadata создает метаданные для пакета, который будет собран. | ||||||
| func buildPkgMetadata(vars *types.BuildVars, dirs types.Directories, pkgFormat string, deps []string) (*nfpm.Info, error) { | func buildPkgMetadata(vars *types.BuildVars, dirs types.Directories, pkgFormat string, deps []string) (*nfpm.Info, error) { | ||||||
| 	pkgInfo := &nfpm.Info{ | 	pkgInfo := &nfpm.Info{ | ||||||
| 		Name:        vars.Name, | 		Name:        vars.Name, | ||||||
| @@ -501,7 +523,7 @@ func buildPkgMetadata(vars *types.BuildVars, dirs types.Directories, pkgFormat s | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if pkgFormat == "apk" { | 	if pkgFormat == "apk" { | ||||||
| 		// Alpine refuses to install packages that provide themselves, so remove any such provides |         // Alpine отказывается устанавливать пакеты, которые предоставляют сами себя, поэтому удаляем такие элементы | ||||||
| 		pkgInfo.Overridables.Provides = slices.DeleteFunc(pkgInfo.Overridables.Provides, func(s string) bool { | 		pkgInfo.Overridables.Provides = slices.DeleteFunc(pkgInfo.Overridables.Provides, func(s string) bool { | ||||||
| 			return s == pkgInfo.Name | 			return s == pkgInfo.Name | ||||||
| 		}) | 		}) | ||||||
| @@ -526,8 +548,8 @@ func buildPkgMetadata(vars *types.BuildVars, dirs types.Directories, pkgFormat s | |||||||
| 	return pkgInfo, nil | 	return pkgInfo, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // buildContents builds the contents section of the package, which contains the files | // Функция buildContents создает секцию содержимого пакета, которая содержит файлы, | ||||||
| // that will be placed into the final package. | // которые будут включены в конечный пакет. | ||||||
| func buildContents(vars *types.BuildVars, dirs types.Directories) ([]*files.Content, error) { | func buildContents(vars *types.BuildVars, dirs types.Directories) ([]*files.Content, error) { | ||||||
| 	contents := []*files.Content{} | 	contents := []*files.Content{} | ||||||
| 	err := filepath.Walk(dirs.PkgDir, func(path string, fi os.FileInfo, err error) error { | 	err := filepath.Walk(dirs.PkgDir, func(path string, fi os.FileInfo, err error) error { | ||||||
| @@ -539,7 +561,7 @@ func buildContents(vars *types.BuildVars, dirs types.Directories) ([]*files.Cont | |||||||
| 				return err | 				return err | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			// If the directory is empty, skip it |             // Если директория пустая, пропускаем её | ||||||
| 			_, err = f.Readdirnames(1) | 			_, err = f.Readdirnames(1) | ||||||
| 			if err != io.EOF { | 			if err != io.EOF { | ||||||
| 				return nil | 				return nil | ||||||
| @@ -556,13 +578,13 @@ func buildContents(vars *types.BuildVars, dirs types.Directories) ([]*files.Cont | |||||||
|  |  | ||||||
| 			return f.Close() | 			return f.Close() | ||||||
| 		} | 		} | ||||||
|  |         // Если файл является символической ссылкой, прорабатываем это | ||||||
| 		if fi.Mode()&os.ModeSymlink != 0 { | 		if fi.Mode()&os.ModeSymlink != 0 { | ||||||
| 			link, err := os.Readlink(path) | 			link, err := os.Readlink(path) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return err | 				return err | ||||||
| 			} | 			} | ||||||
| 			// Remove pkgdir from the symlink's path |             // Удаляем pkgdir из пути символической ссылки | ||||||
| 			link = strings.TrimPrefix(link, dirs.PkgDir) | 			link = strings.TrimPrefix(link, dirs.PkgDir) | ||||||
|  |  | ||||||
| 			contents = append(contents, &files.Content{ | 			contents = append(contents, &files.Content{ | ||||||
| @@ -577,7 +599,7 @@ func buildContents(vars *types.BuildVars, dirs types.Directories) ([]*files.Cont | |||||||
|  |  | ||||||
| 			return nil | 			return nil | ||||||
| 		} | 		} | ||||||
|  |         // Обрабатываем обычные файлы | ||||||
| 		fileContent := &files.Content{ | 		fileContent := &files.Content{ | ||||||
| 			Source:      path, | 			Source:      path, | ||||||
| 			Destination: trimmed, | 			Destination: trimmed, | ||||||
| @@ -588,7 +610,7 @@ func buildContents(vars *types.BuildVars, dirs types.Directories) ([]*files.Cont | |||||||
| 			}, | 			}, | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// If the file is supposed to be backed up, set its type to config|noreplace |         // Если файл должен быть сохранен, установите его тип как config|noreplace | ||||||
| 		if slices.Contains(vars.Backup, trimmed) { | 		if slices.Contains(vars.Backup, trimmed) { | ||||||
| 			fileContent.Type = "config|noreplace" | 			fileContent.Type = "config|noreplace" | ||||||
| 		} | 		} | ||||||
| @@ -600,8 +622,8 @@ func buildContents(vars *types.BuildVars, dirs types.Directories) ([]*files.Cont | |||||||
| 	return contents, err | 	return contents, err | ||||||
| } | } | ||||||
|  |  | ||||||
| // removeBuildDeps asks the user if they'd like to remove the build dependencies that were | // Функция removeBuildDeps спрашивает у пользователя, хочет ли он удалить зависимости, | ||||||
| // installed by installBuildDeps. If so, it uses the package manager to do that. | // установленные для сборки. Если да, использует менеджер пакетов для их удаления. | ||||||
| func removeBuildDeps(ctx context.Context, buildDeps []string, opts types.BuildOpts) error { | func removeBuildDeps(ctx context.Context, buildDeps []string, opts types.BuildOpts) error { | ||||||
| 	if len(buildDeps) > 0 { | 	if len(buildDeps) > 0 { | ||||||
| 		remove, err := cliutils.YesNoPrompt(ctx, "Would you like to remove the build dependencies?", opts.Interactive, false) | 		remove, err := cliutils.YesNoPrompt(ctx, "Would you like to remove the build dependencies?", opts.Interactive, false) | ||||||
| @@ -625,8 +647,8 @@ func removeBuildDeps(ctx context.Context, buildDeps []string, opts types.BuildOp | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // checkForBuiltPackage tries to detect a previously-built package and returns its path | // Функция checkForBuiltPackage пытается обнаружить ранее собранный пакет и вернуть его путь | ||||||
| // and true if it finds one. If it doesn't find it, it returns "", false, nil. | // и true, если нашла. Если нет, возвратит "", false, nil. | ||||||
| func checkForBuiltPackage(mgr manager.Manager, vars *types.BuildVars, pkgFormat, baseDir string) (string, bool, error) { | func checkForBuiltPackage(mgr manager.Manager, vars *types.BuildVars, pkgFormat, baseDir string) (string, bool, error) { | ||||||
| 	filename, err := pkgFileName(vars, pkgFormat) | 	filename, err := pkgFileName(vars, pkgFormat) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -643,8 +665,8 @@ func checkForBuiltPackage(mgr manager.Manager, vars *types.BuildVars, pkgFormat, | |||||||
| 	return pkgPath, true, nil | 	return pkgPath, true, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // pkgFileName returns the filename of the package if it were to be built. | // Функция pkgFileName возвращает имя файла пакета, если оно было бы создано. | ||||||
| // This is used to check if the package has already been built. | // Это используется для проверки, был ли пакет уже собран. | ||||||
| func pkgFileName(vars *types.BuildVars, pkgFormat string) (string, error) { | func pkgFileName(vars *types.BuildVars, pkgFormat string) (string, error) { | ||||||
| 	pkgInfo := &nfpm.Info{ | 	pkgInfo := &nfpm.Info{ | ||||||
| 		Name:    vars.Name, | 		Name:    vars.Name, | ||||||
| @@ -662,8 +684,8 @@ func pkgFileName(vars *types.BuildVars, pkgFormat string) (string, error) { | |||||||
| 	return packager.ConventionalFileName(pkgInfo), nil | 	return packager.ConventionalFileName(pkgInfo), nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // getPkgFormat returns the package format of the package manager, | // Функция getPkgFormat возвращает формат пакета из менеджера пакетов, | ||||||
| // or ALR_PKG_FORMAT if that's set. | // или ALR_PKG_FORMAT, если он установлен. | ||||||
| func getPkgFormat(mgr manager.Manager) string { | func getPkgFormat(mgr manager.Manager) string { | ||||||
| 	pkgFormat := mgr.Format() | 	pkgFormat := mgr.Format() | ||||||
| 	if format, ok := os.LookupEnv("ALR_PKG_FORMAT"); ok { | 	if format, ok := os.LookupEnv("ALR_PKG_FORMAT"); ok { | ||||||
| @@ -672,8 +694,8 @@ func getPkgFormat(mgr manager.Manager) string { | |||||||
| 	return pkgFormat | 	return pkgFormat | ||||||
| } | } | ||||||
|  |  | ||||||
| // createBuildEnvVars creates the environment variables that will be set in the | // Функция createBuildEnvVars создает переменные окружения, которые будут установлены | ||||||
| // build script when it's executed. | // в скрипте сборки при его выполнении. | ||||||
| func createBuildEnvVars(info *distro.OSRelease, dirs types.Directories) []string { | func createBuildEnvVars(info *distro.OSRelease, dirs types.Directories) []string { | ||||||
| 	env := os.Environ() | 	env := os.Environ() | ||||||
|  |  | ||||||
| @@ -703,7 +725,7 @@ func createBuildEnvVars(info *distro.OSRelease, dirs types.Directories) []string | |||||||
| 	return env | 	return env | ||||||
| } | } | ||||||
|  |  | ||||||
| // getSources downloads the sources from the script. | // Функция getSources загружает исходники скрипта. | ||||||
| func getSources(ctx context.Context, dirs types.Directories, bv *types.BuildVars) error { | func getSources(ctx context.Context, dirs types.Directories, bv *types.BuildVars) error { | ||||||
| 	log := loggerctx.From(ctx) | 	log := loggerctx.From(ctx) | ||||||
| 	if len(bv.Sources) != len(bv.Checksums) { | 	if len(bv.Sources) != len(bv.Checksums) { | ||||||
| @@ -720,9 +742,9 @@ func getSources(ctx context.Context, dirs types.Directories, bv *types.BuildVars | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if !strings.EqualFold(bv.Checksums[i], "SKIP") { | 		if !strings.EqualFold(bv.Checksums[i], "SKIP") { | ||||||
| 			// If the checksum contains a colon, use the part before the colon |             // Если контрольная сумма содержит двоеточие, используйте часть до двоеточия | ||||||
| 			// as the algorithm and the part after as the actual checksum. |             // как алгоритм, а часть после как фактическую контрольную сумму. | ||||||
| 			// Otherwise, use the default sha256 with the whole string as the checksum. |             // В противном случае используйте sha256 по умолчанию с целой строкой как контрольной суммой. | ||||||
| 			algo, hashData, ok := strings.Cut(bv.Checksums[i], ":") | 			algo, hashData, ok := strings.Cut(bv.Checksums[i], ":") | ||||||
| 			if ok { | 			if ok { | ||||||
| 				checksum, err := hex.DecodeString(hashData) | 				checksum, err := hex.DecodeString(hashData) | ||||||
| @@ -749,7 +771,7 @@ func getSources(ctx context.Context, dirs types.Directories, bv *types.BuildVars | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // setScripts adds any hook scripts to the package metadata. | // Функция setScripts добавляет скрипты-перехватчики к метаданным пакета. | ||||||
| func setScripts(vars *types.BuildVars, info *nfpm.Info, scriptDir string) { | func setScripts(vars *types.BuildVars, info *nfpm.Info, scriptDir string) { | ||||||
| 	if vars.Scripts.PreInstall != "" { | 	if vars.Scripts.PreInstall != "" { | ||||||
| 		info.Scripts.PreInstall = filepath.Join(scriptDir, vars.Scripts.PreInstall) | 		info.Scripts.PreInstall = filepath.Join(scriptDir, vars.Scripts.PreInstall) | ||||||
| @@ -786,8 +808,8 @@ func setScripts(vars *types.BuildVars, info *nfpm.Info, scriptDir string) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // setVersion changes the version variable in the script runner. | // Функция setVersion изменяет переменную версии в скрипте runner. | ||||||
| // It's used to set the version to the output of the version() function. | // Она используется для установки версии на вывод функции version(). | ||||||
| func setVersion(ctx context.Context, r *interp.Runner, to string) error { | func setVersion(ctx context.Context, r *interp.Runner, to string) error { | ||||||
| 	fl, err := syntax.NewParser().Parse(strings.NewReader("version='"+to+"'"), "") | 	fl, err := syntax.NewParser().Parse(strings.NewReader("version='"+to+"'"), "") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -796,7 +818,7 @@ func setVersion(ctx context.Context, r *interp.Runner, to string) error { | |||||||
| 	return r.Run(ctx, fl) | 	return r.Run(ctx, fl) | ||||||
| } | } | ||||||
|  |  | ||||||
| // removeAlreadyInstalled returns a map without any dependencies that are already installed | // Функция removeAlreadyInstalled возвращает карту без каких-либо зависимостей, которые уже установлены. | ||||||
| func removeAlreadyInstalled(found map[string][]db.Package, installed map[string]string) map[string][]db.Package { | func removeAlreadyInstalled(found map[string][]db.Package, installed map[string]string) map[string][]db.Package { | ||||||
| 	filteredPackages := make(map[string][]db.Package) | 	filteredPackages := make(map[string][]db.Package) | ||||||
|  |  | ||||||
| @@ -813,7 +835,7 @@ func removeAlreadyInstalled(found map[string][]db.Package, installed map[string] | |||||||
| 	return filteredPackages | 	return filteredPackages | ||||||
| } | } | ||||||
|  |  | ||||||
| // packageNames returns the names of all the given packages | // Функция packageNames возвращает имена всех предоставленных пакетов. | ||||||
| func packageNames(pkgs []db.Package) []string { | func packageNames(pkgs []db.Package) []string { | ||||||
| 	names := make([]string, len(pkgs)) | 	names := make([]string, len(pkgs)) | ||||||
| 	for i, p := range pkgs { | 	for i, p := range pkgs { | ||||||
| @@ -822,7 +844,7 @@ func packageNames(pkgs []db.Package) []string { | |||||||
| 	return names | 	return names | ||||||
| } | } | ||||||
|  |  | ||||||
| // removeDuplicates removes any duplicates from the given slice | // Функция removeDuplicates убирает любые дубликаты из предоставленного среза. | ||||||
| func removeDuplicates(slice []string) []string { | func removeDuplicates(slice []string) []string { | ||||||
| 	seen := map[string]struct{}{} | 	seen := map[string]struct{}{} | ||||||
| 	result := []string{} | 	result := []string{} | ||||||
|   | |||||||
| @@ -1,19 +1,30 @@ | |||||||
| /* | /* | ||||||
|  * ALR - Any Linux Repository |  * ALR - Any Linux Repository | ||||||
|  |  * ALR - Любой Linux Репозиторий | ||||||
|  * Copyright (C) 2024 Евгений Храмов |  * Copyright (C) 2024 Евгений Храмов | ||||||
|  * |  * | ||||||
|  * This program is free software: you can redistribute it and/or modify |  * 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 |  * it under the terms of the GNU General Public License as published by | ||||||
|  |  * на условиях GNU General Public License, опубликованной | ||||||
|  * the Free Software Foundation, either version 3 of the License, or |  * the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  * Free Software Foundation, либо версии 3 лицензии, либо | ||||||
|  * (at your option) any later version. |  * (at your option) any later version. | ||||||
|  |  * (по вашему усмотрению) любой более поздней версии. | ||||||
|  * |  * | ||||||
|  * This program is distributed in the hope that it will be useful, |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * Это программное обеспечение распространяется в надежде, что оно будет полезным, | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * но БЕЗ КАКОЙ-ЛИБО ГАРАНТИИ; даже без подразумеваемой гарантии | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * КОММЕРЧЕСКОЙ ПРИГОДНОСТИ или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННОЙ ЦЕЛИ. | ||||||
|  * GNU General Public License for more details. |  * GNU General Public License for more details. | ||||||
|  |  * Подробности смотрите в GNU General Public License. | ||||||
|  * |  * | ||||||
|  * You should have received a copy of the GNU General Public License |  * You should have received a copy of the GNU General Public License | ||||||
|  |  * Вы должны были получить копию GNU General Public License | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. |  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * вместе с этой программой. Если нет, посмотрите <http://www.gnu.org/licenses/>. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| package build | package build | ||||||
| @@ -28,45 +39,53 @@ import ( | |||||||
| 	"plemya-x.ru/alr/pkg/loggerctx" | 	"plemya-x.ru/alr/pkg/loggerctx" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // InstallPkgs installs native packages via the package manager, | // InstallPkgs устанавливает нативные пакеты с использованием менеджера пакетов, | ||||||
| // then builds and installs the ALR packages | // затем строит и устанавливает пакеты ALR | ||||||
| func InstallPkgs(ctx context.Context, alrPkgs []db.Package, nativePkgs []string, opts types.BuildOpts) { | func InstallPkgs(ctx context.Context, alrPkgs []db.Package, nativePkgs []string, opts types.BuildOpts) { | ||||||
| 	log := loggerctx.From(ctx) | 	log := loggerctx.From(ctx) // Инициализируем логгер из контекста | ||||||
|  |  | ||||||
| 	if len(nativePkgs) > 0 { | 	if len(nativePkgs) > 0 { | ||||||
| 		err := opts.Manager.Install(nil, nativePkgs...) | 		err := opts.Manager.Install(nil, nativePkgs...) | ||||||
|  |         // Если есть нативные пакеты, выполняем их установку | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Fatal("Error installing native packages").Err(err).Send() | 			log.Fatal("Error installing native packages").Err(err).Send() | ||||||
|  |             // Логируем и завершаем выполнение при ошибке | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	InstallScripts(ctx, GetScriptPaths(ctx, alrPkgs), opts) | 	InstallScripts(ctx, GetScriptPaths(ctx, alrPkgs), opts) | ||||||
|  |     // Устанавливаем скрипты сборки через функцию InstallScripts | ||||||
| } | } | ||||||
|  |  | ||||||
| // GetScriptPaths returns a slice of script paths corresponding to the | // GetScriptPaths возвращает срез путей к скриптам, соответствующий | ||||||
| // given packages | // данным пакетам | ||||||
| func GetScriptPaths(ctx context.Context, pkgs []db.Package) []string { | func GetScriptPaths(ctx context.Context, pkgs []db.Package) []string { | ||||||
| 	var scripts []string | 	var scripts []string | ||||||
| 	for _, pkg := range pkgs { | 	for _, pkg := range pkgs { | ||||||
|  |         // Для каждого пакета создаем путь к скрипту сборки | ||||||
| 		scriptPath := filepath.Join(config.GetPaths(ctx).RepoDir, pkg.Repository, pkg.Name, "alr.sh") | 		scriptPath := filepath.Join(config.GetPaths(ctx).RepoDir, pkg.Repository, pkg.Name, "alr.sh") | ||||||
| 		scripts = append(scripts, scriptPath) | 		scripts = append(scripts, scriptPath) | ||||||
| 	} | 	} | ||||||
| 	return scripts | 	return scripts | ||||||
| } | } | ||||||
|  |  | ||||||
| // InstallScripts builds and installs the given alr build scripts | // InstallScripts строит и устанавливает переданные alr скрипты сборки | ||||||
| func InstallScripts(ctx context.Context, scripts []string, opts types.BuildOpts) { | func InstallScripts(ctx context.Context, scripts []string, opts types.BuildOpts) { | ||||||
| 	log := loggerctx.From(ctx) | 	log := loggerctx.From(ctx) // Получаем логгер из контекста | ||||||
| 	for _, script := range scripts { | 	for _, script := range scripts { | ||||||
| 		opts.Script = script | 		opts.Script = script // Устанавливаем текущий скрипт в опции | ||||||
| 		builtPkgs, _, err := BuildPackage(ctx, opts) | 		builtPkgs, _, err := BuildPackage(ctx, opts) | ||||||
|  |         // Выполняем сборку пакета | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Fatal("Error building package").Err(err).Send() | 			log.Fatal("Error building package").Err(err).Send() | ||||||
|  |             // Логируем и завершаем выполнение при ошибке сборки | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		err = opts.Manager.InstallLocal(nil, builtPkgs...) | 		err = opts.Manager.InstallLocal(nil, builtPkgs...) | ||||||
|  |         // Устанавливаем локально собранные пакеты | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Fatal("Error installing package").Err(err).Send() | 			log.Fatal("Error installing package").Err(err).Send() | ||||||
|  |             // Логируем и завершаем выполнение при ошибке установки | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,13 +1,20 @@ | |||||||
| package gen | package gen | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"strings" |     "strings" | ||||||
| 	"text/template" |     "text/template" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | // Определяем переменную funcs типа template.FuncMap, которая будет использоваться для | ||||||
|  | // предоставления пользовательских функций в шаблонах | ||||||
| var funcs = template.FuncMap{ | var funcs = template.FuncMap{ | ||||||
| 	"tolower": strings.ToLower, |     // Функция "tolower" использует strings.ToLower | ||||||
| 	"firstchar": func(s string) string { |     // для преобразования строки в нижний регистр | ||||||
| 		return s[:1] |     "tolower": strings.ToLower, | ||||||
| 	}, |  | ||||||
|  |     // Функция "firstchar" — это лямбда-функция, которая берет строку | ||||||
|  |     // и возвращает её первый символ | ||||||
|  |     "firstchar": func(s string) string { | ||||||
|  |         return s[:1] | ||||||
|  |     }, | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										122
									
								
								pkg/gen/pip.go
									
									
									
									
									
								
							
							
						
						
									
										122
									
								
								pkg/gen/pip.go
									
									
									
									
									
								
							| @@ -1,84 +1,98 @@ | |||||||
| package gen | package gen | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	_ "embed" |     _ "embed" // Пакет для встраивания содержимого файлов в бинарники Go, использовав откладку //go:embed | ||||||
| 	"encoding/json" |     "encoding/json" // Пакет для работы с JSON: декодирование и кодирование | ||||||
| 	"errors" |     "errors"    // Пакет для создания и обработки ошибок | ||||||
| 	"fmt" |     "fmt"       // Пакет для форматированного ввода и вывода | ||||||
| 	"io" |     "io"        // Пакет для интерфейсов ввода и вывода | ||||||
| 	"net/http" |     "net/http"  // Пакет для HTTP-клиентов и серверов | ||||||
| 	"text/template" |     "text/template" // Пакет для обработки текстовых шаблонов | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | // Используем директиву //go:embed для встраивания содержимого файла шаблона в строку pipTmpl | ||||||
|  | // Встраивание файла tmpls/pip.tmpl.sh | ||||||
| //go:embed tmpls/pip.tmpl.sh | //go:embed tmpls/pip.tmpl.sh | ||||||
| var pipTmpl string | var pipTmpl string | ||||||
|  |  | ||||||
|  | // PipOptions содержит параметры, которые будут переданы в шаблон | ||||||
| type PipOptions struct { | type PipOptions struct { | ||||||
| 	Name        string |     Name        string // Имя пакета | ||||||
| 	Version     string |     Version     string // Версия пакета | ||||||
| 	Description string |     Description string // Описание пакета | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // pypiAPIResponse представляет структуру ответа от API PyPI | ||||||
| type pypiAPIResponse struct { | type pypiAPIResponse struct { | ||||||
| 	Info pypiInfo  `json:"info"` |     Info pypiInfo  `json:"info"` // Информация о пакете | ||||||
| 	URLs []pypiURL `json:"urls"` |     URLs []pypiURL `json:"urls"` // Список URL-адресов для загрузки пакета | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Метод SourceURL ищет и возвращает URL исходного distribution для пакета, если он существует | ||||||
| func (res pypiAPIResponse) SourceURL() (pypiURL, error) { | func (res pypiAPIResponse) SourceURL() (pypiURL, error) { | ||||||
| 	for _, url := range res.URLs { |     for _, url := range res.URLs { | ||||||
| 		if url.PackageType == "sdist" { |         if url.PackageType == "sdist" { | ||||||
| 			return url, nil |             return url, nil | ||||||
| 		} |         } | ||||||
| 	} |     } | ||||||
| 	return pypiURL{}, errors.New("package doesn't have a source distribution") |     return pypiURL{}, errors.New("package doesn't have a source distribution") | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // pypiInfo содержит основную информацию о пакете, такую как имя, версия и пр. | ||||||
| type pypiInfo struct { | type pypiInfo struct { | ||||||
| 	Name     string `json:"name"` |     Name     string `json:"name"` | ||||||
| 	Version  string `json:"version"` |     Version  string `json:"version"` | ||||||
| 	Summary  string `json:"summary"` |     Summary  string `json:"summary"` | ||||||
| 	Homepage string `json:"home_page"` |     Homepage string `json:"home_page"` | ||||||
| 	License  string `json:"license"` |     License  string `json:"license"` | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // pypiURL представляет информацию об одном из доступных для загрузки URL | ||||||
| type pypiURL struct { | type pypiURL struct { | ||||||
| 	Digests     map[string]string `json:"digests"` |     Digests     map[string]string `json:"digests"` // Контрольные суммы для файлов | ||||||
| 	Filename    string            `json:"filename"` |     Filename    string            `json:"filename"` // Имя файла | ||||||
| 	PackageType string            `json:"packagetype"` |     PackageType string            `json:"packagetype"` // Тип пакета (например sdist) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Функция Pip загружает информацию о пакете из PyPI и использует шаблон для вывода информации | ||||||
| func Pip(w io.Writer, opts PipOptions) error { | func Pip(w io.Writer, opts PipOptions) error { | ||||||
| 	tmpl, err := template.New("pip"). |     // Создаем новый шаблон с добавлением функций из FuncMap | ||||||
| 		Funcs(funcs). |     tmpl, err := template.New("pip"). | ||||||
| 		Parse(pipTmpl) |         Funcs(funcs). | ||||||
| 	if err != nil { |         Parse(pipTmpl) | ||||||
| 		return err |     if err != nil { | ||||||
| 	} |         return err | ||||||
|  |     } | ||||||
|  |  | ||||||
| 	url := fmt.Sprintf( |     // Формируем URL для запроса к PyPI на основании имени и версии пакета | ||||||
| 		"https://pypi.org/pypi/%s/%s/json", |     url := fmt.Sprintf( | ||||||
| 		opts.Name, |         "https://pypi.org/pypi/%s/%s/json", | ||||||
| 		opts.Version, |         opts.Name, | ||||||
| 	) |         opts.Version, | ||||||
|  |     ) | ||||||
|  |  | ||||||
| 	res, err := http.Get(url) |     // Выполняем HTTP GET запрос к PyPI | ||||||
| 	if err != nil { |     res, err := http.Get(url) | ||||||
| 		return err |     if err != nil { | ||||||
| 	} |         return err | ||||||
| 	defer res.Body.Close() |     } | ||||||
| 	if res.StatusCode != 200 { |     defer res.Body.Close() // Закрываем тело ответа после завершения работы | ||||||
| 		return fmt.Errorf("pypi: %s", res.Status) |     if res.StatusCode != 200 { | ||||||
| 	} |         return fmt.Errorf("pypi: %s", res.Status) | ||||||
|  |     } | ||||||
|  |  | ||||||
| 	var resp pypiAPIResponse |     // Раскодируем ответ JSON от PyPI в структуру pypiAPIResponse | ||||||
| 	err = json.NewDecoder(res.Body).Decode(&resp) |     var resp pypiAPIResponse | ||||||
| 	if err != nil { |     err = json.NewDecoder(res.Body).Decode(&resp) | ||||||
| 		return err |     if err != nil { | ||||||
| 	} |         return err | ||||||
|  |     } | ||||||
|  |  | ||||||
| 	if opts.Description != "" { |     // Если в opts указано описание, используем его вместо описания из PyPI | ||||||
| 		resp.Info.Summary = opts.Description |     if opts.Description != "" { | ||||||
| 	} |         resp.Info.Summary = opts.Description | ||||||
|  |     } | ||||||
|  |  | ||||||
| 	return tmpl.Execute(w, resp) |     // Выполняем шаблон с использованием данных из resp и записываем результат в w | ||||||
|  |     return tmpl.Execute(w, resp) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,160 +1,172 @@ | |||||||
| /* | /* | ||||||
|  * ALR - Any Linux Repository |  * ALR - Any Linux Repository | ||||||
|  |  * ALR - Любой Linux Репозиторий | ||||||
|  * Copyright (C) 2024 Евгений Храмов |  * Copyright (C) 2024 Евгений Храмов | ||||||
|  * |  * | ||||||
|  * This program is free software: you can redistribute it and/or modify |  * This program является свободным: вы можете распространять его и/или изменять | ||||||
|  * it under the terms of the GNU General Public License as published by |  * на условиях GNU General Public License, опубликованной Free Software Foundation, | ||||||
|  * the Free Software Foundation, either version 3 of the License, or |  * либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии. | ||||||
|  * (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. |  * Подробности см. в GNU General Public License. | ||||||
|  * |  * | ||||||
|  * You should have received a copy of the GNU General Public License |  * Вы должны были получить копию GNU General Public License | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. |  * вместе с этой программой. Если нет, см. <http://www.gnu.org/licenses/>. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| package manager | package manager | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"bufio" |     "bufio" | ||||||
| 	"fmt" |     "fmt" | ||||||
| 	"os/exec" |     "os/exec" | ||||||
| 	"strings" |     "strings" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // DNF represents the DNF package manager | // DNF представляет менеджер пакетов DNF | ||||||
| type DNF struct { | type DNF struct { | ||||||
| 	rootCmd string |     rootCmd string  // rootCmd хранит команду, используемую для выполнения команд с правами root | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Exists проверяет, доступен ли DNF в системе, возвращает true если да | ||||||
| func (*DNF) Exists() bool { | func (*DNF) Exists() bool { | ||||||
| 	_, err := exec.LookPath("dnf") |     _, err := exec.LookPath("dnf") | ||||||
| 	return err == nil |     return err == nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Name возвращает имя менеджера пакетов, в данном случае "dnf" | ||||||
| func (*DNF) Name() string { | func (*DNF) Name() string { | ||||||
| 	return "dnf" |     return "dnf" | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Format возвращает формат пакетов "rpm", используемый DNF | ||||||
| func (*DNF) Format() string { | func (*DNF) Format() string { | ||||||
| 	return "rpm" |     return "rpm" | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // SetRootCmd устанавливает команду, используемую для выполнения операций с правами root | ||||||
| func (d *DNF) SetRootCmd(s string) { | func (d *DNF) SetRootCmd(s string) { | ||||||
| 	d.rootCmd = s |     d.rootCmd = s | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Sync выполняет upgrade всех установленных пакетов, обновляя их до более новых версий | ||||||
| func (d *DNF) Sync(opts *Opts) error { | func (d *DNF) Sync(opts *Opts) error { | ||||||
| 	opts = ensureOpts(opts) |     opts = ensureOpts(opts)  // Гарантирует, что opts не равен nil и содержит допустимые значения | ||||||
| 	cmd := d.getCmd(opts, "dnf", "upgrade") |     cmd := d.getCmd(opts, "dnf", "upgrade") | ||||||
| 	setCmdEnv(cmd) |     setCmdEnv(cmd)  // Устанавливает переменные окружения для команды | ||||||
| 	err := cmd.Run() |     err := cmd.Run()  // Выполняет команду | ||||||
| 	if err != nil { |     if err != nil { | ||||||
| 		return fmt.Errorf("dnf: sync: %w", err) |         return fmt.Errorf("dnf: sync: %w", err) | ||||||
| 	} |     } | ||||||
| 	return nil |     return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Install устанавливает указанные пакеты с помощью DNF | ||||||
| func (d *DNF) Install(opts *Opts, pkgs ...string) error { | func (d *DNF) Install(opts *Opts, pkgs ...string) error { | ||||||
| 	opts = ensureOpts(opts) |     opts = ensureOpts(opts) | ||||||
| 	cmd := d.getCmd(opts, "dnf", "install", "--allowerasing") |     cmd := d.getCmd(opts, "dnf", "install", "--allowerasing") | ||||||
| 	cmd.Args = append(cmd.Args, pkgs...) |     cmd.Args = append(cmd.Args, pkgs...)  // Добавляем названия пакетов к команде | ||||||
| 	setCmdEnv(cmd) |     setCmdEnv(cmd) | ||||||
| 	err := cmd.Run() |     err := cmd.Run() | ||||||
| 	if err != nil { |     if err != nil { | ||||||
| 		return fmt.Errorf("dnf: install: %w", err) |         return fmt.Errorf("dnf: install: %w", err) | ||||||
| 	} |     } | ||||||
| 	return nil |     return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // InstallLocal расширяет метод Install для установки пакетов, расположенных локально | ||||||
| func (d *DNF) InstallLocal(opts *Opts, pkgs ...string) error { | func (d *DNF) InstallLocal(opts *Opts, pkgs ...string) error { | ||||||
| 	opts = ensureOpts(opts) |     opts = ensureOpts(opts) | ||||||
| 	return d.Install(opts, pkgs...) |     return d.Install(opts, pkgs...) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Remove удаляет указанные пакеты с помощью DNF | ||||||
| func (d *DNF) Remove(opts *Opts, pkgs ...string) error { | func (d *DNF) Remove(opts *Opts, pkgs ...string) error { | ||||||
| 	opts = ensureOpts(opts) |     opts = ensureOpts(opts) | ||||||
| 	cmd := d.getCmd(opts, "dnf", "remove") |     cmd := d.getCmd(opts, "dnf", "remove") | ||||||
| 	cmd.Args = append(cmd.Args, pkgs...) |     cmd.Args = append(cmd.Args, pkgs...) | ||||||
| 	setCmdEnv(cmd) |     setCmdEnv(cmd) | ||||||
| 	err := cmd.Run() |     err := cmd.Run() | ||||||
| 	if err != nil { |     if err != nil { | ||||||
| 		return fmt.Errorf("dnf: remove: %w", err) |         return fmt.Errorf("dnf: remove: %w", err) | ||||||
| 	} |     } | ||||||
| 	return nil |     return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Upgrade обновляет указанные пакеты до более новых версий | ||||||
| func (d *DNF) Upgrade(opts *Opts, pkgs ...string) error { | func (d *DNF) Upgrade(opts *Opts, pkgs ...string) error { | ||||||
| 	opts = ensureOpts(opts) |     opts = ensureOpts(opts) | ||||||
| 	cmd := d.getCmd(opts, "dnf", "upgrade") |     cmd := d.getCmd(opts, "dnf", "upgrade") | ||||||
| 	cmd.Args = append(cmd.Args, pkgs...) |     cmd.Args = append(cmd.Args, pkgs...) | ||||||
| 	setCmdEnv(cmd) |     setCmdEnv(cmd) | ||||||
| 	err := cmd.Run() |     err := cmd.Run() | ||||||
| 	if err != nil { |     if err != nil { | ||||||
| 		return fmt.Errorf("dnf: upgrade: %w", err) |         return fmt.Errorf("dnf: upgrade: %w", err) | ||||||
| 	} |     } | ||||||
| 	return nil |     return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // UpgradeAll обновляет все установленные пакеты | ||||||
| func (d *DNF) UpgradeAll(opts *Opts) error { | func (d *DNF) UpgradeAll(opts *Opts) error { | ||||||
| 	opts = ensureOpts(opts) |     opts = ensureOpts(opts) | ||||||
| 	cmd := d.getCmd(opts, "dnf", "upgrade") |     cmd := d.getCmd(opts, "dnf", "upgrade") | ||||||
| 	setCmdEnv(cmd) |     setCmdEnv(cmd) | ||||||
| 	err := cmd.Run() |     err := cmd.Run() | ||||||
| 	if err != nil { |     if err != nil { | ||||||
| 		return fmt.Errorf("dnf: upgradeall: %w", err) |         return fmt.Errorf("dnf: upgradeall: %w", err) | ||||||
| 	} |     } | ||||||
| 	return nil |     return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // ListInstalled возвращает список установленных пакетов и их версий | ||||||
| func (d *DNF) ListInstalled(opts *Opts) (map[string]string, error) { | func (d *DNF) ListInstalled(opts *Opts) (map[string]string, error) { | ||||||
| 	out := map[string]string{} |     out := map[string]string{} | ||||||
| 	cmd := exec.Command("rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n") |     cmd := exec.Command("rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n") | ||||||
|  |  | ||||||
| 	stdout, err := cmd.StdoutPipe() |     stdout, err := cmd.StdoutPipe() | ||||||
| 	if err != nil { |     if err != nil { | ||||||
| 		return nil, err |         return nil, err | ||||||
| 	} |     } | ||||||
|  |  | ||||||
| 	err = cmd.Start() |     err = cmd.Start() | ||||||
| 	if err != nil { |     if err != nil { | ||||||
| 		return nil, err |         return nil, err | ||||||
| 	} |     } | ||||||
|  |  | ||||||
| 	scanner := bufio.NewScanner(stdout) |     scanner := bufio.NewScanner(stdout) | ||||||
| 	for scanner.Scan() { |     for scanner.Scan() { | ||||||
| 		name, version, ok := strings.Cut(scanner.Text(), "\u200b") |         name, version, ok := strings.Cut(scanner.Text(), "\u200b") | ||||||
| 		if !ok { |         if !ok { | ||||||
| 			continue |             continue | ||||||
| 		} |         } | ||||||
| 		version = strings.TrimPrefix(version, "0:") |         version = strings.TrimPrefix(version, "0:") | ||||||
| 		out[name] = version |         out[name] = version | ||||||
| 	} |     } | ||||||
|  |  | ||||||
| 	err = scanner.Err() |     err = scanner.Err() | ||||||
| 	if err != nil { |     if err != nil { | ||||||
| 		return nil, err |         return nil, err | ||||||
| 	} |     } | ||||||
|  |  | ||||||
| 	return out, nil |     return out, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // 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 | ||||||
| 	if opts.AsRoot { |     if opts.AsRoot { | ||||||
| 		cmd = exec.Command(getRootCmd(d.rootCmd), mgrCmd) |         cmd = exec.Command(getRootCmd(d.rootCmd), mgrCmd) | ||||||
| 		cmd.Args = append(cmd.Args, opts.Args...) |         cmd.Args = append(cmd.Args, opts.Args...) | ||||||
| 		cmd.Args = append(cmd.Args, args...) |         cmd.Args = append(cmd.Args, args...) | ||||||
| 	} else { |     } else { | ||||||
| 		cmd = exec.Command(mgrCmd, args...) |         cmd = exec.Command(mgrCmd, args...) | ||||||
| 	} |     } | ||||||
|  |  | ||||||
| 	if opts.NoConfirm { |     if opts.NoConfirm { | ||||||
| 		cmd.Args = append(cmd.Args, "-y") |         cmd.Args = append(cmd.Args, "-y")  // Добавляет параметр автоматического подтверждения (-y) | ||||||
| 	} |     } | ||||||
|  |  | ||||||
| 	return cmd |     return cmd | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user