forked from Plemya-x/ALR
		
	chore: refactor code
- remove legacy code - refactor search and add tests
This commit is contained in:
		| @@ -47,4 +47,4 @@ issues: | ||||
|     # TODO: remove | ||||
|     - linters: | ||||
|         - staticcheck | ||||
|       text: "SA1019:" | ||||
|       text: "SA1019: interp.ExecHandler" | ||||
							
								
								
									
										2
									
								
								build.go
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								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) | ||||
| 			} | ||||
|  | ||||
|   | ||||
| @@ -11,7 +11,7 @@ | ||||
|     <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> | ||||
|         <text x="33.5" y="15" fill="#010101" fill-opacity=".3">coverage</text> | ||||
|         <text x="33.5" y="14">coverage</text> | ||||
|         <text x="86" y="15" fill="#010101" fill-opacity=".3">20.5%</text> | ||||
|         <text x="86" y="14">20.5%</text> | ||||
|         <text x="86" y="15" fill="#010101" fill-opacity=".3">19.6%</text> | ||||
|         <text x="86" y="14">19.6%</text> | ||||
|     </g> | ||||
| </svg> | ||||
|   | ||||
| Before Width: | Height: | Size: 926 B After Width: | Height: | Size: 926 B | 
							
								
								
									
										17
									
								
								fix.go
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								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) | ||||
|   | ||||
							
								
								
									
										41
									
								
								info.go
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								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) | ||||
|   | ||||
| @@ -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) | ||||
| 			} | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
| } | ||||
|   | ||||
| @@ -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 <http://www.gnu.org/licenses/>. | ||||
|  | ||||
| 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 | ||||
| } | ||||
| @@ -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 <http://www.gnu.org/licenses/>. | ||||
|  | ||||
| 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 | ||||
| } | ||||
| @@ -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) | ||||
| } | ||||
|   | ||||
| @@ -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 <http://www.gnu.org/licenses/>. | ||||
|  | ||||
| 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 | ||||
| } | ||||
| @@ -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 | ||||
| } | ||||
|   | ||||
| @@ -41,7 +41,7 @@ func init() { | ||||
|  | ||||
| 	b2 := lipgloss.RoundedBorder() | ||||
| 	b2.Left = "\u2524" | ||||
| 	infoStyle = titleStyle.Copy().BorderStyle(b2) | ||||
| 	infoStyle = titleStyle.BorderStyle(b2) | ||||
| } | ||||
|  | ||||
| type Pager struct { | ||||
|   | ||||
| @@ -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 | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
|   | ||||
| @@ -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 "" | ||||
|  | ||||
|   | ||||
| @@ -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 "Исполнение версия()" | ||||
|  | ||||
|   | ||||
							
								
								
									
										10
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								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() | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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()), | ||||
| 	) | ||||
|   | ||||
| @@ -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{}), | ||||
|   | ||||
| @@ -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 <http://www.gnu.org/licenses/>. | ||||
|  | ||||
| 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 | ||||
| } | ||||
| @@ -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 | ||||
| } | ||||
|   | ||||
							
								
								
									
										86
									
								
								pkg/search/search_options_builder.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								pkg/search/search_options_builder.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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 <http://www.gnu.org/licenses/>. | ||||
|  | ||||
| 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 | ||||
| } | ||||
							
								
								
									
										65
									
								
								pkg/search/search_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								pkg/search/search_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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 <http://www.gnu.org/licenses/>. | ||||
|  | ||||
| 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) | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										47
									
								
								repo.go
									
									
									
									
									
								
							
							
						
						
									
										47
									
								
								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) | ||||
|   | ||||
							
								
								
									
										53
									
								
								search.go
									
									
									
									
									
								
							
							
						
						
									
										53
									
								
								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 { | ||||
|   | ||||
| @@ -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) | ||||
| 			} | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user