diff --git a/Makefile b/Makefile index ff8412b..ae74c6f 100644 --- a/Makefile +++ b/Makefile @@ -76,7 +76,9 @@ test-coverage: update-deps-cve: bash scripts/update-deps-cve.sh -e2e-test: clean build +prepare-for-e2e-test: clean build rm -f ./e2e-tests/alr cp alr e2e-tests + +e2e-test: prepare-for-e2e-test go test -tags=e2e ./... \ No newline at end of file diff --git a/assets/coverage-badge.svg b/assets/coverage-badge.svg index 4d357e3..776bb24 100644 --- a/assets/coverage-badge.svg +++ b/assets/coverage-badge.svg @@ -11,7 +11,7 @@ coverage coverage - 17.2% - 17.2% + 17.1% + 17.1% diff --git a/e2e-tests/common_test.go b/e2e-tests/common_test.go index fd3b893..88424c3 100644 --- a/e2e-tests/common_test.go +++ b/e2e-tests/common_test.go @@ -175,6 +175,11 @@ func dockerMultipleRun(t *testing.T, name string, ids []string, f func(t *testin }) } +func simpleExec(t *testing.T, r e2e.Runnable, cmd string, args ...string) { + err := r.Exec(e2e.NewCommand(cmd, args...)) + assert.NoError(t, err) +} + func runTestCommands(t *testing.T, r e2e.Runnable, timeout time.Duration, expects []expect.Batcher) { exp, _, err, _ := e2eSpawn( r, diff --git a/e2e-tests/issue_74_upgradable_test.go b/e2e-tests/issue_74_upgradable_test.go new file mode 100644 index 0000000..e04a825 --- /dev/null +++ b/e2e-tests/issue_74_upgradable_test.go @@ -0,0 +1,50 @@ +// ALR - Any Linux Repository +// Copyright (C) 2025 The ALR Authors +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//go:build e2e + +package e2etests_test + +import ( + "testing" + + "github.com/efficientgo/e2e" +) + +func TestE2EIssue74Upgradable(t *testing.T) { + dockerMultipleRun( + t, + "issue-74-upgradable", + COMMON_SYSTEMS, + func(t *testing.T, r e2e.Runnable) { + simpleExec(t, r, "sudo", + "alr", + "addrepo", + "--name", + "alr-repo", + "--url", + REPO_FOR_E2E_TESTS, + ) + simpleExec(t, r, "sudo", "sh", "-c", "sed -i 's/ref = .*/ref = \"bd26236cd7\"/' /etc/alr/alr.toml") + simpleExec(t, r, "alr", "ref") + simpleExec(t, r, "sudo", "alr", "in", "bar-pkg") + simpleExec(t, r, "sh", "-c", "test $(alr list -U | wc -l) -eq 0 || exit 1") + simpleExec(t, r, "sudo", "sh", "-c", "sed -i 's/ref = .*/ref = \"d9a3541561\"/' /etc/alr/alr.toml") + simpleExec(t, r, "sudo", "alr", "ref") + simpleExec(t, r, "sh", "-c", "test $(alr list -U | wc -l) -eq 1 || exit 1") + }, + ) +} diff --git a/internal/translations/default.pot b/internal/translations/default.pot index 0b23dfc..c39fb78 100644 --- a/internal/translations/default.pot +++ b/internal/translations/default.pot @@ -327,10 +327,30 @@ msgstr "" msgid "You need to be root to perform this action" msgstr "" -#: list.go:41 +#: list.go:43 msgid "List ALR repo packages" msgstr "" +#: list.go:57 +msgid "Format output using a Go template" +msgstr "" + +#: list.go:89 +msgid "Error getting packages for upgrade" +msgstr "" + +#: list.go:92 +msgid "No packages for upgrade" +msgstr "" + +#: list.go:102 list.go:187 +msgid "Error parsing format template" +msgstr "" + +#: list.go:108 list.go:191 +msgid "Error executing template" +msgstr "" + #: main.go:45 msgid "Print the current ALR version and exit" msgstr "" @@ -495,22 +515,10 @@ msgstr "" msgid "Search by provides" msgstr "" -#: search.go:71 -msgid "Format output using a Go template" -msgstr "" - #: search.go:130 msgid "Error while executing search" msgstr "" -#: search.go:138 -msgid "Error parsing format template" -msgstr "" - -#: search.go:153 -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 2c9d802..9d7fc9c 100644 --- a/internal/translations/po/ru/default.po +++ b/internal/translations/po/ru/default.po @@ -5,15 +5,15 @@ msgid "" msgstr "" "Project-Id-Version: unnamed project\n" -"PO-Revision-Date: 2025-04-27 18:27+0300\n" +"PO-Revision-Date: 2025-05-13 23:24+0300\n" "Last-Translator: Maxim Slipenko \n" "Language-Team: Russian\n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" -"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Generator: Gtranslator 48.0\n" #: build.go:42 @@ -335,10 +335,30 @@ msgstr "Вы должны быть членом %s чтобы выполнить msgid "You need to be root to perform this action" msgstr "Вы должны быть root чтобы выполнить это" -#: list.go:41 +#: list.go:43 msgid "List ALR repo packages" msgstr "Список пакетов репозитория ALR" +#: list.go:57 +msgid "Format output using a Go template" +msgstr "Формат выходных данных с использованием шаблона Go" + +#: list.go:89 +msgid "Error getting packages for upgrade" +msgstr "Ошибка при получении пакетов для обновления" + +#: list.go:92 +msgid "No packages for upgrade" +msgstr "Нет пакетов к обновлению" + +#: list.go:102 list.go:187 +msgid "Error parsing format template" +msgstr "Ошибка при разборе шаблона" + +#: list.go:108 list.go:191 +msgid "Error executing template" +msgstr "Ошибка при выполнении шаблона" + #: main.go:45 msgid "Print the current ALR version and exit" msgstr "Показать текущую версию ALR и выйти" @@ -509,22 +529,10 @@ msgstr "Искать по репозиторию" msgid "Search by provides" msgstr "Иcкать по provides" -#: search.go:71 -msgid "Format output using a Go template" -msgstr "Формат выходных данных с использованием шаблона Go" - #: search.go:130 msgid "Error while executing search" msgstr "Ошибка при выполнении поиска" -#: search.go:138 -msgid "Error parsing format template" -msgstr "Ошибка при разборе шаблона" - -#: search.go:153 -msgid "Error executing template" -msgstr "Ошибка при выполнении шаблона" - #: upgrade.go:47 msgid "Upgrade all installed packages" msgstr "Обновить все установленные пакеты" diff --git a/list.go b/list.go index 117a7bc..afad318 100644 --- a/list.go +++ b/list.go @@ -22,10 +22,12 @@ package main import ( "fmt" "log/slog" + "os" + "slices" + "text/template" "github.com/leonelquinteros/gotext" "github.com/urfave/cli/v2" - "golang.org/x/exp/slices" "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils" appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder" @@ -45,6 +47,15 @@ func ListCmd() *cli.Command { Name: "installed", Aliases: []string{"I"}, }, + &cli.BoolFlag{ + Name: "upgradable", + Aliases: []string{"U"}, + }, + &cli.StringFlag{ + Name: "format", + Aliases: []string{"f"}, + Usage: gotext.Get("Format output using a Go template"), + }, }, Action: func(c *cli.Context) error { if err := utils.ExitIfCantDropCapsToAlrUserNoPrivs(); err != nil { @@ -57,8 +68,10 @@ func ListCmd() *cli.Command { New(ctx). WithConfig(). WithDB(). + WithManager(). // autoPull only WithRepos(). + WithDistroInfo(). Build() if err != nil { return err @@ -67,6 +80,39 @@ func ListCmd() *cli.Command { cfg := deps.Cfg db := deps.DB + mgr := deps.Manager + info := deps.Info + + if c.Bool("upgradable") { + updates, err := checkForUpdates(ctx, mgr, db, info) + if err != nil { + return cliutils.FormatCliExit(gotext.Get("Error getting packages for upgrade"), err) + } + if len(updates) == 0 { + slog.Info(gotext.Get("No packages for upgrade")) + return nil + } + + format := c.String("format") + if format == "" { + format = "{{.Package.Repository}}/{{.Package.Name}} {{.FromVersion}} -> {{.ToVersion}}\n" + } + tmpl, err := template.New("format").Parse(format) + if err != nil { + return cliutils.FormatCliExit(gotext.Get("Error parsing format template"), err) + } + + for _, updateInfo := range updates { + err = tmpl.Execute(os.Stdout, updateInfo) + if err != nil { + return cliutils.FormatCliExit(gotext.Get("Error executing template"), err) + } + } + + return nil + } + + // TODO: refactor code below where := "true" args := []any(nil) @@ -115,17 +161,35 @@ func ListCmd() *cli.Command { continue } - version := pkg.Version + type packageInfo struct { + Package *database.Package + Version string + } + + pkgInfo := &packageInfo{} + pkgInfo.Package = &pkg + pkgInfo.Version = pkg.Version if c.Bool("installed") { instVersion, ok := installedAlrPackages[fmt.Sprintf("%s/%s", pkg.Repository, pkg.Name)] if !ok { continue } else { - version = instVersion + pkgInfo.Version = instVersion } } - fmt.Printf("%s/%s %s\n", pkg.Repository, pkg.Name, version) + format := c.String("format") + if format == "" { + format = "{{.Package.Repository}}/{{.Package.Name}} {{.Version}}\n" + } + tmpl, err := template.New("format").Parse(format) + if err != nil { + return cliutils.FormatCliExit(gotext.Get("Error parsing format template"), err) + } + err = tmpl.Execute(os.Stdout, pkg) + if err != nil { + return cliutils.FormatCliExit(gotext.Get("Error executing template"), err) + } } return nil diff --git a/upgrade.go b/upgrade.go index 7b148aa..6316692 100644 --- a/upgrade.go +++ b/upgrade.go @@ -116,7 +116,7 @@ func UpgradeCmd() *cli.Command { Info: deps.Info, PkgFormat_: build.GetPkgFormat(deps.Manager), }, - updates, + mapUptatesInfoToPackages(updates), ) if err != nil { return cliutils.FormatCliExit(gotext.Get("Error checking for updates"), err) @@ -130,12 +130,27 @@ func UpgradeCmd() *cli.Command { } } +func mapUptatesInfoToPackages(updates []UpdateInfo) []database.Package { + var pkgs []database.Package + for _, info := range updates { + pkgs = append(pkgs, *info.Package) + } + return pkgs +} + +type UpdateInfo struct { + Package *database.Package + + FromVersion string + ToVersion string +} + func checkForUpdates( ctx context.Context, mgr manager.Manager, db *database.Database, info *distro.OSRelease, -) ([]database.Package, error) { +) ([]UpdateInfo, error) { installed, err := mgr.ListInstalled(nil) if err != nil { return nil, err @@ -145,7 +160,7 @@ func checkForUpdates( s := search.New(db) - var out []database.Package + var out []UpdateInfo for _, pkgName := range pkgNames { matches := build.RegexpALRPackageName.FindStringSubmatch(pkgName) if matches != nil { @@ -179,10 +194,13 @@ func checkForUpdates( } c := vercmp.Compare(repoVer, installed[pkgName]) - if c == 0 || c == -1 { - continue - } else if c == 1 { - out = append(out, pkg) + + if c == 1 { + out = append(out, UpdateInfo{ + Package: &pkg, + FromVersion: installed[pkgName], + ToVersion: repoVer, + }) } }