diff --git a/coverage-badge.svg b/coverage-badge.svg
index b686468..fdab882 100644
--- a/coverage-badge.svg
+++ b/coverage-badge.svg
@@ -11,7 +11,7 @@
coverage
coverage
- 20.8%
- 20.8%
+ 20.5%
+ 20.5%
diff --git a/internal/translations/default.pot b/internal/translations/default.pot
index 13eeea8..fe6da00 100644
--- a/internal/translations/default.pot
+++ b/internal/translations/default.pot
@@ -299,13 +299,13 @@ msgstr ""
msgid "Enable interactive questions and prompts"
msgstr ""
-#: main.go:90
+#: main.go:91
msgid ""
"Running ALR as root is forbidden as it may cause catastrophic damage to your "
"system"
msgstr ""
-#: main.go:124
+#: main.go:125
msgid "Error while running app"
msgstr ""
@@ -457,6 +457,46 @@ msgstr ""
msgid "Pull all repositories that have changed"
msgstr ""
+#: search.go:36
+msgid "Search packages"
+msgstr ""
+
+#: search.go:42
+msgid "Search by name"
+msgstr ""
+
+#: search.go:47
+msgid "Search by description"
+msgstr ""
+
+#: search.go:52
+msgid "Search by repository"
+msgstr ""
+
+#: search.go:57
+msgid "Search by provides"
+msgstr ""
+
+#: search.go:62
+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
+msgid "Error parsing format template"
+msgstr ""
+
+#: search.go:124
+msgid "Error executing template"
+msgstr ""
+
#: upgrade.go:47
msgid "Upgrade all installed packages"
msgstr ""
diff --git a/internal/translations/po/ru/default.po b/internal/translations/po/ru/default.po
index 88c2415..9a19c9a 100644
--- a/internal/translations/po/ru/default.po
+++ b/internal/translations/po/ru/default.po
@@ -312,7 +312,7 @@ msgstr "Аргументы, которые будут переданы мене
msgid "Enable interactive questions and prompts"
msgstr "Включение интерактивных вопросов и запросов"
-#: main.go:90
+#: main.go:91
msgid ""
"Running ALR as root is forbidden as it may cause catastrophic damage to your "
"system"
@@ -320,7 +320,7 @@ msgstr ""
"Запуск ALR от имени root запрещён, так как это может привести к "
"катастрофическому повреждению вашей системы"
-#: main.go:124
+#: main.go:125
msgid "Error while running app"
msgstr "Ошибка при запуске приложения"
@@ -479,6 +479,49 @@ msgstr "Ошибка при удалении пакетов из базы дан
msgid "Pull all repositories that have changed"
msgstr "Скачать все изменённые репозитории"
+#: search.go:36
+msgid "Search packages"
+msgstr ""
+
+#: search.go:42
+msgid "Search by name"
+msgstr ""
+
+#: search.go:47
+msgid "Search by description"
+msgstr ""
+
+#: search.go:52
+#, fuzzy
+msgid "Search by repository"
+msgstr "Добавить новый репозиторий"
+
+#: search.go:57
+msgid "Search by provides"
+msgstr ""
+
+#: search.go:62
+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
+#, fuzzy
+msgid "Error parsing format template"
+msgstr "Ошибка при разборе файла выпуска операционной системы"
+
+#: search.go:124
+#, fuzzy
+msgid "Error executing template"
+msgstr "Ошибка при получении пакетов"
+
#: upgrade.go:47
msgid "Upgrade all installed packages"
msgstr "Обновить все установленные пакеты"
diff --git a/main.go b/main.go
index c62fb5e..3d548f5 100644
--- a/main.go
+++ b/main.go
@@ -81,6 +81,7 @@ func GetApp() *cli.App {
GenCmd(),
HelperCmd(),
VersionCmd(),
+ SearchCmd(),
},
Before: func(c *cli.Context) error {
ctx := c.Context
diff --git a/search.go b/search.go
new file mode 100644
index 0000000..e2ace08
--- /dev/null
+++ b/search.go
@@ -0,0 +1,136 @@
+// ALR - Any Linux Repository
+// Copyright (C) 2025 Евгений Храмов
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+package main
+
+import (
+ "fmt"
+ "log/slog"
+ "os"
+ "strings"
+ "text/template"
+
+ "github.com/leonelquinteros/gotext"
+ "github.com/urfave/cli/v2"
+
+ "gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
+ database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
+)
+
+func SearchCmd() *cli.Command {
+ return &cli.Command{
+ Name: "search",
+ Usage: gotext.Get("Search packages"),
+ Aliases: []string{"s"},
+ Flags: []cli.Flag{
+ &cli.StringFlag{
+ Name: "name",
+ Aliases: []string{"n"},
+ Usage: gotext.Get("Search by name"),
+ },
+ &cli.StringFlag{
+ Name: "description",
+ Aliases: []string{"d"},
+ Usage: gotext.Get("Search by description"),
+ },
+ &cli.StringFlag{
+ Name: "repository",
+ Aliases: []string{"repo"},
+ Usage: gotext.Get("Search by repository"),
+ },
+ &cli.StringFlag{
+ Name: "provides",
+ Aliases: []string{"p"},
+ Usage: gotext.Get("Search by provides"),
+ },
+ &cli.StringFlag{
+ Name: "format",
+ Aliases: []string{"f"},
+ Usage: gotext.Get("Format output using a Go template"),
+ },
+ },
+ Action: func(c *cli.Context) error {
+ ctx := c.Context
+ cfg := config.New()
+ db := database.New(cfg)
+ err := db.Init(ctx)
+ 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)
+ os.Exit(1)
+ }
+
+ format := c.String("format")
+ var tmpl *template.Template
+ if format != "" {
+ tmpl, err = template.New("format").Parse(format)
+ if err != nil {
+ slog.Error(gotext.Get("Error parsing format template"), "err", err)
+ os.Exit(1)
+ }
+ }
+
+ for result.Next() {
+ var dbPkg database.Package
+ err = result.StructScan(&dbPkg)
+ if err != nil {
+ os.Exit(1)
+ }
+
+ if tmpl != nil {
+ err = tmpl.Execute(os.Stdout, dbPkg)
+ if err != nil {
+ slog.Error(gotext.Get("Error executing template"), "err", err)
+ os.Exit(1)
+ }
+ fmt.Println()
+ } else {
+ fmt.Println(dbPkg.Name)
+ }
+ }
+
+ return nil
+ },
+ }
+}