From 0bfe88beed9aef8af14f614bbf4400113cb039ac Mon Sep 17 00:00:00 2001 From: Maxim Slipenko Date: Sat, 5 Apr 2025 20:19:00 +0300 Subject: [PATCH] feat: add skiplists for auto_req and auto_prov --- assets/coverage-badge.svg | 4 +- e2e-tests/common_test.go | 8 +- e2e-tests/images/Dockerfile.alt-sisyphus | 3 +- e2e-tests/images/Dockerfile.fedora-41 | 8 ++ e2e-tests/issue_41_autoreq_skiplist_test.go | 80 ++++++++++++ internal/shutils/decoder/decoder.go | 15 ++- internal/translations/default.pot | 54 ++++---- internal/translations/po/ru/default.po | 60 ++++----- internal/types/build.go | 91 +++++--------- pkg/build/build.go | 29 ++--- .../{findDeps.go => find_deps/alt_linux.go} | 20 +-- pkg/build/find_deps/empty.go | 39 ++++++ pkg/build/find_deps/fedora.go | 118 ++++++++++++++++++ pkg/build/find_deps/find_deps.go | 58 +++++++++ 14 files changed, 438 insertions(+), 149 deletions(-) create mode 100644 e2e-tests/images/Dockerfile.fedora-41 create mode 100644 e2e-tests/issue_41_autoreq_skiplist_test.go rename pkg/build/{findDeps.go => find_deps/alt_linux.go} (67%) create mode 100644 pkg/build/find_deps/empty.go create mode 100644 pkg/build/find_deps/fedora.go create mode 100644 pkg/build/find_deps/find_deps.go diff --git a/assets/coverage-badge.svg b/assets/coverage-badge.svg index ffdf7ae..2a6facc 100644 --- a/assets/coverage-badge.svg +++ b/assets/coverage-badge.svg @@ -11,7 +11,7 @@ coverage coverage - 19.4% - 19.4% + 19.2% + 19.2% diff --git a/e2e-tests/common_test.go b/e2e-tests/common_test.go index 51466a1..e346a9f 100644 --- a/e2e-tests/common_test.go +++ b/e2e-tests/common_test.go @@ -100,13 +100,19 @@ func e2eSpawn(runnable e2e.Runnable, command e2e.Command, timeout time.Duration, var ALL_SYSTEMS []string = []string{ "ubuntu-24.04", - // "alt-sisyphus", + "alt-sisyphus", + "fedora-41", // "archlinux", // "alpine", // "opensuse-leap", // "redos-8", } +var AUTOREQ_AUTOPROV_SYSTEMS []string = []string{ + "alt-sisyphus", + "fedora-41", +} + var COMMON_SYSTEMS []string = []string{ "ubuntu-24.04", } diff --git a/e2e-tests/images/Dockerfile.alt-sisyphus b/e2e-tests/images/Dockerfile.alt-sisyphus index 3f7cf1b..cc48dee 100644 --- a/e2e-tests/images/Dockerfile.alt-sisyphus +++ b/e2e-tests/images/Dockerfile.alt-sisyphus @@ -1,5 +1,6 @@ FROM registry.altlinux.org/sisyphus/alt:latest -RUN apt-get update && apt-get install -y ca-certificates +RUN apt-get update && apt-get install -y ca-certificates rpm-build RUN useradd -m -s /bin/bash alr-user USER alr-user +WORKDIR /home/alr-user ENTRYPOINT ["tail", "-f", "/dev/null"] \ No newline at end of file diff --git a/e2e-tests/images/Dockerfile.fedora-41 b/e2e-tests/images/Dockerfile.fedora-41 new file mode 100644 index 0000000..d213964 --- /dev/null +++ b/e2e-tests/images/Dockerfile.fedora-41 @@ -0,0 +1,8 @@ +FROM fedora:41 +RUN dnf install -y ca-certificates sudo rpm-build +RUN useradd -m -s /bin/bash alr-user && \ + echo "alr-user ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/alr-user && \ + chmod 0440 /etc/sudoers.d/alr-user +USER alr-user +WORKDIR /home/alr-user +ENTRYPOINT ["tail", "-f", "/dev/null"] \ No newline at end of file diff --git a/e2e-tests/issue_41_autoreq_skiplist_test.go b/e2e-tests/issue_41_autoreq_skiplist_test.go new file mode 100644 index 0000000..79d2d3d --- /dev/null +++ b/e2e-tests/issue_41_autoreq_skiplist_test.go @@ -0,0 +1,80 @@ +// 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 . + +//go:build e2e + +package e2etests_test + +import ( + "testing" + + "github.com/alecthomas/assert/v2" + "github.com/efficientgo/e2e" +) + +func TestE2EIssue41AutoreqSkiplist(t *testing.T) { + dockerMultipleRun( + t, + "issue-41-autoreq-skiplist", + AUTOREQ_AUTOPROV_SYSTEMS, + func(t *testing.T, r e2e.Runnable) { + err := r.Exec(e2e.NewCommand( + "alr", + "addrepo", + "--name", + "alr-repo", + "--url", + "https://gitea.plemya-x.ru/Maks1mS/repo-for-tests.git", + )) + assert.NoError(t, err) + + err = r.Exec(e2e.NewCommand( + "alr", + "ref", + )) + assert.NoError(t, err) + + err = r.Exec(e2e.NewCommand( + "alr", + "build", + "-p", + "alr-repo/test-autoreq-autoprov", + )) + assert.NoError(t, err) + + err = r.Exec(e2e.NewCommand( + "sh", + "-c", + "rpm -qp --requires *.rpm | grep \"^/bin/sh$\"", + )) + assert.NoError(t, err) + + err = r.Exec(e2e.NewCommand( + "sh", + "-c", + "rpm -qp --requires *.rpm | grep \"^/bin/bash$\"", + )) + assert.Error(t, err) + + err = r.Exec(e2e.NewCommand( + "sh", + "-c", + "rpm -qp --requires *.rpm | grep \"^/bin/zsh$\"", + )) + assert.Error(t, err) + }, + ) +} diff --git a/internal/shutils/decoder/decoder.go b/internal/shutils/decoder/decoder.go index c89bfb4..b6f7d05 100644 --- a/internal/shutils/decoder/decoder.go +++ b/internal/shutils/decoder/decoder.go @@ -123,15 +123,29 @@ func (d *Decoder) DecodeVars(val any) error { } rVal := reflect.ValueOf(val).Elem() + return d.decodeStruct(rVal) +} +func (d *Decoder) decodeStruct(rVal reflect.Value) error { for i := 0; i < rVal.NumField(); i++ { field := rVal.Field(i) fieldType := rVal.Type().Field(i) + // Пропускаем неэкспортируемые поля if !fieldType.IsExported() { continue } + // Обрабатываем встроенные поля рекурсивно + if fieldType.Anonymous { + if field.Kind() == reflect.Struct { + if err := d.decodeStruct(field); err != nil { + return err + } + } + continue + } + name := fieldType.Name tag := fieldType.Tag.Get("sh") required := false @@ -160,7 +174,6 @@ func (d *Decoder) DecodeVars(val any) error { field.Set(newVal.Elem()) } - return nil } diff --git a/internal/translations/default.pot b/internal/translations/default.pot index e199ee8..19526d6 100644 --- a/internal/translations/default.pot +++ b/internal/translations/default.pot @@ -337,92 +337,92 @@ msgstr "" msgid "Error while running app" msgstr "" -#: pkg/build/build.go:156 +#: pkg/build/build.go:157 msgid "Failed to prompt user to view build script" msgstr "" -#: pkg/build/build.go:160 +#: pkg/build/build.go:161 msgid "Building package" msgstr "" -#: pkg/build/build.go:208 +#: pkg/build/build.go:209 msgid "The checksums array must be the same length as sources" msgstr "" -#: pkg/build/build.go:235 +#: pkg/build/build.go:238 msgid "Downloading sources" msgstr "" -#: pkg/build/build.go:257 +#: pkg/build/build.go:260 msgid "Building package metadata" msgstr "" -#: pkg/build/build.go:279 +#: pkg/build/build.go:282 msgid "Compressing package" msgstr "" -#: pkg/build/build.go:438 +#: pkg/build/build.go:441 msgid "" "Your system's CPU architecture doesn't match this package. Do you want to " "build anyway?" msgstr "" -#: pkg/build/build.go:452 +#: pkg/build/build.go:455 msgid "This package is already installed" msgstr "" -#: pkg/build/build.go:476 +#: pkg/build/build.go:479 msgid "Installing build dependencies" msgstr "" -#: pkg/build/build.go:521 +#: pkg/build/build.go:524 msgid "Installing dependencies" msgstr "" -#: pkg/build/build.go:602 +#: pkg/build/build.go:605 msgid "Would you like to remove the build dependencies?" msgstr "" -#: pkg/build/build.go:665 +#: pkg/build/build.go:668 msgid "Executing prepare()" msgstr "" -#: pkg/build/build.go:675 +#: pkg/build/build.go:678 msgid "Executing build()" msgstr "" -#: pkg/build/build.go:705 pkg/build/build.go:725 +#: pkg/build/build.go:708 pkg/build/build.go:728 msgid "Executing %s()" msgstr "" -#: pkg/build/build.go:784 +#: pkg/build/build.go:787 msgid "Error installing native packages" msgstr "" -#: pkg/build/build.go:808 +#: pkg/build/build.go:811 msgid "Error installing package" msgstr "" -#: pkg/build/build.go:867 -msgid "AutoProv is not implemented for this package format, so it's skipped" -msgstr "" - -#: pkg/build/build.go:878 -msgid "AutoReq is not implemented for this package format, so it's skipped" -msgstr "" - -#: pkg/build/findDeps.go:35 +#: pkg/build/find_deps/alt_linux.go:35 msgid "Command not found on the system" msgstr "" -#: pkg/build/findDeps.go:82 +#: pkg/build/find_deps/alt_linux.go:86 msgid "Provided dependency found" msgstr "" -#: pkg/build/findDeps.go:89 +#: pkg/build/find_deps/alt_linux.go:93 msgid "Required dependency found" msgstr "" +#: pkg/build/find_deps/empty.go:32 +msgid "AutoProv is not implemented for this package format, so it's skipped" +msgstr "" + +#: pkg/build/find_deps/empty.go:37 +msgid "AutoReq is not implemented for this package format, so it's skipped" +msgstr "" + #: pkg/repos/pull.go:79 msgid "Pulling repository" msgstr "" diff --git a/internal/translations/po/ru/default.po b/internal/translations/po/ru/default.po index bb2f1f1..3b242ed 100644 --- a/internal/translations/po/ru/default.po +++ b/internal/translations/po/ru/default.po @@ -349,31 +349,31 @@ msgstr "Показать справку" msgid "Error while running app" msgstr "Ошибка при запуске приложения" -#: pkg/build/build.go:156 +#: pkg/build/build.go:157 msgid "Failed to prompt user to view build script" msgstr "Не удалось предложить пользователю просмотреть скрипт сборки" -#: pkg/build/build.go:160 +#: pkg/build/build.go:161 msgid "Building package" msgstr "Сборка пакета" -#: pkg/build/build.go:208 +#: pkg/build/build.go:209 msgid "The checksums array must be the same length as sources" msgstr "Массив контрольных сумм должен быть той же длины, что и источники" -#: pkg/build/build.go:235 +#: pkg/build/build.go:238 msgid "Downloading sources" msgstr "Скачивание источников" -#: pkg/build/build.go:257 +#: pkg/build/build.go:260 msgid "Building package metadata" msgstr "Сборка метаданных пакета" -#: pkg/build/build.go:279 +#: pkg/build/build.go:282 msgid "Compressing package" msgstr "Сжатие пакета" -#: pkg/build/build.go:438 +#: pkg/build/build.go:441 msgid "" "Your system's CPU architecture doesn't match this package. Do you want to " "build anyway?" @@ -381,64 +381,64 @@ msgstr "" "Архитектура процессора вашей системы не соответствует этому пакету. Вы все " "равно хотите выполнить сборку?" -#: pkg/build/build.go:452 +#: pkg/build/build.go:455 msgid "This package is already installed" msgstr "Этот пакет уже установлен" -#: pkg/build/build.go:476 +#: pkg/build/build.go:479 msgid "Installing build dependencies" msgstr "Установка зависимостей сборки" -#: pkg/build/build.go:521 +#: pkg/build/build.go:524 msgid "Installing dependencies" msgstr "Установка зависимостей" -#: pkg/build/build.go:602 +#: pkg/build/build.go:605 msgid "Would you like to remove the build dependencies?" msgstr "Хотели бы вы удалить зависимости сборки?" -#: pkg/build/build.go:665 +#: pkg/build/build.go:668 msgid "Executing prepare()" msgstr "Исполнение prepare()" -#: pkg/build/build.go:675 +#: pkg/build/build.go:678 msgid "Executing build()" msgstr "Исполнение build()" -#: pkg/build/build.go:705 pkg/build/build.go:725 +#: pkg/build/build.go:708 pkg/build/build.go:728 msgid "Executing %s()" msgstr "Исполнение %s()" -#: pkg/build/build.go:784 +#: pkg/build/build.go:787 msgid "Error installing native packages" msgstr "Ошибка при установке нативных пакетов" -#: pkg/build/build.go:808 +#: pkg/build/build.go:811 msgid "Error installing package" msgstr "Ошибка при установке пакета" -#: pkg/build/build.go:867 +#: pkg/build/find_deps/alt_linux.go:35 +msgid "Command not found on the system" +msgstr "Команда не найдена в системе" + +#: pkg/build/find_deps/alt_linux.go:86 +msgid "Provided dependency found" +msgstr "Найденная предоставленная зависимость" + +#: pkg/build/find_deps/alt_linux.go:93 +msgid "Required dependency found" +msgstr "Найдена требуемая зависимость" + +#: pkg/build/find_deps/empty.go:32 msgid "AutoProv is not implemented for this package format, so it's skipped" msgstr "" "AutoProv не реализовано для этого формата пакета, поэтому будет пропущено" -#: pkg/build/build.go:878 +#: pkg/build/find_deps/empty.go:37 msgid "AutoReq is not implemented for this package format, so it's skipped" msgstr "" "AutoReq не реализовано для этого формата пакета, поэтому будет пропущено" -#: pkg/build/findDeps.go:35 -msgid "Command not found on the system" -msgstr "Команда не найдена в системе" - -#: pkg/build/findDeps.go:82 -msgid "Provided dependency found" -msgstr "Найденная предоставленная зависимость" - -#: pkg/build/findDeps.go:89 -msgid "Required dependency found" -msgstr "Найдена требуемая зависимость" - #: pkg/repos/pull.go:79 msgid "Pulling repository" msgstr "Скачивание репозитория" diff --git a/internal/types/build.go b/internal/types/build.go index 6b5c6c9..51999ef 100644 --- a/internal/types/build.go +++ b/internal/types/build.go @@ -31,79 +31,44 @@ type BuildOpts struct { } type BuildVarsPre struct { - Version string `sh:"version,required"` - Release int `sh:"release,required"` - Epoch uint `sh:"epoch"` - Description string `sh:"desc"` - Homepage string `sh:"homepage"` - Maintainer string `sh:"maintainer"` - Architectures []string `sh:"architectures"` - Licenses []string `sh:"license"` - Provides []string `sh:"provides"` - Conflicts []string `sh:"conflicts"` - Depends []string `sh:"deps"` - BuildDepends []string `sh:"build_deps"` - OptDepends []string `sh:"opt_deps"` - Replaces []string `sh:"replaces"` - Sources []string `sh:"sources"` - Checksums []string `sh:"checksums"` - Backup []string `sh:"backup"` - Scripts Scripts `sh:"scripts"` - AutoReq []string `sh:"auto_req"` - AutoProv []string `sh:"auto_prov"` + Version string `sh:"version,required"` + Release int `sh:"release,required"` + Epoch uint `sh:"epoch"` + Description string `sh:"desc"` + Homepage string `sh:"homepage"` + Maintainer string `sh:"maintainer"` + Architectures []string `sh:"architectures"` + Licenses []string `sh:"license"` + Provides []string `sh:"provides"` + Conflicts []string `sh:"conflicts"` + Depends []string `sh:"deps"` + BuildDepends []string `sh:"build_deps"` + OptDepends []string `sh:"opt_deps"` + Replaces []string `sh:"replaces"` + Sources []string `sh:"sources"` + Checksums []string `sh:"checksums"` + Backup []string `sh:"backup"` + Scripts Scripts `sh:"scripts"` + AutoReq []string `sh:"auto_req"` + AutoProv []string `sh:"auto_prov"` + AutoReqSkipList []string `sh:"auto_req_skiplist"` + AutoProvSkipList []string `sh:"auto_prov_skiplist"` } func (bv *BuildVarsPre) ToBuildVars() BuildVars { return BuildVars{ - Name: "", - Version: bv.Version, - Release: bv.Release, - Epoch: bv.Epoch, - Description: bv.Description, - Homepage: bv.Homepage, - Maintainer: bv.Maintainer, - Architectures: bv.Architectures, - Licenses: bv.Licenses, - Provides: bv.Provides, - Conflicts: bv.Conflicts, - Depends: bv.Depends, - BuildDepends: bv.BuildDepends, - OptDepends: bv.OptDepends, - Replaces: bv.Replaces, - Sources: bv.Sources, - Checksums: bv.Checksums, - Backup: bv.Backup, - Scripts: bv.Scripts, - AutoReq: bv.AutoReq, - AutoProv: bv.AutoProv, + Name: "", + Base: "", + BuildVarsPre: *bv, } } // BuildVars represents the script variables required // to build a package type BuildVars struct { - Name string `sh:"name,required"` - Version string `sh:"version,required"` - Release int `sh:"release,required"` - Epoch uint `sh:"epoch"` - Description string `sh:"desc"` - Homepage string `sh:"homepage"` - Maintainer string `sh:"maintainer"` - Architectures []string `sh:"architectures"` - Licenses []string `sh:"license"` - Provides []string `sh:"provides"` - Conflicts []string `sh:"conflicts"` - Depends []string `sh:"deps"` - BuildDepends []string `sh:"build_deps"` - OptDepends []string `sh:"opt_deps"` - Replaces []string `sh:"replaces"` - Sources []string `sh:"sources"` - Checksums []string `sh:"checksums"` - Backup []string `sh:"backup"` - Scripts Scripts `sh:"scripts"` - AutoReq []string `sh:"auto_req"` - AutoProv []string `sh:"auto_prov"` - Base string + Name string `sh:"name,required"` + Base string + BuildVarsPre } type Scripts struct { diff --git a/pkg/build/build.go b/pkg/build/build.go index 27fd2a1..7b5c5ff 100644 --- a/pkg/build/build.go +++ b/pkg/build/build.go @@ -50,6 +50,7 @@ import ( "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" + finddeps "gitea.plemya-x.ru/Plemya-x/ALR/pkg/build/find_deps" "gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" "gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" ) @@ -211,8 +212,10 @@ func (b *Builder) BuildPackage(ctx context.Context) ([]string, []string, error) sources, checksums = removeDuplicatesSources(sources, checksums) mergedVars := types.BuildVars{ - Sources: sources, - Checksums: checksums, + BuildVarsPre: types.BuildVarsPre{ + Sources: sources, + Checksums: checksums, + }, } buildDeps, err := b.installBuildDeps(ctx, buildDepends) // Устанавливаем зависимости для сборки @@ -858,24 +861,18 @@ func (b *Builder) buildPkgMetadata( pkgInfo.Overridables.Contents = contents if len(vars.AutoProv) == 1 && decoder.IsTruthy(vars.AutoProv[0]) { - if pkgFormat == "rpm" { - err = rpmFindProvides(ctx, pkgInfo, dirs) - if err != nil { - return nil, err - } - } else { - slog.Info(gotext.Get("AutoProv is not implemented for this package format, so it's skipped")) + f := finddeps.New(b.info, pkgFormat) + err = f.FindProvides(ctx, pkgInfo, dirs, vars.AutoProvSkipList) + if err != nil { + return nil, err } } if len(vars.AutoReq) == 1 && decoder.IsTruthy(vars.AutoReq[0]) { - if pkgFormat == "rpm" { - err = rpmFindRequires(ctx, pkgInfo, dirs) - if err != nil { - return nil, err - } - } else { - slog.Info(gotext.Get("AutoReq is not implemented for this package format, so it's skipped")) + f := finddeps.New(b.info, pkgFormat) + err = f.FindRequires(ctx, pkgInfo, dirs, vars.AutoReqSkipList) + if err != nil { + return nil, err } } diff --git a/pkg/build/findDeps.go b/pkg/build/find_deps/alt_linux.go similarity index 67% rename from pkg/build/findDeps.go rename to pkg/build/find_deps/alt_linux.go index 0090005..cc003af 100644 --- a/pkg/build/findDeps.go +++ b/pkg/build/find_deps/alt_linux.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -package build +package finddeps import ( "bytes" @@ -30,7 +30,7 @@ import ( "gitea.plemya-x.ru/Plemya-x/ALR/internal/types" ) -func rpmFindDependencies(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories, command string, updateFunc func(string)) error { +func rpmFindDependenciesALTLinux(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories, command string, envs []string, updateFunc func(string)) error { if _, err := exec.LookPath(command); err != nil { slog.Info(gotext.Get("Command not found on the system"), "command", command) return nil @@ -49,8 +49,8 @@ func rpmFindDependencies(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Dir return nil } - cmd := exec.Command(command) - cmd.Stdin = bytes.NewBufferString(strings.Join(paths, "\n")) + cmd := exec.CommandContext(ctx, command) + cmd.Stdin = bytes.NewBufferString(strings.Join(paths, "\n") + "\n") cmd.Env = append(cmd.Env, "RPM_BUILD_ROOT="+dirs.PkgDir, "RPM_FINDPROV_METHOD=", @@ -58,6 +58,7 @@ func rpmFindDependencies(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Dir "RPM_DATADIR=", "RPM_SUBPACKAGE_NAME=", ) + cmd.Env = append(cmd.Env, envs...) var out bytes.Buffer var stderr bytes.Buffer cmd.Stdout = &out @@ -66,6 +67,7 @@ func rpmFindDependencies(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Dir slog.Error(stderr.String()) return err } + slog.Debug(stderr.String()) dependencies := strings.Split(strings.TrimSpace(out.String()), "\n") for _, dep := range dependencies { @@ -77,15 +79,17 @@ func rpmFindDependencies(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Dir return nil } -func rpmFindProvides(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories) error { - return rpmFindDependencies(ctx, pkgInfo, dirs, "/usr/lib/rpm/find-provides", func(dep string) { +type ALTLinuxFindProvReq struct{} + +func (o *ALTLinuxFindProvReq) FindProvides(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories, skiplist []string) error { + return rpmFindDependenciesALTLinux(ctx, pkgInfo, dirs, "/usr/lib/rpm/find-provides", []string{"RPM_FINDPROV_SKIPLIST=" + strings.Join(skiplist, "\n")}, func(dep string) { slog.Info(gotext.Get("Provided dependency found"), "dep", dep) pkgInfo.Overridables.Provides = append(pkgInfo.Overridables.Provides, dep) }) } -func rpmFindRequires(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories) error { - return rpmFindDependencies(ctx, pkgInfo, dirs, "/usr/lib/rpm/find-requires", func(dep string) { +func (o *ALTLinuxFindProvReq) FindRequires(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories, skiplist []string) error { + return rpmFindDependenciesALTLinux(ctx, pkgInfo, dirs, "/usr/lib/rpm/find-requires", []string{"RPM_FINDREQ_SKIPLIST=" + strings.Join(skiplist, "\n")}, func(dep string) { slog.Info(gotext.Get("Required dependency found"), "dep", dep) pkgInfo.Overridables.Depends = append(pkgInfo.Overridables.Depends, dep) }) diff --git a/pkg/build/find_deps/empty.go b/pkg/build/find_deps/empty.go new file mode 100644 index 0000000..05b3a7a --- /dev/null +++ b/pkg/build/find_deps/empty.go @@ -0,0 +1,39 @@ +// 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 . + +package finddeps + +import ( + "context" + "log/slog" + + "github.com/goreleaser/nfpm/v2" + "github.com/leonelquinteros/gotext" + + "gitea.plemya-x.ru/Plemya-x/ALR/internal/types" +) + +type EmptyFindProvReq struct{} + +func (o *EmptyFindProvReq) FindProvides(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories, skiplist []string) error { + slog.Info(gotext.Get("AutoProv is not implemented for this package format, so it's skipped")) + return nil +} + +func (o *EmptyFindProvReq) FindRequires(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories, skiplist []string) error { + slog.Info(gotext.Get("AutoReq is not implemented for this package format, so it's skipped")) + return nil +} diff --git a/pkg/build/find_deps/fedora.go b/pkg/build/find_deps/fedora.go new file mode 100644 index 0000000..c649aae --- /dev/null +++ b/pkg/build/find_deps/fedora.go @@ -0,0 +1,118 @@ +// 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 . + +package finddeps + +import ( + "bytes" + "context" + "fmt" + "log/slog" + "os/exec" + "path" + "strings" + + "github.com/goreleaser/nfpm/v2" + "github.com/leonelquinteros/gotext" + + "gitea.plemya-x.ru/Plemya-x/ALR/internal/types" +) + +type FedoraFindProvReq struct{} + +func rpmFindDependenciesFedora(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories, command string, args []string, updateFunc func(string)) error { + if _, err := exec.LookPath(command); err != nil { + slog.Info(gotext.Get("Command not found on the system"), "command", command) + return nil + } + + var paths []string + for _, content := range pkgInfo.Contents { + if content.Type != "dir" { + paths = append(paths, + path.Join(dirs.PkgDir, content.Destination), + ) + } + } + + if len(paths) == 0 { + return nil + } + + cmd := exec.CommandContext(ctx, command, args...) + cmd.Stdin = bytes.NewBufferString(strings.Join(paths, "\n") + "\n") + cmd.Env = append(cmd.Env, + "RPM_BUILD_ROOT="+dirs.PkgDir, + ) + var out bytes.Buffer + var stderr bytes.Buffer + cmd.Stdout = &out + cmd.Stderr = &stderr + if err := cmd.Run(); err != nil { + slog.Error(stderr.String()) + return err + } + slog.Debug(stderr.String()) + + dependencies := strings.Split(strings.TrimSpace(out.String()), "\n") + for _, dep := range dependencies { + if dep != "" { + updateFunc(dep) + } + } + + return nil +} + +func (o *FedoraFindProvReq) FindProvides(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories, skiplist []string) error { + return rpmFindDependenciesFedora( + ctx, + pkgInfo, + dirs, + "/usr/lib/rpm/rpmdeps", + []string{ + "--define=_use_internal_dependency_generator 1", + "--provides", + fmt.Sprintf( + "--define=__provides_exclude_from %s\"", + strings.Join(skiplist, "|"), + ), + }, + func(dep string) { + slog.Info(gotext.Get("Provided dependency found"), "dep", dep) + pkgInfo.Overridables.Provides = append(pkgInfo.Overridables.Provides, dep) + }) +} + +func (o *FedoraFindProvReq) FindRequires(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories, skiplist []string) error { + return rpmFindDependenciesFedora( + ctx, + pkgInfo, + dirs, + "/usr/lib/rpm/rpmdeps", + []string{ + "--define=_use_internal_dependency_generator 1", + "--requires", + fmt.Sprintf( + "--define=__requires_exclude_from %s", + strings.Join(skiplist, "|"), + ), + }, + func(dep string) { + slog.Info(gotext.Get("Required dependency found"), "dep", dep) + pkgInfo.Overridables.Depends = append(pkgInfo.Overridables.Depends, dep) + }) +} diff --git a/pkg/build/find_deps/find_deps.go b/pkg/build/find_deps/find_deps.go new file mode 100644 index 0000000..4e9b52e --- /dev/null +++ b/pkg/build/find_deps/find_deps.go @@ -0,0 +1,58 @@ +// 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 . + +package finddeps + +import ( + "context" + + "github.com/goreleaser/nfpm/v2" + + "gitea.plemya-x.ru/Plemya-x/ALR/internal/types" + "gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" +) + +type ProvReqFinder interface { + FindProvides(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories, skiplist []string) error + FindRequires(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories, skiplist []string) error +} + +type ProvReqService struct { + finder ProvReqFinder +} + +func New(info *distro.OSRelease, pkgFormat string) *ProvReqService { + s := &ProvReqService{ + finder: &EmptyFindProvReq{}, + } + if pkgFormat == "rpm" { + switch info.ID { + case "altlinux": + s.finder = &ALTLinuxFindProvReq{} + case "fedora": + s.finder = &FedoraFindProvReq{} + } + } + return s +} + +func (s *ProvReqService) FindProvides(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories, skiplist []string) error { + return s.finder.FindProvides(ctx, pkgInfo, dirs, skiplist) +} + +func (s *ProvReqService) FindRequires(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories, skiplist []string) error { + return s.finder.FindRequires(ctx, pkgInfo, dirs, skiplist) +}