diff --git a/.golangci.yml b/.golangci.yml index 863d443..5682194 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -47,4 +47,4 @@ issues: # TODO: remove - linters: - staticcheck - text: "SA1019:" \ No newline at end of file + text: "SA1019: interp.ExecHandler" \ No newline at end of file diff --git a/build.go b/build.go index dc09827..6b372bd 100644 --- a/build.go +++ b/build.go @@ -72,7 +72,7 @@ func BuildCmd() *cli.Command { rs := repos.New(cfg, db) err := db.Init(ctx) if err != nil { - slog.Error(gotext.Get("Error db init"), "err", err) + slog.Error(gotext.Get("Error initialization database"), "err", err) os.Exit(1) } diff --git a/coverage-badge.svg b/coverage-badge.svg index fdab882..db9bea9 100644 --- a/coverage-badge.svg +++ b/coverage-badge.svg @@ -11,7 +11,7 @@ coverage coverage - 20.5% - 20.5% + 19.6% + 19.6% diff --git a/fix.go b/fix.go index 3b34bdc..8c1bb59 100644 --- a/fix.go +++ b/fix.go @@ -27,7 +27,7 @@ import ( "github.com/urfave/cli/v2" "gitea.plemya-x.ru/Plemya-x/ALR/internal/config" - "gitea.plemya-x.ru/Plemya-x/ALR/internal/db" + database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db" "gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos" ) @@ -37,9 +37,8 @@ func FixCmd() *cli.Command { Usage: gotext.Get("Attempt to fix problems with ALR"), Action: func(c *cli.Context) error { ctx := c.Context - - db.Close() - paths := config.GetPaths(ctx) + cfg := config.New() + paths := cfg.GetPaths(ctx) slog.Info(gotext.Get("Removing cache directory")) @@ -57,7 +56,15 @@ func FixCmd() *cli.Command { os.Exit(1) } - err = repos.Pull(ctx, config.Config(ctx).Repos) + cfg = config.New() + db := database.New(cfg) + err = db.Init(ctx) + if err != nil { + slog.Error(gotext.Get("Error initialization database"), "err", err) + os.Exit(1) + } + rs := repos.New(cfg, db) + err = rs.Pull(ctx, cfg.Repos(ctx)) if err != nil { slog.Error(gotext.Get("Error pulling repos"), "err", err) os.Exit(1) diff --git a/info.go b/info.go index 5a4f290..b9b7b12 100644 --- a/info.go +++ b/info.go @@ -24,13 +24,14 @@ import ( "log/slog" "os" + "github.com/jeandeaual/go-locale" "github.com/leonelquinteros/gotext" "github.com/urfave/cli/v2" "gopkg.in/yaml.v3" "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/db" + database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db" "gitea.plemya-x.ru/Plemya-x/ALR/internal/overrides" "gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" "gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos" @@ -47,11 +48,39 @@ func InfoCmd() *cli.Command { Usage: gotext.Get("Show all information, not just for the current distro"), }, }, + BashComplete: func(c *cli.Context) { + ctx := c.Context + cfg := config.New() + db := database.New(cfg) + err := db.Init(ctx) + if err != nil { + slog.Error(gotext.Get("Error initialization database"), "err", err) + os.Exit(1) + } + + result, err := db.GetPkgs(c.Context, "true") + if err != nil { + slog.Error(gotext.Get("Error getting packages"), "err", err) + os.Exit(1) + } + defer result.Close() + + for result.Next() { + var pkg database.Package + err = result.StructScan(&pkg) + if err != nil { + slog.Error(gotext.Get("Error iterating over packages"), "err", err) + os.Exit(1) + } + + fmt.Println(pkg.Name) + } + }, Action: func(c *cli.Context) error { ctx := c.Context cfg := config.New() - db := db.New(cfg) + db := database.New(cfg) err := db.Init(ctx) if err != nil { slog.Error(gotext.Get("Error initialization database"), "err", err) @@ -88,6 +117,12 @@ func InfoCmd() *cli.Command { var names []string all := c.Bool("all") + systemLang, err := locale.GetLanguage() + if err != nil { + slog.Error("Can't detect system language", "err", err) + os.Exit(1) + } + if !all { info, err := distro.ParseOSRelease(ctx) if err != nil { @@ -97,7 +132,7 @@ func InfoCmd() *cli.Command { names, err = overrides.Resolve( info, overrides.DefaultOpts. - WithLanguages([]string{config.SystemLang()}), + WithLanguages([]string{systemLang}), ) if err != nil { slog.Error(gotext.Get("Error resolving overrides"), "err", err) diff --git a/install.go b/install.go index 2706ada..046c170 100644 --- a/install.go +++ b/install.go @@ -69,7 +69,7 @@ func InstallCmd() *cli.Command { rs := repos.New(cfg, db) err := db.Init(ctx) if err != nil { - slog.Error(gotext.Get("Error db init"), "err", err) + slog.Error(gotext.Get("Error initialization database"), "err", err) os.Exit(1) } diff --git a/internal/config/config.go b/internal/config/config.go index 11c69b8..ff400c3 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -152,6 +152,13 @@ func (c *ALRConfig) Repos(ctx context.Context) []types.Repo { return c.cfg.Repos } +func (c *ALRConfig) SetRepos(ctx context.Context, repos []types.Repo) { + c.cfgOnce.Do(func() { + c.Load(ctx) + }) + c.cfg.Repos = repos +} + func (c *ALRConfig) IgnorePkgUpdates(ctx context.Context) []string { c.cfgOnce.Do(func() { c.Load(ctx) @@ -172,3 +179,17 @@ func (c *ALRConfig) PagerStyle(ctx context.Context) string { }) return c.cfg.PagerStyle } + +func (c *ALRConfig) AllowRunAsRoot(ctx context.Context) bool { + c.cfgOnce.Do(func() { + c.Load(ctx) + }) + return c.cfg.Unsafe.AllowRunAsRoot +} + +func (c *ALRConfig) RootCmd(ctx context.Context) string { + c.cfgOnce.Do(func() { + c.Load(ctx) + }) + return c.cfg.RootCmd +} diff --git a/internal/config/config_legacy.go b/internal/config/config_legacy.go deleted file mode 100644 index 5be9bae..0000000 --- a/internal/config/config_legacy.go +++ /dev/null @@ -1,52 +0,0 @@ -// 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 config - -import ( - "context" - "sync" - - "gitea.plemya-x.ru/Plemya-x/ALR/internal/types" -) - -// Config returns a ALR configuration struct. -// The first time it's called, it'll load the config from a file. -// Subsequent calls will just return the same value. -// -// Deprecated: use struct method -func Config(ctx context.Context) *types.Config { - return GetInstance(ctx).cfg -} - -// ======================= -// FOR LEGACY ONLY -// ======================= - -var ( - alrConfig *ALRConfig - alrConfigOnce sync.Once -) - -// Deprecated: For legacy only -func GetInstance(ctx context.Context) *ALRConfig { - alrConfigOnce.Do(func() { - alrConfig = New() - alrConfig.Load(ctx) - }) - - return alrConfig -} diff --git a/internal/config/lang.go b/internal/config/lang.go deleted file mode 100644 index 9d19d1b..0000000 --- a/internal/config/lang.go +++ /dev/null @@ -1,70 +0,0 @@ -// 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 . - -package config - -import ( - "context" - "log/slog" - "os" - "strings" - "sync" - - "github.com/leonelquinteros/gotext" - "golang.org/x/text/language" -) - -var ( - langMtx sync.Mutex - lang language.Tag - langSet bool -) - -// Language returns the system language. -// The first time it's called, it'll detect the langauge based on -// the $LANG environment variable. -// Subsequent calls will just return the same value. -func Language(ctx context.Context) language.Tag { - langMtx.Lock() - if !langSet { - syslang := SystemLang() - tag, err := language.Parse(syslang) - if err != nil { - slog.Error(gotext.Get("Error parsing system language"), "err", err) - langMtx.Unlock() - os.Exit(1) - } - base, _ := tag.Base() - lang = language.Make(base.String()) - langSet = true - } - langMtx.Unlock() - return lang -} - -// SystemLang returns the system language based on -// the $LANG environment variable. -func SystemLang() string { - lang := os.Getenv("LANG") - lang, _, _ = strings.Cut(lang, ".") - if lang == "" || lang == "C" { - lang = "en" - } - return lang -} diff --git a/internal/config/paths.go b/internal/config/paths.go index 78c5bde..9b350d0 100644 --- a/internal/config/paths.go +++ b/internal/config/paths.go @@ -19,10 +19,6 @@ package config -import ( - "context" -) - // Paths contains various paths used by ALR type Paths struct { ConfigDir string @@ -32,14 +28,3 @@ type Paths struct { PkgsDir string DBPath string } - -// GetPaths returns a Paths struct. -// The first time it's called, it'll generate the struct -// using information from the system. -// Subsequent calls will return the same value. -// -// Deprecated: use struct API -func GetPaths(ctx context.Context) *Paths { - alrConfig := GetInstance(ctx) - return alrConfig.GetPaths(ctx) -} diff --git a/internal/db/db_legacy.go b/internal/db/db_legacy.go deleted file mode 100644 index 1d6cc7a..0000000 --- a/internal/db/db_legacy.go +++ /dev/null @@ -1,106 +0,0 @@ -// 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 db - -import ( - "context" - "log/slog" - "os" - "sync" - - "github.com/jmoiron/sqlx" - "github.com/leonelquinteros/gotext" - - "gitea.plemya-x.ru/Plemya-x/ALR/internal/config" -) - -// DB returns the ALR database. -// The first time it's called, it opens the SQLite database file. -// Subsequent calls return the same connection. -// -// Deprecated: use struct method -func DB(ctx context.Context) *sqlx.DB { - return GetInstance(ctx).GetConn() -} - -// Close closes the database -// -// Deprecated: use struct method -func Close() error { - if database != nil { - return database.Close() - } - return nil -} - -// IsEmpty returns true if the database has no packages in it, otherwise it returns false. -// -// Deprecated: use struct method -func IsEmpty(ctx context.Context) bool { - return GetInstance(ctx).IsEmpty(ctx) -} - -// InsertPackage adds a package to the database -// -// Deprecated: use struct method -func InsertPackage(ctx context.Context, pkg Package) error { - return GetInstance(ctx).InsertPackage(ctx, pkg) -} - -// GetPkgs returns a result containing packages that match the where conditions -// -// Deprecated: use struct method -func GetPkgs(ctx context.Context, where string, args ...any) (*sqlx.Rows, error) { - return GetInstance(ctx).GetPkgs(ctx, where, args...) -} - -// GetPkg returns a single package that matches the where conditions -// -// Deprecated: use struct method -func GetPkg(ctx context.Context, where string, args ...any) (*Package, error) { - return GetInstance(ctx).GetPkg(ctx, where, args...) -} - -// DeletePkgs deletes all packages matching the where conditions -// -// Deprecated: use struct method -func DeletePkgs(ctx context.Context, where string, args ...any) error { - return GetInstance(ctx).DeletePkgs(ctx, where, args...) -} - -// ======================= -// FOR LEGACY ONLY -// ======================= - -var ( - dbOnce sync.Once - database *Database -) - -// Deprecated: For legacy only -func GetInstance(ctx context.Context) *Database { - dbOnce.Do(func() { - cfg := config.GetInstance(ctx) - database = New(cfg) - err := database.Init(ctx) - if err != nil { - slog.Error(gotext.Get("Error opening database"), "err", err) - os.Exit(1) - } - }) - return database -} diff --git a/internal/dlcache/dlcache_test.go b/internal/dlcache/dlcache_test.go index e9c0832..087c4cc 100644 --- a/internal/dlcache/dlcache_test.go +++ b/internal/dlcache/dlcache_test.go @@ -32,14 +32,6 @@ import ( "gitea.plemya-x.ru/Plemya-x/ALR/internal/dlcache" ) -func init() { - dir, err := os.MkdirTemp("/tmp", "alr-dlcache-test.*") - if err != nil { - panic(err) - } - config.GetPaths(context.Background()).RepoDir = dir -} - type TestALRConfig struct { CacheDir string } diff --git a/internal/pager/pager.go b/internal/pager/pager.go index f3aef28..55a7bad 100644 --- a/internal/pager/pager.go +++ b/internal/pager/pager.go @@ -41,7 +41,7 @@ func init() { b2 := lipgloss.RoundedBorder() b2.Left = "\u2524" - infoStyle = titleStyle.Copy().BorderStyle(b2) + infoStyle = titleStyle.BorderStyle(b2) } type Pager struct { diff --git a/internal/shutils/handlers/nop.go b/internal/shutils/handlers/nop.go index f8013d0..dfd4045 100644 --- a/internal/shutils/handlers/nop.go +++ b/internal/shutils/handlers/nop.go @@ -22,10 +22,11 @@ package handlers import ( "context" "io" + "io/fs" "os" ) -func NopReadDir(context.Context, string) ([]os.FileInfo, error) { +func NopReadDir(context.Context, string) ([]fs.DirEntry, error) { return nil, os.ErrNotExist } diff --git a/internal/shutils/handlers/restricted.go b/internal/shutils/handlers/restricted.go index 760df98..557c5fe 100644 --- a/internal/shutils/handlers/restricted.go +++ b/internal/shutils/handlers/restricted.go @@ -31,12 +31,12 @@ import ( "mvdan.cc/sh/v3/interp" ) -func RestrictedReadDir(allowedPrefixes ...string) interp.ReadDirHandlerFunc { - return func(ctx context.Context, s string) ([]fs.FileInfo, error) { +func RestrictedReadDir(allowedPrefixes ...string) interp.ReadDirHandlerFunc2 { + return func(ctx context.Context, s string) ([]fs.DirEntry, error) { path := filepath.Clean(s) for _, allowedPrefix := range allowedPrefixes { if strings.HasPrefix(path, allowedPrefix) { - return interp.DefaultReadDirHandler()(ctx, s) + return interp.DefaultReadDirHandler2()(ctx, s) } } diff --git a/internal/translations/default.pot b/internal/translations/default.pot index fe6da00..6739f13 100644 --- a/internal/translations/default.pot +++ b/internal/translations/default.pot @@ -31,7 +31,7 @@ msgid "" msgstr "" #: build.go:75 -msgid "Error db init" +msgid "Error initialization database" msgstr "" #: build.go:105 @@ -66,27 +66,27 @@ msgstr "" msgid "Attempt to fix problems with ALR" msgstr "" -#: fix.go:44 +#: fix.go:43 msgid "Removing cache directory" msgstr "" -#: fix.go:48 +#: fix.go:47 msgid "Unable to remove cache directory" msgstr "" -#: fix.go:52 +#: fix.go:51 msgid "Rebuilding cache" msgstr "" -#: fix.go:56 +#: fix.go:55 msgid "Unable to create new cache directory" msgstr "" -#: fix.go:62 +#: fix.go:69 msgid "Error pulling repos" msgstr "" -#: fix.go:66 +#: fix.go:73 msgid "Done" msgstr "" @@ -114,35 +114,39 @@ msgstr "" msgid "No such helper command" msgstr "" -#: info.go:42 +#: info.go:43 msgid "Print information about a package" msgstr "" -#: info.go:47 +#: info.go:48 msgid "Show all information, not just for the current distro" msgstr "" -#: info.go:57 -msgid "Error initialization database" +#: info.go:63 +msgid "Error getting packages" msgstr "" -#: info.go:64 +#: info.go:72 +msgid "Error iterating over packages" +msgstr "" + +#: info.go:93 msgid "Command info expected at least 1 argument, got %d" msgstr "" -#: info.go:78 +#: info.go:107 msgid "Error finding packages" msgstr "" -#: info.go:94 +#: info.go:129 msgid "Error parsing os-release file" msgstr "" -#: info.go:103 +#: info.go:138 msgid "Error resolving overrides" msgstr "" -#: info.go:112 info.go:118 +#: info.go:147 info.go:153 msgid "Error encoding script variables" msgstr "" @@ -154,14 +158,6 @@ msgstr "" msgid "Command install expected at least 1 argument, got %d" msgstr "" -#: install.go:124 -msgid "Error getting packages" -msgstr "" - -#: install.go:133 -msgid "Error iterating over packages" -msgstr "" - #: install.go:146 msgid "Remove an installed package" msgstr "" @@ -234,10 +230,6 @@ msgstr "" msgid "Unable to create package cache directory" msgstr "" -#: internal/config/lang.go:49 -msgid "Error parsing system language" -msgstr "" - #: internal/db/db.go:133 msgid "Database version mismatch; resetting" msgstr "" @@ -247,10 +239,6 @@ msgid "" "Database version does not exist. Run alr fix if something isn't working." msgstr "" -#: internal/db/db_legacy.go:101 -msgid "Error opening database" -msgstr "" - #: internal/dl/dl.go:170 msgid "Source can be updated, updating if required" msgstr "" @@ -287,15 +275,15 @@ msgstr "" msgid "Error listing installed packages" msgstr "" -#: main.go:45 +#: main.go:44 msgid "Print the current ALR version and exit" msgstr "" -#: main.go:61 +#: main.go:60 msgid "Arguments to be passed on to the package manager" msgstr "" -#: main.go:67 +#: main.go:66 msgid "Enable interactive questions and prompts" msgstr "" @@ -305,7 +293,7 @@ msgid "" "system" msgstr "" -#: main.go:125 +#: main.go:123 msgid "Error while running app" msgstr "" @@ -329,49 +317,49 @@ msgstr "" msgid "Compressing package" msgstr "" -#: pkg/build/build.go:419 +#: pkg/build/build.go:421 msgid "" "Your system's CPU architecture doesn't match this package. Do you want to " "build anyway?" msgstr "" -#: pkg/build/build.go:433 +#: pkg/build/build.go:435 msgid "This package is already installed" msgstr "" -#: pkg/build/build.go:457 +#: pkg/build/build.go:459 msgid "Installing build dependencies" msgstr "" -#: pkg/build/build.go:498 +#: pkg/build/build.go:500 msgid "Installing dependencies" msgstr "" -#: pkg/build/build.go:533 +#: pkg/build/build.go:535 msgid "The checksums array must be the same length as sources" msgstr "" -#: pkg/build/build.go:584 +#: pkg/build/build.go:586 msgid "Would you like to remove the build dependencies?" msgstr "" -#: pkg/build/build.go:647 +#: pkg/build/build.go:649 msgid "Executing prepare()" msgstr "" -#: pkg/build/build.go:657 +#: pkg/build/build.go:659 msgid "Executing build()" msgstr "" -#: pkg/build/build.go:687 pkg/build/build.go:707 +#: pkg/build/build.go:689 pkg/build/build.go:709 msgid "Executing %s()" msgstr "" -#: pkg/build/build.go:766 +#: pkg/build/build.go:768 msgid "Error installing native packages" msgstr "" -#: pkg/build/build.go:790 +#: pkg/build/build.go:792 msgid "Error installing package" msgstr "" @@ -425,35 +413,35 @@ msgstr "" msgid "URL of the new repo" msgstr "" -#: repo.go:79 repo.go:136 +#: repo.go:82 repo.go:147 msgid "Error opening config file" msgstr "" -#: repo.go:85 repo.go:142 +#: repo.go:88 repo.go:153 msgid "Error encoding config" msgstr "" -#: repo.go:103 +#: repo.go:113 msgid "Remove an existing repository" msgstr "" -#: repo.go:110 +#: repo.go:120 msgid "Name of the repo to be deleted" msgstr "" -#: repo.go:128 +#: repo.go:139 msgid "Repo does not exist" msgstr "" -#: repo.go:148 +#: repo.go:159 msgid "Error removing repo directory" msgstr "" -#: repo.go:154 +#: repo.go:170 msgid "Error removing packages from database" msgstr "" -#: repo.go:166 +#: repo.go:182 msgid "Pull all repositories that have changed" msgstr "" @@ -481,19 +469,11 @@ msgstr "" msgid "Format output using a Go template" msgstr "" -#: search.go:94 -msgid "At least one search parameter is required" -msgstr "" - -#: search.go:100 -msgid "Error db search" -msgstr "" - -#: search.go:109 +#: search.go:82 search.go:99 msgid "Error parsing format template" msgstr "" -#: search.go:124 +#: search.go:107 msgid "Error executing template" msgstr "" diff --git a/internal/translations/po/ru/default.po b/internal/translations/po/ru/default.po index 9a19c9a..cb9e07a 100644 --- a/internal/translations/po/ru/default.po +++ b/internal/translations/po/ru/default.po @@ -38,8 +38,8 @@ msgid "" msgstr "Создайте пакет с нуля, даже если уже имеется готовый пакет" #: build.go:75 -msgid "Error db init" -msgstr "" +msgid "Error initialization database" +msgstr "Ошибка инициализации базы данных" #: build.go:105 msgid "Package not found" @@ -74,27 +74,27 @@ msgstr "Ошибка при перемещении пакета" msgid "Attempt to fix problems with ALR" msgstr "Попытка устранить проблемы с ALR" -#: fix.go:44 +#: fix.go:43 msgid "Removing cache directory" msgstr "Удаление каталога кэша" -#: fix.go:48 +#: fix.go:47 msgid "Unable to remove cache directory" msgstr "Не удалось удалить каталог кэша" -#: fix.go:52 +#: fix.go:51 msgid "Rebuilding cache" msgstr "Восстановление кэша" -#: fix.go:56 +#: fix.go:55 msgid "Unable to create new cache directory" msgstr "Не удалось создать новый каталог кэша" -#: fix.go:62 +#: fix.go:69 msgid "Error pulling repos" msgstr "Ошибка при извлечении репозиториев" -#: fix.go:66 +#: fix.go:73 msgid "Done" msgstr "Сделано" @@ -122,35 +122,39 @@ msgstr "Каталог, в который будут устанавливать msgid "No such helper command" msgstr "Такой вспомогательной команды нет" -#: info.go:42 +#: info.go:43 msgid "Print information about a package" msgstr "Отобразить информацию о пакете" -#: info.go:47 +#: info.go:48 msgid "Show all information, not just for the current distro" msgstr "Показывать всю информацию, не только для текущего дистрибутива" -#: info.go:57 -msgid "Error initialization database" -msgstr "Ошибка инициализации базы данных" +#: info.go:63 +msgid "Error getting packages" +msgstr "Ошибка при получении пакетов" -#: info.go:64 +#: info.go:72 +msgid "Error iterating over packages" +msgstr "Ошибка при переборе пакетов" + +#: info.go:93 msgid "Command info expected at least 1 argument, got %d" msgstr "Для команды info ожидался хотя бы 1 аргумент, получено %d" -#: info.go:78 +#: info.go:107 msgid "Error finding packages" msgstr "Ошибка при поиске пакетов" -#: info.go:94 +#: info.go:129 msgid "Error parsing os-release file" msgstr "Ошибка при разборе файла выпуска операционной системы" -#: info.go:103 +#: info.go:138 msgid "Error resolving overrides" msgstr "Ошибка устранения переорпеделений" -#: info.go:112 info.go:118 +#: info.go:147 info.go:153 msgid "Error encoding script variables" msgstr "Ошибка кодирования переменных скрита" @@ -162,14 +166,6 @@ msgstr "Установить новый пакет" msgid "Command install expected at least 1 argument, got %d" msgstr "Для команды install ожидался хотя бы 1 аргумент, получено %d" -#: install.go:124 -msgid "Error getting packages" -msgstr "Ошибка при получении пакетов" - -#: install.go:133 -msgid "Error iterating over packages" -msgstr "Ошибка при переборе пакетов" - #: install.go:146 msgid "Remove an installed package" msgstr "Удалить установленный пакет" @@ -246,10 +242,6 @@ msgstr "Не удалось создать каталог кэша репози msgid "Unable to create package cache directory" msgstr "Не удалось создать каталог кэша пакетов" -#: internal/config/lang.go:49 -msgid "Error parsing system language" -msgstr "Ошибка при парсинге языка системы" - #: internal/db/db.go:133 msgid "Database version mismatch; resetting" msgstr "Несоответствие версий базы данных; сброс настроек" @@ -260,10 +252,6 @@ msgid "" msgstr "" "Версия базы данных не существует. Запустите alr fix, если что-то не работает." -#: internal/db/db_legacy.go:101 -msgid "Error opening database" -msgstr "Ошибка при открытии базы данных" - #: internal/dl/dl.go:170 msgid "Source can be updated, updating if required" msgstr "Исходный код можно обновлять, обновляя при необходимости" @@ -300,15 +288,15 @@ msgstr "Список пакетов репозитория ALR" msgid "Error listing installed packages" msgstr "Ошибка при составлении списка установленных пакетов" -#: main.go:45 +#: main.go:44 msgid "Print the current ALR version and exit" msgstr "Показать текущую версию ALR и выйти" -#: main.go:61 +#: main.go:60 msgid "Arguments to be passed on to the package manager" msgstr "Аргументы, которые будут переданы менеджеру пакетов" -#: main.go:67 +#: main.go:66 msgid "Enable interactive questions and prompts" msgstr "Включение интерактивных вопросов и запросов" @@ -320,7 +308,7 @@ msgstr "" "Запуск ALR от имени root запрещён, так как это может привести к " "катастрофическому повреждению вашей системы" -#: main.go:125 +#: main.go:123 msgid "Error while running app" msgstr "Ошибка при запуске приложения" @@ -344,7 +332,7 @@ msgstr "Сборка метаданных пакета" msgid "Compressing package" msgstr "Сжатие пакета" -#: pkg/build/build.go:419 +#: pkg/build/build.go:421 msgid "" "Your system's CPU architecture doesn't match this package. Do you want to " "build anyway?" @@ -352,44 +340,44 @@ msgstr "" "Архитектура процессора вашей системы не соответствует этому пакету. Вы все " "равно хотите выполнить сборку?" -#: pkg/build/build.go:433 +#: pkg/build/build.go:435 msgid "This package is already installed" msgstr "Этот пакет уже установлен" -#: pkg/build/build.go:457 +#: pkg/build/build.go:459 msgid "Installing build dependencies" msgstr "Установка зависимостей сборки" -#: pkg/build/build.go:498 +#: pkg/build/build.go:500 msgid "Installing dependencies" msgstr "Установка зависимостей" -#: pkg/build/build.go:533 +#: pkg/build/build.go:535 msgid "The checksums array must be the same length as sources" msgstr "Массив контрольных сумм должен быть той же длины, что и источники" -#: pkg/build/build.go:584 +#: pkg/build/build.go:586 msgid "Would you like to remove the build dependencies?" msgstr "Хотели бы вы удалить зависимости сборки?" -#: pkg/build/build.go:647 +#: pkg/build/build.go:649 msgid "Executing prepare()" msgstr "Исполнение prepare()" -#: pkg/build/build.go:657 +#: pkg/build/build.go:659 msgid "Executing build()" msgstr "Исполнение build()" -#: pkg/build/build.go:687 pkg/build/build.go:707 +#: pkg/build/build.go:689 pkg/build/build.go:709 #, fuzzy msgid "Executing %s()" msgstr "Исполнение files()" -#: pkg/build/build.go:766 +#: pkg/build/build.go:768 msgid "Error installing native packages" msgstr "Ошибка при установке нативных пакетов" -#: pkg/build/build.go:790 +#: pkg/build/build.go:792 msgid "Error installing package" msgstr "Ошибка при установке пакета" @@ -447,35 +435,35 @@ msgstr "Название нового репозитория" msgid "URL of the new repo" msgstr "URL-адрес нового репозитория" -#: repo.go:79 repo.go:136 +#: repo.go:82 repo.go:147 msgid "Error opening config file" msgstr "Ошибка при открытии конфигурационного файла" -#: repo.go:85 repo.go:142 +#: repo.go:88 repo.go:153 msgid "Error encoding config" msgstr "Ошибка при кодировании конфигурации" -#: repo.go:103 +#: repo.go:113 msgid "Remove an existing repository" msgstr "Удалить существующий репозиторий" -#: repo.go:110 +#: repo.go:120 msgid "Name of the repo to be deleted" msgstr "Название репозитория удалён" -#: repo.go:128 +#: repo.go:139 msgid "Repo does not exist" msgstr "Репозитория не существует" -#: repo.go:148 +#: repo.go:159 msgid "Error removing repo directory" msgstr "Ошибка при удалении каталога репозитория" -#: repo.go:154 +#: repo.go:170 msgid "Error removing packages from database" msgstr "Ошибка при удалении пакетов из базы данных" -#: repo.go:166 +#: repo.go:182 msgid "Pull all repositories that have changed" msgstr "Скачать все изменённые репозитории" @@ -504,20 +492,12 @@ msgstr "" msgid "Format output using a Go template" msgstr "" -#: search.go:94 -msgid "At least one search parameter is required" -msgstr "" - -#: search.go:100 -msgid "Error db search" -msgstr "" - -#: search.go:109 +#: search.go:82 search.go:99 #, fuzzy msgid "Error parsing format template" msgstr "Ошибка при разборе файла выпуска операционной системы" -#: search.go:124 +#: search.go:107 #, fuzzy msgid "Error executing template" msgstr "Ошибка при получении пакетов" @@ -534,6 +514,12 @@ msgstr "Ошибка при проверке обновлений" msgid "There is nothing to do." msgstr "Здесь нечего делать." +#~ msgid "Error parsing system language" +#~ msgstr "Ошибка при парсинге языка системы" + +#~ msgid "Error opening database" +#~ msgstr "Ошибка при открытии базы данных" + #~ msgid "Executing version()" #~ msgstr "Исполнение версия()" diff --git a/main.go b/main.go index 3d548f5..2bb286a 100644 --- a/main.go +++ b/main.go @@ -32,7 +32,6 @@ import ( "github.com/urfave/cli/v2" "gitea.plemya-x.ru/Plemya-x/ALR/internal/config" - "gitea.plemya-x.ru/Plemya-x/ALR/internal/db" "gitea.plemya-x.ru/Plemya-x/ALR/internal/translations" "gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" @@ -85,9 +84,10 @@ func GetApp() *cli.App { }, Before: func(c *cli.Context) error { ctx := c.Context + cfg := config.New() cmd := c.Args().First() - if cmd != "helper" && !config.Config(ctx).Unsafe.AllowRunAsRoot && os.Geteuid() == 0 { + if cmd != "helper" && !cfg.AllowRunAsRoot(ctx) && os.Geteuid() == 0 { slog.Error(gotext.Get("Running ALR as root is forbidden as it may cause catastrophic damage to your system")) os.Exit(1) } @@ -99,9 +99,6 @@ func GetApp() *cli.App { return nil }, - After: func(ctx *cli.Context) error { - return db.Close() - }, EnableBashCompletion: true, } } @@ -111,11 +108,12 @@ func main() { logger.SetupDefault() app := GetApp() + cfg := config.New() ctx := context.Background() // Set the root command to the one set in the ALR config - manager.DefaultRootCmd = config.Config(ctx).RootCmd + manager.DefaultRootCmd = cfg.RootCmd(ctx) ctx, cancel := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM) defer cancel() diff --git a/pkg/build/build.go b/pkg/build/build.go index 0ecd9b3..38d91a5 100644 --- a/pkg/build/build.go +++ b/pkg/build/build.go @@ -306,7 +306,7 @@ func (b *Builder) executeFirstPass( interp.Env(expand.ListEnviron(env...)), // Устанавливаем окружение interp.StdIO(os.Stdin, os.Stdout, os.Stderr), // Устанавливаем стандартный ввод-вывод interp.ExecHandler(helpers.Restricted.ExecHandler(handlers.NopExec)), // Ограничиваем выполнение - interp.ReadDirHandler(handlers.RestrictedReadDir(scriptDir)), // Ограничиваем чтение директорий + interp.ReadDirHandler2(handlers.RestrictedReadDir(scriptDir)), // Ограничиваем чтение директорий interp.StatHandler(handlers.RestrictedStat(scriptDir)), // Ограничиваем доступ к статистике файлов interp.OpenHandler(handlers.RestrictedOpen(scriptDir)), // Ограничиваем открытие файлов ) @@ -395,9 +395,11 @@ func (b *Builder) executeSecondPass( fakeroot := handlers.FakerootExecHandler(2 * time.Second) // Настраиваем "fakeroot" для выполнения runner, err := interp.New( - interp.Env(expand.ListEnviron(env...)), // Устанавливаем окружение - interp.StdIO(os.Stdin, os.Stdout, os.Stderr), // Устанавливаем стандартный ввод-вывод - interp.ExecHandler(helpers.Helpers.ExecHandler(fakeroot)), // Обрабатываем выполнение через fakeroot + interp.Env(expand.ListEnviron(env...)), // Устанавливаем окружение + interp.StdIO(os.Stdin, os.Stdout, os.Stderr), // Устанавливаем стандартный ввод-вывод + interp.ExecHandlers(func(next interp.ExecHandlerFunc) interp.ExecHandlerFunc { + return helpers.Helpers.ExecHandler(fakeroot) + }), // Обрабатываем выполнение через fakeroot ) if err != nil { return nil, err diff --git a/pkg/distro/osrelease.go b/pkg/distro/osrelease.go index 4a9e2c3..8320f94 100644 --- a/pkg/distro/osrelease.go +++ b/pkg/distro/osrelease.go @@ -79,7 +79,7 @@ func ParseOSRelease(ctx context.Context) (*OSRelease, error) { runner, err := interp.New( interp.OpenHandler(handlers.NopOpen), interp.ExecHandler(handlers.NopExec), - interp.ReadDirHandler(handlers.NopReadDir), + interp.ReadDirHandler2(handlers.NopReadDir), interp.StatHandler(handlers.NopStat), interp.Env(expand.ListEnviron()), ) diff --git a/pkg/repos/pull.go b/pkg/repos/pull.go index 98a86fd..e1bad05 100644 --- a/pkg/repos/pull.go +++ b/pkg/repos/pull.go @@ -77,7 +77,7 @@ func (rs *Repos) Pull(ctx context.Context, repos []types.Repo) error { } slog.Info(gotext.Get("Pulling repository"), "name", repo.Name) - repoDir := filepath.Join(config.GetPaths(ctx).RepoDir, repo.Name) + repoDir := filepath.Join(rs.cfg.GetPaths(ctx).RepoDir, repo.Name) var repoFS billy.Filesystem gitDir := filepath.Join(repoDir, ".git") @@ -264,7 +264,7 @@ func (rs *Repos) processRepoChangesRunner(repoDir, scriptDir string) (*interp.Ru return interp.New( interp.Env(expand.ListEnviron(env...)), interp.ExecHandler(handlers.NopExec), - interp.ReadDirHandler(handlers.RestrictedReadDir(repoDir)), + interp.ReadDirHandler2(handlers.RestrictedReadDir(repoDir)), interp.StatHandler(handlers.RestrictedStat(repoDir)), interp.OpenHandler(handlers.RestrictedOpen(repoDir)), interp.StdIO(handlers.NopRWC{}, handlers.NopRWC{}, handlers.NopRWC{}), diff --git a/pkg/repos/repos_legacy.go b/pkg/repos/repos_legacy.go deleted file mode 100644 index 81b3bb6..0000000 --- a/pkg/repos/repos_legacy.go +++ /dev/null @@ -1,60 +0,0 @@ -// 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 repos - -import ( - "context" - "sync" - - "gitea.plemya-x.ru/Plemya-x/ALR/internal/config" - database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db" - "gitea.plemya-x.ru/Plemya-x/ALR/internal/types" -) - -// Pull pulls the provided repositories. If a repo doesn't exist, it will be cloned -// and its packages will be written to the DB. If it does exist, it will be pulled. -// In this case, only changed packages will be processed if possible. -// If repos is set to nil, the repos in the ALR config will be used. -// -// Deprecated: use struct method -func Pull(ctx context.Context, repos []types.Repo) error { - return GetInstance(ctx).Pull(ctx, repos) -} - -// ======================= -// FOR LEGACY ONLY -// ======================= - -var ( - reposInstance *Repos - alrConfigOnce sync.Once -) - -// Deprecated: For legacy only -func GetInstance(ctx context.Context) *Repos { - alrConfigOnce.Do(func() { - cfg := config.GetInstance(ctx) - db := database.GetInstance(ctx) - - reposInstance = New( - cfg, - db, - ) - }) - - return reposInstance -} diff --git a/pkg/search/search.go b/pkg/search/search.go index 9716df9..2bee506 100644 --- a/pkg/search/search.go +++ b/pkg/search/search.go @@ -21,166 +21,46 @@ package search import ( "context" - "errors" - "io" - "io/fs" - "os" - "path/filepath" - "strconv" - "strings" - "gitea.plemya-x.ru/Plemya-x/ALR/internal/config" - "gitea.plemya-x.ru/Plemya-x/ALR/internal/db" + "github.com/jmoiron/sqlx" + + database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db" ) -// Filter represents search filters. -type Filter int - -// Filters -const ( - FilterNone Filter = iota - FilterInRepo - FilterSupportsArch -) - -// SoryBy represents a value that packages can be sorted by. -type SortBy int - -// Sort values -const ( - SortByNone = iota - SortByName - SortByRepo - SortByVersion -) - -// Package represents a package from ALR's database -type Package struct { - Name string - Version string - Release int - Epoch uint - Description map[string]string - Homepage map[string]string - Maintainer map[string]string - Architectures []string - Licenses []string - Provides []string - Conflicts []string - Replaces []string - Depends map[string][]string - BuildDepends map[string][]string - OptDepends map[string][]string - Repository string +type PackagesProvider interface { + GetPkgs(ctx context.Context, where string, args ...any) (*sqlx.Rows, error) } -func convertPkg(p db.Package) Package { - return Package{ - Name: p.Name, - Version: p.Version, - Release: p.Release, - Epoch: p.Epoch, - Description: p.Description.Val, - Homepage: p.Homepage.Val, - Maintainer: p.Maintainer.Val, - Architectures: p.Architectures.Val, - Licenses: p.Licenses.Val, - Provides: p.Provides.Val, - Conflicts: p.Conflicts.Val, - Replaces: p.Replaces.Val, - Depends: p.Depends.Val, - BuildDepends: p.BuildDepends.Val, - OptDepends: p.OptDepends.Val, - Repository: p.Repository, +type Searcher struct { + pp PackagesProvider +} + +func New(pp PackagesProvider) *Searcher { + return &Searcher{ + pp: pp, } } -// Options contains the options for a search. -type Options struct { - Filter Filter - FilterValue string - SortBy SortBy - Limit int64 - Query string -} +func (s *Searcher) Search( + ctx context.Context, + opts *SearchOptions, +) ([]database.Package, error) { + var packages []database.Package -// Search searches for packages in the database based on the given options. -func Search(ctx context.Context, opts Options) ([]Package, error) { - query := "(name LIKE ? OR description LIKE ? OR json_array_contains(provides, ?))" - args := []any{"%" + opts.Query + "%", "%" + opts.Query + "%", opts.Query} - - if opts.Filter != FilterNone { - switch opts.Filter { - case FilterInRepo: - query += " AND repository = ?" - case FilterSupportsArch: - query += " AND json_array_contains(architectures, ?)" - } - args = append(args, opts.FilterValue) - } - - if opts.SortBy != SortByNone { - switch opts.SortBy { - case SortByName: - query += " ORDER BY name" - case SortByRepo: - query += " ORDER BY repository" - case SortByVersion: - query += " ORDER BY version" - } - } - - if opts.Limit != 0 { - query += " LIMIT " + strconv.FormatInt(opts.Limit, 10) - } - - result, err := db.GetPkgs(ctx, query, args...) + where, args := opts.WhereClause() + result, err := s.pp.GetPkgs(ctx, where, args...) if err != nil { return nil, err } - var out []Package for result.Next() { - pkg := db.Package{} - err = result.StructScan(&pkg) + var dbPkg database.Package + err = result.StructScan(&dbPkg) if err != nil { return nil, err } - out = append(out, convertPkg(pkg)) + packages = append(packages, dbPkg) } - return out, err -} - -// GetPkg gets a single package from the database and returns it. -func GetPkg(ctx context.Context, repo, name string) (Package, error) { - pkg, err := db.GetPkg(ctx, "name = ? AND repository = ?", name, repo) - return convertPkg(*pkg), err -} - -var ( - // ErrInvalidArgument is an error returned by GetScript when one of its arguments - // contain invalid characters - ErrInvalidArgument = errors.New("name and repository must not contain . or /") - - // ErrScriptNotFound is returned by GetScript if it can't find the script requested - // by the user. - ErrScriptNotFound = errors.New("requested script not found") -) - -// GetScript returns a reader containing the build script for a given package. -func GetScript(ctx context.Context, repo, name string) (io.ReadCloser, error) { - if strings.Contains(name, "./") || strings.ContainsAny(repo, "./") { - return nil, ErrInvalidArgument - } - - scriptPath := filepath.Join(config.GetPaths(ctx).RepoDir, repo, name, "alr.sh") - fl, err := os.Open(scriptPath) - if errors.Is(err, fs.ErrNotExist) { - return nil, ErrScriptNotFound - } else if err != nil { - return nil, err - } - - return fl, nil + return packages, nil } diff --git a/pkg/search/search_options_builder.go b/pkg/search/search_options_builder.go new file mode 100644 index 0000000..c823ebf --- /dev/null +++ b/pkg/search/search_options_builder.go @@ -0,0 +1,86 @@ +// 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 search + +import ( + "fmt" + "strings" +) + +type SearchOptions struct { + conditions []string + args []any +} + +func (o *SearchOptions) WhereClause() (string, []any) { + if len(o.conditions) == 0 { + return "", nil + } + return strings.Join(o.conditions, " AND "), o.args +} + +type SearchOptionsBuilder struct { + options SearchOptions +} + +func NewSearchOptions() *SearchOptionsBuilder { + return &SearchOptionsBuilder{} +} + +func (b *SearchOptionsBuilder) withGeneralLike(key, value string) *SearchOptionsBuilder { + if value != "" { + b.options.conditions = append(b.options.conditions, fmt.Sprintf("%s LIKE ?", key)) + b.options.args = append(b.options.args, "%"+value+"%") + } + return b +} + +func (b *SearchOptionsBuilder) withGeneralEqual(key string, value any) *SearchOptionsBuilder { + if value != "" { + b.options.conditions = append(b.options.conditions, fmt.Sprintf("%s = ?", key)) + b.options.args = append(b.options.args, value) + } + return b +} + +func (b *SearchOptionsBuilder) withGeneralJsonArrayContains(key string, value any) *SearchOptionsBuilder { + if value != "" { + b.options.conditions = append(b.options.conditions, fmt.Sprintf("json_array_contains(%s, ?)", key)) + b.options.args = append(b.options.args, value) + } + return b +} + +func (b *SearchOptionsBuilder) WithName(name string) *SearchOptionsBuilder { + return b.withGeneralLike("name", name) +} + +func (b *SearchOptionsBuilder) WithDescription(description string) *SearchOptionsBuilder { + return b.withGeneralLike("description", description) +} + +func (b *SearchOptionsBuilder) WithRepository(repository string) *SearchOptionsBuilder { + return b.withGeneralEqual("repository", repository) +} + +func (b *SearchOptionsBuilder) WithProvides(provides string) *SearchOptionsBuilder { + return b.withGeneralJsonArrayContains("provides", provides) +} + +func (b *SearchOptionsBuilder) Build() *SearchOptions { + return &b.options +} diff --git a/pkg/search/search_test.go b/pkg/search/search_test.go new file mode 100644 index 0000000..3f5542e --- /dev/null +++ b/pkg/search/search_test.go @@ -0,0 +1,65 @@ +// 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 search_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "gitea.plemya-x.ru/Plemya-x/ALR/pkg/search" +) + +func TestSearhOptionsBuilder(t *testing.T) { + type testCase struct { + name string + prepare func() *search.SearchOptions + expectedWhere string + expectedArgs []any + } + + for _, tc := range []testCase{ + { + name: "Empty fields", + prepare: func() *search.SearchOptions { + return search.NewSearchOptions(). + Build() + }, + expectedWhere: "", + expectedArgs: []any{}, + }, + { + name: "All fields", + prepare: func() *search.SearchOptions { + return search.NewSearchOptions(). + WithName("foo"). + WithDescription("bar"). + WithRepository("buz"). + WithProvides("test"). + Build() + }, + expectedWhere: "name LIKE ? AND description LIKE ? AND repository = ? AND json_array_contains(provides, ?)", + expectedArgs: []any{"%foo%", "%bar%", "buz", "test"}, + }, + } { + t.Run(tc.name, func(t *testing.T) { + whereClause, args := tc.prepare().WhereClause() + assert.Equal(t, tc.expectedWhere, whereClause) + assert.ElementsMatch(t, tc.expectedArgs, args) + }) + } +} diff --git a/repo.go b/repo.go index de18f8a..e6df733 100644 --- a/repo.go +++ b/repo.go @@ -30,7 +30,7 @@ import ( "golang.org/x/exp/slices" "gitea.plemya-x.ru/Plemya-x/ALR/internal/config" - "gitea.plemya-x.ru/Plemya-x/ALR/internal/db" + database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db" "gitea.plemya-x.ru/Plemya-x/ALR/internal/types" "gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos" ) @@ -60,21 +60,24 @@ func AddRepoCmd() *cli.Command { name := c.String("name") repoURL := c.String("url") - cfg := config.Config(ctx) + cfg := config.New() + reposSlice := cfg.Repos(ctx) - for _, repo := range cfg.Repos { + for _, repo := range reposSlice { if repo.URL == repoURL { slog.Error("Repo already exists", "name", repo.Name) os.Exit(1) } } - cfg.Repos = append(cfg.Repos, types.Repo{ + reposSlice = append(reposSlice, types.Repo{ Name: name, URL: repoURL, }) - cfgFl, err := os.Create(config.GetPaths(ctx).ConfigPath) + cfg.SetRepos(ctx, reposSlice) + + cfgFl, err := os.Create(cfg.GetPaths(ctx).ConfigPath) if err != nil { slog.Error(gotext.Get("Error opening config file"), "err", err) os.Exit(1) @@ -86,7 +89,14 @@ func AddRepoCmd() *cli.Command { os.Exit(1) } - err = repos.Pull(ctx, cfg.Repos) + db := database.New(cfg) + err = db.Init(ctx) + if err != nil { + slog.Error(gotext.Get("Error pulling repos"), "err", err) + } + + rs := repos.New(cfg, db) + err = rs.Pull(ctx, cfg.Repos(ctx)) if err != nil { slog.Error(gotext.Get("Error pulling repos"), "err", err) os.Exit(1) @@ -114,11 +124,12 @@ func RemoveRepoCmd() *cli.Command { ctx := c.Context name := c.String("name") - cfg := config.Config(ctx) + cfg := config.New() found := false index := 0 - for i, repo := range cfg.Repos { + reposSlice := cfg.Repos(ctx) + for i, repo := range reposSlice { if repo.Name == name { index = i found = true @@ -129,9 +140,9 @@ func RemoveRepoCmd() *cli.Command { os.Exit(1) } - cfg.Repos = slices.Delete(cfg.Repos, index, index+1) + cfg.SetRepos(ctx, slices.Delete(reposSlice, index, index+1)) - cfgFl, err := os.Create(config.GetPaths(ctx).ConfigPath) + cfgFl, err := os.Create(cfg.GetPaths(ctx).ConfigPath) if err != nil { slog.Error(gotext.Get("Error opening config file"), "err", err) os.Exit(1) @@ -143,12 +154,17 @@ func RemoveRepoCmd() *cli.Command { os.Exit(1) } - err = os.RemoveAll(filepath.Join(config.GetPaths(ctx).RepoDir, name)) + err = os.RemoveAll(filepath.Join(cfg.GetPaths(ctx).RepoDir, name)) if err != nil { slog.Error(gotext.Get("Error removing repo directory"), "err", err) os.Exit(1) } + db := database.New(cfg) + err = db.Init(ctx) + if err != nil { + os.Exit(1) + } err = db.DeletePkgs(ctx, "repository = ?", name) if err != nil { slog.Error(gotext.Get("Error removing packages from database"), "err", err) @@ -167,7 +183,14 @@ func RefreshCmd() *cli.Command { Aliases: []string{"ref"}, Action: func(c *cli.Context) error { ctx := c.Context - err := repos.Pull(ctx, config.Config(ctx).Repos) + cfg := config.New() + db := database.New(cfg) + err := db.Init(ctx) + if err != nil { + os.Exit(1) + } + rs := repos.New(cfg, db) + err = rs.Pull(ctx, cfg.Repos(ctx)) if err != nil { slog.Error(gotext.Get("Error pulling repos"), "err", err) os.Exit(1) diff --git a/search.go b/search.go index e2ace08..b8cf7d2 100644 --- a/search.go +++ b/search.go @@ -20,7 +20,6 @@ import ( "fmt" "log/slog" "os" - "strings" "text/template" "github.com/leonelquinteros/gotext" @@ -28,6 +27,7 @@ import ( "gitea.plemya-x.ru/Plemya-x/ALR/internal/config" database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db" + "gitea.plemya-x.ru/Plemya-x/ALR/pkg/search" ) func SearchCmd() *cli.Command { @@ -70,34 +70,7 @@ func SearchCmd() *cli.Command { defer db.Close() if err != nil { - slog.Error(gotext.Get("Error db init"), "err", err) - os.Exit(1) - } - - var conditions []string - if name := c.String("name"); name != "" { - conditions = append(conditions, fmt.Sprintf("name LIKE '%%%s%%'", name)) - } - if description := c.String("description"); description != "" { - conditions = append(conditions, fmt.Sprintf("description LIKE '%%%s%%'", description)) - } - if repo := c.String("repository"); repo != "" { - conditions = append(conditions, fmt.Sprintf("repository = '%s'", repo)) - } - if provides := c.String("provides"); provides != "" { - conditions = append(conditions, fmt.Sprintf("EXISTS (SELECT 1 FROM json_each(provides) WHERE value = '%s')", provides)) - } - query := "" - if len(conditions) > 0 { - query = strings.Join(conditions, " AND ") - } else { - slog.Error(gotext.Get("At least one search parameter is required")) - os.Exit(1) - } - - result, err := db.GetPkgs(ctx, query) - if err != nil { - slog.Error(gotext.Get("Error db search"), "err", err) + slog.Error(gotext.Get("Error initialization database"), "err", err) os.Exit(1) } @@ -111,13 +84,23 @@ func SearchCmd() *cli.Command { } } - for result.Next() { - var dbPkg database.Package - err = result.StructScan(&dbPkg) - if err != nil { - os.Exit(1) - } + s := search.New(db) + packages, err := s.Search( + ctx, + search.NewSearchOptions(). + WithName(c.String("name")). + WithDescription(c.String("description")). + WithRepository(c.String("repository")). + WithProvides(c.String("provides")). + Build(), + ) + if err != nil { + slog.Error(gotext.Get("Error parsing format template"), "err", err) + os.Exit(1) + } + + for _, dbPkg := range packages { if tmpl != nil { err = tmpl.Execute(os.Stdout, dbPkg) if err != nil { diff --git a/upgrade.go b/upgrade.go index 553cf41..f3aadf7 100644 --- a/upgrade.go +++ b/upgrade.go @@ -61,7 +61,7 @@ func UpgradeCmd() *cli.Command { rs := repos.New(cfg, db) err := db.Init(ctx) if err != nil { - slog.Error(gotext.Get("Error db init"), "err", err) + slog.Error(gotext.Get("Error initialization database"), "err", err) os.Exit(1) }