2025-01-18 16:30:02 +00:00
|
|
|
|
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
|
|
|
|
|
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
|
|
|
|
|
//
|
|
|
|
|
// 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/>.
|
2024-01-22 10:36:06 +00:00
|
|
|
|
|
|
|
|
|
package build
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"context"
|
|
|
|
|
"encoding/hex"
|
2025-02-12 15:34:35 +00:00
|
|
|
|
"errors"
|
2024-01-22 10:36:06 +00:00
|
|
|
|
"fmt"
|
2025-01-22 13:37:16 +00:00
|
|
|
|
"log/slog"
|
2024-01-22 10:36:06 +00:00
|
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"strings"
|
|
|
|
|
"time"
|
|
|
|
|
|
2025-01-28 21:19:02 +00:00
|
|
|
|
"github.com/google/shlex"
|
2025-02-12 15:34:35 +00:00
|
|
|
|
"github.com/goreleaser/nfpm/v2"
|
2025-01-22 13:37:16 +00:00
|
|
|
|
"github.com/leonelquinteros/gotext"
|
2024-11-15 13:25:09 +00:00
|
|
|
|
"mvdan.cc/sh/v3/expand"
|
|
|
|
|
"mvdan.cc/sh/v3/interp"
|
|
|
|
|
"mvdan.cc/sh/v3/syntax"
|
2024-01-22 10:36:06 +00:00
|
|
|
|
|
2025-01-20 16:58:24 +00:00
|
|
|
|
"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils"
|
|
|
|
|
"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
|
|
|
|
|
"gitea.plemya-x.ru/Plemya-x/ALR/internal/cpu"
|
|
|
|
|
"gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
|
|
|
|
|
"gitea.plemya-x.ru/Plemya-x/ALR/internal/dl"
|
2025-01-31 13:56:26 +00:00
|
|
|
|
"gitea.plemya-x.ru/Plemya-x/ALR/internal/dlcache"
|
2025-01-20 16:58:24 +00:00
|
|
|
|
"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/decoder"
|
|
|
|
|
"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/handlers"
|
|
|
|
|
"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/helpers"
|
|
|
|
|
"gitea.plemya-x.ru/Plemya-x/ALR/internal/types"
|
|
|
|
|
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
|
|
|
|
|
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager"
|
2024-01-22 10:36:06 +00:00
|
|
|
|
)
|
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
type PackageFinder interface {
|
|
|
|
|
FindPkgs(ctx context.Context, pkgs []string) (map[string][]db.Package, []string, error)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Config interface {
|
|
|
|
|
GetPaths(ctx context.Context) *config.Paths
|
|
|
|
|
PagerStyle(ctx context.Context) string
|
|
|
|
|
}
|
2024-01-22 10:36:06 +00:00
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
type Builder struct {
|
|
|
|
|
ctx context.Context
|
|
|
|
|
opts types.BuildOpts
|
|
|
|
|
info *distro.OSRelease
|
|
|
|
|
repos PackageFinder
|
|
|
|
|
config Config
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewBuilder(
|
|
|
|
|
ctx context.Context,
|
|
|
|
|
opts types.BuildOpts,
|
|
|
|
|
repos PackageFinder,
|
|
|
|
|
info *distro.OSRelease,
|
|
|
|
|
config Config,
|
|
|
|
|
) *Builder {
|
|
|
|
|
return &Builder{
|
|
|
|
|
ctx: ctx,
|
|
|
|
|
opts: opts,
|
|
|
|
|
info: info,
|
|
|
|
|
repos: repos,
|
|
|
|
|
config: config,
|
2024-01-22 10:36:06 +00:00
|
|
|
|
}
|
2025-02-12 15:34:35 +00:00
|
|
|
|
}
|
2024-01-22 10:36:06 +00:00
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
func (b *Builder) UpdateOptsFromPkg(pkg *db.Package, packages []string) {
|
|
|
|
|
repodir := b.config.GetPaths(b.ctx).RepoDir
|
|
|
|
|
if pkg.BasePkgName != "" {
|
|
|
|
|
b.opts.Script = filepath.Join(repodir, pkg.Repository, pkg.BasePkgName, "alr.sh")
|
|
|
|
|
b.opts.Packages = packages
|
|
|
|
|
} else {
|
|
|
|
|
b.opts.Script = filepath.Join(repodir, pkg.Repository, pkg.Name, "alr.sh")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (b *Builder) BuildPackage(ctx context.Context) ([]string, []string, error) {
|
|
|
|
|
fl, err := readScript(b.opts.Script)
|
2024-01-22 10:36:06 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-19 16:21:41 +00:00
|
|
|
|
// Первый проход предназначен для получения значений переменных и выполняется
|
|
|
|
|
// до отображения скрипта, чтобы предотвратить выполнение вредоносного кода.
|
2025-02-12 15:34:35 +00:00
|
|
|
|
basePkg, varsOfPackages, err := b.executeFirstPass(fl)
|
2024-01-22 10:36:06 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
dirs := b.getDirs(basePkg)
|
|
|
|
|
|
|
|
|
|
builtPaths := make([]string, 0)
|
2024-01-22 10:36:06 +00:00
|
|
|
|
|
2024-12-19 16:21:41 +00:00
|
|
|
|
// Если флаг opts.Clean не установлен, и пакет уже собран,
|
|
|
|
|
// возвращаем его, а не собираем заново.
|
2025-02-12 15:34:35 +00:00
|
|
|
|
if !b.opts.Clean {
|
|
|
|
|
var remainingVars []*types.BuildVars
|
|
|
|
|
|
|
|
|
|
for _, vars := range varsOfPackages {
|
|
|
|
|
builtPkgPath, ok, err := checkForBuiltPackage(
|
|
|
|
|
b.opts.Manager,
|
|
|
|
|
vars,
|
|
|
|
|
getPkgFormat(b.opts.Manager),
|
|
|
|
|
dirs.BaseDir,
|
|
|
|
|
b.info,
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ok {
|
|
|
|
|
builtPaths = append(builtPaths, builtPkgPath)
|
|
|
|
|
} else {
|
|
|
|
|
remainingVars = append(remainingVars, vars)
|
|
|
|
|
}
|
2024-01-22 10:36:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
if len(remainingVars) == 0 {
|
|
|
|
|
return builtPaths, nil, nil
|
2024-01-22 10:36:06 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-19 16:21:41 +00:00
|
|
|
|
// Спрашиваем у пользователя, хочет ли он увидеть скрипт сборки.
|
2025-02-12 15:34:35 +00:00
|
|
|
|
err = cliutils.PromptViewScript(
|
|
|
|
|
ctx,
|
|
|
|
|
b.opts.Script,
|
|
|
|
|
basePkg,
|
|
|
|
|
b.config.PagerStyle(ctx),
|
|
|
|
|
b.opts.Interactive,
|
|
|
|
|
)
|
2024-01-22 10:36:06 +00:00
|
|
|
|
if err != nil {
|
2025-01-22 13:37:16 +00:00
|
|
|
|
slog.Error(gotext.Get("Failed to prompt user to view build script"), "err", err)
|
|
|
|
|
os.Exit(1)
|
2024-01-22 10:36:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
slog.Info(gotext.Get("Building package"), "name", basePkg)
|
2024-01-22 10:36:06 +00:00
|
|
|
|
|
2024-12-19 16:21:41 +00:00
|
|
|
|
// Второй проход будет использоваться для выполнения реального кода,
|
|
|
|
|
// поэтому он не ограничен. Скрипт уже был показан
|
|
|
|
|
// пользователю к этому моменту, так что это должно быть безопасно.
|
2025-02-12 15:34:35 +00:00
|
|
|
|
dec, err := b.executeSecondPass(ctx, fl, dirs)
|
2024-01-22 10:36:06 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-16 08:32:47 +00:00
|
|
|
|
// Получаем список установленных пакетов в системе
|
2025-02-12 15:34:35 +00:00
|
|
|
|
installed, err := b.opts.Manager.ListInstalled(nil)
|
2024-01-22 10:36:06 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
for _, vars := range varsOfPackages {
|
|
|
|
|
cont, err := b.performChecks(ctx, vars, installed) // Выполняем различные проверки
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
} else if !cont {
|
|
|
|
|
os.Exit(1) // Если проверки не пройдены, выходим из программы
|
|
|
|
|
}
|
2024-01-22 10:36:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-12-19 16:21:41 +00:00
|
|
|
|
// Подготавливаем директории для сборки
|
2024-01-22 10:36:06 +00:00
|
|
|
|
err = prepareDirs(dirs)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
buildDepends := []string{}
|
|
|
|
|
optDepends := []string{}
|
|
|
|
|
depends := []string{}
|
|
|
|
|
sources := []string{}
|
|
|
|
|
checksums := []string{}
|
|
|
|
|
for _, vars := range varsOfPackages {
|
|
|
|
|
buildDepends = append(buildDepends, vars.BuildDepends...)
|
|
|
|
|
optDepends = append(optDepends, vars.OptDepends...)
|
|
|
|
|
depends = append(depends, vars.Depends...)
|
|
|
|
|
sources = append(sources, vars.Sources...)
|
|
|
|
|
checksums = append(checksums, vars.Checksums...)
|
2024-01-22 10:36:06 +00:00
|
|
|
|
}
|
2025-02-12 15:34:35 +00:00
|
|
|
|
buildDepends = removeDuplicates(buildDepends)
|
|
|
|
|
optDepends = removeDuplicates(optDepends)
|
|
|
|
|
depends = removeDuplicates(depends)
|
|
|
|
|
sources = removeDuplicates(sources)
|
|
|
|
|
checksums = removeDuplicates(checksums)
|
2024-01-22 10:36:06 +00:00
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
mergedVars := types.BuildVars{
|
|
|
|
|
Sources: sources,
|
|
|
|
|
Checksums: checksums,
|
2024-01-22 10:36:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
buildDeps, err := b.installBuildDeps(ctx, buildDepends) // Устанавливаем зависимости для сборки
|
2024-01-22 10:36:06 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
err = b.installOptDeps(ctx, optDepends) // Устанавливаем опциональные зависимости
|
2024-01-22 10:36:06 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
newBuildPaths, builtNames, repoDeps, err := b.buildALRDeps(ctx, depends) // Собираем зависимости
|
2024-01-22 10:36:06 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
builtPaths = append(builtPaths, newBuildPaths...)
|
2024-01-22 10:36:06 +00:00
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
slog.Info(gotext.Get("Downloading sources")) // Записываем в лог загрузку источников
|
2024-01-22 10:36:06 +00:00
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
err = b.getSources(ctx, dirs, &mergedVars) // Загружаем исходники
|
2024-01-22 10:36:06 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
err = b.executeFunctions(ctx, dec, dirs) // Выполняем специальные функции
|
2024-01-22 10:36:06 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
for _, vars := range varsOfPackages {
|
|
|
|
|
funcOut, err := b.executePackageFunctions(ctx, dec, dirs, vars.Name)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
2024-01-22 10:36:06 +00:00
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
slog.Info(gotext.Get("Building package metadata"), "name", basePkg)
|
2024-01-22 10:36:06 +00:00
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
pkgFormat := getPkgFormat(b.opts.Manager) // Получаем формат пакета
|
2024-01-22 10:36:06 +00:00
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
pkgInfo, err := buildPkgMetadata(ctx, vars, dirs, pkgFormat, b.info, append(repoDeps, builtNames...), funcOut.Contents) // Собираем метаданные пакета
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
packager, err := nfpm.Get(pkgFormat) // Получаем упаковщик для формата пакета
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pkgName := packager.ConventionalFileName(pkgInfo) // Получаем имя файла пакета
|
|
|
|
|
pkgPath := filepath.Join(dirs.BaseDir, pkgName) // Определяем путь к пакету
|
|
|
|
|
|
|
|
|
|
pkgFile, err := os.Create(pkgPath) // Создаём файл пакета
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
slog.Info(gotext.Get("Compressing package"), "name", pkgName) // Логгируем сжатие пакета
|
|
|
|
|
|
|
|
|
|
err = packager.Package(pkgInfo, pkgFile) // Упаковываем пакет
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Добавляем путь и имя только что собранного пакета в
|
|
|
|
|
// соответствующие срезы
|
|
|
|
|
builtPaths = append(builtPaths, pkgPath)
|
|
|
|
|
builtNames = append(builtNames, vars.Name)
|
2024-01-22 10:36:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
err = b.removeBuildDeps(ctx, buildDeps) // Удаляем зависимости для сборки
|
2024-01-22 10:36:06 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-19 16:21:41 +00:00
|
|
|
|
// Удаляем дубликаты из pkgPaths и pkgNames.
|
|
|
|
|
// Дубликаты могут появиться, если несколько зависимостей
|
|
|
|
|
// зависят от одних и тех же пакетов.
|
2025-01-30 07:10:42 +00:00
|
|
|
|
pkgPaths := removeDuplicates(builtPaths)
|
|
|
|
|
pkgNames := removeDuplicates(builtNames)
|
2024-01-22 10:36:06 +00:00
|
|
|
|
|
2024-11-16 08:32:47 +00:00
|
|
|
|
return pkgPaths, pkgNames, nil // Возвращаем пути и имена пакетов
|
2024-01-22 10:36:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-11-16 08:32:47 +00:00
|
|
|
|
// Функция executeFirstPass выполняет парсированный скрипт в ограниченной среде,
|
|
|
|
|
// чтобы извлечь переменные сборки без выполнения реального кода.
|
2025-02-12 15:34:35 +00:00
|
|
|
|
func (b *Builder) executeFirstPass(
|
|
|
|
|
fl *syntax.File,
|
|
|
|
|
) (string, []*types.BuildVars, error) {
|
|
|
|
|
varsOfPackages := []*types.BuildVars{}
|
|
|
|
|
|
|
|
|
|
scriptDir := filepath.Dir(b.opts.Script) // Получаем директорию скрипта
|
|
|
|
|
env := createBuildEnvVars(b.info, types.Directories{ScriptDir: scriptDir}) // Создаём переменные окружения для сборки
|
2024-01-22 10:36:06 +00:00
|
|
|
|
|
|
|
|
|
runner, err := interp.New(
|
2024-12-19 16:21:41 +00:00
|
|
|
|
interp.Env(expand.ListEnviron(env...)), // Устанавливаем окружение
|
|
|
|
|
interp.StdIO(os.Stdin, os.Stdout, os.Stderr), // Устанавливаем стандартный ввод-вывод
|
2024-11-16 08:32:47 +00:00
|
|
|
|
interp.ExecHandler(helpers.Restricted.ExecHandler(handlers.NopExec)), // Ограничиваем выполнение
|
2024-12-19 16:21:41 +00:00
|
|
|
|
interp.ReadDirHandler(handlers.RestrictedReadDir(scriptDir)), // Ограничиваем чтение директорий
|
|
|
|
|
interp.StatHandler(handlers.RestrictedStat(scriptDir)), // Ограничиваем доступ к статистике файлов
|
|
|
|
|
interp.OpenHandler(handlers.RestrictedOpen(scriptDir)), // Ограничиваем открытие файлов
|
2024-01-22 10:36:06 +00:00
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
2025-02-12 15:34:35 +00:00
|
|
|
|
return "", nil, err
|
2024-01-22 10:36:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
err = runner.Run(b.ctx, fl) // Запускаем скрипт
|
2024-01-22 10:36:06 +00:00
|
|
|
|
if err != nil {
|
2025-02-12 15:34:35 +00:00
|
|
|
|
return "", nil, err
|
2024-01-22 10:36:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
dec := decoder.New(b.info, runner) // Создаём новый декодер
|
2024-01-22 10:36:06 +00:00
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
type packages struct {
|
|
|
|
|
BasePkgName string `sh:"basepkg_name"`
|
|
|
|
|
Names []string `sh:"name"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var pkgs packages
|
|
|
|
|
err = dec.DecodeVars(&pkgs)
|
2024-01-22 10:36:06 +00:00
|
|
|
|
if err != nil {
|
2025-02-12 15:34:35 +00:00
|
|
|
|
return "", nil, err
|
|
|
|
|
}
|
|
|
|
|
if len(pkgs.Names) == 0 {
|
|
|
|
|
return "", nil, errors.New("package name is missing")
|
2024-01-22 10:36:06 +00:00
|
|
|
|
}
|
2025-02-12 15:34:35 +00:00
|
|
|
|
var vars types.BuildVars
|
|
|
|
|
if len(pkgs.Names) == 1 {
|
|
|
|
|
err = dec.DecodeVars(&vars) // Декодируем переменные
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", nil, err
|
|
|
|
|
}
|
|
|
|
|
varsOfPackages = append(varsOfPackages, &vars)
|
2024-01-22 10:36:06 +00:00
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
return vars.Name, varsOfPackages, nil
|
|
|
|
|
}
|
|
|
|
|
if len(b.opts.Packages) == 0 {
|
|
|
|
|
return "", nil, errors.New("script has multiple packages but package is not specified")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, pkgName := range b.opts.Packages {
|
|
|
|
|
var preVars types.BuildVarsPre
|
|
|
|
|
funcName := fmt.Sprintf("meta_%s", pkgName)
|
|
|
|
|
meta, ok := dec.GetFuncWithSubshell(funcName)
|
|
|
|
|
if !ok {
|
|
|
|
|
return "", nil, errors.New("func is missing")
|
|
|
|
|
}
|
|
|
|
|
r, err := meta(b.ctx)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", nil, err
|
|
|
|
|
}
|
|
|
|
|
d := decoder.New(&distro.OSRelease{}, r)
|
|
|
|
|
err = d.DecodeVars(&preVars)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", nil, err
|
|
|
|
|
}
|
|
|
|
|
vars := preVars.ToBuildVars()
|
|
|
|
|
vars.Name = pkgName
|
|
|
|
|
|
|
|
|
|
varsOfPackages = append(varsOfPackages, &vars)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return pkgs.BasePkgName, varsOfPackages, nil // Возвращаем переменные сборки
|
2024-01-22 10:36:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-11-16 08:32:47 +00:00
|
|
|
|
// Функция getDirs возвращает соответствующие директории для скрипта
|
2025-02-12 15:34:35 +00:00
|
|
|
|
func (b *Builder) getDirs(basePkg string) types.Directories {
|
|
|
|
|
baseDir := filepath.Join(b.config.GetPaths(b.ctx).PkgsDir, basePkg) // Определяем базовую директорию
|
2024-01-22 10:36:06 +00:00
|
|
|
|
return types.Directories{
|
|
|
|
|
BaseDir: baseDir,
|
|
|
|
|
SrcDir: filepath.Join(baseDir, "src"),
|
|
|
|
|
PkgDir: filepath.Join(baseDir, "pkg"),
|
2025-02-12 15:34:35 +00:00
|
|
|
|
ScriptDir: filepath.Dir(b.opts.Script),
|
2024-01-22 10:36:06 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-16 08:32:47 +00:00
|
|
|
|
// Функция executeSecondPass выполняет скрипт сборки второй раз без каких-либо ограничений. Возвращается декодер,
|
|
|
|
|
// который может быть использован для получения функций и переменных из скрипта.
|
2025-02-12 15:34:35 +00:00
|
|
|
|
func (b *Builder) executeSecondPass(
|
|
|
|
|
ctx context.Context,
|
|
|
|
|
fl *syntax.File,
|
|
|
|
|
dirs types.Directories,
|
|
|
|
|
) (*decoder.Decoder, error) {
|
|
|
|
|
env := createBuildEnvVars(b.info, dirs) // Создаём переменные окружения для сборки
|
2024-01-22 10:36:06 +00:00
|
|
|
|
|
2024-11-16 08:32:47 +00:00
|
|
|
|
fakeroot := handlers.FakerootExecHandler(2 * time.Second) // Настраиваем "fakeroot" для выполнения
|
2024-01-22 10:36:06 +00:00
|
|
|
|
runner, err := interp.New(
|
2024-12-19 16:21:41 +00:00
|
|
|
|
interp.Env(expand.ListEnviron(env...)), // Устанавливаем окружение
|
|
|
|
|
interp.StdIO(os.Stdin, os.Stdout, os.Stderr), // Устанавливаем стандартный ввод-вывод
|
2024-11-16 08:32:47 +00:00
|
|
|
|
interp.ExecHandler(helpers.Helpers.ExecHandler(fakeroot)), // Обрабатываем выполнение через fakeroot
|
2024-01-22 10:36:06 +00:00
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-16 08:32:47 +00:00
|
|
|
|
err = runner.Run(ctx, fl) // Запускаем скрипт
|
2024-01-22 10:36:06 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
return decoder.New(b.info, runner), nil // Возвращаем новый декодер
|
2024-01-22 10:36:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-11-16 08:32:47 +00:00
|
|
|
|
// Функция performChecks проверяет различные аспекты в системе, чтобы убедиться, что пакет может быть установлен.
|
2025-02-12 15:34:35 +00:00
|
|
|
|
func (b *Builder) performChecks(ctx context.Context, vars *types.BuildVars, installed map[string]string) (bool, error) {
|
2024-11-16 08:32:47 +00:00
|
|
|
|
if !cpu.IsCompatibleWith(cpu.Arch(), vars.Architectures) { // Проверяем совместимость архитектуры
|
2025-02-12 15:34:35 +00:00
|
|
|
|
cont, err := cliutils.YesNoPrompt(
|
|
|
|
|
ctx,
|
|
|
|
|
gotext.Get("Your system's CPU architecture doesn't match this package. Do you want to build anyway?"),
|
|
|
|
|
b.opts.Interactive,
|
|
|
|
|
true,
|
|
|
|
|
)
|
2024-01-22 10:36:06 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return false, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !cont {
|
|
|
|
|
return false, nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-16 08:32:47 +00:00
|
|
|
|
if instVer, ok := installed[vars.Name]; ok { // Если пакет уже установлен, выводим предупреждение
|
2025-01-22 13:37:16 +00:00
|
|
|
|
slog.Warn(gotext.Get("This package is already installed"),
|
|
|
|
|
"name", vars.Name,
|
|
|
|
|
"version", instVer,
|
|
|
|
|
)
|
2024-01-22 10:36:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true, nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-16 08:32:47 +00:00
|
|
|
|
// Функция installBuildDeps устанавливает все зависимости сборки, которые еще не установлены, и возвращает
|
|
|
|
|
// срез, содержащий имена всех установленных пакетов.
|
2025-02-12 15:34:35 +00:00
|
|
|
|
func (b *Builder) installBuildDeps(ctx context.Context, buildDepends []string) ([]string, error) {
|
2024-01-22 10:36:06 +00:00
|
|
|
|
var buildDeps []string
|
2025-02-12 15:34:35 +00:00
|
|
|
|
if len(buildDepends) > 0 {
|
|
|
|
|
deps, err := removeAlreadyInstalled(b.opts, buildDepends)
|
2024-01-22 10:36:06 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
found, notFound, err := b.repos.FindPkgs(ctx, deps) // Находим пакеты-зависимости
|
2025-01-19 08:49:00 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2024-01-22 10:36:06 +00:00
|
|
|
|
|
2025-01-22 13:37:16 +00:00
|
|
|
|
slog.Info(gotext.Get("Installing build dependencies")) // Логгируем установку зависимостей
|
2024-01-22 10:36:06 +00:00
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
flattened := cliutils.FlattenPkgs(ctx, found, "install", b.opts.Interactive) // Уплощаем список зависимостей
|
2024-01-22 10:36:06 +00:00
|
|
|
|
buildDeps = packageNames(flattened)
|
2025-02-12 15:34:35 +00:00
|
|
|
|
b.InstallPkgs(ctx, flattened, notFound, b.opts) // Устанавливаем пакеты
|
2024-01-22 10:36:06 +00:00
|
|
|
|
}
|
|
|
|
|
return buildDeps, nil
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
func (b *Builder) getBuildersForPackages(pkgs []db.Package) []*Builder {
|
|
|
|
|
type item struct {
|
|
|
|
|
pkg *db.Package
|
|
|
|
|
packages []string
|
2025-01-19 08:49:00 +00:00
|
|
|
|
}
|
2025-02-12 15:34:35 +00:00
|
|
|
|
pkgsMap := make(map[string]*item)
|
|
|
|
|
for _, pkg := range pkgs {
|
|
|
|
|
if pkgsMap[pkg.BasePkgName] == nil {
|
|
|
|
|
pkgsMap[pkg.BasePkgName] = &item{
|
|
|
|
|
pkg: &pkg,
|
|
|
|
|
}
|
2024-01-22 10:36:06 +00:00
|
|
|
|
}
|
2025-02-12 15:34:35 +00:00
|
|
|
|
pkgsMap[pkg.BasePkgName].packages = append(
|
|
|
|
|
pkgsMap[pkg.BasePkgName].packages,
|
|
|
|
|
pkg.Name,
|
|
|
|
|
)
|
|
|
|
|
}
|
2024-01-22 10:36:06 +00:00
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
builders := []*Builder{}
|
2024-01-22 10:36:06 +00:00
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
for basePkgName := range pkgsMap {
|
|
|
|
|
pkg := pkgsMap[basePkgName].pkg
|
|
|
|
|
builder := *b
|
|
|
|
|
builder.UpdateOptsFromPkg(pkg, pkgsMap[basePkgName].packages)
|
|
|
|
|
builders = append(builders, &builder)
|
2024-01-22 10:36:06 +00:00
|
|
|
|
}
|
2025-02-12 15:34:35 +00:00
|
|
|
|
|
|
|
|
|
return builders
|
2024-01-22 10:36:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
func (b *Builder) buildALRDeps(ctx context.Context, depends []string) (builtPaths, builtNames, repoDeps []string, err error) {
|
|
|
|
|
if len(depends) > 0 {
|
2025-01-22 13:37:16 +00:00
|
|
|
|
slog.Info(gotext.Get("Installing dependencies"))
|
2024-01-22 10:36:06 +00:00
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
found, notFound, err := b.repos.FindPkgs(ctx, depends) // Поиск зависимостей
|
2024-01-22 10:36:06 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
repoDeps = notFound
|
|
|
|
|
|
2024-12-19 16:21:41 +00:00
|
|
|
|
// Если для некоторых пакетов есть несколько опций, упрощаем их все в один срез
|
2025-02-12 15:34:35 +00:00
|
|
|
|
pkgs := cliutils.FlattenPkgs(ctx, found, "install", b.opts.Interactive)
|
|
|
|
|
builders := b.getBuildersForPackages(pkgs)
|
|
|
|
|
for _, builder := range builders {
|
2024-12-19 16:21:41 +00:00
|
|
|
|
// Собираем зависимости
|
2025-02-12 15:34:35 +00:00
|
|
|
|
pkgPaths, pkgNames, err := builder.BuildPackage(ctx)
|
2024-01-22 10:36:06 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-19 16:21:41 +00:00
|
|
|
|
// Добавляем пути всех собранных пакетов в builtPaths
|
2024-01-22 10:36:06 +00:00
|
|
|
|
builtPaths = append(builtPaths, pkgPaths...)
|
2024-12-19 16:21:41 +00:00
|
|
|
|
// Добавляем пути всех собранных пакетов в builtPaths
|
2024-01-22 10:36:06 +00:00
|
|
|
|
builtNames = append(builtNames, pkgNames...)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-19 16:21:41 +00:00
|
|
|
|
// Удаляем возможные дубликаты, которые могут быть введены, если
|
|
|
|
|
// несколько зависимостей зависят от одних и тех же пакетов.
|
2024-01-22 10:36:06 +00:00
|
|
|
|
repoDeps = removeDuplicates(repoDeps)
|
|
|
|
|
builtPaths = removeDuplicates(builtPaths)
|
|
|
|
|
builtNames = removeDuplicates(builtNames)
|
|
|
|
|
return builtPaths, builtNames, repoDeps, nil
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
func (b *Builder) getSources(ctx context.Context, dirs types.Directories, bv *types.BuildVars) error {
|
|
|
|
|
if len(bv.Sources) != len(bv.Checksums) {
|
|
|
|
|
slog.Error(gotext.Get("The checksums array must be the same length as sources"))
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
2025-01-24 16:23:41 +00:00
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
for i, src := range bv.Sources {
|
|
|
|
|
opts := dl.Options{
|
|
|
|
|
Name: fmt.Sprintf("%s[%d]", bv.Name, i),
|
|
|
|
|
URL: src,
|
|
|
|
|
Destination: dirs.SrcDir,
|
|
|
|
|
Progress: os.Stderr,
|
|
|
|
|
LocalDir: dirs.ScriptDir,
|
|
|
|
|
}
|
2024-01-22 10:36:06 +00:00
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
if !strings.EqualFold(bv.Checksums[i], "SKIP") {
|
|
|
|
|
// Если контрольная сумма содержит двоеточие, используйте часть до двоеточия
|
|
|
|
|
// как алгоритм, а часть после как фактическую контрольную сумму.
|
|
|
|
|
// В противном случае используйте sha256 по умолчанию с целой строкой как контрольной суммой.
|
|
|
|
|
algo, hashData, ok := strings.Cut(bv.Checksums[i], ":")
|
|
|
|
|
if ok {
|
|
|
|
|
checksum, err := hex.DecodeString(hashData)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
opts.Hash = checksum
|
|
|
|
|
opts.HashAlgorithm = algo
|
|
|
|
|
} else {
|
|
|
|
|
checksum, err := hex.DecodeString(bv.Checksums[i])
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
opts.Hash = checksum
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-01-22 10:36:06 +00:00
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
opts.DlCache = dlcache.New(b.config)
|
|
|
|
|
|
|
|
|
|
err := dl.Download(ctx, opts)
|
2024-01-22 10:36:06 +00:00
|
|
|
|
if err != nil {
|
2025-02-12 15:34:35 +00:00
|
|
|
|
return err
|
2024-01-22 10:36:06 +00:00
|
|
|
|
}
|
2025-02-12 15:34:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2024-01-22 10:36:06 +00:00
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
// Функция removeBuildDeps спрашивает у пользователя, хочет ли он удалить зависимости,
|
|
|
|
|
// установленные для сборки. Если да, использует менеджер пакетов для их удаления.
|
|
|
|
|
func (b *Builder) removeBuildDeps(ctx context.Context, buildDeps []string) error {
|
|
|
|
|
if len(buildDeps) > 0 {
|
|
|
|
|
remove, err := cliutils.YesNoPrompt(
|
|
|
|
|
ctx,
|
|
|
|
|
gotext.Get("Would you like to remove the build dependencies?"),
|
|
|
|
|
b.opts.Interactive,
|
|
|
|
|
false,
|
|
|
|
|
)
|
2024-01-22 10:36:06 +00:00
|
|
|
|
if err != nil {
|
2025-02-12 15:34:35 +00:00
|
|
|
|
return err
|
2024-01-22 10:36:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
if remove {
|
|
|
|
|
err = b.opts.Manager.Remove(
|
|
|
|
|
&manager.Opts{
|
|
|
|
|
AsRoot: true,
|
|
|
|
|
NoConfirm: true,
|
|
|
|
|
},
|
|
|
|
|
buildDeps...,
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-01-22 10:36:06 +00:00
|
|
|
|
}
|
2025-02-12 15:34:35 +00:00
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type FunctionsOutput struct {
|
|
|
|
|
Contents *[]string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Функция executeFunctions выполняет специальные функции ALR, такие как version(), prepare() и т.д.
|
|
|
|
|
func (b *Builder) executeFunctions(
|
|
|
|
|
ctx context.Context,
|
|
|
|
|
dec *decoder.Decoder,
|
|
|
|
|
dirs types.Directories,
|
|
|
|
|
) error {
|
|
|
|
|
/*
|
|
|
|
|
version, ok := dec.GetFunc("version")
|
|
|
|
|
if ok {
|
|
|
|
|
slog.Info(gotext.Get("Executing version()"))
|
|
|
|
|
|
|
|
|
|
buf := &bytes.Buffer{}
|
|
|
|
|
|
|
|
|
|
err := version(
|
|
|
|
|
ctx,
|
|
|
|
|
interp.Dir(dirs.SrcDir),
|
|
|
|
|
interp.StdIO(os.Stdin, buf, os.Stderr),
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
newVer := strings.TrimSpace(buf.String())
|
|
|
|
|
err = setVersion(ctx, dec.Runner, newVer)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
vars.Version = newVer
|
|
|
|
|
|
|
|
|
|
slog.Info(gotext.Get("Updating version"), "new", newVer)
|
|
|
|
|
}
|
|
|
|
|
*/
|
2024-01-22 10:36:06 +00:00
|
|
|
|
|
|
|
|
|
prepare, ok := dec.GetFunc("prepare")
|
|
|
|
|
if ok {
|
2025-01-22 13:37:16 +00:00
|
|
|
|
slog.Info(gotext.Get("Executing prepare()"))
|
2024-01-22 10:36:06 +00:00
|
|
|
|
|
2025-01-24 16:23:41 +00:00
|
|
|
|
err := prepare(ctx, interp.Dir(dirs.SrcDir))
|
2024-01-22 10:36:06 +00:00
|
|
|
|
if err != nil {
|
2025-02-12 15:34:35 +00:00
|
|
|
|
return err
|
2024-01-22 10:36:06 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
build, ok := dec.GetFunc("build")
|
|
|
|
|
if ok {
|
2025-01-22 13:37:16 +00:00
|
|
|
|
slog.Info(gotext.Get("Executing build()"))
|
2024-01-22 10:36:06 +00:00
|
|
|
|
|
2025-01-24 16:23:41 +00:00
|
|
|
|
err := build(ctx, interp.Dir(dirs.SrcDir))
|
2024-01-22 10:36:06 +00:00
|
|
|
|
if err != nil {
|
2025-02-12 15:34:35 +00:00
|
|
|
|
return err
|
2024-01-22 10:36:06 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (b *Builder) executePackageFunctions(
|
|
|
|
|
ctx context.Context,
|
|
|
|
|
dec *decoder.Decoder,
|
|
|
|
|
dirs types.Directories,
|
|
|
|
|
packageName string,
|
|
|
|
|
) (*FunctionsOutput, error) {
|
|
|
|
|
output := &FunctionsOutput{}
|
|
|
|
|
var packageFuncName string
|
|
|
|
|
var filesFuncName string
|
|
|
|
|
|
|
|
|
|
if packageName == "" {
|
|
|
|
|
filesFuncName = "files"
|
|
|
|
|
packageFuncName = "package"
|
|
|
|
|
} else {
|
|
|
|
|
packageFuncName = fmt.Sprintf("package_%s", packageName)
|
|
|
|
|
filesFuncName = fmt.Sprintf("files_%s", packageName)
|
|
|
|
|
}
|
|
|
|
|
packageFn, ok := dec.GetFunc(packageFuncName)
|
2025-01-30 07:10:42 +00:00
|
|
|
|
if ok {
|
2025-02-12 15:34:35 +00:00
|
|
|
|
slog.Info(gotext.Get("Executing %s()", packageFuncName))
|
2025-01-30 07:10:42 +00:00
|
|
|
|
err := packageFn(ctx, interp.Dir(dirs.SrcDir))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
2024-12-19 16:21:41 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-01-22 10:36:06 +00:00
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
files, ok := dec.GetFuncP(filesFuncName, func(ctx context.Context, s *interp.Runner) error {
|
2025-01-24 16:23:41 +00:00
|
|
|
|
// It should be done via interp.RunnerOption,
|
|
|
|
|
// but due to the issues below, it cannot be done.
|
|
|
|
|
// - https://github.com/mvdan/sh/issues/962
|
|
|
|
|
// - https://github.com/mvdan/sh/issues/1125
|
|
|
|
|
script, err := syntax.NewParser().Parse(strings.NewReader("cd $pkgdir && shopt -s globstar"), "")
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return s.Run(ctx, script)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if ok {
|
2025-02-12 15:34:35 +00:00
|
|
|
|
slog.Info(gotext.Get("Executing %s()", filesFuncName))
|
2025-01-24 16:23:41 +00:00
|
|
|
|
|
|
|
|
|
buf := &bytes.Buffer{}
|
|
|
|
|
|
|
|
|
|
err := files(
|
|
|
|
|
ctx,
|
|
|
|
|
interp.Dir(dirs.PkgDir),
|
|
|
|
|
interp.StdIO(os.Stdin, buf, os.Stderr),
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-28 21:19:02 +00:00
|
|
|
|
contents, err := shlex.Split(buf.String())
|
2025-01-25 06:39:33 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2025-01-24 16:23:41 +00:00
|
|
|
|
output.Contents = &contents
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return output, nil
|
2024-01-22 10:36:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
func (b *Builder) installOptDeps(ctx context.Context, optDepends []string) error {
|
|
|
|
|
optDeps, err := removeAlreadyInstalled(b.opts, optDepends)
|
2024-01-22 10:36:06 +00:00
|
|
|
|
if err != nil {
|
2025-02-12 15:34:35 +00:00
|
|
|
|
return err
|
2024-12-19 16:21:41 +00:00
|
|
|
|
}
|
2025-02-12 15:34:35 +00:00
|
|
|
|
if len(optDeps) > 0 {
|
|
|
|
|
optDeps, err := cliutils.ChooseOptDepends(ctx, optDeps, "install", b.opts.Interactive) // Пользователя просят выбрать опциональные зависимости
|
2025-01-24 16:23:41 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2024-01-22 10:36:06 +00:00
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
if len(optDeps) == 0 {
|
2024-01-22 10:36:06 +00:00
|
|
|
|
return nil
|
|
|
|
|
}
|
2025-01-24 16:23:41 +00:00
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
found, notFound, err := b.repos.FindPkgs(ctx, optDeps) // Находим опциональные зависимости
|
2024-01-22 10:36:06 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
flattened := cliutils.FlattenPkgs(ctx, found, "install", b.opts.Interactive)
|
|
|
|
|
b.InstallPkgs(ctx, flattened, notFound, b.opts) // Устанавливаем выбранные пакеты
|
2024-01-22 10:36:06 +00:00
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
func (b *Builder) InstallPkgs(
|
|
|
|
|
ctx context.Context,
|
|
|
|
|
alrPkgs []db.Package,
|
|
|
|
|
nativePkgs []string,
|
|
|
|
|
opts types.BuildOpts,
|
|
|
|
|
) {
|
|
|
|
|
if len(nativePkgs) > 0 {
|
|
|
|
|
err := opts.Manager.Install(nil, nativePkgs...)
|
|
|
|
|
// Если есть нативные пакеты, выполняем их установку
|
2024-01-22 10:36:06 +00:00
|
|
|
|
if err != nil {
|
2025-02-12 15:34:35 +00:00
|
|
|
|
slog.Error(gotext.Get("Error installing native packages"), "err", err)
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
// Логируем и завершаем выполнение при ошибке
|
2024-01-22 10:36:06 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
b.InstallALRPackages(ctx, alrPkgs, opts)
|
|
|
|
|
// Устанавливаем скрипты сборки через функцию InstallScripts
|
2024-01-22 10:36:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
func (b *Builder) InstallALRPackages(ctx context.Context, pkgs []db.Package, opts types.BuildOpts) {
|
|
|
|
|
builders := b.getBuildersForPackages(pkgs)
|
|
|
|
|
for _, builder := range builders {
|
|
|
|
|
builtPkgs, _, err := builder.BuildPackage(ctx)
|
|
|
|
|
// Выполняем сборку пакета
|
2025-01-19 08:49:00 +00:00
|
|
|
|
if err != nil {
|
2025-02-12 15:34:35 +00:00
|
|
|
|
slog.Error(gotext.Get("Error building package"), "err", err)
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
// Логируем и завершаем выполнение при ошибке сборки
|
2024-01-22 10:36:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 15:34:35 +00:00
|
|
|
|
err = opts.Manager.InstallLocal(nil, builtPkgs...)
|
|
|
|
|
// Устанавливаем локально собранные пакеты
|
|
|
|
|
if err != nil {
|
|
|
|
|
slog.Error(gotext.Get("Error installing package"), "err", err)
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
// Логируем и завершаем выполнение при ошибке установки
|
2024-01-22 10:36:06 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|