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 |     # TODO: remove | ||||||
|     - linters: |     - linters: | ||||||
|         - staticcheck |         - 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) | 			rs := repos.New(cfg, db) | ||||||
| 			err := db.Init(ctx) | 			err := db.Init(ctx) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error db init"), "err", err) | 				slog.Error(gotext.Get("Error initialization database"), "err", err) | ||||||
| 				os.Exit(1) | 				os.Exit(1) | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ | |||||||
|     <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> |     <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="15" fill="#010101" fill-opacity=".3">coverage</text> | ||||||
|         <text x="33.5" y="14">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="15" fill="#010101" fill-opacity=".3">19.6%</text> | ||||||
|         <text x="86" y="14">20.5%</text> |         <text x="86" y="14">19.6%</text> | ||||||
|     </g> |     </g> | ||||||
| </svg> | </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" | 	"github.com/urfave/cli/v2" | ||||||
|  |  | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | 	"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" | 	"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"), | 		Usage: gotext.Get("Attempt to fix problems with ALR"), | ||||||
| 		Action: func(c *cli.Context) error { | 		Action: func(c *cli.Context) error { | ||||||
| 			ctx := c.Context | 			ctx := c.Context | ||||||
|  | 			cfg := config.New() | ||||||
| 			db.Close() | 			paths := cfg.GetPaths(ctx) | ||||||
| 			paths := config.GetPaths(ctx) |  | ||||||
|  |  | ||||||
| 			slog.Info(gotext.Get("Removing cache directory")) | 			slog.Info(gotext.Get("Removing cache directory")) | ||||||
|  |  | ||||||
| @@ -57,7 +56,15 @@ func FixCmd() *cli.Command { | |||||||
| 				os.Exit(1) | 				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 { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error pulling repos"), "err", err) | 				slog.Error(gotext.Get("Error pulling repos"), "err", err) | ||||||
| 				os.Exit(1) | 				os.Exit(1) | ||||||
|   | |||||||
							
								
								
									
										41
									
								
								info.go
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								info.go
									
									
									
									
									
								
							| @@ -24,13 +24,14 @@ import ( | |||||||
| 	"log/slog" | 	"log/slog" | ||||||
| 	"os" | 	"os" | ||||||
|  |  | ||||||
|  | 	"github.com/jeandeaual/go-locale" | ||||||
| 	"github.com/leonelquinteros/gotext" | 	"github.com/leonelquinteros/gotext" | ||||||
| 	"github.com/urfave/cli/v2" | 	"github.com/urfave/cli/v2" | ||||||
| 	"gopkg.in/yaml.v3" | 	"gopkg.in/yaml.v3" | ||||||
|  |  | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils" | 	"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/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/internal/overrides" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos" | 	"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"), | 				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 { | 		Action: func(c *cli.Context) error { | ||||||
| 			ctx := c.Context | 			ctx := c.Context | ||||||
|  |  | ||||||
| 			cfg := config.New() | 			cfg := config.New() | ||||||
| 			db := db.New(cfg) | 			db := database.New(cfg) | ||||||
| 			err := db.Init(ctx) | 			err := db.Init(ctx) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error initialization database"), "err", err) | 				slog.Error(gotext.Get("Error initialization database"), "err", err) | ||||||
| @@ -88,6 +117,12 @@ func InfoCmd() *cli.Command { | |||||||
| 			var names []string | 			var names []string | ||||||
| 			all := c.Bool("all") | 			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 { | 			if !all { | ||||||
| 				info, err := distro.ParseOSRelease(ctx) | 				info, err := distro.ParseOSRelease(ctx) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| @@ -97,7 +132,7 @@ func InfoCmd() *cli.Command { | |||||||
| 				names, err = overrides.Resolve( | 				names, err = overrides.Resolve( | ||||||
| 					info, | 					info, | ||||||
| 					overrides.DefaultOpts. | 					overrides.DefaultOpts. | ||||||
| 						WithLanguages([]string{config.SystemLang()}), | 						WithLanguages([]string{systemLang}), | ||||||
| 				) | 				) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					slog.Error(gotext.Get("Error resolving overrides"), "err", err) | 					slog.Error(gotext.Get("Error resolving overrides"), "err", err) | ||||||
|   | |||||||
| @@ -69,7 +69,7 @@ func InstallCmd() *cli.Command { | |||||||
| 			rs := repos.New(cfg, db) | 			rs := repos.New(cfg, db) | ||||||
| 			err := db.Init(ctx) | 			err := db.Init(ctx) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error db init"), "err", err) | 				slog.Error(gotext.Get("Error initialization database"), "err", err) | ||||||
| 				os.Exit(1) | 				os.Exit(1) | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -152,6 +152,13 @@ func (c *ALRConfig) Repos(ctx context.Context) []types.Repo { | |||||||
| 	return c.cfg.Repos | 	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 { | func (c *ALRConfig) IgnorePkgUpdates(ctx context.Context) []string { | ||||||
| 	c.cfgOnce.Do(func() { | 	c.cfgOnce.Do(func() { | ||||||
| 		c.Load(ctx) | 		c.Load(ctx) | ||||||
| @@ -172,3 +179,17 @@ func (c *ALRConfig) PagerStyle(ctx context.Context) string { | |||||||
| 	}) | 	}) | ||||||
| 	return c.cfg.PagerStyle | 	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 | package config | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // Paths contains various paths used by ALR | // Paths contains various paths used by ALR | ||||||
| type Paths struct { | type Paths struct { | ||||||
| 	ConfigDir  string | 	ConfigDir  string | ||||||
| @@ -32,14 +28,3 @@ type Paths struct { | |||||||
| 	PkgsDir    string | 	PkgsDir    string | ||||||
| 	DBPath     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" | 	"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 { | type TestALRConfig struct { | ||||||
| 	CacheDir string | 	CacheDir string | ||||||
| } | } | ||||||
|   | |||||||
| @@ -41,7 +41,7 @@ func init() { | |||||||
|  |  | ||||||
| 	b2 := lipgloss.RoundedBorder() | 	b2 := lipgloss.RoundedBorder() | ||||||
| 	b2.Left = "\u2524" | 	b2.Left = "\u2524" | ||||||
| 	infoStyle = titleStyle.Copy().BorderStyle(b2) | 	infoStyle = titleStyle.BorderStyle(b2) | ||||||
| } | } | ||||||
|  |  | ||||||
| type Pager struct { | type Pager struct { | ||||||
|   | |||||||
| @@ -22,10 +22,11 @@ package handlers | |||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"io" | 	"io" | ||||||
|  | 	"io/fs" | ||||||
| 	"os" | 	"os" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func NopReadDir(context.Context, string) ([]os.FileInfo, error) { | func NopReadDir(context.Context, string) ([]fs.DirEntry, error) { | ||||||
| 	return nil, os.ErrNotExist | 	return nil, os.ErrNotExist | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -31,12 +31,12 @@ import ( | |||||||
| 	"mvdan.cc/sh/v3/interp" | 	"mvdan.cc/sh/v3/interp" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func RestrictedReadDir(allowedPrefixes ...string) interp.ReadDirHandlerFunc { | func RestrictedReadDir(allowedPrefixes ...string) interp.ReadDirHandlerFunc2 { | ||||||
| 	return func(ctx context.Context, s string) ([]fs.FileInfo, error) { | 	return func(ctx context.Context, s string) ([]fs.DirEntry, error) { | ||||||
| 		path := filepath.Clean(s) | 		path := filepath.Clean(s) | ||||||
| 		for _, allowedPrefix := range allowedPrefixes { | 		for _, allowedPrefix := range allowedPrefixes { | ||||||
| 			if strings.HasPrefix(path, allowedPrefix) { | 			if strings.HasPrefix(path, allowedPrefix) { | ||||||
| 				return interp.DefaultReadDirHandler()(ctx, s) | 				return interp.DefaultReadDirHandler2()(ctx, s) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -31,7 +31,7 @@ msgid "" | |||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: build.go:75 | #: build.go:75 | ||||||
| msgid "Error db init" | msgid "Error initialization database" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: build.go:105 | #: build.go:105 | ||||||
| @@ -66,27 +66,27 @@ msgstr "" | |||||||
| msgid "Attempt to fix problems with ALR" | msgid "Attempt to fix problems with ALR" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: fix.go:44 | #: fix.go:43 | ||||||
| msgid "Removing cache directory" | msgid "Removing cache directory" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: fix.go:48 | #: fix.go:47 | ||||||
| msgid "Unable to remove cache directory" | msgid "Unable to remove cache directory" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: fix.go:52 | #: fix.go:51 | ||||||
| msgid "Rebuilding cache" | msgid "Rebuilding cache" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: fix.go:56 | #: fix.go:55 | ||||||
| msgid "Unable to create new cache directory" | msgid "Unable to create new cache directory" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: fix.go:62 | #: fix.go:69 | ||||||
| msgid "Error pulling repos" | msgid "Error pulling repos" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: fix.go:66 | #: fix.go:73 | ||||||
| msgid "Done" | msgid "Done" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| @@ -114,35 +114,39 @@ msgstr "" | |||||||
| msgid "No such helper command" | msgid "No such helper command" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: info.go:42 | #: info.go:43 | ||||||
| msgid "Print information about a package" | msgid "Print information about a package" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: info.go:47 | #: info.go:48 | ||||||
| msgid "Show all information, not just for the current distro" | msgid "Show all information, not just for the current distro" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: info.go:57 | #: info.go:63 | ||||||
| msgid "Error initialization database" | msgid "Error getting packages" | ||||||
| msgstr "" | 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" | msgid "Command info expected at least 1 argument, got %d" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: info.go:78 | #: info.go:107 | ||||||
| msgid "Error finding packages" | msgid "Error finding packages" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: info.go:94 | #: info.go:129 | ||||||
| msgid "Error parsing os-release file" | msgid "Error parsing os-release file" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: info.go:103 | #: info.go:138 | ||||||
| msgid "Error resolving overrides" | msgid "Error resolving overrides" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: info.go:112 info.go:118 | #: info.go:147 info.go:153 | ||||||
| msgid "Error encoding script variables" | msgid "Error encoding script variables" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| @@ -154,14 +158,6 @@ msgstr "" | |||||||
| msgid "Command install expected at least 1 argument, got %d" | msgid "Command install expected at least 1 argument, got %d" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: install.go:124 |  | ||||||
| msgid "Error getting packages" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: install.go:133 |  | ||||||
| msgid "Error iterating over packages" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: install.go:146 | #: install.go:146 | ||||||
| msgid "Remove an installed package" | msgid "Remove an installed package" | ||||||
| msgstr "" | msgstr "" | ||||||
| @@ -234,10 +230,6 @@ msgstr "" | |||||||
| msgid "Unable to create package cache directory" | msgid "Unable to create package cache directory" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: internal/config/lang.go:49 |  | ||||||
| msgid "Error parsing system language" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: internal/db/db.go:133 | #: internal/db/db.go:133 | ||||||
| msgid "Database version mismatch; resetting" | msgid "Database version mismatch; resetting" | ||||||
| msgstr "" | msgstr "" | ||||||
| @@ -247,10 +239,6 @@ msgid "" | |||||||
| "Database version does not exist. Run alr fix if something isn't working." | "Database version does not exist. Run alr fix if something isn't working." | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: internal/db/db_legacy.go:101 |  | ||||||
| msgid "Error opening database" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: internal/dl/dl.go:170 | #: internal/dl/dl.go:170 | ||||||
| msgid "Source can be updated, updating if required" | msgid "Source can be updated, updating if required" | ||||||
| msgstr "" | msgstr "" | ||||||
| @@ -287,15 +275,15 @@ msgstr "" | |||||||
| msgid "Error listing installed packages" | msgid "Error listing installed packages" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: main.go:45 | #: main.go:44 | ||||||
| msgid "Print the current ALR version and exit" | msgid "Print the current ALR version and exit" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: main.go:61 | #: main.go:60 | ||||||
| msgid "Arguments to be passed on to the package manager" | msgid "Arguments to be passed on to the package manager" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: main.go:67 | #: main.go:66 | ||||||
| msgid "Enable interactive questions and prompts" | msgid "Enable interactive questions and prompts" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| @@ -305,7 +293,7 @@ msgid "" | |||||||
| "system" | "system" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: main.go:125 | #: main.go:123 | ||||||
| msgid "Error while running app" | msgid "Error while running app" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| @@ -329,49 +317,49 @@ msgstr "" | |||||||
| msgid "Compressing package" | msgid "Compressing package" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:419 | #: pkg/build/build.go:421 | ||||||
| msgid "" | msgid "" | ||||||
| "Your system's CPU architecture doesn't match this package. Do you want to " | "Your system's CPU architecture doesn't match this package. Do you want to " | ||||||
| "build anyway?" | "build anyway?" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:433 | #: pkg/build/build.go:435 | ||||||
| msgid "This package is already installed" | msgid "This package is already installed" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:457 | #: pkg/build/build.go:459 | ||||||
| msgid "Installing build dependencies" | msgid "Installing build dependencies" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:498 | #: pkg/build/build.go:500 | ||||||
| msgid "Installing dependencies" | msgid "Installing dependencies" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:533 | #: pkg/build/build.go:535 | ||||||
| msgid "The checksums array must be the same length as sources" | msgid "The checksums array must be the same length as sources" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:584 | #: pkg/build/build.go:586 | ||||||
| msgid "Would you like to remove the build dependencies?" | msgid "Would you like to remove the build dependencies?" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:647 | #: pkg/build/build.go:649 | ||||||
| msgid "Executing prepare()" | msgid "Executing prepare()" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:657 | #: pkg/build/build.go:659 | ||||||
| msgid "Executing build()" | msgid "Executing build()" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:687 pkg/build/build.go:707 | #: pkg/build/build.go:689 pkg/build/build.go:709 | ||||||
| msgid "Executing %s()" | msgid "Executing %s()" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:766 | #: pkg/build/build.go:768 | ||||||
| msgid "Error installing native packages" | msgid "Error installing native packages" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:790 | #: pkg/build/build.go:792 | ||||||
| msgid "Error installing package" | msgid "Error installing package" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| @@ -425,35 +413,35 @@ msgstr "" | |||||||
| msgid "URL of the new repo" | msgid "URL of the new repo" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: repo.go:79 repo.go:136 | #: repo.go:82 repo.go:147 | ||||||
| msgid "Error opening config file" | msgid "Error opening config file" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: repo.go:85 repo.go:142 | #: repo.go:88 repo.go:153 | ||||||
| msgid "Error encoding config" | msgid "Error encoding config" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: repo.go:103 | #: repo.go:113 | ||||||
| msgid "Remove an existing repository" | msgid "Remove an existing repository" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: repo.go:110 | #: repo.go:120 | ||||||
| msgid "Name of the repo to be deleted" | msgid "Name of the repo to be deleted" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: repo.go:128 | #: repo.go:139 | ||||||
| msgid "Repo does not exist" | msgid "Repo does not exist" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: repo.go:148 | #: repo.go:159 | ||||||
| msgid "Error removing repo directory" | msgid "Error removing repo directory" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: repo.go:154 | #: repo.go:170 | ||||||
| msgid "Error removing packages from database" | msgid "Error removing packages from database" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: repo.go:166 | #: repo.go:182 | ||||||
| msgid "Pull all repositories that have changed" | msgid "Pull all repositories that have changed" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| @@ -481,19 +469,11 @@ msgstr "" | |||||||
| msgid "Format output using a Go template" | msgid "Format output using a Go template" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: search.go:94 | #: search.go:82 search.go:99 | ||||||
| msgid "At least one search parameter is required" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: search.go:100 |  | ||||||
| msgid "Error db search" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: search.go:109 |  | ||||||
| msgid "Error parsing format template" | msgid "Error parsing format template" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: search.go:124 | #: search.go:107 | ||||||
| msgid "Error executing template" | msgid "Error executing template" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
|   | |||||||
| @@ -38,8 +38,8 @@ msgid "" | |||||||
| msgstr "Создайте пакет с нуля, даже если уже имеется готовый пакет" | msgstr "Создайте пакет с нуля, даже если уже имеется готовый пакет" | ||||||
|  |  | ||||||
| #: build.go:75 | #: build.go:75 | ||||||
| msgid "Error db init" | msgid "Error initialization database" | ||||||
| msgstr "" | msgstr "Ошибка инициализации базы данных" | ||||||
|  |  | ||||||
| #: build.go:105 | #: build.go:105 | ||||||
| msgid "Package not found" | msgid "Package not found" | ||||||
| @@ -74,27 +74,27 @@ msgstr "Ошибка при перемещении пакета" | |||||||
| msgid "Attempt to fix problems with ALR" | msgid "Attempt to fix problems with ALR" | ||||||
| msgstr "Попытка устранить проблемы с ALR" | msgstr "Попытка устранить проблемы с ALR" | ||||||
|  |  | ||||||
| #: fix.go:44 | #: fix.go:43 | ||||||
| msgid "Removing cache directory" | msgid "Removing cache directory" | ||||||
| msgstr "Удаление каталога кэша" | msgstr "Удаление каталога кэша" | ||||||
|  |  | ||||||
| #: fix.go:48 | #: fix.go:47 | ||||||
| msgid "Unable to remove cache directory" | msgid "Unable to remove cache directory" | ||||||
| msgstr "Не удалось удалить каталог кэша" | msgstr "Не удалось удалить каталог кэша" | ||||||
|  |  | ||||||
| #: fix.go:52 | #: fix.go:51 | ||||||
| msgid "Rebuilding cache" | msgid "Rebuilding cache" | ||||||
| msgstr "Восстановление кэша" | msgstr "Восстановление кэша" | ||||||
|  |  | ||||||
| #: fix.go:56 | #: fix.go:55 | ||||||
| msgid "Unable to create new cache directory" | msgid "Unable to create new cache directory" | ||||||
| msgstr "Не удалось создать новый каталог кэша" | msgstr "Не удалось создать новый каталог кэша" | ||||||
|  |  | ||||||
| #: fix.go:62 | #: fix.go:69 | ||||||
| msgid "Error pulling repos" | msgid "Error pulling repos" | ||||||
| msgstr "Ошибка при извлечении репозиториев" | msgstr "Ошибка при извлечении репозиториев" | ||||||
|  |  | ||||||
| #: fix.go:66 | #: fix.go:73 | ||||||
| msgid "Done" | msgid "Done" | ||||||
| msgstr "Сделано" | msgstr "Сделано" | ||||||
|  |  | ||||||
| @@ -122,35 +122,39 @@ msgstr "Каталог, в который будут устанавливать | |||||||
| msgid "No such helper command" | msgid "No such helper command" | ||||||
| msgstr "Такой вспомогательной команды нет" | msgstr "Такой вспомогательной команды нет" | ||||||
|  |  | ||||||
| #: info.go:42 | #: info.go:43 | ||||||
| msgid "Print information about a package" | msgid "Print information about a package" | ||||||
| msgstr "Отобразить информацию о пакете" | msgstr "Отобразить информацию о пакете" | ||||||
|  |  | ||||||
| #: info.go:47 | #: info.go:48 | ||||||
| msgid "Show all information, not just for the current distro" | msgid "Show all information, not just for the current distro" | ||||||
| msgstr "Показывать всю информацию, не только для текущего дистрибутива" | msgstr "Показывать всю информацию, не только для текущего дистрибутива" | ||||||
|  |  | ||||||
| #: info.go:57 | #: info.go:63 | ||||||
| msgid "Error initialization database" | msgid "Error getting packages" | ||||||
| msgstr "Ошибка инициализации базы данных" | 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" | msgid "Command info expected at least 1 argument, got %d" | ||||||
| msgstr "Для команды info ожидался хотя бы 1 аргумент, получено %d" | msgstr "Для команды info ожидался хотя бы 1 аргумент, получено %d" | ||||||
|  |  | ||||||
| #: info.go:78 | #: info.go:107 | ||||||
| msgid "Error finding packages" | msgid "Error finding packages" | ||||||
| msgstr "Ошибка при поиске пакетов" | msgstr "Ошибка при поиске пакетов" | ||||||
|  |  | ||||||
| #: info.go:94 | #: info.go:129 | ||||||
| msgid "Error parsing os-release file" | msgid "Error parsing os-release file" | ||||||
| msgstr "Ошибка при разборе файла выпуска операционной системы" | msgstr "Ошибка при разборе файла выпуска операционной системы" | ||||||
|  |  | ||||||
| #: info.go:103 | #: info.go:138 | ||||||
| msgid "Error resolving overrides" | msgid "Error resolving overrides" | ||||||
| msgstr "Ошибка устранения переорпеделений" | msgstr "Ошибка устранения переорпеделений" | ||||||
|  |  | ||||||
| #: info.go:112 info.go:118 | #: info.go:147 info.go:153 | ||||||
| msgid "Error encoding script variables" | msgid "Error encoding script variables" | ||||||
| msgstr "Ошибка кодирования переменных скрита" | msgstr "Ошибка кодирования переменных скрита" | ||||||
|  |  | ||||||
| @@ -162,14 +166,6 @@ msgstr "Установить новый пакет" | |||||||
| msgid "Command install expected at least 1 argument, got %d" | msgid "Command install expected at least 1 argument, got %d" | ||||||
| msgstr "Для команды install ожидался хотя бы 1 аргумент, получено %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 | #: install.go:146 | ||||||
| msgid "Remove an installed package" | msgid "Remove an installed package" | ||||||
| msgstr "Удалить установленный пакет" | msgstr "Удалить установленный пакет" | ||||||
| @@ -246,10 +242,6 @@ msgstr "Не удалось создать каталог кэша репози | |||||||
| msgid "Unable to create package cache directory" | msgid "Unable to create package cache directory" | ||||||
| msgstr "Не удалось создать каталог кэша пакетов" | msgstr "Не удалось создать каталог кэша пакетов" | ||||||
|  |  | ||||||
| #: internal/config/lang.go:49 |  | ||||||
| msgid "Error parsing system language" |  | ||||||
| msgstr "Ошибка при парсинге языка системы" |  | ||||||
|  |  | ||||||
| #: internal/db/db.go:133 | #: internal/db/db.go:133 | ||||||
| msgid "Database version mismatch; resetting" | msgid "Database version mismatch; resetting" | ||||||
| msgstr "Несоответствие версий базы данных; сброс настроек" | msgstr "Несоответствие версий базы данных; сброс настроек" | ||||||
| @@ -260,10 +252,6 @@ msgid "" | |||||||
| msgstr "" | msgstr "" | ||||||
| "Версия базы данных не существует. Запустите alr fix, если что-то не работает." | "Версия базы данных не существует. Запустите alr fix, если что-то не работает." | ||||||
|  |  | ||||||
| #: internal/db/db_legacy.go:101 |  | ||||||
| msgid "Error opening database" |  | ||||||
| msgstr "Ошибка при открытии базы данных" |  | ||||||
|  |  | ||||||
| #: internal/dl/dl.go:170 | #: internal/dl/dl.go:170 | ||||||
| msgid "Source can be updated, updating if required" | msgid "Source can be updated, updating if required" | ||||||
| msgstr "Исходный код можно обновлять, обновляя при необходимости" | msgstr "Исходный код можно обновлять, обновляя при необходимости" | ||||||
| @@ -300,15 +288,15 @@ msgstr "Список пакетов репозитория ALR" | |||||||
| msgid "Error listing installed packages" | msgid "Error listing installed packages" | ||||||
| msgstr "Ошибка при составлении списка установленных пакетов" | msgstr "Ошибка при составлении списка установленных пакетов" | ||||||
|  |  | ||||||
| #: main.go:45 | #: main.go:44 | ||||||
| msgid "Print the current ALR version and exit" | msgid "Print the current ALR version and exit" | ||||||
| msgstr "Показать текущую версию ALR и выйти" | msgstr "Показать текущую версию ALR и выйти" | ||||||
|  |  | ||||||
| #: main.go:61 | #: main.go:60 | ||||||
| msgid "Arguments to be passed on to the package manager" | msgid "Arguments to be passed on to the package manager" | ||||||
| msgstr "Аргументы, которые будут переданы менеджеру пакетов" | msgstr "Аргументы, которые будут переданы менеджеру пакетов" | ||||||
|  |  | ||||||
| #: main.go:67 | #: main.go:66 | ||||||
| msgid "Enable interactive questions and prompts" | msgid "Enable interactive questions and prompts" | ||||||
| msgstr "Включение интерактивных вопросов и запросов" | msgstr "Включение интерактивных вопросов и запросов" | ||||||
|  |  | ||||||
| @@ -320,7 +308,7 @@ msgstr "" | |||||||
| "Запуск ALR от имени root запрещён, так как это может привести к " | "Запуск ALR от имени root запрещён, так как это может привести к " | ||||||
| "катастрофическому повреждению вашей системы" | "катастрофическому повреждению вашей системы" | ||||||
|  |  | ||||||
| #: main.go:125 | #: main.go:123 | ||||||
| msgid "Error while running app" | msgid "Error while running app" | ||||||
| msgstr "Ошибка при запуске приложения" | msgstr "Ошибка при запуске приложения" | ||||||
|  |  | ||||||
| @@ -344,7 +332,7 @@ msgstr "Сборка метаданных пакета" | |||||||
| msgid "Compressing package" | msgid "Compressing package" | ||||||
| msgstr "Сжатие пакета" | msgstr "Сжатие пакета" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:419 | #: pkg/build/build.go:421 | ||||||
| msgid "" | msgid "" | ||||||
| "Your system's CPU architecture doesn't match this package. Do you want to " | "Your system's CPU architecture doesn't match this package. Do you want to " | ||||||
| "build anyway?" | "build anyway?" | ||||||
| @@ -352,44 +340,44 @@ msgstr "" | |||||||
| "Архитектура процессора вашей системы не соответствует этому пакету. Вы все " | "Архитектура процессора вашей системы не соответствует этому пакету. Вы все " | ||||||
| "равно хотите выполнить сборку?" | "равно хотите выполнить сборку?" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:433 | #: pkg/build/build.go:435 | ||||||
| msgid "This package is already installed" | msgid "This package is already installed" | ||||||
| msgstr "Этот пакет уже установлен" | msgstr "Этот пакет уже установлен" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:457 | #: pkg/build/build.go:459 | ||||||
| msgid "Installing build dependencies" | msgid "Installing build dependencies" | ||||||
| msgstr "Установка зависимостей сборки" | msgstr "Установка зависимостей сборки" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:498 | #: pkg/build/build.go:500 | ||||||
| msgid "Installing dependencies" | msgid "Installing dependencies" | ||||||
| msgstr "Установка зависимостей" | msgstr "Установка зависимостей" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:533 | #: pkg/build/build.go:535 | ||||||
| msgid "The checksums array must be the same length as sources" | msgid "The checksums array must be the same length as sources" | ||||||
| msgstr "Массив контрольных сумм должен быть той же длины, что и источники" | msgstr "Массив контрольных сумм должен быть той же длины, что и источники" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:584 | #: pkg/build/build.go:586 | ||||||
| msgid "Would you like to remove the build dependencies?" | msgid "Would you like to remove the build dependencies?" | ||||||
| msgstr "Хотели бы вы удалить зависимости сборки?" | msgstr "Хотели бы вы удалить зависимости сборки?" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:647 | #: pkg/build/build.go:649 | ||||||
| msgid "Executing prepare()" | msgid "Executing prepare()" | ||||||
| msgstr "Исполнение prepare()" | msgstr "Исполнение prepare()" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:657 | #: pkg/build/build.go:659 | ||||||
| msgid "Executing build()" | msgid "Executing build()" | ||||||
| msgstr "Исполнение build()" | msgstr "Исполнение build()" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:687 pkg/build/build.go:707 | #: pkg/build/build.go:689 pkg/build/build.go:709 | ||||||
| #, fuzzy | #, fuzzy | ||||||
| msgid "Executing %s()" | msgid "Executing %s()" | ||||||
| msgstr "Исполнение files()" | msgstr "Исполнение files()" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:766 | #: pkg/build/build.go:768 | ||||||
| msgid "Error installing native packages" | msgid "Error installing native packages" | ||||||
| msgstr "Ошибка при установке нативных пакетов" | msgstr "Ошибка при установке нативных пакетов" | ||||||
|  |  | ||||||
| #: pkg/build/build.go:790 | #: pkg/build/build.go:792 | ||||||
| msgid "Error installing package" | msgid "Error installing package" | ||||||
| msgstr "Ошибка при установке пакета" | msgstr "Ошибка при установке пакета" | ||||||
|  |  | ||||||
| @@ -447,35 +435,35 @@ msgstr "Название нового репозитория" | |||||||
| msgid "URL of the new repo" | msgid "URL of the new repo" | ||||||
| msgstr "URL-адрес нового репозитория" | msgstr "URL-адрес нового репозитория" | ||||||
|  |  | ||||||
| #: repo.go:79 repo.go:136 | #: repo.go:82 repo.go:147 | ||||||
| msgid "Error opening config file" | msgid "Error opening config file" | ||||||
| msgstr "Ошибка при открытии конфигурационного файла" | msgstr "Ошибка при открытии конфигурационного файла" | ||||||
|  |  | ||||||
| #: repo.go:85 repo.go:142 | #: repo.go:88 repo.go:153 | ||||||
| msgid "Error encoding config" | msgid "Error encoding config" | ||||||
| msgstr "Ошибка при кодировании конфигурации" | msgstr "Ошибка при кодировании конфигурации" | ||||||
|  |  | ||||||
| #: repo.go:103 | #: repo.go:113 | ||||||
| msgid "Remove an existing repository" | msgid "Remove an existing repository" | ||||||
| msgstr "Удалить существующий репозиторий" | msgstr "Удалить существующий репозиторий" | ||||||
|  |  | ||||||
| #: repo.go:110 | #: repo.go:120 | ||||||
| msgid "Name of the repo to be deleted" | msgid "Name of the repo to be deleted" | ||||||
| msgstr "Название репозитория  удалён" | msgstr "Название репозитория  удалён" | ||||||
|  |  | ||||||
| #: repo.go:128 | #: repo.go:139 | ||||||
| msgid "Repo does not exist" | msgid "Repo does not exist" | ||||||
| msgstr "Репозитория не существует" | msgstr "Репозитория не существует" | ||||||
|  |  | ||||||
| #: repo.go:148 | #: repo.go:159 | ||||||
| msgid "Error removing repo directory" | msgid "Error removing repo directory" | ||||||
| msgstr "Ошибка при удалении каталога репозитория" | msgstr "Ошибка при удалении каталога репозитория" | ||||||
|  |  | ||||||
| #: repo.go:154 | #: repo.go:170 | ||||||
| msgid "Error removing packages from database" | msgid "Error removing packages from database" | ||||||
| msgstr "Ошибка при удалении пакетов из базы данных" | msgstr "Ошибка при удалении пакетов из базы данных" | ||||||
|  |  | ||||||
| #: repo.go:166 | #: repo.go:182 | ||||||
| msgid "Pull all repositories that have changed" | msgid "Pull all repositories that have changed" | ||||||
| msgstr "Скачать все изменённые репозитории" | msgstr "Скачать все изменённые репозитории" | ||||||
|  |  | ||||||
| @@ -504,20 +492,12 @@ msgstr "" | |||||||
| msgid "Format output using a Go template" | msgid "Format output using a Go template" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: search.go:94 | #: search.go:82 search.go:99 | ||||||
| msgid "At least one search parameter is required" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: search.go:100 |  | ||||||
| msgid "Error db search" |  | ||||||
| msgstr "" |  | ||||||
|  |  | ||||||
| #: search.go:109 |  | ||||||
| #, fuzzy | #, fuzzy | ||||||
| msgid "Error parsing format template" | msgid "Error parsing format template" | ||||||
| msgstr "Ошибка при разборе файла выпуска операционной системы" | msgstr "Ошибка при разборе файла выпуска операционной системы" | ||||||
|  |  | ||||||
| #: search.go:124 | #: search.go:107 | ||||||
| #, fuzzy | #, fuzzy | ||||||
| msgid "Error executing template" | msgid "Error executing template" | ||||||
| msgstr "Ошибка при получении пакетов" | msgstr "Ошибка при получении пакетов" | ||||||
| @@ -534,6 +514,12 @@ msgstr "Ошибка при проверке обновлений" | |||||||
| msgid "There is nothing to do." | msgid "There is nothing to do." | ||||||
| msgstr "Здесь нечего делать." | msgstr "Здесь нечего делать." | ||||||
|  |  | ||||||
|  | #~ msgid "Error parsing system language" | ||||||
|  | #~ msgstr "Ошибка при парсинге языка системы" | ||||||
|  |  | ||||||
|  | #~ msgid "Error opening database" | ||||||
|  | #~ msgstr "Ошибка при открытии базы данных" | ||||||
|  |  | ||||||
| #~ msgid "Executing version()" | #~ msgid "Executing version()" | ||||||
| #~ msgstr "Исполнение версия()" | #~ msgstr "Исполнение версия()" | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								main.go
									
									
									
									
									
								
							| @@ -32,7 +32,6 @@ import ( | |||||||
| 	"github.com/urfave/cli/v2" | 	"github.com/urfave/cli/v2" | ||||||
|  |  | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | 	"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/internal/translations" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" | ||||||
|  |  | ||||||
| @@ -85,9 +84,10 @@ func GetApp() *cli.App { | |||||||
| 		}, | 		}, | ||||||
| 		Before: func(c *cli.Context) error { | 		Before: func(c *cli.Context) error { | ||||||
| 			ctx := c.Context | 			ctx := c.Context | ||||||
|  | 			cfg := config.New() | ||||||
|  |  | ||||||
| 			cmd := c.Args().First() | 			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")) | 				slog.Error(gotext.Get("Running ALR as root is forbidden as it may cause catastrophic damage to your system")) | ||||||
| 				os.Exit(1) | 				os.Exit(1) | ||||||
| 			} | 			} | ||||||
| @@ -99,9 +99,6 @@ func GetApp() *cli.App { | |||||||
|  |  | ||||||
| 			return nil | 			return nil | ||||||
| 		}, | 		}, | ||||||
| 		After: func(ctx *cli.Context) error { |  | ||||||
| 			return db.Close() |  | ||||||
| 		}, |  | ||||||
| 		EnableBashCompletion: true, | 		EnableBashCompletion: true, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -111,11 +108,12 @@ func main() { | |||||||
| 	logger.SetupDefault() | 	logger.SetupDefault() | ||||||
|  |  | ||||||
| 	app := GetApp() | 	app := GetApp() | ||||||
|  | 	cfg := config.New() | ||||||
|  |  | ||||||
| 	ctx := context.Background() | 	ctx := context.Background() | ||||||
|  |  | ||||||
| 	// Set the root command to the one set in the ALR config | 	// 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) | 	ctx, cancel := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM) | ||||||
| 	defer cancel() | 	defer cancel() | ||||||
|   | |||||||
| @@ -306,7 +306,7 @@ func (b *Builder) executeFirstPass( | |||||||
| 		interp.Env(expand.ListEnviron(env...)),                               // Устанавливаем окружение | 		interp.Env(expand.ListEnviron(env...)),                               // Устанавливаем окружение | ||||||
| 		interp.StdIO(os.Stdin, os.Stdout, os.Stderr),                         // Устанавливаем стандартный ввод-вывод | 		interp.StdIO(os.Stdin, os.Stdout, os.Stderr),                         // Устанавливаем стандартный ввод-вывод | ||||||
| 		interp.ExecHandler(helpers.Restricted.ExecHandler(handlers.NopExec)), // Ограничиваем выполнение | 		interp.ExecHandler(helpers.Restricted.ExecHandler(handlers.NopExec)), // Ограничиваем выполнение | ||||||
| 		interp.ReadDirHandler(handlers.RestrictedReadDir(scriptDir)),         // Ограничиваем чтение директорий | 		interp.ReadDirHandler2(handlers.RestrictedReadDir(scriptDir)),        // Ограничиваем чтение директорий | ||||||
| 		interp.StatHandler(handlers.RestrictedStat(scriptDir)),               // Ограничиваем доступ к статистике файлов | 		interp.StatHandler(handlers.RestrictedStat(scriptDir)),               // Ограничиваем доступ к статистике файлов | ||||||
| 		interp.OpenHandler(handlers.RestrictedOpen(scriptDir)),               // Ограничиваем открытие файлов | 		interp.OpenHandler(handlers.RestrictedOpen(scriptDir)),               // Ограничиваем открытие файлов | ||||||
| 	) | 	) | ||||||
| @@ -397,7 +397,9 @@ func (b *Builder) executeSecondPass( | |||||||
| 	runner, err := interp.New( | 	runner, err := interp.New( | ||||||
| 		interp.Env(expand.ListEnviron(env...)),       // Устанавливаем окружение | 		interp.Env(expand.ListEnviron(env...)),       // Устанавливаем окружение | ||||||
| 		interp.StdIO(os.Stdin, os.Stdout, os.Stderr), // Устанавливаем стандартный ввод-вывод | 		interp.StdIO(os.Stdin, os.Stdout, os.Stderr), // Устанавливаем стандартный ввод-вывод | ||||||
| 		interp.ExecHandler(helpers.Helpers.ExecHandler(fakeroot)), // Обрабатываем выполнение через fakeroot | 		interp.ExecHandlers(func(next interp.ExecHandlerFunc) interp.ExecHandlerFunc { | ||||||
|  | 			return helpers.Helpers.ExecHandler(fakeroot) | ||||||
|  | 		}), // Обрабатываем выполнение через fakeroot | ||||||
| 	) | 	) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
|   | |||||||
| @@ -79,7 +79,7 @@ func ParseOSRelease(ctx context.Context) (*OSRelease, error) { | |||||||
| 	runner, err := interp.New( | 	runner, err := interp.New( | ||||||
| 		interp.OpenHandler(handlers.NopOpen), | 		interp.OpenHandler(handlers.NopOpen), | ||||||
| 		interp.ExecHandler(handlers.NopExec), | 		interp.ExecHandler(handlers.NopExec), | ||||||
| 		interp.ReadDirHandler(handlers.NopReadDir), | 		interp.ReadDirHandler2(handlers.NopReadDir), | ||||||
| 		interp.StatHandler(handlers.NopStat), | 		interp.StatHandler(handlers.NopStat), | ||||||
| 		interp.Env(expand.ListEnviron()), | 		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) | 		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 | 		var repoFS billy.Filesystem | ||||||
| 		gitDir := filepath.Join(repoDir, ".git") | 		gitDir := filepath.Join(repoDir, ".git") | ||||||
| @@ -264,7 +264,7 @@ func (rs *Repos) processRepoChangesRunner(repoDir, scriptDir string) (*interp.Ru | |||||||
| 	return interp.New( | 	return interp.New( | ||||||
| 		interp.Env(expand.ListEnviron(env...)), | 		interp.Env(expand.ListEnviron(env...)), | ||||||
| 		interp.ExecHandler(handlers.NopExec), | 		interp.ExecHandler(handlers.NopExec), | ||||||
| 		interp.ReadDirHandler(handlers.RestrictedReadDir(repoDir)), | 		interp.ReadDirHandler2(handlers.RestrictedReadDir(repoDir)), | ||||||
| 		interp.StatHandler(handlers.RestrictedStat(repoDir)), | 		interp.StatHandler(handlers.RestrictedStat(repoDir)), | ||||||
| 		interp.OpenHandler(handlers.RestrictedOpen(repoDir)), | 		interp.OpenHandler(handlers.RestrictedOpen(repoDir)), | ||||||
| 		interp.StdIO(handlers.NopRWC{}, handlers.NopRWC{}, handlers.NopRWC{}), | 		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 ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"errors" |  | ||||||
| 	"io" |  | ||||||
| 	"io/fs" |  | ||||||
| 	"os" |  | ||||||
| 	"path/filepath" |  | ||||||
| 	"strconv" |  | ||||||
| 	"strings" |  | ||||||
|  |  | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | 	"github.com/jmoiron/sqlx" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/db" |  | ||||||
|  | 	database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // Filter represents search filters. | type PackagesProvider interface { | ||||||
| type Filter int | 	GetPkgs(ctx context.Context, where string, args ...any) (*sqlx.Rows, error) | ||||||
|  |  | ||||||
| // 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 |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func convertPkg(p db.Package) Package { | type Searcher struct { | ||||||
| 	return Package{ | 	pp PackagesProvider | ||||||
| 		Name:          p.Name, | } | ||||||
| 		Version:       p.Version, |  | ||||||
| 		Release:       p.Release, | func New(pp PackagesProvider) *Searcher { | ||||||
| 		Epoch:         p.Epoch, | 	return &Searcher{ | ||||||
| 		Description:   p.Description.Val, | 		pp: pp, | ||||||
| 		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, |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // Options contains the options for a search. | func (s *Searcher) Search( | ||||||
| type Options struct { | 	ctx context.Context, | ||||||
| 	Filter      Filter | 	opts *SearchOptions, | ||||||
| 	FilterValue string | ) ([]database.Package, error) { | ||||||
| 	SortBy      SortBy | 	var packages []database.Package | ||||||
| 	Limit       int64 |  | ||||||
| 	Query       string |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Search searches for packages in the database based on the given options. | 	where, args := opts.WhereClause() | ||||||
| func Search(ctx context.Context, opts Options) ([]Package, error) { | 	result, err := s.pp.GetPkgs(ctx, where, args...) | ||||||
| 	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...) |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	var out []Package |  | ||||||
| 	for result.Next() { | 	for result.Next() { | ||||||
| 		pkg := db.Package{} | 		var dbPkg database.Package | ||||||
| 		err = result.StructScan(&pkg) | 		err = result.StructScan(&dbPkg) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return nil, err | 			return nil, err | ||||||
| 		} | 		} | ||||||
| 		out = append(out, convertPkg(pkg)) | 		packages = append(packages, dbPkg) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return out, err | 	return packages, nil | ||||||
| } |  | ||||||
|  |  | ||||||
| // 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 |  | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										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" | 	"golang.org/x/exp/slices" | ||||||
|  |  | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | 	"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/internal/types" | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos" | 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos" | ||||||
| ) | ) | ||||||
| @@ -60,21 +60,24 @@ func AddRepoCmd() *cli.Command { | |||||||
| 			name := c.String("name") | 			name := c.String("name") | ||||||
| 			repoURL := c.String("url") | 			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 { | 				if repo.URL == repoURL { | ||||||
| 					slog.Error("Repo already exists", "name", repo.Name) | 					slog.Error("Repo already exists", "name", repo.Name) | ||||||
| 					os.Exit(1) | 					os.Exit(1) | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			cfg.Repos = append(cfg.Repos, types.Repo{ | 			reposSlice = append(reposSlice, types.Repo{ | ||||||
| 				Name: name, | 				Name: name, | ||||||
| 				URL:  repoURL, | 				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 { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error opening config file"), "err", err) | 				slog.Error(gotext.Get("Error opening config file"), "err", err) | ||||||
| 				os.Exit(1) | 				os.Exit(1) | ||||||
| @@ -86,7 +89,14 @@ func AddRepoCmd() *cli.Command { | |||||||
| 				os.Exit(1) | 				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 { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error pulling repos"), "err", err) | 				slog.Error(gotext.Get("Error pulling repos"), "err", err) | ||||||
| 				os.Exit(1) | 				os.Exit(1) | ||||||
| @@ -114,11 +124,12 @@ func RemoveRepoCmd() *cli.Command { | |||||||
| 			ctx := c.Context | 			ctx := c.Context | ||||||
|  |  | ||||||
| 			name := c.String("name") | 			name := c.String("name") | ||||||
| 			cfg := config.Config(ctx) | 			cfg := config.New() | ||||||
|  |  | ||||||
| 			found := false | 			found := false | ||||||
| 			index := 0 | 			index := 0 | ||||||
| 			for i, repo := range cfg.Repos { | 			reposSlice := cfg.Repos(ctx) | ||||||
|  | 			for i, repo := range reposSlice { | ||||||
| 				if repo.Name == name { | 				if repo.Name == name { | ||||||
| 					index = i | 					index = i | ||||||
| 					found = true | 					found = true | ||||||
| @@ -129,9 +140,9 @@ func RemoveRepoCmd() *cli.Command { | |||||||
| 				os.Exit(1) | 				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 { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error opening config file"), "err", err) | 				slog.Error(gotext.Get("Error opening config file"), "err", err) | ||||||
| 				os.Exit(1) | 				os.Exit(1) | ||||||
| @@ -143,12 +154,17 @@ func RemoveRepoCmd() *cli.Command { | |||||||
| 				os.Exit(1) | 				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 { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error removing repo directory"), "err", err) | 				slog.Error(gotext.Get("Error removing repo directory"), "err", err) | ||||||
| 				os.Exit(1) | 				os.Exit(1) | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|  | 			db := database.New(cfg) | ||||||
|  | 			err = db.Init(ctx) | ||||||
|  | 			if err != nil { | ||||||
|  | 				os.Exit(1) | ||||||
|  | 			} | ||||||
| 			err = db.DeletePkgs(ctx, "repository = ?", name) | 			err = db.DeletePkgs(ctx, "repository = ?", name) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error removing packages from database"), "err", err) | 				slog.Error(gotext.Get("Error removing packages from database"), "err", err) | ||||||
| @@ -167,7 +183,14 @@ func RefreshCmd() *cli.Command { | |||||||
| 		Aliases: []string{"ref"}, | 		Aliases: []string{"ref"}, | ||||||
| 		Action: func(c *cli.Context) error { | 		Action: func(c *cli.Context) error { | ||||||
| 			ctx := c.Context | 			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 { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error pulling repos"), "err", err) | 				slog.Error(gotext.Get("Error pulling repos"), "err", err) | ||||||
| 				os.Exit(1) | 				os.Exit(1) | ||||||
|   | |||||||
							
								
								
									
										47
									
								
								search.go
									
									
									
									
									
								
							
							
						
						
									
										47
									
								
								search.go
									
									
									
									
									
								
							| @@ -20,7 +20,6 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"log/slog" | 	"log/slog" | ||||||
| 	"os" | 	"os" | ||||||
| 	"strings" |  | ||||||
| 	"text/template" | 	"text/template" | ||||||
|  |  | ||||||
| 	"github.com/leonelquinteros/gotext" | 	"github.com/leonelquinteros/gotext" | ||||||
| @@ -28,6 +27,7 @@ import ( | |||||||
|  |  | ||||||
| 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | 	"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" | ||||||
| 	database "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/search" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func SearchCmd() *cli.Command { | func SearchCmd() *cli.Command { | ||||||
| @@ -70,34 +70,7 @@ func SearchCmd() *cli.Command { | |||||||
| 			defer db.Close() | 			defer db.Close() | ||||||
|  |  | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error db init"), "err", err) | 				slog.Error(gotext.Get("Error initialization database"), "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) |  | ||||||
| 				os.Exit(1) | 				os.Exit(1) | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| @@ -111,13 +84,23 @@ func SearchCmd() *cli.Command { | |||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			for result.Next() { | 			s := search.New(db) | ||||||
| 				var dbPkg database.Package |  | ||||||
| 				err = result.StructScan(&dbPkg) | 			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 { | 			if err != nil { | ||||||
|  | 				slog.Error(gotext.Get("Error parsing format template"), "err", err) | ||||||
| 				os.Exit(1) | 				os.Exit(1) | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|  | 			for _, dbPkg := range packages { | ||||||
| 				if tmpl != nil { | 				if tmpl != nil { | ||||||
| 					err = tmpl.Execute(os.Stdout, dbPkg) | 					err = tmpl.Execute(os.Stdout, dbPkg) | ||||||
| 					if err != nil { | 					if err != nil { | ||||||
|   | |||||||
| @@ -61,7 +61,7 @@ func UpgradeCmd() *cli.Command { | |||||||
| 			rs := repos.New(cfg, db) | 			rs := repos.New(cfg, db) | ||||||
| 			err := db.Init(ctx) | 			err := db.Init(ctx) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				slog.Error(gotext.Get("Error db init"), "err", err) | 				slog.Error(gotext.Get("Error initialization database"), "err", err) | ||||||
| 				os.Exit(1) | 				os.Exit(1) | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user