14 Commits

Author SHA1 Message Date
2b7c2bbbb3 Merge pull request 'feat: add group and summary fields' (#69) from Maks1mS/ALR:add-new-fileds into master
Reviewed-on: Plemya-x/ALR#69
2025-04-20 06:45:41 +00:00
afe35f407e security: update vulnerable packages
Vulnerabilities detected by Trivy scan:
- github.com/go-git/go-git/v5 (CVE-2025-21613)
- github.com/go-git/go-git/v5 (CVE-2025-21614)
- golang.org/x/crypto (CVE-2025-22869)
- golang.org/x/net (CVE-2025-22870)
- golang.org/x/net (CVE-2025-22872)
2025-04-20 09:04:46 +03:00
c51d1d9202 add group and summary fields 2025-04-20 09:04:46 +03:00
b46dd41ada fix tests 2025-04-20 09:04:46 +03:00
f623cba5c0 use fakeroot from gitea.plemya-x.ru/Plemya-x/fakeroot 2025-04-20 09:04:39 +03:00
e552663442 Merge pull request 'i18n: russian translation' (#68) from Maks1mS/ALR:i18n-russian-translation into master
Reviewed-on: Plemya-x/ALR#68
2025-04-19 08:20:31 +00:00
7bbceb76c9 i18n: russian translation 2025-04-18 07:40:15 +03:00
bd6e3bbe27 Merge pull request 'some fixes' (#67) from Maks1mS/ALR:fix/pass-lang-to-child into master
Reviewed-on: Plemya-x/ALR#67
2025-04-17 08:01:25 +00:00
0d917190ab fix non-interactive install and add fallback in HandleExitCoder 2025-04-17 10:15:51 +03:00
83b8f3b047 fix(i18n): pass LANG vars to _internal 2025-04-16 08:33:40 +03:00
3483cf57f8 Merge pull request 'feat: migrate to system cache with changing core logic' (#66) from Maks1mS/ALR:migrate-to-global-cache into master
Reviewed-on: Plemya-x/ALR#66
2025-04-15 19:38:58 +00:00
efa4f59403 feat: migrate to system cache with changing core logic 2025-04-15 21:41:21 +03:00
c59ed6d505 Исправлено: определение корректной версии .rpm
Дополнено: шаблон генерации пакетов pip
2025-04-06 15:48:28 +03:00
2bbc308810 Merge pull request 'feat: add completion for remove command' (#65) from Maks1mS/ALR:master into master
Reviewed-on: Plemya-x/ALR#65
2025-04-06 10:41:53 +00:00
58 changed files with 1716 additions and 1409 deletions

3
.gitignore vendored
View File

@@ -9,4 +9,5 @@
*.out *.out
e2e-tests/alr e2e-tests/alr
commit_msg.txt

View File

@@ -73,6 +73,9 @@ test-coverage:
go test ./... -v -coverpkg=./... -coverprofile=coverage.out go test ./... -v -coverpkg=./... -coverprofile=coverage.out
bash scripts/coverage-badge.sh bash scripts/coverage-badge.sh
update-deps-cve:
bash scripts/update-deps-cve.sh
e2e-test: clean build e2e-test: clean build
rm -f ./e2e-tests/alr rm -f ./e2e-tests/alr
cp alr e2e-tests cp alr e2e-tests

View File

@@ -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">15.7%</text> <text x="86" y="15" fill="#010101" fill-opacity=".3">16.4%</text>
<text x="86" y="14">15.7%</text> <text x="86" y="14">16.4%</text>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 926 B

After

Width:  |  Height:  |  Size: 926 B

View File

@@ -12,7 +12,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="37" y="15" fill="#010101" fill-opacity=".3">ru translate</text> <text x="37" y="15" fill="#010101" fill-opacity=".3">ru translate</text>
<text x="37" y="14">ru translate</text> <text x="37" y="14">ru translate</text>
<text x="100" y="15" fill="#010101" fill-opacity=".3">98.00%</text> <text x="100" y="15" fill="#010101" fill-opacity=".3">100.00%</text>
<text x="100" y="14">98.00%</text> <text x="100" y="14">100.00%</text>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 940 B

After

Width:  |  Height:  |  Size: 942 B

238
build.go
View File

@@ -20,25 +20,20 @@
package main package main
import ( import (
"bytes"
"log/slog" "log/slog"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/leonelquinteros/gotext" "github.com/leonelquinteros/gotext"
"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/cliutils"
database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db" appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/osutils" "gitea.plemya-x.ru/Plemya-x/ALR/internal/osutils"
"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/internal/utils" "gitea.plemya-x.ru/Plemya-x/ALR/internal/utils"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/build" "gitea.plemya-x.ru/Plemya-x/ALR/pkg/build"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos"
) )
func BuildCmd() *cli.Command { func BuildCmd() *cli.Command {
@@ -69,115 +64,66 @@ func BuildCmd() *cli.Command {
}, },
}, },
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
if err := utils.EnuseIsPrivilegedGroupMember(); err != nil {
return err
}
wd, err := os.Getwd() wd, err := os.Getwd()
if err != nil { if err != nil {
slog.Error(gotext.Get("Error getting working directory"), "err", err) return cliutils.FormatCliExit(gotext.Get("Error getting working directory"), err)
os.Exit(1)
}
executable, err := os.Executable()
if err != nil {
slog.Error(gotext.Get("Error getting working directory"), "err", err)
os.Exit(1)
} }
cmd := exec.Command(executable, "_internal-mount", wd) wd, wdCleanup, err := Mount(wd)
var stdout bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil { if err != nil {
slog.Error(gotext.Get("Error getting working directory"), "err", err) return err
os.Exit(1)
}
wd = stdout.String()
defer func() {
slog.Warn("unmounting...")
cmd := exec.Command(executable, "_internal-umount", wd)
var stdout bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
slog.Error(gotext.Get("Error getting working directory"), "err", err)
os.Exit(1)
}
}()
err = utils.DropCapsToAlrUser()
if err != nil {
slog.Error(gotext.Get("Error dropping capabilities"), "err", err)
os.Exit(1)
}
_, err = os.Stat(wd)
if err != nil {
slog.Error(gotext.Get("Error dropping capabilities"), "err", err)
os.Exit(1)
} }
defer wdCleanup()
ctx := c.Context ctx := c.Context
cfg := config.New()
err = cfg.Load()
if err != nil {
slog.Error(gotext.Get("Error loading config"), "err", err)
os.Exit(1)
}
db := database.New(cfg) deps, err := appbuilder.
rs := repos.New(cfg, db) New(ctx).
err = db.Init(ctx) WithConfig().
WithDB().
WithReposNoPull().
WithDistroInfo().
WithManager().
Build()
if err != nil { if err != nil {
slog.Error(gotext.Get("Error initialization database"), "err", err) return cli.Exit(err, 1)
os.Exit(1)
} }
defer deps.Defer()
var script string var script string
var packages []string var packages []string
// Обнаружение менеджера пакетов
mgr := manager.Detect()
if mgr == nil {
slog.Error(gotext.Get("Unable to detect a supported package manager on the system"))
os.Exit(1)
}
info, err := distro.ParseOSRelease(ctx)
if err != nil {
slog.Error(gotext.Get("Error parsing os release"), "err", err)
os.Exit(1)
}
builder := build.NewMainBuilder(
cfg,
rs,
)
var res *build.BuildResult var res *build.BuildResult
var scriptArgs *build.BuildPackageFromScriptArgs
var dbArgs *build.BuildPackageFromDbArgs
buildArgs := &build.BuildArgs{
Opts: &types.BuildOpts{
Clean: c.Bool("clean"),
Interactive: c.Bool("interactive"),
},
PkgFormat_: build.GetPkgFormat(deps.Manager),
Info: deps.Info,
}
switch { switch {
case c.IsSet("script"): case c.IsSet("script"):
script = c.String("script") script, err = filepath.Abs(c.String("script"))
if err != nil {
return cliutils.FormatCliExit(gotext.Get("Cannot get absolute script path"), err)
}
packages = append(packages, c.String("script-package")) packages = append(packages, c.String("script-package"))
res, err = builder.BuildPackageFromScript( scriptArgs = &build.BuildPackageFromScriptArgs{
ctx, Script: script,
&build.BuildPackageFromScriptArgs{ Packages: packages,
Script: script, BuildArgs: *buildArgs,
Packages: packages,
BuildArgs: build.BuildArgs{
Opts: &types.BuildOpts{
Clean: c.Bool("clean"),
Interactive: c.Bool("interactive"),
},
PkgFormat_: build.GetPkgFormat(mgr),
Info: info,
},
},
)
if err != nil {
slog.Error(gotext.Get("Error building package"), "err", err)
os.Exit(1)
} }
case c.IsSet("package"): case c.IsSet("package"):
// TODO: handle multiple packages // TODO: handle multiple packages
@@ -191,51 +137,97 @@ func BuildCmd() *cli.Command {
packageSearch = arr[0] packageSearch = arr[0]
} }
pkgs, _, _ := rs.FindPkgs(ctx, []string{packageSearch}) pkgs, _, err := deps.Repos.FindPkgs(ctx, []string{packageSearch})
pkg, ok := pkgs[packageSearch] if err != nil {
if len(pkg) < 1 || !ok { return cliutils.FormatCliExit("failed to find pkgs", err)
slog.Error(gotext.Get("Package not found")) }
os.Exit(1)
pkg := cliutils.FlattenPkgs(ctx, pkgs, "build", c.Bool("interactive"))
if len(pkg) < 1 {
return cliutils.FormatCliExit(gotext.Get("Package not found"), nil)
} }
if pkg[0].BasePkgName != "" { if pkg[0].BasePkgName != "" {
packages = append(packages, pkg[0].Name) packages = append(packages, pkg[0].Name)
} }
res, err = builder.BuildPackageFromDb( dbArgs = &build.BuildPackageFromDbArgs{
ctx, Package: &pkg[0],
&build.BuildPackageFromDbArgs{ Packages: packages,
Package: &pkg[0], BuildArgs: *buildArgs,
Packages: packages,
BuildArgs: build.BuildArgs{
Opts: &types.BuildOpts{
Clean: c.Bool("clean"),
Interactive: c.Bool("interactive"),
},
PkgFormat_: build.GetPkgFormat(mgr),
Info: info,
},
},
)
if err != nil {
slog.Error(gotext.Get("Error building package"), "err", err)
os.Exit(1)
} }
default: default:
slog.Error(gotext.Get("Nothing to build")) return cliutils.FormatCliExit(gotext.Get("Nothing to build"), nil)
os.Exit(1) }
if scriptArgs != nil {
scriptFile := filepath.Base(scriptArgs.Script)
newScriptDir, scriptDirCleanup, err := Mount(filepath.Dir(scriptArgs.Script))
if err != nil {
return err
}
defer scriptDirCleanup()
scriptArgs.Script = filepath.Join(newScriptDir, scriptFile)
}
if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil {
return err
}
installer, installerClose, err := build.GetSafeInstaller()
if err != nil {
return err
}
defer installerClose()
if err := utils.ExitIfCantSetNoNewPrivs(); err != nil {
return err
}
scripter, scripterClose, err := build.GetSafeScriptExecutor()
if err != nil {
return err
}
defer scripterClose()
builder, err := build.NewMainBuilder(
deps.Cfg,
deps.Manager,
deps.Repos,
scripter,
installer,
)
if err != nil {
return err
}
if scriptArgs != nil {
res, err = builder.BuildPackageFromScript(
ctx,
scriptArgs,
)
} else if dbArgs != nil {
res, err = builder.BuildPackageFromDb(
ctx,
dbArgs,
)
}
if err != nil {
return cliutils.FormatCliExit(gotext.Get("Error building package"), err)
} }
// Перемещение собранных пакетов в рабочую директорию
for _, pkgPath := range res.PackagePaths { for _, pkgPath := range res.PackagePaths {
name := filepath.Base(pkgPath) name := filepath.Base(pkgPath)
err = osutils.Move(pkgPath, filepath.Join(wd, name)) err = osutils.Move(pkgPath, filepath.Join(wd, name))
if err != nil { if err != nil {
slog.Error(gotext.Get("Error moving the package"), "err", err) return cliutils.FormatCliExit(gotext.Get("Error moving the package"), err)
os.Exit(1)
} }
} }
slog.Info(gotext.Get("Done"))
return nil return nil
}, },
} }

View File

@@ -113,6 +113,10 @@ var AUTOREQ_AUTOPROV_SYSTEMS []string = []string{
"fedora-41", "fedora-41",
} }
var RPM_SYSTEMS []string = []string{
"fedora-41",
}
var COMMON_SYSTEMS []string = []string{ var COMMON_SYSTEMS []string = []string{
"ubuntu-24.04", "ubuntu-24.04",
} }

View File

@@ -0,0 +1,56 @@
// 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/>.
//go:build e2e
package e2etests_test
import (
"testing"
"github.com/alecthomas/assert/v2"
"github.com/efficientgo/e2e"
)
func TestE2EGroupAndSummaryField(t *testing.T) {
dockerMultipleRun(
t,
"group-and-summary-field",
RPM_SYSTEMS,
func(t *testing.T, r e2e.Runnable) {
err := r.Exec(e2e.NewCommand(
"sudo",
"alr",
"addrepo",
"--name",
"alr-repo",
"--url",
"https://gitea.plemya-x.ru/Maks1mS/repo-for-tests.git",
))
assert.NoError(t, err)
err = r.Exec(e2e.NewCommand(
"sh", "-c", "alr search --name test-group-and-summary --format \"{{.Group}}\" | grep ^System/Base$",
))
assert.NoError(t, err)
err = r.Exec(e2e.NewCommand(
"sh", "-c", "alr search --name test-group-and-summary --format \"{{.Summary}}\" | grep \"^Custom summary$\"",
))
assert.NoError(t, err)
},
)
}

View File

@@ -1,7 +1,7 @@
FROM fedora:41 FROM fedora:41
RUN dnf install -y ca-certificates sudo rpm-build RUN dnf install -y ca-certificates sudo rpm-build bindfs
RUN <<EOF RUN <<EOF
useradd -m -s /bin/bash user useradd -m -s /bin/bash -G wheel user
echo "user ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/user echo "user ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/user
chmod 0440 /etc/sudoers.d/user chmod 0440 /etc/sudoers.d/user
@@ -14,4 +14,5 @@ RUN <<EOF
setcap cap_setuid,cap_setgid+ep /usr/bin/alr setcap cap_setuid,cap_setgid+ep /usr/bin/alr
EOF EOF
USER user USER user
WORKDIR /home/user
ENTRYPOINT ["tail", "-f", "/dev/null"] ENTRYPOINT ["tail", "-f", "/dev/null"]

View File

@@ -31,10 +31,21 @@ func TestE2EIssue32Interactive(t *testing.T) {
"issue-32-interactive", "issue-32-interactive",
COMMON_SYSTEMS, COMMON_SYSTEMS,
func(t *testing.T, r e2e.Runnable) { func(t *testing.T, r e2e.Runnable) {
err := r.Exec(e2e.NewCommand( assert.NoError(t, r.Exec(e2e.NewCommand(
"alr", "--interactive=false", "remove", "ca-certificates", "sudo", "alr", "--interactive=false", "remove", "ca-certificates",
)) )))
assert.NoError(t, err)
assert.NoError(t, r.Exec(e2e.NewCommand(
"sudo", "alr", "--interactive=false", "remove", "openssl",
)))
assert.NoError(t, r.Exec(e2e.NewCommand(
"alr", "fix",
)))
assert.NoError(t, r.Exec(e2e.NewCommand(
"sudo", "alr", "--interactive=false", "install", "ca-certificates",
)))
}, },
) )
} }

View File

@@ -43,7 +43,7 @@ func TestE2EIssue50InstallMultiple(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
err = r.Exec(e2e.NewCommand( err = r.Exec(e2e.NewCommand(
"alr", "in", "foo-pkg", "bar-pkg", "sudo", "alr", "in", "foo-pkg", "bar-pkg",
)) ))
assert.NoError(t, err) assert.NoError(t, err)

View File

@@ -43,7 +43,7 @@ func TestE2EIssue59RmCompletion(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
err = r.Exec(e2e.NewCommand( err = r.Exec(e2e.NewCommand(
"alr", "in", "foo-pkg", "bar-pkg", "sudo", "alr", "in", "foo-pkg", "bar-pkg",
)) ))
assert.NoError(t, err) assert.NoError(t, err)

17
fix.go
View File

@@ -27,6 +27,7 @@ import (
"github.com/leonelquinteros/gotext" "github.com/leonelquinteros/gotext"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils"
appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder" appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils" "gitea.plemya-x.ru/Plemya-x/ALR/internal/utils"
) )
@@ -36,7 +37,7 @@ func FixCmd() *cli.Command {
Name: "fix", Name: "fix",
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 {
if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil { if err := utils.ExitIfCantDropCapsToAlrUserNoPrivs(); err != nil {
return err return err
} }
@@ -60,22 +61,19 @@ func FixCmd() *cli.Command {
dir, err := os.Open(paths.CacheDir) dir, err := os.Open(paths.CacheDir)
if err != nil { if err != nil {
slog.Error(gotext.Get("Unable to open cache directory")) return cliutils.FormatCliExit(gotext.Get("Unable to open cache directory"), err)
return cli.Exit(err, 1)
} }
defer dir.Close() defer dir.Close()
entries, err := dir.Readdirnames(-1) entries, err := dir.Readdirnames(-1)
if err != nil { if err != nil {
slog.Error(gotext.Get("Unable to read cache directory contents")) return cliutils.FormatCliExit(gotext.Get("Unable to read cache directory contents"), err)
return cli.Exit(err, 1)
} }
for _, entry := range entries { for _, entry := range entries {
err = os.RemoveAll(filepath.Join(paths.CacheDir, entry)) err = os.RemoveAll(filepath.Join(paths.CacheDir, entry))
if err != nil { if err != nil {
slog.Error(gotext.Get("Unable to remove cache item"), "item", entry) return cliutils.FormatCliExit(gotext.Get("Unable to remove cache item (%s)", entry), err)
return cli.Exit(err, 1)
} }
} }
@@ -83,15 +81,14 @@ func FixCmd() *cli.Command {
err = os.MkdirAll(paths.CacheDir, 0o755) err = os.MkdirAll(paths.CacheDir, 0o755)
if err != nil { if err != nil {
slog.Error(gotext.Get("Unable to create new cache directory")) return cliutils.FormatCliExit(gotext.Get("Unable to create new cache directory"), err)
return cli.Exit(err, 1)
} }
deps, err = appbuilder. deps, err = appbuilder.
New(ctx). New(ctx).
WithConfig(). WithConfig().
WithDB(). WithDB().
WithRepos(). WithReposForcePull().
Build() Build()
if err != nil { if err != nil {
return cli.Exit(err, 1) return cli.Exit(err, 1)

37
go.mod
View File

@@ -1,12 +1,11 @@
module gitea.plemya-x.ru/Plemya-x/ALR module gitea.plemya-x.ru/Plemya-x/ALR
go 1.22 go 1.23.0
toolchain go1.23.5 toolchain go1.24.2
replace github.com/creack/pty => github.com/creack/pty v1.1.19
require ( require (
gitea.plemya-x.ru/Plemya-x/fakeroot v0.0.2-0.20250408104831-427aaa7713c3
github.com/AlecAivazis/survey/v2 v2.3.7 github.com/AlecAivazis/survey/v2 v2.3.7
github.com/PuerkitoBio/purell v1.2.0 github.com/PuerkitoBio/purell v1.2.0
github.com/alecthomas/assert/v2 v2.2.1 github.com/alecthomas/assert/v2 v2.2.1
@@ -16,10 +15,9 @@ require (
github.com/charmbracelet/bubbletea v1.2.4 github.com/charmbracelet/bubbletea v1.2.4
github.com/charmbracelet/lipgloss v1.0.0 github.com/charmbracelet/lipgloss v1.0.0
github.com/charmbracelet/log v0.4.0 github.com/charmbracelet/log v0.4.0
github.com/creack/pty v1.1.24
github.com/efficientgo/e2e v0.14.1-0.20240418111536-97db25a0c6c0 github.com/efficientgo/e2e v0.14.1-0.20240418111536-97db25a0c6c0
github.com/go-git/go-billy/v5 v5.5.0 github.com/go-git/go-billy/v5 v5.6.0
github.com/go-git/go-git/v5 v5.12.0 github.com/go-git/go-git/v5 v5.13.0
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/goreleaser/nfpm/v2 v2.41.0 github.com/goreleaser/nfpm/v2 v2.41.0
github.com/hashicorp/go-hclog v0.14.1 github.com/hashicorp/go-hclog v0.14.1
@@ -37,10 +35,10 @@ require (
github.com/urfave/cli/v2 v2.25.7 github.com/urfave/cli/v2 v2.25.7
github.com/vmihailenco/msgpack/v5 v5.3.5 github.com/vmihailenco/msgpack/v5 v5.3.5
go.elara.ws/vercmp v0.0.0-20230622214216-0b2b067575c4 go.elara.ws/vercmp v0.0.0-20230622214216-0b2b067575c4
golang.org/x/crypto v0.32.0 golang.org/x/crypto v0.36.0
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
golang.org/x/sys v0.29.0 golang.org/x/sys v0.31.0
golang.org/x/text v0.21.0 golang.org/x/text v0.23.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
modernc.org/sqlite v1.25.0 modernc.org/sqlite v1.25.0
mvdan.cc/sh/v3 v3.10.0 mvdan.cc/sh/v3 v3.10.0
@@ -53,7 +51,7 @@ require (
github.com/Masterminds/semver/v3 v3.3.0 // indirect github.com/Masterminds/semver/v3 v3.3.0 // indirect
github.com/Masterminds/sprig/v3 v3.2.3 // indirect github.com/Masterminds/sprig/v3 v3.2.3 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/ProtonMail/go-crypto v1.0.0 // indirect github.com/ProtonMail/go-crypto v1.1.3 // indirect
github.com/alecthomas/repr v0.2.0 // indirect github.com/alecthomas/repr v0.2.0 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect github.com/andybalholm/brotli v1.0.4 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
@@ -68,7 +66,8 @@ require (
github.com/cloudflare/circl v1.3.8 // indirect github.com/cloudflare/circl v1.3.8 // indirect
github.com/connesc/cipherio v0.2.1 // indirect github.com/connesc/cipherio v0.2.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/creack/pty v1.1.24 // indirect
github.com/cyphar/filepath-securejoin v0.2.5 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dlclark/regexp2 v1.10.0 // indirect github.com/dlclark/regexp2 v1.10.0 // indirect
github.com/dsnet/compress v0.0.1 // indirect github.com/dsnet/compress v0.0.1 // indirect
@@ -119,7 +118,7 @@ require (
github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/shopspring/decimal v1.2.0 // indirect github.com/shopspring/decimal v1.2.0 // indirect
github.com/skeema/knownhosts v1.2.2 // indirect github.com/skeema/knownhosts v1.3.0 // indirect
github.com/spf13/cast v1.6.0 // indirect github.com/spf13/cast v1.6.0 // indirect
github.com/therootcompany/xz v1.0.1 // indirect github.com/therootcompany/xz v1.0.1 // indirect
github.com/ulikunitz/xz v0.5.12 // indirect github.com/ulikunitz/xz v0.5.12 // indirect
@@ -128,11 +127,11 @@ require (
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
gitlab.com/digitalxero/go-conventional-commit v1.0.7 // indirect gitlab.com/digitalxero/go-conventional-commit v1.0.7 // indirect
go4.org v0.0.0-20200411211856-f5505b9728dd // indirect go4.org v0.0.0-20200411211856-f5505b9728dd // indirect
golang.org/x/mod v0.18.0 // indirect golang.org/x/mod v0.19.0 // indirect
golang.org/x/net v0.34.0 // indirect golang.org/x/net v0.38.0 // indirect
golang.org/x/sync v0.10.0 // indirect golang.org/x/sync v0.12.0 // indirect
golang.org/x/term v0.28.0 // indirect golang.org/x/term v0.30.0 // indirect
golang.org/x/tools v0.22.0 // indirect golang.org/x/tools v0.23.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect
google.golang.org/grpc v1.58.3 // indirect google.golang.org/grpc v1.58.3 // indirect
google.golang.org/protobuf v1.36.1 // indirect google.golang.org/protobuf v1.36.1 // indirect

90
go.sum
View File

@@ -17,6 +17,8 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
gitea.plemya-x.ru/Plemya-x/fakeroot v0.0.2-0.20250408104831-427aaa7713c3 h1:56BjRJJ2Sv50DfSvNUydUMJwwFuiBMWC1uYtH2GYjk8=
gitea.plemya-x.ru/Plemya-x/fakeroot v0.0.2-0.20250408104831-427aaa7713c3/go.mod h1:iKQM6uttMJgE5CFrPw6SQqAV7TKtlJNICRAie/dTciw=
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
github.com/AlekSi/pointer v1.2.0 h1:glcy/gc4h8HnG2Z3ZECSzZ1IX1x2JxRVuDzaJwQE0+w= github.com/AlekSi/pointer v1.2.0 h1:glcy/gc4h8HnG2Z3ZECSzZ1IX1x2JxRVuDzaJwQE0+w=
@@ -37,8 +39,8 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk=
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/ProtonMail/go-crypto v1.1.3/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k= github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k=
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw= github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw=
github.com/ProtonMail/gopenpgp/v2 v2.7.1 h1:Awsg7MPc2gD3I7IFac2qE3Gdls0lZW8SzrFZ3k1oz0s= github.com/ProtonMail/gopenpgp/v2 v2.7.1 h1:Awsg7MPc2gD3I7IFac2qE3Gdls0lZW8SzrFZ3k1oz0s=
@@ -71,7 +73,6 @@ github.com/bodgit/windows v1.0.0 h1:rLQ/XjsleZvx4fR1tB/UxQrK+SJ2OFHzfPjLWWOhDIA=
github.com/bodgit/windows v1.0.0/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM= github.com/bodgit/windows v1.0.0/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM=
github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/caarlos0/env v3.5.0+incompatible h1:Yy0UN8o9Wtr/jGHZDpCBLpNrzcFLLM2yixi/rBrKyJs= github.com/caarlos0/env v3.5.0+incompatible h1:Yy0UN8o9Wtr/jGHZDpCBLpNrzcFLLM2yixi/rBrKyJs=
github.com/caarlos0/env v3.5.0+incompatible/go.mod h1:tdCsowwCzMLdkqRYDlHpZCp2UooDD3MspDBjZ2AD02Y= github.com/caarlos0/env v3.5.0+incompatible/go.mod h1:tdCsowwCzMLdkqRYDlHpZCp2UooDD3MspDBjZ2AD02Y=
github.com/caarlos0/testfs v0.4.4 h1:3PHvzHi5Lt+g332CiShwS8ogTgS3HjrmzZxCm6JCDr8= github.com/caarlos0/testfs v0.4.4 h1:3PHvzHi5Lt+g332CiShwS8ogTgS3HjrmzZxCm6JCDr8=
@@ -99,17 +100,17 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/cloudflare/circl v1.3.8 h1:j+V8jJt09PoeMFIu2uh5JUyEaIHTXVOHslFoLNAKqwI= github.com/cloudflare/circl v1.3.8 h1:j+V8jJt09PoeMFIu2uh5JUyEaIHTXVOHslFoLNAKqwI=
github.com/cloudflare/circl v1.3.8/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU= github.com/cloudflare/circl v1.3.8/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
github.com/connesc/cipherio v0.2.1 h1:FGtpTPMbKNNWByNrr9aEBtaJtXjqOzkIXNYJp6OEycw= github.com/connesc/cipherio v0.2.1 h1:FGtpTPMbKNNWByNrr9aEBtaJtXjqOzkIXNYJp6OEycw=
github.com/connesc/cipherio v0.2.1/go.mod h1:ukY0MWJDFnJEbXMQtOcn2VmTpRfzcTz4OoVrWGGJZcA= github.com/connesc/cipherio v0.2.1/go.mod h1:ukY0MWJDFnJEbXMQtOcn2VmTpRfzcTz4OoVrWGGJZcA=
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.19 h1:tUN6H7LWqNx4hQVxomd0CVsDwaDr9gaRQaI4GpSmrsA= github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/creack/pty v1.1.19/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/cyphar/filepath-securejoin v0.2.5 h1:6iR5tXJ/e6tJZzzdMc1km3Sa7RRIVBKAK32O2s7AYfo=
github.com/cyphar/filepath-securejoin v0.2.5/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -124,8 +125,8 @@ github.com/efficientgo/core v1.0.0-rc.0 h1:jJoA0N+C4/knWYVZ6GrdHOtDyrg8Y/TR4vFpT
github.com/efficientgo/core v1.0.0-rc.0/go.mod h1:kQa0V74HNYMfuJH6jiPiwNdpWXl4xd/K4tzlrcvYDQI= github.com/efficientgo/core v1.0.0-rc.0/go.mod h1:kQa0V74HNYMfuJH6jiPiwNdpWXl4xd/K4tzlrcvYDQI=
github.com/efficientgo/e2e v0.14.1-0.20240418111536-97db25a0c6c0 h1:C/FNIs+MtAJgQYLJ9FX/ACFYyDRuLYoXTmueErrOJyA= github.com/efficientgo/e2e v0.14.1-0.20240418111536-97db25a0c6c0 h1:C/FNIs+MtAJgQYLJ9FX/ACFYyDRuLYoXTmueErrOJyA=
github.com/efficientgo/e2e v0.14.1-0.20240418111536-97db25a0c6c0/go.mod h1:plsKU0YHE9uX+7utvr7SiDtVBSHJyEfHRO4UnUgDmts= github.com/efficientgo/e2e v0.14.1-0.20240418111536-97db25a0c6c0/go.mod h1:plsKU0YHE9uX+7utvr7SiDtVBSHJyEfHRO4UnUgDmts=
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= github.com/elazarl/goproxy v1.2.1 h1:njjgvO6cRG9rIqN2ebkqy6cQz2Njkx7Fsfv/zIZqgug=
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/elazarl/goproxy v1.2.1/go.mod h1:YfEbZtqP4AetfO6d40vWchF3znWX7C7Vd6ZMfdL8z64=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@@ -136,16 +137,16 @@ github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE= github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8= github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= github.com/go-git/go-billy/v5 v5.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cjio8=
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= github.com/go-git/go-billy/v5 v5.6.0/go.mod h1:sFDq7xD3fn3E0GOwUSZqHo9lrkmx8xJhA0ZrfvjBRGM=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys= github.com/go-git/go-git/v5 v5.13.0 h1:vLn5wlGIh/X78El6r3Jr+30W16Blk0CTcxTYcYPWi5E=
github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY= github.com/go-git/go-git/v5 v5.13.0/go.mod h1:Wjo7/JyVKtQgUNdXYXIepzWfJQkUEIGvkvVkiXRR/zw=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
@@ -319,8 +320,8 @@ github.com/nwaples/rardecode/v2 v2.0.0-beta.2 h1:e3mzJFJs4k83GXBEiTaQ5HgSc/kOK8q
github.com/nwaples/rardecode/v2 v2.0.0-beta.2/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY= github.com/nwaples/rardecode/v2 v2.0.0-beta.2/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY=
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0= github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0=
@@ -360,8 +361,8 @@ github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A= github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY=
github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M=
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY= github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec= github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY= github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=
@@ -418,10 +419,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -430,8 +429,8 @@ golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb h1:c0vyKkb6yr3KR7jEfJaOSv4lG7xPkbN6r52aJz1d8a8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -450,9 +449,8 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -471,10 +469,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -489,9 +485,8 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -518,18 +513,14 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -538,10 +529,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -569,9 +558,8 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@@ -30,6 +30,7 @@ import (
"mvdan.cc/sh/v3/expand" "mvdan.cc/sh/v3/expand"
"mvdan.cc/sh/v3/interp" "mvdan.cc/sh/v3/interp"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/cpu" "gitea.plemya-x.ru/Plemya-x/ALR/internal/cpu"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/helpers" "gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/helpers"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro" "gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
@@ -71,19 +72,17 @@ func HelperCmd() *cli.Command {
helper, ok := helpers.Helpers[c.Args().First()] helper, ok := helpers.Helpers[c.Args().First()]
if !ok { if !ok {
slog.Error(gotext.Get("No such helper command"), "name", c.Args().First()) slog.Error(gotext.Get("No such helper command"), "name", c.Args().First())
os.Exit(1) return cli.Exit(gotext.Get("No such helper command"), 1)
} }
wd, err := os.Getwd() wd, err := os.Getwd()
if err != nil { if err != nil {
slog.Error(gotext.Get("Error getting working directory"), "err", err) return cliutils.FormatCliExit(gotext.Get("Error getting working directory"), err)
os.Exit(1)
} }
info, err := distro.ParseOSRelease(ctx) info, err := distro.ParseOSRelease(ctx)
if err != nil { if err != nil {
slog.Error(gotext.Get("Error getting working directory"), "err", err) return cliutils.FormatCliExit(gotext.Get("Error parsing os-release file"), err)
os.Exit(1)
} }
hc := interp.HandlerContext{ hc := interp.HandlerContext{

59
info.go
View File

@@ -21,7 +21,6 @@ package main
import ( import (
"fmt" "fmt"
"log/slog"
"os" "os"
"github.com/jeandeaual/go-locale" "github.com/jeandeaual/go-locale"
@@ -31,7 +30,6 @@ import (
"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils" "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils"
appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder" appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder"
"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/internal/overrides" "gitea.plemya-x.ru/Plemya-x/ALR/internal/overrides"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils" "gitea.plemya-x.ru/Plemya-x/ALR/internal/utils"
@@ -49,31 +47,25 @@ 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) { BashComplete: cliutils.BashCompleteWithError(func(c *cli.Context) error {
if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil { if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil {
slog.Error("Can't drop caps") return err
os.Exit(1)
} }
ctx := c.Context ctx := c.Context
cfg := config.New() deps, err := appbuilder.
err := cfg.Load() New(ctx).
WithConfig().
WithDB().
Build()
if err != nil { if err != nil {
slog.Error(gotext.Get("Error loading config"), "err", err) return err
os.Exit(1)
} }
defer deps.Defer()
db := database.New(cfg) result, err := deps.DB.GetPkgs(c.Context, "true")
err = db.Init(ctx)
if err != nil { if err != nil {
slog.Error(gotext.Get("Error initialization database"), "err", err) return cliutils.FormatCliExit(gotext.Get("Error getting packages"), 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() defer result.Close()
@@ -81,15 +73,15 @@ func InfoCmd() *cli.Command {
var pkg database.Package var pkg database.Package
err = result.StructScan(&pkg) err = result.StructScan(&pkg)
if err != nil { if err != nil {
slog.Error(gotext.Get("Error iterating over packages"), "err", err) return cliutils.FormatCliExit(gotext.Get("Error iterating over packages"), err)
os.Exit(1)
} }
fmt.Println(pkg.Name) fmt.Println(pkg.Name)
} }
}, return nil
}),
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil { if err := utils.ExitIfCantDropCapsToAlrUserNoPrivs(); err != nil {
return err return err
} }
@@ -115,13 +107,11 @@ func InfoCmd() *cli.Command {
found, _, err := rs.FindPkgs(ctx, args.Slice()) found, _, err := rs.FindPkgs(ctx, args.Slice())
if err != nil { if err != nil {
slog.Error(gotext.Get("Error finding packages")) return cliutils.FormatCliExit(gotext.Get("Error finding packages"), err)
return cli.Exit(err, 1)
} }
if len(found) == 0 { if len(found) == 0 {
slog.Error(gotext.Get("Package not found")) return cliutils.FormatCliExit(gotext.Get("Package not found"), err)
return cli.Exit(err, 1)
} }
pkgs := cliutils.FlattenPkgs(ctx, found, "show", c.Bool("interactive")) pkgs := cliutils.FlattenPkgs(ctx, found, "show", c.Bool("interactive"))
@@ -131,8 +121,7 @@ func InfoCmd() *cli.Command {
systemLang, err := locale.GetLanguage() systemLang, err := locale.GetLanguage()
if err != nil { if err != nil {
slog.Error(gotext.Get("Can't detect system language")) return cliutils.FormatCliExit(gotext.Get("Can't detect system language"), err)
return cli.Exit(err, 1)
} }
if systemLang == "" { if systemLang == "" {
systemLang = "en" systemLang = "en"
@@ -141,8 +130,7 @@ func InfoCmd() *cli.Command {
if !all { if !all {
info, err := distro.ParseOSRelease(ctx) info, err := distro.ParseOSRelease(ctx)
if err != nil { if err != nil {
slog.Error(gotext.Get("Error parsing os-release file")) return cliutils.FormatCliExit(gotext.Get("Error parsing os-release file"), err)
return cli.Exit(err, 1)
} }
names, err = overrides.Resolve( names, err = overrides.Resolve(
info, info,
@@ -150,8 +138,7 @@ func InfoCmd() *cli.Command {
WithLanguages([]string{systemLang}), WithLanguages([]string{systemLang}),
) )
if err != nil { if err != nil {
slog.Error(gotext.Get("Error resolving overrides")) return cliutils.FormatCliExit(gotext.Get("Error resolving overrides"), err)
return cli.Exit(err, 1)
} }
} }
@@ -159,14 +146,12 @@ func InfoCmd() *cli.Command {
if !all { if !all {
err = yaml.NewEncoder(os.Stdout).Encode(overrides.ResolvePackage(&pkg, names)) err = yaml.NewEncoder(os.Stdout).Encode(overrides.ResolvePackage(&pkg, names))
if err != nil { if err != nil {
slog.Error(gotext.Get("Error encoding script variables")) return cliutils.FormatCliExit(gotext.Get("Error encoding script variables"), err)
return cli.Exit(err, 1)
} }
} else { } else {
err = yaml.NewEncoder(os.Stdout).Encode(pkg) err = yaml.NewEncoder(os.Stdout).Encode(pkg)
if err != nil { if err != nil {
slog.Error(gotext.Get("Error encoding script variables")) return cliutils.FormatCliExit(gotext.Get("Error encoding script variables"), err)
return cli.Exit(err, 1)
} }
} }

View File

@@ -21,20 +21,17 @@ package main
import ( import (
"fmt" "fmt"
"log/slog"
"os"
"github.com/leonelquinteros/gotext" "github.com/leonelquinteros/gotext"
"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/cliutils"
appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder"
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/internal/types" "gitea.plemya-x.ru/Plemya-x/ALR/internal/types"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils" "gitea.plemya-x.ru/Plemya-x/ALR/internal/utils"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/build" "gitea.plemya-x.ru/Plemya-x/ALR/pkg/build"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" "gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos"
) )
func InstallCmd() *cli.Command { func InstallCmd() *cli.Command {
@@ -50,58 +47,59 @@ func InstallCmd() *cli.Command {
}, },
}, },
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
ctx := c.Context if err := utils.ExitIfNotRoot(); err != nil {
return err
}
args := c.Args() args := c.Args()
if args.Len() < 1 { if args.Len() < 1 {
slog.Error(gotext.Get("Command install expected at least 1 argument, got %d", args.Len())) return cliutils.FormatCliExit(gotext.Get("Command install expected at least 1 argument, got %d", args.Len()), nil)
os.Exit(1)
} }
mgr := manager.Detect() if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil {
if mgr == nil { return err
slog.Error(gotext.Get("Unable to detect a supported package manager on the system"))
os.Exit(1)
} }
cfg := config.New() installer, installerClose, err := build.GetSafeInstaller()
err := cfg.Load()
if err != nil { if err != nil {
slog.Error(gotext.Get("Error loading config"), "err", err) return err
os.Exit(1) }
defer installerClose()
if err := utils.ExitIfCantSetNoNewPrivs(); err != nil {
return err
} }
db := database.New(cfg) scripter, scripterClose, err := build.GetSafeScriptExecutor()
rs := repos.New(cfg, db)
err = db.Init(ctx)
if err != nil { if err != nil {
slog.Error(gotext.Get("Error initialization database"), "err", err) return err
os.Exit(1)
} }
defer scripterClose()
err = utils.DropCapsToAlrUser() ctx := c.Context
deps, err := appbuilder.
New(ctx).
WithConfig().
WithDB().
WithRepos().
WithDistroInfo().
WithManager().
Build()
if err != nil { if err != nil {
slog.Error(gotext.Get("Error dropping capabilities"), "err", err) return err
os.Exit(1)
} }
defer deps.Defer()
builder := build.NewMainBuilder( builder, err := build.NewMainBuilder(
cfg, deps.Cfg,
rs, deps.Manager,
deps.Repos,
scripter,
installer,
) )
if cfg.AutoPull() {
err := rs.Pull(ctx, cfg.Repos())
if err != nil {
slog.Error(gotext.Get("Error pulling repositories"), "err", err)
os.Exit(1)
}
}
info, err := distro.ParseOSRelease(ctx)
if err != nil { if err != nil {
slog.Error(gotext.Get("Error parsing os release"), "err", err) return err
os.Exit(1)
} }
err = builder.InstallPkgs( err = builder.InstallPkgs(
@@ -111,36 +109,36 @@ func InstallCmd() *cli.Command {
Clean: c.Bool("clean"), Clean: c.Bool("clean"),
Interactive: c.Bool("interactive"), Interactive: c.Bool("interactive"),
}, },
Info: info, Info: deps.Info,
PkgFormat_: build.GetPkgFormat(mgr), PkgFormat_: build.GetPkgFormat(deps.Manager),
}, },
args.Slice(), args.Slice(),
) )
if err != nil { if err != nil {
slog.Error(gotext.Get("Error parsing os release"), "err", err) return cliutils.FormatCliExit(gotext.Get("Error parsing os release"), err)
os.Exit(1)
} }
return nil return nil
}, },
BashComplete: func(c *cli.Context) { BashComplete: cliutils.BashCompleteWithError(func(c *cli.Context) error {
cfg := config.New() if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil {
err := cfg.Load() return err
if err != nil {
slog.Error(gotext.Get("Error loading config"), "err", err)
os.Exit(1)
} }
db := database.New(cfg) ctx := c.Context
err = db.Init(c.Context) deps, err := appbuilder.
New(ctx).
WithConfig().
WithDB().
Build()
if err != nil { if err != nil {
slog.Error(gotext.Get("Error initialization database"), "err", err) return err
os.Exit(1)
} }
result, err := db.GetPkgs(c.Context, "true") defer deps.Defer()
result, err := deps.DB.GetPkgs(c.Context, "true")
if err != nil { if err != nil {
slog.Error(gotext.Get("Error getting packages"), "err", err) return cliutils.FormatCliExit(gotext.Get("Error getting packages"), err)
os.Exit(1)
} }
defer result.Close() defer result.Close()
@@ -148,13 +146,14 @@ func InstallCmd() *cli.Command {
var pkg database.Package var pkg database.Package
err = result.StructScan(&pkg) err = result.StructScan(&pkg)
if err != nil { if err != nil {
slog.Error(gotext.Get("Error iterating over packages"), "err", err) return cliutils.FormatCliExit(gotext.Get("Error iterating over packages"), err)
os.Exit(1)
} }
fmt.Println(pkg.Name) fmt.Println(pkg.Name)
} }
},
return nil
}),
} }
} }
@@ -163,31 +162,24 @@ func RemoveCmd() *cli.Command {
Name: "remove", Name: "remove",
Usage: gotext.Get("Remove an installed package"), Usage: gotext.Get("Remove an installed package"),
Aliases: []string{"rm"}, Aliases: []string{"rm"},
BashComplete: func(c *cli.Context) { BashComplete: cliutils.BashCompleteWithError(func(c *cli.Context) error {
cfg := config.New() ctx := c.Context
err := cfg.Load()
if err != nil {
slog.Error(gotext.Get("Error loading config"), "err", err)
os.Exit(1)
}
db := database.New(cfg) deps, err := appbuilder.
err = db.Init(c.Context) New(ctx).
WithConfig().
WithDB().
WithManager().
Build()
if err != nil { if err != nil {
slog.Error(gotext.Get("Error initialization database"), "err", err) return cli.Exit(err, 1)
os.Exit(1)
} }
defer deps.Defer()
installedAlrPackages := map[string]string{} installedAlrPackages := map[string]string{}
mgr := manager.Detect() installed, err := deps.Manager.ListInstalled(&manager.Opts{})
if mgr == nil {
slog.Error(gotext.Get("Unable to detect a supported package manager on the system"))
os.Exit(1)
}
installed, err := mgr.ListInstalled(&manager.Opts{AsRoot: false})
if err != nil { if err != nil {
slog.Error(gotext.Get("Error listing installed packages"), "err", err) return cliutils.FormatCliExit(gotext.Get("Error listing installed packages"), err)
os.Exit(1)
} }
for pkgName, version := range installed { for pkgName, version := range installed {
matches := build.RegexpALRPackageName.FindStringSubmatch(pkgName) matches := build.RegexpALRPackageName.FindStringSubmatch(pkgName)
@@ -198,10 +190,9 @@ func RemoveCmd() *cli.Command {
} }
} }
result, err := db.GetPkgs(c.Context, "true") result, err := deps.DB.GetPkgs(c.Context, "true")
if err != nil { if err != nil {
slog.Error(gotext.Get("Error getting packages"), "err", err) return cliutils.FormatCliExit(gotext.Get("Error getting packages"), err)
os.Exit(1)
} }
defer result.Close() defer result.Close()
@@ -209,8 +200,7 @@ func RemoveCmd() *cli.Command {
var pkg database.Package var pkg database.Package
err = result.StructScan(&pkg) err = result.StructScan(&pkg)
if err != nil { if err != nil {
slog.Error(gotext.Get("Error iterating over packages"), "err", err) return cliutils.FormatCliExit(gotext.Get("Error iterating over packages"), err)
os.Exit(1)
} }
_, ok := installedAlrPackages[fmt.Sprintf("%s/%s", pkg.Repository, pkg.Name)] _, ok := installedAlrPackages[fmt.Sprintf("%s/%s", pkg.Repository, pkg.Name)]
@@ -220,27 +210,32 @@ func RemoveCmd() *cli.Command {
fmt.Println(pkg.Name) fmt.Println(pkg.Name)
} }
},
return nil
}),
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
if err := utils.ExitIfNotRoot(); err != nil {
return err
}
args := c.Args() args := c.Args()
if args.Len() < 1 { if args.Len() < 1 {
slog.Error(gotext.Get("Command remove expected at least 1 argument, got %d", args.Len())) return cliutils.FormatCliExit(gotext.Get("Command remove expected at least 1 argument, got %d", args.Len()), nil)
os.Exit(1)
} }
mgr := manager.Detect() deps, err := appbuilder.
if mgr == nil { New(c.Context).
slog.Error(gotext.Get("Unable to detect a supported package manager on the system")) WithManager().
os.Exit(1) Build()
}
err := mgr.Remove(&manager.Opts{
AsRoot: true,
NoConfirm: !c.Bool("interactive"),
}, c.Args().Slice()...)
if err != nil { if err != nil {
slog.Error(gotext.Get("Error removing packages"), "err", err) return err
os.Exit(1) }
defer deps.Defer()
if err := deps.Manager.Remove(&manager.Opts{
NoConfirm: !c.Bool("interactive"),
}, c.Args().Slice()...); err != nil {
return cliutils.FormatCliExit(gotext.Get("Error removing packages"), err)
} }
return nil return nil

View File

@@ -17,13 +17,14 @@
package main package main
import ( import (
"bufio"
"errors"
"fmt" "fmt"
"log/slog" "log/slog"
"os" "os"
"os/exec" "os/exec"
"os/user" "os/user"
"path/filepath" "path/filepath"
"strings"
"syscall" "syscall"
"github.com/hashicorp/go-hclog" "github.com/hashicorp/go-hclog"
@@ -31,13 +32,14 @@ import (
"github.com/leonelquinteros/gotext" "github.com/leonelquinteros/gotext"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils"
appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder"
"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" "gitea.plemya-x.ru/Plemya-x/ALR/internal/constants"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/logger" "gitea.plemya-x.ru/Plemya-x/ALR/internal/logger"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils" "gitea.plemya-x.ru/Plemya-x/ALR/internal/utils"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/build" "gitea.plemya-x.ru/Plemya-x/ALR/pkg/build"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" "gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos"
) )
func InternalBuildCmd() *cli.Command { func InternalBuildCmd() *cli.Command {
@@ -47,16 +49,17 @@ func InternalBuildCmd() *cli.Command {
Hidden: true, Hidden: true,
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
logger.SetupForGoPlugin() logger.SetupForGoPlugin()
err := utils.DropCapsToAlrUser()
if err != nil { slog.Debug("start _internal-safe-script-executor", "uid", syscall.Getuid(), "gid", syscall.Getgid())
slog.Error("aa", "err", err)
os.Exit(1) if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil {
return err
} }
cfg := config.New() cfg := config.New()
err = cfg.Load() err := cfg.Load()
if err != nil { if err != nil {
slog.Error(gotext.Get("Error loading config"), "err", err) return cliutils.FormatCliExit(gotext.Get("Error loading config"), err)
os.Exit(1)
} }
logger := hclog.New(&hclog.LoggerOptions{ logger := hclog.New(&hclog.LoggerOptions{
@@ -66,6 +69,7 @@ func InternalBuildCmd() *cli.Command {
JSONFormat: false, JSONFormat: false,
DisableTime: true, DisableTime: true,
}) })
plugin.Serve(&plugin.ServeConfig{ plugin.Serve(&plugin.ServeConfig{
HandshakeConfig: build.HandshakeConfig, HandshakeConfig: build.HandshakeConfig,
Plugins: map[string]plugin.Plugin{ Plugins: map[string]plugin.Plugin{
@@ -87,26 +91,28 @@ func InternalInstallCmd() *cli.Command {
Hidden: true, Hidden: true,
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
logger.SetupForGoPlugin() logger.SetupForGoPlugin()
err := syscall.Setuid(0)
if err != nil { if err := utils.EnsureIsAlrUser(); err != nil {
slog.Error("err") return err
os.Exit(1)
} }
cfg := config.New() // Before escalating the rights, we made sure that
err = cfg.Load() // this is an ALR user, so it looks safe.
err := utils.EscalateToRootUid()
if err != nil { if err != nil {
slog.Error(gotext.Get("Error loading config"), "err", err) return cliutils.FormatCliExit("cannot escalate to root", err)
os.Exit(1)
} }
db := database.New(cfg) deps, err := appbuilder.
rs := repos.New(cfg, db) New(c.Context).
err = db.Init(c.Context) WithConfig().
WithDB().
WithReposNoPull().
Build()
if err != nil { if err != nil {
slog.Error(gotext.Get("Error initialization database"), "err", err) return err
os.Exit(1)
} }
defer deps.Defer()
logger := hclog.New(&hclog.LoggerOptions{ logger := hclog.New(&hclog.LoggerOptions{
Name: "plugin", Name: "plugin",
@@ -121,7 +127,6 @@ func InternalInstallCmd() *cli.Command {
Plugins: map[string]plugin.Plugin{ Plugins: map[string]plugin.Plugin{
"installer": &build.InstallerPlugin{ "installer": &build.InstallerPlugin{
Impl: build.NewInstaller( Impl: build.NewInstaller(
rs,
manager.Detect(), manager.Detect(),
), ),
}, },
@@ -133,52 +138,111 @@ func InternalInstallCmd() *cli.Command {
} }
} }
func Mount(target string) (string, func(), error) {
exe, err := os.Executable()
if err != nil {
return "", nil, fmt.Errorf("failed to get executable path: %w", err)
}
cmd := exec.Command(exe, "_internal-temporary-mount", target)
stdoutPipe, err := cmd.StdoutPipe()
if err != nil {
return "", nil, fmt.Errorf("failed to get stdout pipe: %w", err)
}
stdinPipe, err := cmd.StdinPipe()
if err != nil {
return "", nil, fmt.Errorf("failed to get stdin pipe: %w", err)
}
cmd.Stderr = os.Stderr
if err := cmd.Start(); err != nil {
return "", nil, fmt.Errorf("failed to start mount: %w", err)
}
scanner := bufio.NewScanner(stdoutPipe)
var mountPath string
if scanner.Scan() {
mountPath = scanner.Text()
}
if err := scanner.Err(); err != nil {
_ = cmd.Process.Kill()
return "", nil, fmt.Errorf("failed to read mount output: %w", err)
}
if mountPath == "" {
_ = cmd.Process.Kill()
return "", nil, errors.New("mount failed: no target path returned")
}
cleanup := func() {
slog.Debug("cleanup triggered")
_, _ = fmt.Fprintln(stdinPipe, "")
_ = cmd.Wait()
}
return mountPath, cleanup, nil
}
func InternalMountCmd() *cli.Command { func InternalMountCmd() *cli.Command {
return &cli.Command{ return &cli.Command{
Name: "_internal-mount", Name: "_internal-temporary-mount",
HideHelp: true, HideHelp: true,
Hidden: true, Hidden: true,
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
logger.SetupForGoPlugin()
sourceDir := c.Args().First() sourceDir := c.Args().First()
u, _ := user.Current() u, err := user.Current()
logger.SetupForGoPlugin()
err := syscall.Setuid(0)
if err != nil { if err != nil {
slog.Error("Failed to setuid(0)", "err", err) return cliutils.FormatCliExit("cannot get current user", err)
os.Exit(1)
} }
alrRunDir := "/var/run/alr" _, alrGid, err := utils.GetUidGidAlrUser()
err = os.MkdirAll(alrRunDir, 0o750)
if err != nil { if err != nil {
slog.Error("Error creating /var/run/alr directory", "err", err) return cliutils.FormatCliExit("cannot get alr user", err)
os.Exit(1)
} }
_, gid, _ := utils.GetUidGidAlrUser() if _, err := os.Stat(sourceDir); err != nil {
return cliutils.FormatCliExit(fmt.Sprintf("cannot read %s", sourceDir), err)
// Меняем группу на alr и права
err = os.Chown(alrRunDir, 0, gid) // root:alr
if err != nil {
slog.Error("Failed to chown /var/run/alr", "err", err)
os.Exit(1)
} }
// Создаем поддиректорию для bindfs if err := utils.EnuseIsPrivilegedGroupMember(); err != nil {
targetDir := filepath.Join(alrRunDir, fmt.Sprintf("bindfs-%d", os.Getpid())) return err
err = os.MkdirAll(targetDir, 0o750) // 0750: владелец (root) и группа (alr) имеют доступ
if err != nil {
slog.Error("Error creating bindfs target directory", "err", err)
os.Exit(1)
} }
// Устанавливаем владельца и группу (root:alr) // Before escalating the rights, we made sure that
err = os.Chown(targetDir, 0, gid) // 1. user in wheel group
if err != nil { // 2. user can access sourceDir
slog.Error("Failed to chown bindfs directory", "err", err) if err := utils.EscalateToRootUid(); err != nil {
os.Exit(1) return err
}
if err := syscall.Setgid(alrGid); err != nil {
return err
}
if err := os.MkdirAll(constants.AlrRunDir, 0o770); err != nil {
return cliutils.FormatCliExit(fmt.Sprintf("failed to create %s", constants.AlrRunDir), err)
}
if err := os.Chown(constants.AlrRunDir, 0, alrGid); err != nil {
return cliutils.FormatCliExit(fmt.Sprintf("failed to chown %s", constants.AlrRunDir), err)
}
targetDir := filepath.Join(constants.AlrRunDir, fmt.Sprintf("bindfs-%d", os.Getpid()))
// 0750: owner (root) and group (alr)
if err := os.MkdirAll(targetDir, 0o750); err != nil {
return cliutils.FormatCliExit("error creating bindfs target directory", err)
}
// chown AlrRunDir/mounts/bindfs-* to (root:alr),
// so alr user can access dir
if err := os.Chown(targetDir, 0, alrGid); err != nil {
return cliutils.FormatCliExit("failed to chown bindfs directory", err)
} }
bindfsCmd := exec.Command( bindfsCmd := exec.Command(
@@ -190,74 +254,24 @@ func InternalMountCmd() *cli.Command {
bindfsCmd.Stderr = os.Stderr bindfsCmd.Stderr = os.Stderr
if err := bindfsCmd.Start(); err != nil { if err := bindfsCmd.Run(); err != nil {
slog.Error("Error starting bindfs", "err", err) return cliutils.FormatCliExit("failed to strart bindfs", err)
os.Exit(1)
} }
fmt.Print(targetDir) fmt.Println(targetDir)
return nil _, _ = bufio.NewReader(os.Stdin).ReadString('\n')
},
}
}
func InternalUnmountCmd() *cli.Command { slog.Debug("start unmount", "dir", targetDir)
return &cli.Command{
Name: "_internal-umount",
HideHelp: true,
Hidden: true,
Action: func(c *cli.Context) error {
currentUser, err := user.Current()
if err != nil {
slog.Error("Failed to get current user", "err", err)
os.Exit(1)
}
uid, gid, err := utils.GetUidGidAlrUserString()
if err != nil {
slog.Error("Failed to get alr user info", "err", err)
os.Exit(1)
}
if currentUser.Uid != uid && currentUser.Gid != gid {
slog.Error("Only alr user can unmount these directories")
os.Exit(1)
}
targetDir := c.Args().First()
if targetDir == "" {
slog.Error("No target directory specified")
os.Exit(1)
}
if !strings.HasPrefix(targetDir, "/var/run/alr/") {
slog.Error("Can only unmount directories under /var/run/alr")
os.Exit(1)
}
if _, err := os.Stat(targetDir); os.IsNotExist(err) {
slog.Error("Target directory does not exist", "dir", targetDir)
os.Exit(1)
}
err = syscall.Setuid(0)
if err != nil {
slog.Error("Failed to setuid(0)", "err", err)
os.Exit(1)
}
umountCmd := exec.Command("umount", targetDir) umountCmd := exec.Command("umount", targetDir)
umountCmd.Stderr = os.Stderr umountCmd.Stderr = os.Stderr
if err := umountCmd.Run(); err != nil { if err := umountCmd.Run(); err != nil {
slog.Error("Error unmounting directory", "dir", targetDir, "err", err) return cliutils.FormatCliExit(fmt.Sprintf("failed to unmount %s", targetDir), err)
os.Exit(1)
} }
if err := os.Remove(targetDir); err != nil { if err := os.Remove(targetDir); err != nil {
slog.Error("Error removing directory", "dir", targetDir, "err", err) return cliutils.FormatCliExit(fmt.Sprintf("error removing directory %s", targetDir), err)
os.Exit(1)
} }
return nil return nil

View File

@@ -22,17 +22,21 @@ import (
"log/slog" "log/slog"
"github.com/leonelquinteros/gotext" "github.com/leonelquinteros/gotext"
"github.com/urfave/cli/v2"
"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" "gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos" "gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos"
) )
type AppDeps struct { type AppDeps struct {
Cfg *config.ALRConfig Cfg *config.ALRConfig
DB *db.Database DB *db.Database
Repos *repos.Repos Repos *repos.Repos
Info *distro.OSRelease
Manager manager.Manager
} }
func (d *AppDeps) Defer() { func (d *AppDeps) Defer() {
@@ -53,6 +57,14 @@ func New(ctx context.Context) *AppBuilder {
return &AppBuilder{ctx: ctx} return &AppBuilder{ctx: ctx}
} }
func (b *AppBuilder) UseConfig(cfg *config.ALRConfig) *AppBuilder {
if b.err != nil {
return b
}
b.deps.Cfg = cfg
return b
}
func (b *AppBuilder) WithConfig() *AppBuilder { func (b *AppBuilder) WithConfig() *AppBuilder {
if b.err != nil { if b.err != nil {
return b return b
@@ -60,8 +72,7 @@ func (b *AppBuilder) WithConfig() *AppBuilder {
cfg := config.New() cfg := config.New()
if err := cfg.Load(); err != nil { if err := cfg.Load(); err != nil {
slog.Error(gotext.Get("Error loading config"), "err", err) b.err = cliutils.FormatCliExit(gotext.Get("Error loading config"), err)
b.err = cli.Exit("", 1)
return b return b
} }
@@ -82,8 +93,7 @@ func (b *AppBuilder) WithDB() *AppBuilder {
db := db.New(cfg) db := db.New(cfg)
if err := db.Init(b.ctx); err != nil { if err := db.Init(b.ctx); err != nil {
slog.Error(gotext.Get("Error initialization database"), "err", err) b.err = cliutils.FormatCliExit(gotext.Get("Error initialization database"), err)
b.err = cli.Exit("", 1)
return b return b
} }
@@ -92,6 +102,21 @@ func (b *AppBuilder) WithDB() *AppBuilder {
} }
func (b *AppBuilder) WithRepos() *AppBuilder { func (b *AppBuilder) WithRepos() *AppBuilder {
b.withRepos(true, false)
return b
}
func (b *AppBuilder) WithReposForcePull() *AppBuilder {
b.withRepos(true, true)
return b
}
func (b *AppBuilder) WithReposNoPull() *AppBuilder {
b.withRepos(false, false)
return b
}
func (b *AppBuilder) withRepos(enablePull, forcePull bool) *AppBuilder {
if b.err != nil { if b.err != nil {
return b return b
} }
@@ -105,10 +130,9 @@ func (b *AppBuilder) WithRepos() *AppBuilder {
rs := repos.New(cfg, db) rs := repos.New(cfg, db)
if cfg.AutoPull() { if enablePull && (forcePull || cfg.AutoPull()) {
if err := rs.Pull(b.ctx, cfg.Repos()); err != nil { if err := rs.Pull(b.ctx, cfg.Repos()); err != nil {
slog.Error(gotext.Get("Error pulling repositories"), "err", err) b.err = cliutils.FormatCliExit(gotext.Get("Error pulling repositories"), err)
b.err = cli.Exit("", 1)
return b return b
} }
} }
@@ -118,6 +142,32 @@ func (b *AppBuilder) WithRepos() *AppBuilder {
return b return b
} }
func (b *AppBuilder) WithDistroInfo() *AppBuilder {
if b.err != nil {
return b
}
b.deps.Info, b.err = distro.ParseOSRelease(b.ctx)
if b.err != nil {
b.err = cliutils.FormatCliExit(gotext.Get("Error parsing os release"), b.err)
}
return b
}
func (b *AppBuilder) WithManager() *AppBuilder {
if b.err != nil {
return b
}
b.deps.Manager = manager.Detect()
if b.deps.Manager == nil {
b.err = cliutils.FormatCliExit(gotext.Get("Unable to detect a supported package manager on the system"), nil)
}
return b
}
func (b *AppBuilder) Build() (*AppDeps, error) { func (b *AppBuilder) Build() (*AppDeps, error) {
if b.err != nil { if b.err != nil {
return nil, b.err return nil, b.err

View File

@@ -0,0 +1,63 @@
// 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 cliutils
import (
"errors"
"fmt"
"log/slog"
"github.com/urfave/cli/v2"
)
type BashCompleteWithErrorFunc func(c *cli.Context) error
func BashCompleteWithError(f BashCompleteWithErrorFunc) cli.BashCompleteFunc {
return func(c *cli.Context) { HandleExitCoder(f(c)) }
}
func HandleExitCoder(err error) {
if err == nil {
return
}
if exitErr, ok := err.(cli.ExitCoder); ok {
if err.Error() != "" {
if _, ok := exitErr.(cli.ErrorFormatter); ok {
slog.Error(fmt.Sprintf("%+v\n", err))
} else {
slog.Error(err.Error())
}
}
cli.OsExiter(exitErr.ExitCode())
return
}
slog.Error(err.Error())
cli.OsExiter(1)
}
func FormatCliExit(msg string, err error) cli.ExitCoder {
return FormatCliExitWithCode(msg, err, 1)
}
func FormatCliExitWithCode(msg string, err error, exitCode int) cli.ExitCoder {
if err == nil {
return cli.Exit(errors.New(msg), exitCode)
}
return cli.Exit(fmt.Errorf("%s: %w", msg, err), exitCode)
}

View File

@@ -28,6 +28,7 @@ import (
"github.com/caarlos0/env" "github.com/caarlos0/env"
"github.com/pelletier/go-toml/v2" "github.com/pelletier/go-toml/v2"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/constants"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" "gitea.plemya-x.ru/Plemya-x/ALR/internal/types"
) )
@@ -83,14 +84,9 @@ func mergeStructs(dst, src interface{}) {
} }
} }
const (
systemConfigPath = "/etc/alr/alr.toml"
systemCachePath = "/var/cache/alr"
)
func (c *ALRConfig) Load() error { func (c *ALRConfig) Load() error {
systemConfig, err := readConfig( systemConfig, err := readConfig(
systemConfigPath, constants.SystemConfigPath,
) )
if err != nil { if err != nil {
slog.Debug("Cannot read system config", "err", err) slog.Debug("Cannot read system config", "err", err)
@@ -108,8 +104,8 @@ func (c *ALRConfig) Load() error {
c.cfg = config c.cfg = config
c.paths = &Paths{} c.paths = &Paths{}
c.paths.UserConfigPath = systemConfigPath c.paths.UserConfigPath = constants.SystemConfigPath
c.paths.CacheDir = systemCachePath c.paths.CacheDir = constants.SystemCachePath
c.paths.RepoDir = filepath.Join(c.paths.CacheDir, "repo") c.paths.RepoDir = filepath.Join(c.paths.CacheDir, "repo")
c.paths.PkgsDir = filepath.Join(c.paths.CacheDir, "pkgs") c.paths.PkgsDir = filepath.Join(c.paths.CacheDir, "pkgs")
c.paths.DBPath = filepath.Join(c.paths.CacheDir, "db") c.paths.DBPath = filepath.Join(c.paths.CacheDir, "db")
@@ -130,10 +126,6 @@ func (c *ALRConfig) AutoPull() bool {
return c.cfg.AutoPull return c.cfg.AutoPull
} }
func (c *ALRConfig) AllowRunAsRoot() bool {
return c.cfg.Unsafe.AllowRunAsRoot
}
func (c *ALRConfig) Repos() []types.Repo { func (c *ALRConfig) Repos() []types.Repo {
return c.cfg.Repos return c.cfg.Repos
} }

View File

@@ -0,0 +1,24 @@
// 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 constants
const (
SystemConfigPath = "/etc/alr/alr.toml"
SystemCachePath = "/var/cache/alr"
AlrRunDir = "/var/run/alr"
PrivilegedGroup = "wheel"
)

View File

@@ -31,7 +31,7 @@ import (
// CurrentVersion is the current version of the database. // CurrentVersion is the current version of the database.
// The database is reset if its version doesn't match this. // The database is reset if its version doesn't match this.
const CurrentVersion = 3 const CurrentVersion = 4
// Package is a ALR package's database representation // Package is a ALR package's database representation
type Package struct { type Package struct {
@@ -40,7 +40,9 @@ type Package struct {
Version string `sh:"version,required" db:"version"` Version string `sh:"version,required" db:"version"`
Release int `sh:"release,required" db:"release"` Release int `sh:"release,required" db:"release"`
Epoch uint `sh:"epoch" db:"epoch"` Epoch uint `sh:"epoch" db:"epoch"`
Summary JSON[map[string]string] `db:"summary"`
Description JSON[map[string]string] `db:"description"` Description JSON[map[string]string] `db:"description"`
Group JSON[map[string]string] `db:"group_name"`
Homepage JSON[map[string]string] `db:"homepage"` Homepage JSON[map[string]string] `db:"homepage"`
Maintainer JSON[map[string]string] `db:"maintainer"` Maintainer JSON[map[string]string] `db:"maintainer"`
Architectures JSON[[]string] `sh:"architectures" db:"architectures"` Architectures JSON[[]string] `sh:"architectures" db:"architectures"`
@@ -106,7 +108,9 @@ func (d *Database) initDB(ctx context.Context) error {
version TEXT NOT NULL, version TEXT NOT NULL,
release INT NOT NULL, release INT NOT NULL,
epoch INT, epoch INT,
summary TEXT CHECK(summary = 'null' OR (JSON_VALID(summary) AND JSON_TYPE(summary) = 'object')),
description TEXT CHECK(description = 'null' OR (JSON_VALID(description) AND JSON_TYPE(description) = 'object')), description TEXT CHECK(description = 'null' OR (JSON_VALID(description) AND JSON_TYPE(description) = 'object')),
group_name TEXT CHECK(group_name = 'null' OR (JSON_VALID(group_name) AND JSON_TYPE(group_name) = 'object')),
homepage TEXT CHECK(homepage = 'null' OR (JSON_VALID(homepage) AND JSON_TYPE(homepage) = 'object')), homepage TEXT CHECK(homepage = 'null' OR (JSON_VALID(homepage) AND JSON_TYPE(homepage) = 'object')),
maintainer TEXT CHECK(maintainer = 'null' OR (JSON_VALID(maintainer) AND JSON_TYPE(maintainer) = 'object')), maintainer TEXT CHECK(maintainer = 'null' OR (JSON_VALID(maintainer) AND JSON_TYPE(maintainer) = 'object')),
architectures TEXT CHECK(architectures = 'null' OR (JSON_VALID(architectures) AND JSON_TYPE(architectures) = 'array')), architectures TEXT CHECK(architectures = 'null' OR (JSON_VALID(architectures) AND JSON_TYPE(architectures) = 'array')),
@@ -204,7 +208,9 @@ func (d *Database) InsertPackage(ctx context.Context, pkg Package) error {
version, version,
release, release,
epoch, epoch,
summary,
description, description,
group_name,
homepage, homepage,
maintainer, maintainer,
architectures, architectures,
@@ -222,7 +228,9 @@ func (d *Database) InsertPackage(ctx context.Context, pkg Package) error {
:version, :version,
:release, :release,
:epoch, :epoch,
:summary,
:description, :description,
:group_name,
:homepage, :homepage,
:maintainer, :maintainer,
:architectures, :architectures,

View File

@@ -19,6 +19,7 @@ package logger
import ( import (
"io" "io"
"log" "log"
"strings"
chLog "github.com/charmbracelet/log" chLog "github.com/charmbracelet/log"
"github.com/hashicorp/go-hclog" "github.com/hashicorp/go-hclog"
@@ -55,7 +56,22 @@ func (a *HCLoggerAdapter) Log(level hclog.Level, msg string, args ...interface{}
filteredArgs = append(filteredArgs, args[i], args[i+1]) filteredArgs = append(filteredArgs, args[i], args[i+1])
} }
} }
a.logger.l.Log(hclogLevelTochLog(level), msg, filteredArgs...)
// Start ugly hacks
// Ignore exit messages
// - https://github.com/hashicorp/go-plugin/issues/331
// - https://github.com/hashicorp/go-plugin/issues/203
// - https://github.com/hashicorp/go-plugin/issues/192
var chLogLevel chLog.Level
if msg == "plugin process exited" ||
strings.HasPrefix(msg, "[ERR] plugin: stream copy 'stderr' error") ||
strings.HasPrefix(msg, "[DEBUG] plugin") {
chLogLevel = chLog.DebugLevel
} else {
chLogLevel = hclogLevelTochLog(level)
}
a.logger.l.Log(chLogLevel, msg, filteredArgs...)
} }
func (a *HCLoggerAdapter) Trace(msg string, args ...interface{}) { func (a *HCLoggerAdapter) Trace(msg string, args ...interface{}) {

View File

@@ -157,6 +157,8 @@ type ResolvedPackage struct {
Version string `sh:"version"` Version string `sh:"version"`
Release int `sh:"release"` Release int `sh:"release"`
Epoch uint `sh:"epoch"` Epoch uint `sh:"epoch"`
Group string `db:"group_name"`
Summary string `db:"summary"`
Description string `db:"description"` Description string `db:"description"`
Homepage string `db:"homepage"` Homepage string `db:"homepage"`
Maintainer string `db:"maintainer"` Maintainer string `db:"maintainer"`

View File

@@ -25,11 +25,11 @@ import (
"os" "os"
"os/exec" "os/exec"
"runtime" "runtime"
"slices"
"strings" "strings"
"syscall" "syscall"
"time" "time"
"gitea.plemya-x.ru/Plemya-x/fakeroot"
"mvdan.cc/sh/v3/expand" "mvdan.cc/sh/v3/expand"
"mvdan.cc/sh/v3/interp" "mvdan.cc/sh/v3/interp"
) )
@@ -54,7 +54,7 @@ func FakerootExecHandler(killTimeout time.Duration) interp.ExecHandlerFunc {
Stderr: hc.Stderr, Stderr: hc.Stderr,
} }
err = Apply(cmd) err = fakeroot.Apply(cmd)
if err != nil { if err != nil {
return err return err
} }
@@ -108,52 +108,6 @@ func FakerootExecHandler(killTimeout time.Duration) interp.ExecHandlerFunc {
} }
} }
func rootMap(m syscall.SysProcIDMap) bool {
return m.ContainerID == 0
}
func Apply(cmd *exec.Cmd) error {
uid := os.Getuid()
gid := os.Getgid()
// If the user is already root, there's no need for fakeroot
if uid == 0 {
return nil
}
// Ensure SysProcAttr isn't nil
if cmd.SysProcAttr == nil {
cmd.SysProcAttr = &syscall.SysProcAttr{}
}
// Create a new user namespace
cmd.SysProcAttr.Cloneflags |= syscall.CLONE_NEWUSER
// If the command already contains a mapping for the root user, return an error
if slices.ContainsFunc(cmd.SysProcAttr.UidMappings, rootMap) {
return nil
}
// If the command already contains a mapping for the root group, return an error
if slices.ContainsFunc(cmd.SysProcAttr.GidMappings, rootMap) {
return nil
}
cmd.SysProcAttr.UidMappings = append(cmd.SysProcAttr.UidMappings, syscall.SysProcIDMap{
ContainerID: 0,
HostID: uid,
Size: 1,
})
cmd.SysProcAttr.GidMappings = append(cmd.SysProcAttr.GidMappings, syscall.SysProcIDMap{
ContainerID: 0,
HostID: gid,
Size: 1,
})
return nil
}
// execEnv was extracted from github.com/mvdan/sh/interp/vars.go // execEnv was extracted from github.com/mvdan/sh/interp/vars.go
func execEnv(env expand.Environ) []string { func execEnv(env expand.Environ) []string {
list := make([]string, 0, 64) list := make([]string, 0, 64)

View File

@@ -9,76 +9,64 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: build.go:47 #: build.go:42
msgid "Build a local package" msgid "Build a local package"
msgstr "" msgstr ""
#: build.go:53 #: build.go:48
msgid "Path to the build script" msgid "Path to the build script"
msgstr "" msgstr ""
#: build.go:58 #: build.go:53
msgid "Specify subpackage in script (for multi package script only)" msgid "Specify subpackage in script (for multi package script only)"
msgstr "" msgstr ""
#: build.go:63 #: build.go:58
msgid "Name of the package to build and its repo (example: default/go-bin)" msgid "Name of the package to build and its repo (example: default/go-bin)"
msgstr "" msgstr ""
#: build.go:68 #: build.go:63
msgid "" msgid ""
"Build package from scratch even if there's an already built package available" "Build package from scratch even if there's an already built package available"
msgstr "" msgstr ""
#: build.go:74 build.go:79 build.go:89 build.go:103 #: build.go:73
msgid "Error getting working directory" msgid "Error getting working directory"
msgstr "" msgstr ""
#: build.go:110 build.go:115 #: build.go:118
msgid "Error dropping capabilities" msgid "Cannot get absolute script path"
msgstr "" msgstr ""
#: build.go:123 #: build.go:148
msgid "Error loading config"
msgstr ""
#: build.go:131
msgid "Error initialization database"
msgstr ""
#: build.go:141
msgid "Unable to detect a supported package manager on the system"
msgstr ""
#: build.go:147
msgid "Error parsing os release"
msgstr ""
#: build.go:179 build.go:221
msgid "Error building package"
msgstr ""
#: build.go:197
msgid "Package not found" msgid "Package not found"
msgstr "" msgstr ""
#: build.go:225 #: build.go:161
msgid "Nothing to build" msgid "Nothing to build"
msgstr "" msgstr ""
#: build.go:234 #: build.go:218
msgid "Error building package"
msgstr ""
#: build.go:225
msgid "Error moving the package" msgid "Error moving the package"
msgstr "" msgstr ""
#: fix.go:37 #: build.go:229
msgid "Done"
msgstr ""
#: fix.go:38
msgid "Attempt to fix problems with ALR" msgid "Attempt to fix problems with ALR"
msgstr "" msgstr ""
#: fix.go:58 #: fix.go:59
msgid "Clearing cache directory" msgid "Clearing cache directory"
msgstr "" msgstr ""
#: fix.go:63 #: fix.go:64
msgid "Unable to open cache directory" msgid "Unable to open cache directory"
msgstr "" msgstr ""
@@ -86,22 +74,18 @@ msgstr ""
msgid "Unable to read cache directory contents" msgid "Unable to read cache directory contents"
msgstr "" msgstr ""
#: fix.go:77 #: fix.go:76
msgid "Unable to remove cache item" msgid "Unable to remove cache item (%s)"
msgstr "" msgstr ""
#: fix.go:82 #: fix.go:80
msgid "Rebuilding cache" msgid "Rebuilding cache"
msgstr "" msgstr ""
#: fix.go:86 #: fix.go:84
msgid "Unable to create new cache directory" msgid "Unable to create new cache directory"
msgstr "" msgstr ""
#: fix.go:101
msgid "Done"
msgstr ""
#: gen.go:34 #: gen.go:34
msgid "Generate a ALR script from a template" msgid "Generate a ALR script from a template"
msgstr "" msgstr ""
@@ -110,90 +94,106 @@ msgstr ""
msgid "Generate a ALR script for a pip module" msgid "Generate a ALR script for a pip module"
msgstr "" msgstr ""
#: helper.go:41 #: helper.go:42
msgid "List all the available helper commands" msgid "List all the available helper commands"
msgstr "" msgstr ""
#: helper.go:53 #: helper.go:54
msgid "Run a ALR helper command" msgid "Run a ALR helper command"
msgstr "" msgstr ""
#: helper.go:60 #: helper.go:61
msgid "The directory that the install commands will install to" msgid "The directory that the install commands will install to"
msgstr "" msgstr ""
#: helper.go:73 #: helper.go:74 helper.go:75
msgid "No such helper command" msgid "No such helper command"
msgstr "" msgstr ""
#: info.go:44 #: helper.go:85
msgid "Print information about a package"
msgstr ""
#: info.go:49
msgid "Show all information, not just for the current distro"
msgstr ""
#: info.go:75
msgid "Error getting packages"
msgstr ""
#: info.go:84
msgid "Error iterating over packages"
msgstr ""
#: info.go:98
msgid "Command info expected at least 1 argument, got %d"
msgstr ""
#: info.go:118
msgid "Error finding packages"
msgstr ""
#: info.go:134
msgid "Can't detect system language"
msgstr ""
#: info.go:144
msgid "Error parsing os-release file" msgid "Error parsing os-release file"
msgstr "" msgstr ""
#: info.go:153 #: info.go:42
msgid "Print information about a package"
msgstr ""
#: info.go:47
msgid "Show all information, not just for the current distro"
msgstr ""
#: info.go:68
msgid "Error getting packages"
msgstr ""
#: info.go:76
msgid "Error iterating over packages"
msgstr ""
#: info.go:90
msgid "Command info expected at least 1 argument, got %d"
msgstr ""
#: info.go:110
msgid "Error finding packages"
msgstr ""
#: info.go:124
msgid "Can't detect system language"
msgstr ""
#: info.go:141
msgid "Error resolving overrides" msgid "Error resolving overrides"
msgstr "" msgstr ""
#: info.go:162 info.go:168 #: info.go:149 info.go:154
msgid "Error encoding script variables" msgid "Error encoding script variables"
msgstr "" msgstr ""
#: install.go:43 #: install.go:40
msgid "Install a new package" msgid "Install a new package"
msgstr "" msgstr ""
#: install.go:57 #: install.go:56
msgid "Command install expected at least 1 argument, got %d" msgid "Command install expected at least 1 argument, got %d"
msgstr "" msgstr ""
#: install.go:96 #: install.go:118
msgid "Error pulling repositories" msgid "Error parsing os release"
msgstr "" msgstr ""
#: install.go:164 #: install.go:163
msgid "Remove an installed package" msgid "Remove an installed package"
msgstr "" msgstr ""
#: install.go:189 #: install.go:182
msgid "Error listing installed packages" msgid "Error listing installed packages"
msgstr "" msgstr ""
#: install.go:227 #: install.go:223
msgid "Command remove expected at least 1 argument, got %d" msgid "Command remove expected at least 1 argument, got %d"
msgstr "" msgstr ""
#: install.go:242 #: install.go:238
msgid "Error removing packages" msgid "Error removing packages"
msgstr "" msgstr ""
#: internal/cliutils/app_builder/builder.go:75
msgid "Error loading config"
msgstr ""
#: internal/cliutils/app_builder/builder.go:96
msgid "Error initialization database"
msgstr ""
#: internal/cliutils/app_builder/builder.go:135
msgid "Error pulling repositories"
msgstr ""
#: internal/cliutils/app_builder/builder.go:165
msgid "Unable to detect a supported package manager on the system"
msgstr ""
#: internal/cliutils/prompt.go:60 #: internal/cliutils/prompt.go:60
msgid "Would you like to view the build script for %s" msgid "Would you like to view the build script for %s"
msgstr "" msgstr ""
@@ -274,11 +274,11 @@ msgstr ""
msgid "OPTIONS" msgid "OPTIONS"
msgstr "" msgstr ""
#: internal/db/db.go:133 #: internal/db/db.go:137
msgid "Database version mismatch; resetting" msgid "Database version mismatch; resetting"
msgstr "" msgstr ""
#: internal/db/db.go:140 #: internal/db/db.go:144
msgid "" 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 ""
@@ -311,10 +311,18 @@ msgstr ""
msgid "ERROR" msgid "ERROR"
msgstr "" msgstr ""
#: internal/utils/cmd.go:94 #: internal/utils/cmd.go:95
msgid "Error on dropping capabilities"
msgstr ""
#: internal/utils/cmd.go:123
msgid "You need to be root to perform this action" msgid "You need to be root to perform this action"
msgstr "" msgstr ""
#: internal/utils/cmd.go:165
msgid "You need to be a %s member to perform this action"
msgstr ""
#: list.go:41 #: list.go:41
msgid "List ALR repo packages" msgid "List ALR repo packages"
msgstr "" msgstr ""
@@ -323,35 +331,35 @@ msgstr ""
msgid "Print the current ALR version and exit" msgid "Print the current ALR version and exit"
msgstr "" msgstr ""
#: main.go:79 #: main.go:61
msgid "Arguments to be passed on to the package manager" msgid "Arguments to be passed on to the package manager"
msgstr "" msgstr ""
#: main.go:85 #: main.go:67
msgid "Enable interactive questions and prompts" msgid "Enable interactive questions and prompts"
msgstr "" msgstr ""
#: main.go:185 #: main.go:145
msgid "Show help" msgid "Show help"
msgstr "" msgstr ""
#: main.go:189 #: main.go:149
msgid "Error while running app" msgid "Error while running app"
msgstr "" msgstr ""
#: pkg/build/build.go:392 #: pkg/build/build.go:395
msgid "Building package" msgid "Building package"
msgstr "" msgstr ""
#: pkg/build/build.go:421 #: pkg/build/build.go:424
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:448 #: pkg/build/build.go:455
msgid "Downloading sources" msgid "Downloading sources"
msgstr "" msgstr ""
#: pkg/build/build.go:535 #: pkg/build/build.go:549
msgid "Installing dependencies" msgid "Installing dependencies"
msgstr "" msgstr ""
@@ -389,15 +397,15 @@ msgstr ""
msgid "Building package metadata" msgid "Building package metadata"
msgstr "" msgstr ""
#: pkg/build/script_executor.go:356 #: pkg/build/script_executor.go:368
msgid "Executing prepare()" msgid "Executing prepare()"
msgstr "" msgstr ""
#: pkg/build/script_executor.go:365 #: pkg/build/script_executor.go:377
msgid "Executing build()" msgid "Executing build()"
msgstr "" msgstr ""
#: pkg/build/script_executor.go:394 pkg/build/script_executor.go:414 #: pkg/build/script_executor.go:406 pkg/build/script_executor.go:426
msgid "Executing %s()" msgid "Executing %s()"
msgstr "" msgstr ""
@@ -419,98 +427,94 @@ msgid ""
"updating ALR if something doesn't work." "updating ALR if something doesn't work."
msgstr "" msgstr ""
#: repo.go:41 #: repo.go:39
msgid "Add a new repository" msgid "Add a new repository"
msgstr "" msgstr ""
#: repo.go:48 #: repo.go:46
msgid "Name of the new repo" msgid "Name of the new repo"
msgstr "" msgstr ""
#: repo.go:54 #: repo.go:52
msgid "URL of the new repo" msgid "URL of the new repo"
msgstr "" msgstr ""
#: repo.go:92 repo.go:172 #: repo.go:79
msgid "Repo \"%s\" already exists"
msgstr ""
#: repo.go:90 repo.go:167
msgid "Error saving config" msgid "Error saving config"
msgstr "" msgstr ""
#: repo.go:97 repo.go:199 #: repo.go:116
msgid "Can't drop privileges"
msgstr ""
#: repo.go:104 repo.go:110 repo.go:219
msgid "Error pulling repos"
msgstr ""
#: repo.go:122
msgid "Remove an existing repository" msgid "Remove an existing repository"
msgstr "" msgstr ""
#: repo.go:129 #: repo.go:123
msgid "Name of the repo to be deleted" msgid "Name of the repo to be deleted"
msgstr "" msgstr ""
#: repo.go:158 #: repo.go:156
msgid "Repo does not exist" msgid "Repo \"%s\" does not exist"
msgstr "" msgstr ""
#: repo.go:166 #: repo.go:163
msgid "Error removing repo directory" msgid "Error removing repo directory"
msgstr "" msgstr ""
#: repo.go:183 #: repo.go:186
msgid "Error removing packages from database" msgid "Error removing packages from database"
msgstr "" msgstr ""
#: repo.go:195 #: repo.go:197
msgid "Pull all repositories that have changed" msgid "Pull all repositories that have changed"
msgstr "" msgstr ""
#: search.go:36 #: search.go:40
msgid "Search packages" msgid "Search packages"
msgstr "" msgstr ""
#: search.go:42 #: search.go:51
msgid "Search by name" msgid "Search by name"
msgstr "" msgstr ""
#: search.go:47 #: search.go:56
msgid "Search by description" msgid "Search by description"
msgstr "" msgstr ""
#: search.go:52 #: search.go:61
msgid "Search by repository" msgid "Search by repository"
msgstr "" msgstr ""
#: search.go:57 #: search.go:66
msgid "Search by provides" msgid "Search by provides"
msgstr "" msgstr ""
#: search.go:62 #: search.go:71
msgid "Format output using a Go template" msgid "Format output using a Go template"
msgstr "" msgstr ""
#: search.go:96 #: search.go:130
msgid "Error while executing search" msgid "Error while executing search"
msgstr "" msgstr ""
#: search.go:105 #: search.go:138
msgid "Error parsing format template" msgid "Error parsing format template"
msgstr "" msgstr ""
#: search.go:114 #: search.go:153
msgid "Error executing template" msgid "Error executing template"
msgstr "" msgstr ""
#: upgrade.go:48 #: upgrade.go:47
msgid "Upgrade all installed packages" msgid "Upgrade all installed packages"
msgstr "" msgstr ""
#: upgrade.go:111 upgrade.go:129 #: upgrade.go:109 upgrade.go:126
msgid "Error checking for updates" msgid "Error checking for updates"
msgstr "" msgstr ""
#: upgrade.go:133 #: upgrade.go:129
msgid "There is nothing to do." msgid "There is nothing to do."
msgstr "" msgstr ""

View File

@@ -5,7 +5,7 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: unnamed project\n" "Project-Id-Version: unnamed project\n"
"PO-Revision-Date: 2025-03-09 17:31+0300\n" "PO-Revision-Date: 2025-04-18 07:38+0300\n"
"Last-Translator: Maxim Slipenko <maks1ms@alt-gnome.ru>\n" "Last-Translator: Maxim Slipenko <maks1ms@alt-gnome.ru>\n"
"Language-Team: Russian\n" "Language-Team: Russian\n"
"Language: ru\n" "Language: ru\n"
@@ -14,108 +14,85 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=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" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
"X-Generator: Gtranslator 47.1\n" "X-Generator: Gtranslator 48.0\n"
#: build.go:47 #: build.go:42
msgid "Build a local package" msgid "Build a local package"
msgstr "Сборка локального пакета" msgstr "Сборка локального пакета"
#: build.go:53 #: build.go:48
msgid "Path to the build script" msgid "Path to the build script"
msgstr "Путь к скрипту сборки" msgstr "Путь к скрипту сборки"
#: build.go:58 #: build.go:53
msgid "Specify subpackage in script (for multi package script only)" msgid "Specify subpackage in script (for multi package script only)"
msgstr "Укажите подпакет в скрипте (только для многопакетного скрипта)" msgstr "Укажите подпакет в скрипте (только для многопакетного скрипта)"
#: build.go:63 #: build.go:58
msgid "Name of the package to build and its repo (example: default/go-bin)" msgid "Name of the package to build and its repo (example: default/go-bin)"
msgstr "Имя пакета для сборки и его репозиторий (пример: default/go-bin)" msgstr "Имя пакета для сборки и его репозиторий (пример: default/go-bin)"
#: build.go:68 #: build.go:63
msgid "" msgid ""
"Build package from scratch even if there's an already built package available" "Build package from scratch even if there's an already built package available"
msgstr "Создайте пакет с нуля, даже если уже имеется готовый пакет" msgstr "Создайте пакет с нуля, даже если уже имеется готовый пакет"
#: build.go:74 build.go:79 build.go:89 build.go:103 #: build.go:73
msgid "Error getting working directory" msgid "Error getting working directory"
msgstr "Ошибка при получении рабочего каталога" msgstr "Ошибка при получении рабочего каталога"
#: build.go:110 build.go:115 #: build.go:118
#, fuzzy msgid "Cannot get absolute script path"
msgid "Error dropping capabilities" msgstr "Невозможно получить абсолютный путь к скрипту"
msgstr "Ошибка при открытии базы данных"
#: build.go:123 #: build.go:148
#, fuzzy
msgid "Error loading config"
msgstr "Ошибка при кодировании конфигурации"
#: build.go:131
msgid "Error initialization database"
msgstr "Ошибка инициализации базы данных"
#: build.go:141
msgid "Unable to detect a supported package manager on the system"
msgstr "Не удалось обнаружить поддерживаемый менеджер пакетов в системе"
#: build.go:147
msgid "Error parsing os release"
msgstr "Ошибка при разборе файла выпуска операционной системы"
#: build.go:179 build.go:221
msgid "Error building package"
msgstr "Ошибка при сборке пакета"
#: build.go:197
msgid "Package not found" msgid "Package not found"
msgstr "Пакет не найден" msgstr "Пакет не найден"
#: build.go:225 #: build.go:161
#, fuzzy
msgid "Nothing to build" msgid "Nothing to build"
msgstr "Исполнение build()" msgstr "Нечего собирать"
#: build.go:234 #: build.go:218
msgid "Error building package"
msgstr "Ошибка при сборке пакета"
#: build.go:225
msgid "Error moving the package" msgid "Error moving the package"
msgstr "Ошибка при перемещении пакета" msgstr "Ошибка при перемещении пакета"
#: fix.go:37 #: build.go:229
msgid "Done"
msgstr "Сделано"
#: fix.go:38
msgid "Attempt to fix problems with ALR" msgid "Attempt to fix problems with ALR"
msgstr "Попытка устранить проблемы с ALR" msgstr "Попытка устранить проблемы с ALR"
#: fix.go:58 #: fix.go:59
#, fuzzy
msgid "Clearing cache directory" msgid "Clearing cache directory"
msgstr "Удаление каталога кэша" msgstr "Очистка каталога кэша"
#: fix.go:63 #: fix.go:64
#, fuzzy
msgid "Unable to open cache directory" msgid "Unable to open cache directory"
msgstr "Не удалось удалить каталог кэша" msgstr "Невозможно открыть каталог кэша"
#: fix.go:70 #: fix.go:70
#, fuzzy
msgid "Unable to read cache directory contents" msgid "Unable to read cache directory contents"
msgstr "Не удалось удалить каталог кэша" msgstr "Невозможно прочитать содержимое каталога кэша"
#: fix.go:77 #: fix.go:76
#, fuzzy msgid "Unable to remove cache item (%s)"
msgid "Unable to remove cache item" msgstr "Невозможно удалить элемент кэша (%s)"
msgstr "Не удалось удалить каталог кэша"
#: fix.go:82 #: fix.go:80
msgid "Rebuilding cache" msgid "Rebuilding cache"
msgstr "Восстановление кэша" msgstr "Восстановление кэша"
#: fix.go:86 #: fix.go:84
msgid "Unable to create new cache directory" msgid "Unable to create new cache directory"
msgstr "Не удалось создать новый каталог кэша" msgstr "Не удалось создать новый каталог кэша"
#: fix.go:101
msgid "Done"
msgstr "Сделано"
#: gen.go:34 #: gen.go:34
msgid "Generate a ALR script from a template" msgid "Generate a ALR script from a template"
msgstr "Генерация скрипта ALR из шаблона" msgstr "Генерация скрипта ALR из шаблона"
@@ -124,91 +101,106 @@ msgstr "Генерация скрипта ALR из шаблона"
msgid "Generate a ALR script for a pip module" msgid "Generate a ALR script for a pip module"
msgstr "Генерация скрипта ALR для модуля pip" msgstr "Генерация скрипта ALR для модуля pip"
#: helper.go:41 #: helper.go:42
msgid "List all the available helper commands" msgid "List all the available helper commands"
msgstr "Список всех доступных вспомогательных команды" msgstr "Список всех доступных вспомогательных команды"
#: helper.go:53 #: helper.go:54
msgid "Run a ALR helper command" msgid "Run a ALR helper command"
msgstr "Запустить вспомогательную команду ALR" msgstr "Запустить вспомогательную команду ALR"
#: helper.go:60 #: helper.go:61
msgid "The directory that the install commands will install to" msgid "The directory that the install commands will install to"
msgstr "Каталог, в который будут устанавливать команды установки" msgstr "Каталог, в который будут устанавливать команды установки"
#: helper.go:73 #: helper.go:74 helper.go:75
msgid "No such helper command" msgid "No such helper command"
msgstr "Такой вспомогательной команды нет" msgstr "Такой вспомогательной команды нет"
#: info.go:44 #: helper.go:85
msgid "Print information about a package"
msgstr "Отобразить информацию о пакете"
#: info.go:49
msgid "Show all information, not just for the current distro"
msgstr "Показывать всю информацию, не только для текущего дистрибутива"
#: info.go:75
msgid "Error getting packages"
msgstr "Ошибка при получении пакетов"
#: info.go:84
msgid "Error iterating over packages"
msgstr "Ошибка при переборе пакетов"
#: info.go:98
msgid "Command info expected at least 1 argument, got %d"
msgstr "Для команды info ожидался хотя бы 1 аргумент, получено %d"
#: info.go:118
msgid "Error finding packages"
msgstr "Ошибка при поиске пакетов"
#: info.go:134
#, fuzzy
msgid "Can't detect system language"
msgstr "Ошибка при парсинге языка системы"
#: info.go:144
msgid "Error parsing os-release file" msgid "Error parsing os-release file"
msgstr "Ошибка при разборе файла выпуска операционной системы" msgstr "Ошибка при разборе файла выпуска операционной системы"
#: info.go:153 #: info.go:42
msgid "Print information about a package"
msgstr "Отобразить информацию о пакете"
#: info.go:47
msgid "Show all information, not just for the current distro"
msgstr "Показывать всю информацию, не только для текущего дистрибутива"
#: info.go:68
msgid "Error getting packages"
msgstr "Ошибка при получении пакетов"
#: info.go:76
msgid "Error iterating over packages"
msgstr "Ошибка при переборе пакетов"
#: info.go:90
msgid "Command info expected at least 1 argument, got %d"
msgstr "Для команды info ожидался хотя бы 1 аргумент, получено %d"
#: info.go:110
msgid "Error finding packages"
msgstr "Ошибка при поиске пакетов"
#: info.go:124
msgid "Can't detect system language"
msgstr "Ошибка при определении языка системы"
#: info.go:141
msgid "Error resolving overrides" msgid "Error resolving overrides"
msgstr "Ошибка устранения переорпеделений" msgstr "Ошибка устранения переорпеделений"
#: info.go:162 info.go:168 #: info.go:149 info.go:154
msgid "Error encoding script variables" msgid "Error encoding script variables"
msgstr "Ошибка кодирования переменных скрита" msgstr "Ошибка кодирования переменных скрита"
#: install.go:43 #: install.go:40
msgid "Install a new package" msgid "Install a new package"
msgstr "Установить новый пакет" msgstr "Установить новый пакет"
#: install.go:57 #: install.go:56
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:96 #: install.go:118
msgid "Error pulling repositories" msgid "Error parsing os release"
msgstr "Ошибка при извлечении репозиториев" msgstr "Ошибка при разборе файла выпуска операционной системы"
#: install.go:164 #: install.go:163
msgid "Remove an installed package" msgid "Remove an installed package"
msgstr "Удалить установленный пакет" msgstr "Удалить установленный пакет"
#: install.go:189 #: install.go:182
msgid "Error listing installed packages" msgid "Error listing installed packages"
msgstr "Ошибка при составлении списка установленных пакетов" msgstr "Ошибка при составлении списка установленных пакетов"
#: install.go:227 #: install.go:223
msgid "Command remove expected at least 1 argument, got %d" msgid "Command remove expected at least 1 argument, got %d"
msgstr "Для команды remove ожидался хотя бы 1 аргумент, получено %d" msgstr "Для команды remove ожидался хотя бы 1 аргумент, получено %d"
#: install.go:242 #: install.go:238
msgid "Error removing packages" msgid "Error removing packages"
msgstr "Ошибка при удалении пакетов" msgstr "Ошибка при удалении пакетов"
#: internal/cliutils/app_builder/builder.go:75
msgid "Error loading config"
msgstr "Ошибка при загрузке"
#: internal/cliutils/app_builder/builder.go:96
msgid "Error initialization database"
msgstr "Ошибка инициализации базы данных"
#: internal/cliutils/app_builder/builder.go:135
msgid "Error pulling repositories"
msgstr "Ошибка при извлечении репозиториев"
#: internal/cliutils/app_builder/builder.go:165
msgid "Unable to detect a supported package manager on the system"
msgstr "Не удалось обнаружить поддерживаемый менеджер пакетов в системе"
#: internal/cliutils/prompt.go:60 #: internal/cliutils/prompt.go:60
msgid "Would you like to view the build script for %s" msgid "Would you like to view the build script for %s"
msgstr "Показать скрипт для пакета %s" msgstr "Показать скрипт для пакета %s"
@@ -289,11 +281,11 @@ msgstr "КАТЕГОРИЯ"
msgid "OPTIONS" msgid "OPTIONS"
msgstr "ПАРАМЕТРЫ" msgstr "ПАРАМЕТРЫ"
#: internal/db/db.go:133 #: internal/db/db.go:137
msgid "Database version mismatch; resetting" msgid "Database version mismatch; resetting"
msgstr "Несоответствие версий базы данных; сброс настроек" msgstr "Несоответствие версий базы данных; сброс настроек"
#: internal/db/db.go:140 #: internal/db/db.go:144
msgid "" 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 ""
@@ -327,9 +319,17 @@ msgstr "%s %s загружается — %s/с\n"
msgid "ERROR" msgid "ERROR"
msgstr "ОШИБКА" msgstr "ОШИБКА"
#: internal/utils/cmd.go:94 #: internal/utils/cmd.go:95
msgid "Error on dropping capabilities"
msgstr "Ошибка при понижении привилегий"
#: internal/utils/cmd.go:123
msgid "You need to be root to perform this action" msgid "You need to be root to perform this action"
msgstr "" msgstr "Вы должны быть root чтобы выполнить это"
#: internal/utils/cmd.go:165
msgid "You need to be a %s member to perform this action"
msgstr "Вы должны быть членом %s чтобы выполнить это"
#: list.go:41 #: list.go:41
msgid "List ALR repo packages" msgid "List ALR repo packages"
@@ -339,35 +339,35 @@ msgstr "Список пакетов репозитория ALR"
msgid "Print the current ALR version and exit" msgid "Print the current ALR version and exit"
msgstr "Показать текущую версию ALR и выйти" msgstr "Показать текущую версию ALR и выйти"
#: main.go:79 #: main.go:61
msgid "Arguments to be passed on to the package manager" msgid "Arguments to be passed on to the package manager"
msgstr "Аргументы, которые будут переданы менеджеру пакетов" msgstr "Аргументы, которые будут переданы менеджеру пакетов"
#: main.go:85 #: main.go:67
msgid "Enable interactive questions and prompts" msgid "Enable interactive questions and prompts"
msgstr "Включение интерактивных вопросов и запросов" msgstr "Включение интерактивных вопросов и запросов"
#: main.go:185 #: main.go:145
msgid "Show help" msgid "Show help"
msgstr "Показать справку" msgstr "Показать справку"
#: main.go:189 #: main.go:149
msgid "Error while running app" msgid "Error while running app"
msgstr "Ошибка при запуске приложения" msgstr "Ошибка при запуске приложения"
#: pkg/build/build.go:392 #: pkg/build/build.go:395
msgid "Building package" msgid "Building package"
msgstr "Сборка пакета" msgstr "Сборка пакета"
#: pkg/build/build.go:421 #: pkg/build/build.go:424
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:448 #: pkg/build/build.go:455
msgid "Downloading sources" msgid "Downloading sources"
msgstr "Скачивание источников" msgstr "Скачивание источников"
#: pkg/build/build.go:535 #: pkg/build/build.go:549
msgid "Installing dependencies" msgid "Installing dependencies"
msgstr "Установка зависимостей" msgstr "Установка зависимостей"
@@ -409,17 +409,17 @@ msgstr ""
msgid "Building package metadata" msgid "Building package metadata"
msgstr "Сборка метаданных пакета" msgstr "Сборка метаданных пакета"
#: pkg/build/script_executor.go:356 #: pkg/build/script_executor.go:368
msgid "Executing prepare()" msgid "Executing prepare()"
msgstr "Исполнение prepare()" msgstr "Выполнение prepare()"
#: pkg/build/script_executor.go:365 #: pkg/build/script_executor.go:377
msgid "Executing build()" msgid "Executing build()"
msgstr "Исполнение build()" msgstr "Выполнение build()"
#: pkg/build/script_executor.go:394 pkg/build/script_executor.go:414 #: pkg/build/script_executor.go:406 pkg/build/script_executor.go:426
msgid "Executing %s()" msgid "Executing %s()"
msgstr "Исполнение %s()" msgstr "Выполнение %s()"
#: pkg/repos/pull.go:79 #: pkg/repos/pull.go:79
msgid "Pulling repository" msgid "Pulling repository"
@@ -441,104 +441,109 @@ msgstr ""
"Минимальная версия ALR для ALR-репозитория выше текущей версии. Попробуйте " "Минимальная версия ALR для ALR-репозитория выше текущей версии. Попробуйте "
"обновить ALR, если что-то не работает." "обновить ALR, если что-то не работает."
#: repo.go:41 #: repo.go:39
msgid "Add a new repository" msgid "Add a new repository"
msgstr "Добавить новый репозиторий" msgstr "Добавить новый репозиторий"
#: repo.go:48 #: repo.go:46
msgid "Name of the new repo" msgid "Name of the new repo"
msgstr "Название нового репозитория" msgstr "Название нового репозитория"
#: repo.go:54 #: repo.go:52
msgid "URL of the new repo" msgid "URL of the new repo"
msgstr "URL-адрес нового репозитория" msgstr "URL-адрес нового репозитория"
#: repo.go:92 repo.go:172 #: repo.go:79
#, fuzzy msgid "Repo \"%s\" already exists"
msgstr "Репозиторий \"%s\" уже существует"
#: repo.go:90 repo.go:167
msgid "Error saving config" msgid "Error saving config"
msgstr "Ошибка при кодировании конфигурации" msgstr "Ошибка при сохранении конфигурации"
#: repo.go:97 repo.go:199 #: repo.go:116
msgid "Can't drop privileges"
msgstr ""
#: repo.go:104 repo.go:110 repo.go:219
msgid "Error pulling repos"
msgstr "Ошибка при извлечении репозиториев"
#: repo.go:122
msgid "Remove an existing repository" msgid "Remove an existing repository"
msgstr "Удалить существующий репозиторий" msgstr "Удалить существующий репозиторий"
#: repo.go:129 #: repo.go:123
msgid "Name of the repo to be deleted" msgid "Name of the repo to be deleted"
msgstr "Название репозитория удалён" msgstr "Название репозитория удалён"
#: repo.go:158 #: repo.go:156
msgid "Repo does not exist" msgid "Repo \"%s\" does not exist"
msgstr "Репозитория не существует" msgstr "Репозитория \"%s\" не существует"
#: repo.go:166 #: repo.go:163
msgid "Error removing repo directory" msgid "Error removing repo directory"
msgstr "Ошибка при удалении каталога репозитория" msgstr "Ошибка при удалении каталога репозитория"
#: repo.go:183 #: repo.go:186
msgid "Error removing packages from database" msgid "Error removing packages from database"
msgstr "Ошибка при удалении пакетов из базы данных" msgstr "Ошибка при удалении пакетов из базы данных"
#: repo.go:195 #: repo.go:197
msgid "Pull all repositories that have changed" msgid "Pull all repositories that have changed"
msgstr "Скачать все изменённые репозитории" msgstr "Скачать все изменённые репозитории"
#: search.go:36 #: search.go:40
msgid "Search packages" msgid "Search packages"
msgstr "Поиск пакетов" msgstr "Поиск пакетов"
#: search.go:42 #: search.go:51
msgid "Search by name" msgid "Search by name"
msgstr "Искать по имени" msgstr "Искать по имени"
#: search.go:47 #: search.go:56
msgid "Search by description" msgid "Search by description"
msgstr "Искать по описанию" msgstr "Искать по описанию"
#: search.go:52 #: search.go:61
msgid "Search by repository" msgid "Search by repository"
msgstr "Искать по репозиторию" msgstr "Искать по репозиторию"
#: search.go:57 #: search.go:66
msgid "Search by provides" msgid "Search by provides"
msgstr "Иcкать по provides" msgstr "Иcкать по provides"
#: search.go:62 #: search.go:71
msgid "Format output using a Go template" msgid "Format output using a Go template"
msgstr "Формат выходных данных с использованием шаблона Go" msgstr "Формат выходных данных с использованием шаблона Go"
#: search.go:96 #: search.go:130
#, fuzzy
msgid "Error while executing search" msgid "Error while executing search"
msgstr "Ошибка при запуске приложения" msgstr "Ошибка при выполнении поиска"
#: search.go:105 #: search.go:138
msgid "Error parsing format template" msgid "Error parsing format template"
msgstr "Ошибка при разборе шаблона" msgstr "Ошибка при разборе шаблона"
#: search.go:114 #: search.go:153
msgid "Error executing template" msgid "Error executing template"
msgstr "Ошибка при выполнении шаблона" msgstr "Ошибка при выполнении шаблона"
#: upgrade.go:48 #: upgrade.go:47
msgid "Upgrade all installed packages" msgid "Upgrade all installed packages"
msgstr "Обновить все установленные пакеты" msgstr "Обновить все установленные пакеты"
#: upgrade.go:111 upgrade.go:129 #: upgrade.go:109 upgrade.go:126
msgid "Error checking for updates" msgid "Error checking for updates"
msgstr "Ошибка при проверке обновлений" msgstr "Ошибка при проверке обновлений"
#: upgrade.go:133 #: upgrade.go:129
msgid "There is nothing to do." msgid "There is nothing to do."
msgstr "Здесь нечего делать." msgstr "Здесь нечего делать."
#~ msgid "Error pulling repos"
#~ msgstr "Ошибка при извлечении репозиториев"
#, fuzzy
#~ msgid "Error getting current executable"
#~ msgstr "Ошибка при получении рабочего каталога"
#, fuzzy
#~ msgid "Error mounting"
#~ msgstr "Ошибка при кодировании конфигурации"
#, fuzzy #, fuzzy
#~ msgid "Unable to create config directory" #~ msgid "Unable to create config directory"
#~ msgstr "Не удалось создать каталог конфигурации ALR" #~ msgstr "Не удалось создать каталог конфигурации ALR"

View File

@@ -20,10 +20,6 @@
package types package types
type BuildOpts struct { type BuildOpts struct {
// Script string
// Repository string
// Packages []string
// Manager manager.Manager
Clean bool Clean bool
Interactive bool Interactive bool
} }
@@ -32,7 +28,9 @@ type BuildVarsPre struct {
Version string `sh:"version,required"` Version string `sh:"version,required"`
Release int `sh:"release,required"` Release int `sh:"release,required"`
Epoch uint `sh:"epoch"` Epoch uint `sh:"epoch"`
Summary string `sh:"summary"`
Description string `sh:"desc"` Description string `sh:"desc"`
Group string `sh:"group"`
Homepage string `sh:"homepage"` Homepage string `sh:"homepage"`
Maintainer string `sh:"maintainer"` Maintainer string `sh:"maintainer"`
Architectures []string `sh:"architectures"` Architectures []string `sh:"architectures"`

View File

@@ -25,7 +25,6 @@ type Config struct {
PagerStyle string `toml:"pagerStyle" env:"ALR_PAGER_STYLE"` PagerStyle string `toml:"pagerStyle" env:"ALR_PAGER_STYLE"`
IgnorePkgUpdates []string `toml:"ignorePkgUpdates"` IgnorePkgUpdates []string `toml:"ignorePkgUpdates"`
Repos []Repo `toml:"repo"` Repos []Repo `toml:"repo"`
Unsafe Unsafe `toml:"unsafe"`
AutoPull bool `toml:"autoPull" env:"ALR_AUTOPULL"` AutoPull bool `toml:"autoPull" env:"ALR_AUTOPULL"`
LogLevel string `toml:"logLevel" env:"ALR_LOG_LEVEL"` LogLevel string `toml:"logLevel" env:"ALR_LOG_LEVEL"`
} }
@@ -35,7 +34,3 @@ type Repo struct {
Name string `toml:"name"` Name string `toml:"name"`
URL string `toml:"url"` URL string `toml:"url"`
} }
type Unsafe struct {
AllowRunAsRoot bool `toml:"allowRunAsRoot" env:"ALR_UNSAFE_ALLOW_RUN_AS_ROOT"`
}

View File

@@ -18,7 +18,6 @@ package utils
import ( import (
"errors" "errors"
"log/slog"
"os" "os"
"os/user" "os/user"
"strconv" "strconv"
@@ -26,6 +25,9 @@ import (
"github.com/leonelquinteros/gotext" "github.com/leonelquinteros/gotext"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/constants"
) )
func GetUidGidAlrUserString() (string, string, error) { func GetUidGidAlrUserString() (string, string, error) {
@@ -68,6 +70,66 @@ func DropCapsToAlrUser() error {
if err != nil { if err != nil {
return err return err
} }
return EnsureIsAlrUser()
}
func ExitIfCantDropGidToAlr() cli.ExitCoder {
_, gid, err := GetUidGidAlrUser()
if err != nil {
return cliutils.FormatCliExit("cannot get gid alr", err)
}
err = syscall.Setgid(gid)
if err != nil {
return cliutils.FormatCliExit("cannot get setgid alr", err)
}
return nil
}
// ExitIfCantDropCapsToAlrUser attempts to drop capabilities to the already
// running user. Returns a cli.ExitCoder with an error if the operation fails.
// See also [ExitIfCantDropCapsToAlrUserNoPrivs] for a version that also applies
// no-new-privs.
func ExitIfCantDropCapsToAlrUser() cli.ExitCoder {
err := DropCapsToAlrUser()
if err != nil {
return cliutils.FormatCliExit(gotext.Get("Error on dropping capabilities"), err)
}
return nil
}
func ExitIfCantSetNoNewPrivs() cli.ExitCoder {
if err := NoNewPrivs(); err != nil {
return cliutils.FormatCliExit("error on NoNewPrivs", err)
}
return nil
}
// ExitIfCantDropCapsToAlrUserNoPrivs combines [ExitIfCantDropCapsToAlrUser] with [ExitIfCantSetNoNewPrivs]
func ExitIfCantDropCapsToAlrUserNoPrivs() cli.ExitCoder {
if err := ExitIfCantDropCapsToAlrUser(); err != nil {
return err
}
if err := ExitIfCantSetNoNewPrivs(); err != nil {
return err
}
return nil
}
func ExitIfNotRoot() error {
if os.Getuid() != 0 {
return cli.Exit(gotext.Get("You need to be root to perform this action"), 1)
}
return nil
}
func EnsureIsAlrUser() error {
uid, gid, err := GetUidGidAlrUser()
if err != nil {
return err
}
newUid := syscall.Getuid() newUid := syscall.Getuid()
if newUid != uid { if newUid != uid {
return errors.New("new uid don't matches requested") return errors.New("new uid don't matches requested")
@@ -79,19 +141,46 @@ func DropCapsToAlrUser() error {
return nil return nil
} }
// Returns cli.Exit to func EnuseIsPrivilegedGroupMember() error {
func ExitIfCantDropCapsToAlrUser() cli.ExitCoder { currentUser, err := user.Current()
err := DropCapsToAlrUser()
if err != nil { if err != nil {
slog.Debug("dropping capabilities error", "err", err) return err
return cli.Exit(gotext.Get("Error dropping capabilities"), 1)
} }
return nil
group, err := user.LookupGroup(constants.PrivilegedGroup)
if err != nil {
return err
}
groups, err := currentUser.GroupIds()
if err != nil {
return err
}
for _, gid := range groups {
if gid == group.Gid {
return nil
}
}
return cliutils.FormatCliExit(gotext.Get("You need to be a %s member to perform this action", constants.PrivilegedGroup), nil)
} }
func ExitIfNotRoot() error { func EscalateToRootGid() error {
if os.Getuid() != 0 { return syscall.Setgid(0)
return cli.Exit(gotext.Get("You need to be root to perform this action"), 1) }
func EscalateToRootUid() error {
return syscall.Setuid(0)
}
func EscalateToRoot() error {
err := EscalateToRootUid()
if err != nil {
return err
}
err = EscalateToRootGid()
if err != nil {
return err
} }
return nil return nil
} }

23
internal/utils/utils.go Normal file
View File

@@ -0,0 +1,23 @@
// 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 utils
import "golang.org/x/sys/unix"
func NoNewPrivs() error {
return unix.Prctl(unix.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)
}

21
list.go
View File

@@ -22,12 +22,12 @@ package main
import ( import (
"fmt" "fmt"
"log/slog" "log/slog"
"os"
"github.com/leonelquinteros/gotext" "github.com/leonelquinteros/gotext"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"golang.org/x/exp/slices" "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" appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder"
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/internal/utils" "gitea.plemya-x.ru/Plemya-x/ALR/internal/utils"
@@ -47,7 +47,7 @@ func ListCmd() *cli.Command {
}, },
}, },
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil { if err := utils.ExitIfCantDropCapsToAlrUserNoPrivs(); err != nil {
return err return err
} }
@@ -77,8 +77,7 @@ func ListCmd() *cli.Command {
result, err := db.GetPkgs(ctx, where, args...) result, err := db.GetPkgs(ctx, where, args...)
if err != nil { if err != nil {
slog.Error(gotext.Get("Error getting packages"), "err", err) return cliutils.FormatCliExit(gotext.Get("Error getting packages"), err)
os.Exit(1)
} }
defer result.Close() defer result.Close()
@@ -86,14 +85,13 @@ func ListCmd() *cli.Command {
if c.Bool("installed") { if c.Bool("installed") {
mgr := manager.Detect() mgr := manager.Detect()
if mgr == nil { if mgr == nil {
slog.Error(gotext.Get("Unable to detect a supported package manager on the system")) return cli.Exit(gotext.Get("Unable to detect a supported package manager on the system"), 1)
os.Exit(1)
} }
installed, err := mgr.ListInstalled(&manager.Opts{AsRoot: false}) installed, err := mgr.ListInstalled(&manager.Opts{})
if err != nil { if err != nil {
slog.Error(gotext.Get("Error listing installed packages"), "err", err) slog.Error(gotext.Get("Error listing installed packages"), "err", err)
os.Exit(1) return cli.Exit(err, 1)
} }
for pkgName, version := range installed { for pkgName, version := range installed {
@@ -110,7 +108,7 @@ func ListCmd() *cli.Command {
var pkg database.Package var pkg database.Package
err := result.StructScan(&pkg) err := result.StructScan(&pkg)
if err != nil { if err != nil {
return err return cli.Exit(err, 1)
} }
if slices.Contains(cfg.IgnorePkgUpdates(), pkg.Name) { if slices.Contains(cfg.IgnorePkgUpdates(), pkg.Name) {
@@ -130,11 +128,6 @@ func ListCmd() *cli.Command {
fmt.Printf("%s/%s %s\n", pkg.Repository, pkg.Name, version) fmt.Printf("%s/%s %s\n", pkg.Repository, pkg.Name, version)
} }
if err != nil {
slog.Error(gotext.Get("Error iterating over packages"), "err", err)
os.Exit(1)
}
return nil return nil
}, },
} }

56
main.go
View File

@@ -21,10 +21,10 @@ package main
import ( import (
"context" "context"
"fmt"
"log/slog" "log/slog"
"os" "os"
"os/signal" "os/signal"
"strings"
"syscall" "syscall"
"github.com/leonelquinteros/gotext" "github.com/leonelquinteros/gotext"
@@ -50,24 +50,6 @@ func VersionCmd() *cli.Command {
} }
} }
func HandleExitCoder(err error) {
if err == nil {
return
}
if exitErr, ok := err.(cli.ExitCoder); ok {
if err.Error() != "" {
if _, ok := exitErr.(cli.ErrorFormatter); ok {
slog.Error(fmt.Sprintf("%+v\n", err))
} else {
slog.Error(err.Error())
}
}
cli.OsExiter(exitErr.ExitCode())
return
}
}
func GetApp() *cli.App { func GetApp() *cli.App {
return &cli.App{ return &cli.App{
Name: "alr", Name: "alr",
@@ -100,41 +82,21 @@ func GetApp() *cli.App {
HelperCmd(), HelperCmd(),
VersionCmd(), VersionCmd(),
SearchCmd(), SearchCmd(),
// TEST // Internal commands
InternalBuildCmd(), InternalBuildCmd(),
InternalInstallCmd(), InternalInstallCmd(),
InternalMountCmd(), InternalMountCmd(),
InternalUnmountCmd(),
// InternalBuild2Cmd(),
}, },
Before: func(c *cli.Context) error { Before: func(c *cli.Context) error {
/* if trimmed := strings.TrimSpace(c.String("pm-args")); trimmed != "" {
cfg := config.New() args := strings.Split(trimmed, " ")
err := cfg.Load() manager.Args = append(manager.Args, args...)
if err != nil { }
slog.Error(gotext.Get("Error loading config"), "err", err)
os.Exit(1)
}
/*
cmd := c.Args().First()
if cmd != "helper" && !cfg.AllowRunAsRoot() && os.Geteuid() == 0 {
slog.Error(gotext.Get("Running ALR as root is forbidden as it may cause catastrophic damage to your system"))
os.Exit(1)
}
if trimmed := strings.TrimSpace(c.String("pm-args")); trimmed != "" {
args := strings.Split(trimmed, " ")
manager.Args = append(manager.Args, args...)
}
return nil
*/
return nil return nil
}, },
EnableBashCompletion: true, EnableBashCompletion: true,
ExitErrHandler: func(c *cli.Context, err error) { ExitErrHandler: func(cCtx *cli.Context, err error) {
cliutils.HandleExitCoder(err)
}, },
} }
} }
@@ -173,8 +135,6 @@ func main() {
os.Exit(1) os.Exit(1)
} }
setLogLevel(cfg.LogLevel()) setLogLevel(cfg.LogLevel())
// Set the root command to the one set in the ALR config
manager.DefaultRootCmd = cfg.RootCmd()
ctx, cancel := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM) ctx, cancel := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM)
defer cancel() defer cancel()

View File

@@ -35,6 +35,7 @@ import (
"gitea.plemya-x.ru/Plemya-x/ALR/internal/db" "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/distro" "gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager"
) )
type BuildInput struct { type BuildInput struct {
@@ -233,8 +234,8 @@ type CheckerExecutor interface {
} }
type InstallerExecutor interface { type InstallerExecutor interface {
InstallLocal(paths []string) error InstallLocal(paths []string, opts *manager.Opts) error
Install(pkgs []string) error Install(pkgs []string, opts *manager.Opts) error
RemoveAlreadyInstalled(pkgs []string) ([]string, error) RemoveAlreadyInstalled(pkgs []string) ([]string, error)
} }
@@ -352,16 +353,17 @@ func (b *Builder) BuildPackage(
) (*BuildResult, error) { ) (*BuildResult, error) {
scriptPath := input.script scriptPath := input.script
slog.Debug("ReadScript")
sf, err := b.scriptExecutor.ReadScript(ctx, scriptPath) sf, err := b.scriptExecutor.ReadScript(ctx, scriptPath)
if err != nil { if err != nil {
return nil, err return nil, err
} }
slog.Debug("ExecuteFirstPass")
basePkg, varsOfPackages, err := b.scriptExecutor.ExecuteFirstPass(ctx, input, sf) basePkg, varsOfPackages, err := b.scriptExecutor.ExecuteFirstPass(ctx, input, sf)
if err != nil { if err != nil {
return nil, err return nil, err
} }
slog.Debug("ExecuteFirstPass", "basePkg", basePkg, "varsOfPackages", varsOfPackages)
builtPaths := make([]string, 0) builtPaths := make([]string, 0)
@@ -384,6 +386,7 @@ func (b *Builder) BuildPackage(
} }
} }
slog.Debug("ViewScript")
err = b.scriptViewerExecutor.ViewScript(ctx, input, sf, basePkg) err = b.scriptViewerExecutor.ViewScript(ctx, input, sf, basePkg)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -423,21 +426,25 @@ func (b *Builder) BuildPackage(
} }
sources, checksums = removeDuplicatesSources(sources, checksums) sources, checksums = removeDuplicatesSources(sources, checksums)
slog.Debug("installBuildDeps")
err = b.installBuildDeps(ctx, input, buildDepends) err = b.installBuildDeps(ctx, input, buildDepends)
if err != nil { if err != nil {
return nil, err return nil, err
} }
slog.Debug("installOptDeps")
err = b.installOptDeps(ctx, input, optDepends) err = b.installOptDeps(ctx, input, optDepends)
if err != nil { if err != nil {
return nil, err return nil, err
} }
slog.Debug("BuildALRDeps")
_, builtNames, repoDeps, err := b.BuildALRDeps(ctx, input, depends) _, builtNames, repoDeps, err := b.BuildALRDeps(ctx, input, depends)
if err != nil { if err != nil {
return nil, err return nil, err
} }
slog.Debug("PrepareDirs")
err = b.scriptExecutor.PrepareDirs(ctx, input, basePkg) err = b.scriptExecutor.PrepareDirs(ctx, input, basePkg)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -446,6 +453,7 @@ func (b *Builder) BuildPackage(
// builtPaths = append(builtPaths, newBuildPaths...) // builtPaths = append(builtPaths, newBuildPaths...)
slog.Info(gotext.Get("Downloading sources")) slog.Info(gotext.Get("Downloading sources"))
slog.Debug("DownloadSources")
err = b.sourceExecutor.DownloadSources( err = b.sourceExecutor.DownloadSources(
ctx, ctx,
input, input,
@@ -459,6 +467,7 @@ func (b *Builder) BuildPackage(
return nil, err return nil, err
} }
slog.Debug("ExecuteSecondPass")
res, err := b.scriptExecutor.ExecuteSecondPass( res, err := b.scriptExecutor.ExecuteSecondPass(
ctx, ctx,
input, input,
@@ -513,7 +522,12 @@ func (b *Builder) InstallALRPackages(
return err return err
} }
err = b.installerExecutor.InstallLocal(res.PackagePaths) err = b.installerExecutor.InstallLocal(
res.PackagePaths,
&manager.Opts{
NoConfirm: !input.BuildOpts().Interactive,
},
)
if err != nil { if err != nil {
return err return err
} }
@@ -673,14 +687,18 @@ func (i *Builder) InstallPkgs(
} }
if len(builtPaths) > 0 { if len(builtPaths) > 0 {
err = i.installerExecutor.InstallLocal(builtPaths) err = i.installerExecutor.InstallLocal(builtPaths, &manager.Opts{
NoConfirm: !input.BuildOpts().Interactive,
})
if err != nil { if err != nil {
return err return err
} }
} }
if len(repoDeps) > 0 { if len(repoDeps) > 0 {
err = i.installerExecutor.Install(repoDeps) err = i.installerExecutor.Install(repoDeps, &manager.Opts{
NoConfirm: !input.BuildOpts().Interactive,
})
if err != nil { if err != nil {
return err return err
} }

View File

@@ -20,27 +20,20 @@ import (
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" "gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager"
) )
func NewInstaller( func NewInstaller(mgr manager.Manager) *Installer {
repos PackageFinder,
mgr manager.Manager,
) *Installer {
return &Installer{ return &Installer{
repos: repos, mgr: mgr,
mgr: mgr,
} }
} }
type Installer struct { type Installer struct{ mgr manager.Manager }
repos PackageFinder
mgr manager.Manager func (i *Installer) InstallLocal(paths []string, opts *manager.Opts) error {
return i.mgr.InstallLocal(opts, paths...)
} }
func (i *Installer) InstallLocal(paths []string) error { func (i *Installer) Install(pkgs []string, opts *manager.Opts) error {
return i.mgr.InstallLocal(nil, paths...) return i.mgr.Install(opts, pkgs...)
}
func (i *Installer) Install(pkgs []string) error {
return i.mgr.Install(nil, pkgs...)
} }
func (i *Installer) RemoveAlreadyInstalled(pkgs []string) ([]string, error) { func (i *Installer) RemoveAlreadyInstalled(pkgs []string) ([]string, error) {

View File

@@ -17,31 +17,18 @@
package build package build
import ( import (
"log/slog"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager" "gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager"
) )
func NewMainBuilder( func NewMainBuilder(
cfg Config, cfg Config,
mgr manager.Manager,
repos PackageFinder, repos PackageFinder,
) *Builder { scriptExecutor ScriptExecutor,
s, err := GetSafeScriptExecutor() installerExecutor InstallerExecutor,
if err != nil { ) (*Builder, error) {
slog.Info("i will panic")
panic(err)
}
mgr := manager.Detect()
installerExecutor, err := GetSafeInstaller()
if err != nil {
slog.Info("i will panic")
panic(err)
}
builder := &Builder{ builder := &Builder{
scriptExecutor: s, scriptExecutor: scriptExecutor,
cacheExecutor: &Cache{ cacheExecutor: &Cache{
cfg, cfg,
}, },
@@ -61,5 +48,5 @@ func NewMainBuilder(
repos: repos, repos: repos,
} }
return builder return builder, nil
} }

40
pkg/build/safe_common.go Normal file
View File

@@ -0,0 +1,40 @@
// 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 build
import (
"os"
"os/exec"
"strings"
)
func setCommonCmdEnv(cmd *exec.Cmd) {
cmd.Env = []string{
"HOME=/var/cache/alr",
"LOGNAME=alr",
"USER=alr",
"PATH=/usr/bin:/bin:/usr/local/bin",
}
for _, env := range os.Environ() {
if strings.HasPrefix(env, "LANG=") ||
strings.HasPrefix(env, "LANGUAGE=") ||
strings.HasPrefix(env, "LC_") ||
strings.HasPrefix(env, "ALR_LOG_LEVEL=") {
cmd.Env = append(cmd.Env, env)
}
}
}

View File

@@ -17,16 +17,18 @@
package build package build
import ( import (
"fmt"
"log/slog" "log/slog"
"net/rpc" "net/rpc"
"os" "os"
"os/exec" "os/exec"
"sync"
"syscall" "syscall"
"github.com/hashicorp/go-plugin" "github.com/hashicorp/go-plugin"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/logger" "gitea.plemya-x.ru/Plemya-x/ALR/internal/logger"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils" "gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager"
) )
type InstallerPlugin struct { type InstallerPlugin struct {
@@ -41,27 +43,37 @@ type InstallerRPCServer struct {
Impl InstallerExecutor Impl InstallerExecutor
} }
func (r *InstallerRPC) InstallLocal(paths []string) error { type InstallArgs struct {
return r.client.Call("Plugin.InstallLocal", paths, nil) PackagesOrPaths []string
Opts *manager.Opts
} }
func (s *InstallerRPCServer) InstallLocal(paths []string, reply *struct{}) error { func (r *InstallerRPC) InstallLocal(paths []string, opts *manager.Opts) error {
slog.Warn("install", "paths", paths) return r.client.Call("Plugin.InstallLocal", &InstallArgs{
return s.Impl.InstallLocal(paths) PackagesOrPaths: paths,
Opts: opts,
}, nil)
} }
func (r *InstallerRPC) Install(pkgs []string) error { func (s *InstallerRPCServer) InstallLocal(args *InstallArgs, reply *struct{}) error {
return r.client.Call("Plugin.Install", pkgs, nil) return s.Impl.InstallLocal(args.PackagesOrPaths, args.Opts)
} }
func (s *InstallerRPCServer) Install(pkgs []string, reply *struct{}) error { func (r *InstallerRPC) Install(pkgs []string, opts *manager.Opts) error {
slog.Debug("install", "pkgs", pkgs) return r.client.Call("Plugin.Install", &InstallArgs{
return s.Impl.Install(pkgs) PackagesOrPaths: pkgs,
Opts: opts,
}, nil)
}
func (s *InstallerRPCServer) Install(args *InstallArgs, reply *struct{}) error {
return s.Impl.Install(args.PackagesOrPaths, args.Opts)
} }
func (r *InstallerRPC) RemoveAlreadyInstalled(paths []string) ([]string, error) { func (r *InstallerRPC) RemoveAlreadyInstalled(paths []string) ([]string, error) {
err := r.client.Call("Plugin.RemoveAlreadyInstalled", paths, nil) var val []string
return nil, err err := r.client.Call("Plugin.RemoveAlreadyInstalled", paths, &val)
return val, err
} }
func (s *InstallerRPCServer) RemoveAlreadyInstalled(pkgs []string, res *[]string) error { func (s *InstallerRPCServer) RemoveAlreadyInstalled(pkgs []string, res *[]string) error {
@@ -81,30 +93,17 @@ func (p *InstallerPlugin) Server(*plugin.MuxBroker) (interface{}, error) {
return &InstallerRPCServer{Impl: p.Impl}, nil return &InstallerRPCServer{Impl: p.Impl}, nil
} }
func GetSafeInstaller() (InstallerExecutor, error) { func GetSafeInstaller() (InstallerExecutor, func(), error) {
var err error
executable, err := os.Executable() executable, err := os.Executable()
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
cmd := exec.Command(executable, "_internal-installer") cmd := exec.Command(executable, "_internal-installer")
cmd.Env = append(os.Environ(), setCommonCmdEnv(cmd)
"HOME=/var/cache/alr",
"LOGNAME=alr", slog.Debug("safe installer setup", "uid", syscall.Getuid(), "gid", syscall.Getgid())
"USER=alr",
"PATH=/usr/bin:/bin:/usr/local/bin",
"ALR_LOG_LEVEL=DEBUG",
"XDG_SESSION_CLASS=user",
)
uid, gid, err := utils.GetUidGidAlrUser()
if err != nil {
return nil, err
}
cmd.SysProcAttr = &syscall.SysProcAttr{
Credential: &syscall.Credential{
Uid: uint32(uid),
Gid: uint32(gid),
},
}
client := plugin.NewClient(&plugin.ClientConfig{ client := plugin.NewClient(&plugin.ClientConfig{
HandshakeConfig: HandshakeConfig, HandshakeConfig: HandshakeConfig,
@@ -119,14 +118,33 @@ func GetSafeInstaller() (InstallerExecutor, error) {
}) })
rpcClient, err := client.Client() rpcClient, err := client.Client()
if err != nil { if err != nil {
slog.Info("1") return nil, nil, err
return nil, err
} }
raw1, err := rpcClient.Dispense("installer") var cleanupOnce sync.Once
cleanup := func() {
cleanupOnce.Do(func() {
client.Kill()
})
}
defer func() {
if err != nil {
slog.Debug("close installer")
cleanup()
}
}()
raw, err := rpcClient.Dispense("installer")
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
return raw1.(InstallerExecutor), nil executor, ok := raw.(InstallerExecutor)
if !ok {
err = fmt.Errorf("dispensed object is not a ScriptExecutor (got %T)", raw)
return nil, nil, err
}
return executor, cleanup, nil
} }

View File

@@ -18,17 +18,17 @@ package build
import ( import (
"context" "context"
"fmt"
"log/slog" "log/slog"
"net/rpc" "net/rpc"
"os" "os"
"os/exec" "os/exec"
"syscall" "sync"
"github.com/hashicorp/go-plugin" "github.com/hashicorp/go-plugin"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/logger" "gitea.plemya-x.ru/Plemya-x/ALR/internal/logger"
"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/internal/utils"
) )
var HandshakeConfig = plugin.HandshakeConfig{ var HandshakeConfig = plugin.HandshakeConfig{
@@ -217,29 +217,16 @@ var pluginMap = map[string]plugin.Plugin{
"installer": &InstallerPlugin{}, "installer": &InstallerPlugin{},
} }
func GetSafeScriptExecutor() (ScriptExecutor, error) { func GetSafeScriptExecutor() (ScriptExecutor, func(), error) {
var err error
executable, err := os.Executable() executable, err := os.Executable()
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
cmd := exec.Command(executable, "_internal-safe-script-executor") cmd := exec.Command(executable, "_internal-safe-script-executor")
cmd.Env = []string{ setCommonCmdEnv(cmd)
"HOME=/var/cache/alr",
"LOGNAME=alr",
"USER=alr",
"PATH=/usr/bin:/bin:/usr/local/bin",
}
uid, gid, err := utils.GetUidGidAlrUser()
if err != nil {
return nil, err
}
cmd.SysProcAttr = &syscall.SysProcAttr{
Credential: &syscall.Credential{
Uid: uint32(uid),
Gid: uint32(gid),
},
}
client := plugin.NewClient(&plugin.ClientConfig{ client := plugin.NewClient(&plugin.ClientConfig{
HandshakeConfig: HandshakeConfig, HandshakeConfig: HandshakeConfig,
@@ -247,17 +234,39 @@ func GetSafeScriptExecutor() (ScriptExecutor, error) {
Cmd: cmd, Cmd: cmd,
Logger: logger.GetHCLoggerAdapter(), Logger: logger.GetHCLoggerAdapter(),
SkipHostEnv: true, SkipHostEnv: true,
UnixSocketConfig: &plugin.UnixSocketConfig{
Group: "alr",
},
}) })
rpcClient, err := client.Client() rpcClient, err := client.Client()
if err != nil { if err != nil {
slog.Info("1") return nil, nil, err
return nil, err
} }
raw1, err := rpcClient.Dispense("script-executor") var cleanupOnce sync.Once
cleanup := func() {
cleanupOnce.Do(func() {
client.Kill()
})
}
defer func() {
if err != nil {
slog.Debug("close script-executor")
cleanup()
}
}()
raw, err := rpcClient.Dispense("script-executor")
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
return raw1.(ScriptExecutor), nil executor, ok := raw.(ScriptExecutor)
if !ok {
err = fmt.Errorf("dispensed object is not a ScriptExecutor (got %T)", raw)
return nil, nil, err
}
return executor, cleanup, nil
} }

View File

@@ -304,6 +304,7 @@ func buildPkgMetadata(
Provides: append(vars.Provides, vars.Name), Provides: append(vars.Provides, vars.Name),
Depends: deps, Depends: deps,
} }
pkgInfo.Section = vars.Group
pkgFormat := input.PkgFormat() pkgFormat := input.PkgFormat()
info := input.OSRelease() info := input.OSRelease()
@@ -315,6 +316,17 @@ func buildPkgMetadata(
}) })
} }
if pkgFormat == "rpm" {
pkgInfo.RPM.Group = vars.Group
if vars.Summary != "" {
pkgInfo.RPM.Summary = vars.Summary
} else {
lines := strings.SplitN(vars.Description, "\n", 2)
pkgInfo.RPM.Summary = lines[0]
}
}
if vars.Epoch != 0 { if vars.Epoch != 0 {
pkgInfo.Epoch = strconv.FormatUint(uint64(vars.Epoch), 10) pkgInfo.Epoch = strconv.FormatUint(uint64(vars.Epoch), 10)
} }

View File

@@ -41,10 +41,10 @@ checksums=('blake2b-256:{{.SourceURL.Digests.blake2b_256}}')
build() { build() {
cd "$srcdir/{{.Info.Name}}-${version}" cd "$srcdir/{{.Info.Name}}-${version}"
python3 -m build python -m build --wheel --no-isolation
} }
package() { package() {
cd "$srcdir/{{.Info.Name}}-${version}" cd "$srcdir/{{.Info.Name}}-${version}"
pip install --root="${pkgdir}/" . --no-deps --disable-pip-version-check pip install --root="${pkgdir}/" . --no-deps --ignore-installed --disable-pip-version-check
} }

View File

@@ -28,7 +28,15 @@ import (
// APK represents the APK package manager // APK represents the APK package manager
type APK struct { type APK struct {
rootCmd string CommonPackageManager
}
func NewAPK() *APK {
return &APK{
CommonPackageManager: CommonPackageManager{
noConfirmArg: "-i",
},
}
} }
func (*APK) Exists() bool { func (*APK) Exists() bool {
@@ -44,10 +52,6 @@ func (*APK) Format() string {
return "apk" return "apk"
} }
func (a *APK) SetRootCmd(s string) {
a.rootCmd = s
}
func (a *APK) Sync(opts *Opts) error { func (a *APK) Sync(opts *Opts) error {
opts = ensureOpts(opts) opts = ensureOpts(opts)
cmd := a.getCmd(opts, "apk", "update") cmd := a.getCmd(opts, "apk", "update")
@@ -163,20 +167,3 @@ func (a *APK) IsInstalled(pkg string) (bool, error) {
} }
return true, nil return true, nil
} }
func (a *APK) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
var cmd *exec.Cmd
if opts.AsRoot {
cmd = exec.Command(getRootCmd(a.rootCmd), mgrCmd)
cmd.Args = append(cmd.Args, opts.Args...)
cmd.Args = append(cmd.Args, args...)
} else {
cmd = exec.Command(mgrCmd, args...)
}
if !opts.NoConfirm {
cmd.Args = append(cmd.Args, "-i")
}
return cmd
}

View File

@@ -28,7 +28,15 @@ import (
// APT represents the APT package manager // APT represents the APT package manager
type APT struct { type APT struct {
rootCmd string CommonPackageManager
}
func NewAPT() *APT {
return &APT{
CommonPackageManager: CommonPackageManager{
noConfirmArg: "-y",
},
}
} }
func (*APT) Exists() bool { func (*APT) Exists() bool {
@@ -44,10 +52,6 @@ func (*APT) Format() string {
return "deb" return "deb"
} }
func (a *APT) SetRootCmd(s string) {
a.rootCmd = s
}
func (a *APT) Sync(opts *Opts) error { func (a *APT) Sync(opts *Opts) error {
opts = ensureOpts(opts) opts = ensureOpts(opts)
cmd := a.getCmd(opts, "apt", "update") cmd := a.getCmd(opts, "apt", "update")
@@ -149,20 +153,3 @@ func (a *APT) IsInstalled(pkg string) (bool, error) {
} }
return true, nil return true, nil
} }
func (a *APT) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
var cmd *exec.Cmd
if opts.AsRoot {
cmd = exec.Command(getRootCmd(a.rootCmd), mgrCmd)
cmd.Args = append(cmd.Args, opts.Args...)
cmd.Args = append(cmd.Args, args...)
} else {
cmd = exec.Command(mgrCmd, args...)
}
if opts.NoConfirm {
cmd.Args = append(cmd.Args, "-y")
}
return cmd
}

View File

@@ -24,18 +24,16 @@ import (
// APTRpm represents the APT-RPM package manager // APTRpm represents the APT-RPM package manager
type APTRpm struct { type APTRpm struct {
CommonPackageManager
CommonRPM CommonRPM
rootCmd string
} }
func (*APTRpm) Exists() bool { func NewAPTRpm() *APTRpm {
cmd := exec.Command("apt-config", "dump") return &APTRpm{
output, err := cmd.Output() CommonPackageManager: CommonPackageManager{
if err != nil { noConfirmArg: "-y",
return false },
} }
return strings.Contains(string(output), "RPM")
} }
func (*APTRpm) Name() string { func (*APTRpm) Name() string {
@@ -46,8 +44,14 @@ func (*APTRpm) Format() string {
return "rpm" return "rpm"
} }
func (a *APTRpm) SetRootCmd(s string) { func (*APTRpm) Exists() bool {
a.rootCmd = s cmd := exec.Command("apt-config", "dump")
output, err := cmd.Output()
if err != nil {
return false
}
return strings.Contains(string(output), "RPM")
} }
func (a *APTRpm) Sync(opts *Opts) error { func (a *APTRpm) Sync(opts *Opts) error {
@@ -106,20 +110,3 @@ func (a *APTRpm) UpgradeAll(opts *Opts) error {
} }
return nil return nil
} }
func (a *APTRpm) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
var cmd *exec.Cmd
if opts.AsRoot {
cmd = exec.Command(getRootCmd(a.rootCmd), mgrCmd)
cmd.Args = append(cmd.Args, opts.Args...)
cmd.Args = append(cmd.Args, args...)
} else {
cmd = exec.Command(mgrCmd, args...)
}
if opts.NoConfirm {
cmd.Args = append(cmd.Args, "-y")
}
return cmd
}

35
pkg/manager/common.go Normal file
View File

@@ -0,0 +1,35 @@
// 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 manager
import "os/exec"
type CommonPackageManager struct {
noConfirmArg string
}
func (m *CommonPackageManager) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
cmd := exec.Command(mgrCmd)
cmd.Args = append(cmd.Args, opts.Args...)
cmd.Args = append(cmd.Args, args...)
if opts.NoConfirm {
cmd.Args = append(cmd.Args, m.noConfirmArg)
}
return cmd
}

View File

@@ -1,20 +1,21 @@
/* // This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
* ALR - Any Linux Repository // It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
* ALR - Любой Linux Репозиторий //
* Copyright (C) 2024 Евгений Храмов // ALR - Any Linux Repository
* // Copyright (C) 2025 Евгений Храмов
* This program является свободным: вы можете распространять его и/или изменять //
* на условиях GNU General Public License, опубликованной Free Software Foundation, // This program is free software: you can redistribute it and/or modify
* либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии. // 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,
* Подробности см. в GNU General Public License. // but WITHOUT ANY WARRANTY; without even the implied warranty of
* // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Вы должны были получить копию GNU General Public License // GNU General Public License for more details.
* вместе с этой программой. Если нет, см. <http://www.gnu.org/licenses/>. //
*/ // 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 manager package manager
@@ -23,33 +24,32 @@ import (
"os/exec" "os/exec"
) )
// DNF представляет менеджер пакетов DNF
type DNF struct { type DNF struct {
CommonPackageManager
CommonRPM CommonRPM
rootCmd string // rootCmd хранит команду, используемую для выполнения команд с правами root
} }
// Exists проверяет, доступен ли DNF в системе, возвращает true если да func NewDNF() *DNF {
return &DNF{
CommonPackageManager: CommonPackageManager{
noConfirmArg: "-y",
},
}
}
func (*DNF) Exists() bool { func (*DNF) Exists() bool {
_, err := exec.LookPath("dnf") _, err := exec.LookPath("dnf")
return err == nil return err == nil
} }
// Name возвращает имя менеджера пакетов, в данном случае "dnf"
func (*DNF) Name() string { func (*DNF) Name() string {
return "dnf" return "dnf"
} }
// Format возвращает формат пакетов "rpm", используемый DNF
func (*DNF) Format() string { func (*DNF) Format() string {
return "rpm" return "rpm"
} }
// SetRootCmd устанавливает команду, используемую для выполнения операций с правами root
func (d *DNF) SetRootCmd(s string) {
d.rootCmd = s
}
// Sync выполняет upgrade всех установленных пакетов, обновляя их до более новых версий // Sync выполняет upgrade всех установленных пакетов, обновляя их до более новых версий
func (d *DNF) Sync(opts *Opts) error { func (d *DNF) Sync(opts *Opts) error {
opts = ensureOpts(opts) // Гарантирует, что opts не равен nil и содержит допустимые значения opts = ensureOpts(opts) // Гарантирует, что opts не равен nil и содержит допустимые значения
@@ -118,21 +118,3 @@ func (d *DNF) UpgradeAll(opts *Opts) error {
} }
return nil return nil
} }
// getCmd создает и возвращает команду exec.Cmd для менеджера пакетов DNF
func (d *DNF) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
var cmd *exec.Cmd
if opts.AsRoot {
cmd = exec.Command(getRootCmd(d.rootCmd), mgrCmd)
cmd.Args = append(cmd.Args, opts.Args...)
cmd.Args = append(cmd.Args, args...)
} else {
cmd = exec.Command(mgrCmd, args...)
}
if opts.NoConfirm {
cmd.Args = append(cmd.Args, "-y") // Добавляет параметр автоматического подтверждения (-y)
}
return cmd
}

View File

@@ -27,27 +27,22 @@ import (
var Args []string var Args []string
type Opts struct { type Opts struct {
AsRoot bool
NoConfirm bool NoConfirm bool
Args []string Args []string
} }
var DefaultOpts = &Opts{ var DefaultOpts = &Opts{
AsRoot: true,
NoConfirm: false, NoConfirm: false,
} }
// DefaultRootCmd is the command used for privilege elevation by default
var DefaultRootCmd = "sudo"
var managers = []Manager{ var managers = []Manager{
&Pacman{}, NewPacman(),
&APT{}, NewAPT(),
&DNF{}, NewDNF(),
&YUM{}, NewYUM(),
&APK{}, NewAPK(),
&Zypper{}, NewZypper(),
&APTRpm{}, NewAPTRpm(),
} }
// Register registers a new package manager // Register registers a new package manager
@@ -64,8 +59,7 @@ type Manager interface {
Format() string Format() string
// Returns true if the package manager exists on the system. // Returns true if the package manager exists on the system.
Exists() bool Exists() bool
// Sets the command used to elevate privileges. Defaults to DefaultRootCmd.
SetRootCmd(string)
// Sync fetches repositories without installing anything // Sync fetches repositories without installing anything
Sync(*Opts) error Sync(*Opts) error
// Install installs packages // Install installs packages
@@ -104,18 +98,10 @@ func Get(name string) Manager {
return nil return nil
} }
// getRootCmd returns rootCmd if it's not empty, otherwise returns DefaultRootCmd
func getRootCmd(rootCmd string) string {
if rootCmd != "" {
return rootCmd
}
return DefaultRootCmd
}
func setCmdEnv(cmd *exec.Cmd) { func setCmdEnv(cmd *exec.Cmd) {
cmd.Env = os.Environ() cmd.Env = os.Environ()
cmd.Stdin = os.Stdin cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout cmd.Stdout = os.Stderr
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
} }

View File

@@ -28,7 +28,15 @@ import (
// Pacman represents the Pacman package manager // Pacman represents the Pacman package manager
type Pacman struct { type Pacman struct {
rootCmd string CommonPackageManager
}
func NewPacman() *Pacman {
return &Pacman{
CommonPackageManager: CommonPackageManager{
noConfirmArg: "--noconfirm",
},
}
} }
func (*Pacman) Exists() bool { func (*Pacman) Exists() bool {
@@ -44,10 +52,6 @@ func (*Pacman) Format() string {
return "archlinux" return "archlinux"
} }
func (p *Pacman) SetRootCmd(s string) {
p.rootCmd = s
}
func (p *Pacman) Sync(opts *Opts) error { func (p *Pacman) Sync(opts *Opts) error {
opts = ensureOpts(opts) opts = ensureOpts(opts)
cmd := p.getCmd(opts, "pacman", "-Sy") cmd := p.getCmd(opts, "pacman", "-Sy")
@@ -156,20 +160,3 @@ func (p *Pacman) IsInstalled(pkg string) (bool, error) {
} }
return true, nil return true, nil
} }
func (p *Pacman) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
var cmd *exec.Cmd
if opts.AsRoot {
cmd = exec.Command(getRootCmd(p.rootCmd), mgrCmd)
cmd.Args = append(cmd.Args, opts.Args...)
cmd.Args = append(cmd.Args, args...)
} else {
cmd = exec.Command(mgrCmd, args...)
}
if opts.NoConfirm {
cmd.Args = append(cmd.Args, "--noconfirm")
}
return cmd
}

View File

@@ -26,9 +26,16 @@ import (
// YUM represents the YUM package manager // YUM represents the YUM package manager
type YUM struct { type YUM struct {
CommonPackageManager
CommonRPM CommonRPM
}
rootCmd string func NewYUM() *YUM {
return &YUM{
CommonPackageManager: CommonPackageManager{
noConfirmArg: "-y",
},
}
} }
func (*YUM) Exists() bool { func (*YUM) Exists() bool {
@@ -44,10 +51,6 @@ func (*YUM) Format() string {
return "rpm" return "rpm"
} }
func (y *YUM) SetRootCmd(s string) {
y.rootCmd = s
}
func (y *YUM) Sync(opts *Opts) error { func (y *YUM) Sync(opts *Opts) error {
opts = ensureOpts(opts) opts = ensureOpts(opts)
cmd := y.getCmd(opts, "yum", "upgrade") cmd := y.getCmd(opts, "yum", "upgrade")
@@ -110,20 +113,3 @@ func (y *YUM) UpgradeAll(opts *Opts) error {
} }
return nil return nil
} }
func (y *YUM) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
var cmd *exec.Cmd
if opts.AsRoot {
cmd = exec.Command(getRootCmd(y.rootCmd), mgrCmd)
cmd.Args = append(cmd.Args, opts.Args...)
cmd.Args = append(cmd.Args, args...)
} else {
cmd = exec.Command(mgrCmd, args...)
}
if opts.NoConfirm {
cmd.Args = append(cmd.Args, "-y")
}
return cmd
}

View File

@@ -26,8 +26,16 @@ import (
// Zypper represents the Zypper package manager // Zypper represents the Zypper package manager
type Zypper struct { type Zypper struct {
CommonPackageManager
CommonRPM CommonRPM
rootCmd string }
func NewZypper() *YUM {
return &YUM{
CommonPackageManager: CommonPackageManager{
noConfirmArg: "-y",
},
}
} }
func (*Zypper) Exists() bool { func (*Zypper) Exists() bool {
@@ -43,10 +51,6 @@ func (*Zypper) Format() string {
return "rpm" return "rpm"
} }
func (z *Zypper) SetRootCmd(s string) {
z.rootCmd = s
}
func (z *Zypper) Sync(opts *Opts) error { func (z *Zypper) Sync(opts *Opts) error {
opts = ensureOpts(opts) opts = ensureOpts(opts)
cmd := z.getCmd(opts, "zypper", "refresh") cmd := z.getCmd(opts, "zypper", "refresh")
@@ -109,20 +113,3 @@ func (z *Zypper) UpgradeAll(opts *Opts) error {
} }
return nil return nil
} }
func (z *Zypper) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
var cmd *exec.Cmd
if opts.AsRoot {
cmd = exec.Command(getRootCmd(z.rootCmd), mgrCmd)
cmd.Args = append(cmd.Args, opts.Args...)
cmd.Args = append(cmd.Args, args...)
} else {
cmd = exec.Command(mgrCmd, args...)
}
if opts.NoConfirm {
cmd.Args = append(cmd.Args, "-y")
}
return cmd
}

View File

@@ -79,26 +79,22 @@ type PackageInfo struct {
} }
func (inf *PackageInfo) ToPackage(repoName string) *db.Package { func (inf *PackageInfo) ToPackage(repoName string) *db.Package {
return &db.Package{ pkg := EmptyPackage(repoName)
Version: inf.Version, pkg.Version = inf.Version
Release: inf.Release, pkg.Release = inf.Release
Epoch: inf.Epoch, pkg.Epoch = inf.Epoch
Architectures: inf.Architectures, pkg.Architectures = inf.Architectures
Licenses: inf.Licenses, pkg.Licenses = inf.Licenses
Provides: inf.Provides, pkg.Provides = inf.Provides
Conflicts: inf.Conflicts, pkg.Conflicts = inf.Conflicts
Replaces: inf.Replaces, pkg.Replaces = inf.Replaces
Description: db.NewJSON(map[string]string{}), return pkg
Homepage: db.NewJSON(map[string]string{}),
Maintainer: db.NewJSON(map[string]string{}),
Depends: db.NewJSON(map[string][]string{}),
BuildDepends: db.NewJSON(map[string][]string{}),
Repository: repoName,
}
} }
func EmptyPackage(repoName string) *db.Package { func EmptyPackage(repoName string) *db.Package {
return &db.Package{ return &db.Package{
Group: db.NewJSON(map[string]string{}),
Summary: db.NewJSON(map[string]string{}),
Description: db.NewJSON(map[string]string{}), Description: db.NewJSON(map[string]string{}),
Homepage: db.NewJSON(map[string]string{}), Homepage: db.NewJSON(map[string]string{}),
Maintainer: db.NewJSON(map[string]string{}), Maintainer: db.NewJSON(map[string]string{}),
@@ -114,6 +110,8 @@ var overridable = map[string]string{
"desc": "Description", "desc": "Description",
"homepage": "Homepage", "homepage": "Homepage",
"maintainer": "Maintainer", "maintainer": "Maintainer",
"group": "Group",
"summary": "Summary",
} }
func resolveOverrides(runner *interp.Runner, pkg *db.Package) { func resolveOverrides(runner *interp.Runner, pkg *db.Package) {

134
repo.go
View File

@@ -20,7 +20,6 @@
package main package main
import ( import (
"log/slog"
"os" "os"
"path/filepath" "path/filepath"
@@ -28,11 +27,10 @@ import (
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"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/cliutils"
database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db" appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder"
"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/internal/utils" "gitea.plemya-x.ru/Plemya-x/ALR/internal/utils"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos"
) )
func AddRepoCmd() *cli.Command { func AddRepoCmd() *cli.Command {
@@ -55,32 +53,32 @@ func AddRepoCmd() *cli.Command {
}, },
}, },
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
err := utils.ExitIfNotRoot() if err := utils.ExitIfNotRoot(); err != nil {
if err != nil {
return err return err
} }
ctx := c.Context
name := c.String("name") name := c.String("name")
repoURL := c.String("url") repoURL := c.String("url")
cfg := config.New() ctx := c.Context
err = cfg.Load()
deps, err := appbuilder.
New(ctx).
WithConfig().
Build()
if err != nil { if err != nil {
slog.Error(gotext.Get("Error loading config"), "err", err) return err
os.Exit(1)
} }
defer deps.Defer()
cfg := deps.Cfg
reposSlice := cfg.Repos() reposSlice := cfg.Repos()
for _, repo := range reposSlice { for _, repo := range reposSlice {
if repo.URL == repoURL { if repo.URL == repoURL || repo.Name == name {
slog.Error("Repo already exists", "name", repo.Name) return cliutils.FormatCliExit(gotext.Get("Repo \"%s\" already exists", repo.Name), nil)
os.Exit(1)
} }
} }
reposSlice = append(reposSlice, types.Repo{ reposSlice = append(reposSlice, types.Repo{
Name: name, Name: name,
URL: repoURL, URL: repoURL,
@@ -89,27 +87,23 @@ func AddRepoCmd() *cli.Command {
err = cfg.SaveUserConfig() err = cfg.SaveUserConfig()
if err != nil { if err != nil {
slog.Error(gotext.Get("Error saving config"), "err", err) return cliutils.FormatCliExit(gotext.Get("Error saving config"), err)
os.Exit(1)
} }
if utils.DropCapsToAlrUser() != nil { if err := utils.ExitIfCantDropCapsToAlrUserNoPrivs(); err != nil {
slog.Error(gotext.Get("Can't drop privileges")) return err
os.Exit(1)
} }
db := database.New(cfg) deps, err = appbuilder.
err = db.Init(ctx) New(ctx).
UseConfig(cfg).
WithDB().
WithReposForcePull().
Build()
if err != nil { if err != nil {
slog.Error(gotext.Get("Error pulling repos"), "err", err) return err
}
rs := repos.New(cfg, db)
err = rs.Pull(ctx, cfg.Repos())
if err != nil {
slog.Error(gotext.Get("Error pulling repos"), "err", err)
os.Exit(1)
} }
defer deps.Defer()
return nil return nil
}, },
@@ -130,20 +124,24 @@ func RemoveRepoCmd() *cli.Command {
}, },
}, },
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
err := utils.ExitIfNotRoot() if err := utils.ExitIfNotRoot(); err != nil {
if err != nil {
return err return err
} }
ctx := c.Context ctx := c.Context
name := c.String("name") name := c.String("name")
cfg := config.New()
err = cfg.Load() deps, err := appbuilder.
New(ctx).
WithConfig().
Build()
if err != nil { if err != nil {
slog.Error(gotext.Get("Error loading config"), "err", err) return err
os.Exit(1)
} }
defer deps.Defer()
cfg := deps.Cfg
found := false found := false
index := 0 index := 0
@@ -155,33 +153,37 @@ func RemoveRepoCmd() *cli.Command {
} }
} }
if !found { if !found {
slog.Error(gotext.Get("Repo does not exist"), "name", name) return cliutils.FormatCliExit(gotext.Get("Repo \"%s\" does not exist", name), nil)
os.Exit(1)
} }
cfg.SetRepos(slices.Delete(reposSlice, index, index+1)) cfg.SetRepos(slices.Delete(reposSlice, index, index+1))
err = os.RemoveAll(filepath.Join(cfg.GetPaths().RepoDir, name)) err = os.RemoveAll(filepath.Join(cfg.GetPaths().RepoDir, name))
if err != nil { if err != nil {
slog.Error(gotext.Get("Error removing repo directory"), "err", err) return cliutils.FormatCliExit(gotext.Get("Error removing repo directory"), err)
os.Exit(1)
} }
err = cfg.SaveUserConfig() err = cfg.SaveUserConfig()
if err != nil { if err != nil {
slog.Error(gotext.Get("Error saving config"), "err", err) return cliutils.FormatCliExit(gotext.Get("Error saving config"), err)
os.Exit(1)
} }
db := database.New(cfg) if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil {
err = db.Init(ctx) return err
if err != nil {
os.Exit(1)
} }
err = db.DeletePkgs(ctx, "repository = ?", name)
deps, err = appbuilder.
New(ctx).
UseConfig(cfg).
WithDB().
Build()
if err != nil { if err != nil {
slog.Error(gotext.Get("Error removing packages from database"), "err", err) return err
os.Exit(1) }
defer deps.Defer()
err = deps.DB.DeletePkgs(ctx, "repository = ?", name)
if err != nil {
return cliutils.FormatCliExit(gotext.Get("Error removing packages from database"), err)
} }
return nil return nil
@@ -195,30 +197,22 @@ func RefreshCmd() *cli.Command {
Usage: gotext.Get("Pull all repositories that have changed"), Usage: gotext.Get("Pull all repositories that have changed"),
Aliases: []string{"ref"}, Aliases: []string{"ref"},
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
if utils.DropCapsToAlrUser() != nil { if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil {
slog.Error(gotext.Get("Can't drop privileges")) return err
os.Exit(1)
} }
ctx := c.Context ctx := c.Context
cfg := config.New()
err := cfg.Load()
if err != nil {
slog.Error(gotext.Get("Error loading config"), "err", err)
os.Exit(1)
}
db := database.New(cfg) deps, err := appbuilder.
err = db.Init(ctx) New(ctx).
WithConfig().
WithDB().
WithReposForcePull().
Build()
if err != nil { if err != nil {
os.Exit(1) return err
}
rs := repos.New(cfg, db)
err = rs.Pull(ctx, cfg.Repos())
if err != nil {
slog.Error(gotext.Get("Error pulling repos"), "err", err)
os.Exit(1)
} }
defer deps.Defer()
return nil return nil
}, },
} }

View File

@@ -96,54 +96,52 @@ if [ -z "$noPkgMgr" ]; then
echo "Полученный список файлов:" echo "Полученный список файлов:"
echo "$fileList" echo "$fileList"
if [ "$pkgMgr" == "pacman" ]; then if [ "$pkgMgr" == "pacman" ]; then
latestFile=$(echo "$fileList" | grep -E 'alr-bin-.*\.pkg\.tar\.zst' | sort -V | tail -n 1) latestFile=$(echo "$fileList" | grep -E 'alr-bin-.*\.pkg\.tar\.zst' | sort -V | tail -n 1)
elif [ "$pkgMgr" == "apt" ]; then elif [ "$pkgMgr" == "apt" ]; then
latestFile=$(echo "$fileList" | grep -E 'alr-bin-.*\.amd64\.deb' | sort -V | tail -n 1) latestFile=$(echo "$fileList" | grep -E 'alr-bin-.*\.amd64\.deb' | sort -V | tail -n 1)
elif [[ "$pkgMgr" == "dnf" || "$pkgMgr" == "yum" || "$pkgMgr" == "zypper" ]]; then
latestFile=$(echo "$fileList" | grep -E 'alr-bin-.*\.x86_64\.rpm' | grep -v 'alt1' | sort -V | tail -n 1)
elif [ "$pkgMgr" == "apt-get" ]; then elif [ "$pkgMgr" == "apt-get" ]; then
latestFile=$(echo "$fileList" | grep -E 'alr-bin-.*-alt[0-9]+\.x86_64\.rpm' | sort -V | tail -n 1) latestFile=$(echo "$fileList" | grep -E 'alr-bin-.*-alt[0-9]+\.x86_64\.rpm' | sort -V | tail -n 1)
elif [[ "$pkgMgr" == "dnf" || "$pkgMgr" == "yum" || "$pkgMgr" == "zypper" ]]; then
latestFile=$(echo "$fileList" | grep -E 'alr-bin-.*\.x86_64\.rpm' | sort -V | tail -n 1)
fi
else
error "Не поддерживаемый менеджер пакетов для автоматической установки"
fi
if [ -z "$latestFile" ]; then
error "Не удалось найти соответствующий пакет для $pkgMgr"
fi
info "Найдена последняя версия ALR: $latestFile"
url="https://plemya-x.ru/$latestFile"
fname="$(mktemp -u -p /tmp "alr.XXXXXXXXXX").${pkgFormat}"
info "Загрузка пакета ALR"
curl -L $url -o $fname
if [ ! -f "$fname" ]; then
error "Ошибка загрузки пакета ALR"
fi
info "Установка пакета ALR"
installPkg $pkgMgr $fname
info "Очистка"
rm $fname
info "Готово!"
else else
info "Клонирование репозитория ALR" error "Не поддерживаемый менеджер пакетов для автоматической установки"
git clone https://gitea.plemya-x.ru/xpamych/ALR.git /tmp/alr fi
info "Установка ALR" if [ -z "$latestFile" ]; then
cd /tmp/alr error "Не удалось найти соответствующий пакет для $pkgMgr"
sudo make install fi
info "Очистка репозитория ALR" info "Найдена последняя версия ALR: $latestFile"
rm -rf /tmp/alr
url="https://plemya-x.ru/$latestFile"
info "Все задачи выполнены!" fname="$(mktemp -u -p /tmp "alr.XXXXXXXXXX").${pkgFormat}"
info "Загрузка пакета ALR"
curl -L $url -o $fname
if [ ! -f "$fname" ]; then
error "Ошибка загрузки пакета ALR"
fi
info "Установка пакета ALR"
installPkg $pkgMgr $fname
info "Очистка"
rm $fname
info "Готово!"
else
info "Клонирование репозитория ALR"
git clone https://gitea.plemya-x.ru/xpamych/ALR.git /tmp/alr
info "Установка ALR"
cd /tmp/alr
sudo make install
info "Очистка репозитория ALR"
rm -rf /tmp/alr
info "Все задачи выполнены!"
fi fi

View File

@@ -0,0 +1,58 @@
#!/bin/bash
# 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/>.
VULNS_FILE="vulns.json"
COMMIT_MSG_FILE="commit_msg.txt"
echo "Scanning for vulnerabilities with Trivy..."
trivy fs --scanners vuln --format json . > "$VULNS_FILE"
echo "security: update vulnerable packages" > "$COMMIT_MSG_FILE"
echo "" >> "$COMMIT_MSG_FILE"
echo "Vulnerabilities detected by Trivy scan:" >> "$COMMIT_MSG_FILE"
echo "Processing vulnerabilities..."
jq -r '
.Results[].Vulnerabilities[] |
select(.PkgName and .FixedVersion) |
"\(.PkgName)|\(.FixedVersion)|\(.VulnerabilityID)"
' "$VULNS_FILE" | sort | uniq | while IFS="|" read -r pkg version cve; do
echo "- ${pkg} (${cve})" >> "$COMMIT_MSG_FILE"
echo "Updating ${pkg} to v${version} (${cve})..."
go get "${pkg}@v${version}" || echo "Failed to update ${pkg}"
done
echo "Running go mod tidy..."
go mod tidy
echo "Verifying fixes..."
trivy fs --scanners vuln .
echo ""
echo "Suggested commit message:"
echo "------------------------"
cat "$COMMIT_MSG_FILE"
echo "------------------------"
rm "$VULNS_FILE"
git add go.mod go.sum
echo ""
echo "To commit these changes, run:"
echo "git commit -a -F $(pwd)/$COMMIT_MSG_FILE"

View File

@@ -18,15 +18,19 @@ package main
import ( import (
"fmt" "fmt"
"log/slog"
"os" "os"
"text/template" "text/template"
"github.com/jeandeaual/go-locale"
"github.com/leonelquinteros/gotext" "github.com/leonelquinteros/gotext"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils"
appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder" appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder"
"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/utils" "gitea.plemya-x.ru/Plemya-x/ALR/internal/utils"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/search" "gitea.plemya-x.ru/Plemya-x/ALR/pkg/search"
) )
@@ -36,6 +40,11 @@ func SearchCmd() *cli.Command {
Usage: gotext.Get("Search packages"), Usage: gotext.Get("Search packages"),
Aliases: []string{"s"}, Aliases: []string{"s"},
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.BoolFlag{
Name: "all",
Aliases: []string{"a"},
Usage: gotext.Get("Show all information, not just for the current distro"),
},
&cli.StringFlag{ &cli.StringFlag{
Name: "name", Name: "name",
Aliases: []string{"n"}, Aliases: []string{"n"},
@@ -63,12 +72,37 @@ func SearchCmd() *cli.Command {
}, },
}, },
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil { if err := utils.ExitIfCantDropCapsToAlrUserNoPrivs(); err != nil {
return err return err
} }
ctx := c.Context ctx := c.Context
var names []string
all := c.Bool("all")
systemLang, err := locale.GetLanguage()
if err != nil {
return cliutils.FormatCliExit(gotext.Get("Can't detect system language"), err)
}
if systemLang == "" {
systemLang = "en"
}
if !all {
info, err := distro.ParseOSRelease(ctx)
if err != nil {
return cliutils.FormatCliExit(gotext.Get("Error parsing os-release file"), err)
}
names, err = overrides.Resolve(
info,
overrides.DefaultOpts.
WithLanguages([]string{systemLang}),
)
if err != nil {
return cliutils.FormatCliExit(gotext.Get("Error resolving overrides"), err)
}
}
deps, err := appbuilder. deps, err := appbuilder.
New(ctx). New(ctx).
WithConfig(). WithConfig().
@@ -79,9 +113,9 @@ func SearchCmd() *cli.Command {
} }
defer deps.Defer() defer deps.Defer()
db := deps.DB database := deps.DB
s := search.New(db) s := search.New(database)
packages, err := s.Search( packages, err := s.Search(
ctx, ctx,
@@ -93,8 +127,7 @@ func SearchCmd() *cli.Command {
Build(), Build(),
) )
if err != nil { if err != nil {
slog.Error(gotext.Get("Error while executing search")) return cliutils.FormatCliExit(gotext.Get("Error while executing search"), err)
return cli.Exit(err, 1)
} }
format := c.String("format") format := c.String("format")
@@ -102,21 +135,31 @@ func SearchCmd() *cli.Command {
if format != "" { if format != "" {
tmpl, err = template.New("format").Parse(format) tmpl, err = template.New("format").Parse(format)
if err != nil { if err != nil {
slog.Error(gotext.Get("Error parsing format template")) return cliutils.FormatCliExit(gotext.Get("Error parsing format template"), err)
return cli.Exit(err, 1)
} }
} }
for _, dbPkg := range packages { for _, dbPkg := range packages {
var pkg any
if !all {
pkg = overrides.ResolvePackage(&dbPkg, names)
} else {
pkg = &dbPkg
}
if tmpl != nil { if tmpl != nil {
err = tmpl.Execute(os.Stdout, dbPkg) err = tmpl.Execute(os.Stdout, pkg)
if err != nil { if err != nil {
slog.Error(gotext.Get("Error executing template")) return cliutils.FormatCliExit(gotext.Get("Error executing template"), err)
return cli.Exit(err, 1)
} }
fmt.Println() fmt.Println()
} else { } else {
fmt.Println(dbPkg.Name) switch v := pkg.(type) {
case *overrides.ResolvedPackage:
fmt.Println(v.Name)
case *db.Package:
fmt.Println(v.Name)
}
} }
} }

View File

@@ -23,14 +23,14 @@ import (
"context" "context"
"fmt" "fmt"
"log/slog" "log/slog"
"os"
"github.com/leonelquinteros/gotext" "github.com/leonelquinteros/gotext"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"go.elara.ws/vercmp" "go.elara.ws/vercmp"
"golang.org/x/exp/maps" "golang.org/x/exp/maps"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils"
appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder"
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/internal/overrides" "gitea.plemya-x.ru/Plemya-x/ALR/internal/overrides"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" "gitea.plemya-x.ru/Plemya-x/ALR/internal/types"
@@ -38,7 +38,6 @@ import (
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/build" "gitea.plemya-x.ru/Plemya-x/ALR/pkg/build"
"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/manager" "gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/search" "gitea.plemya-x.ru/Plemya-x/ALR/pkg/search"
) )
@@ -55,61 +54,59 @@ func UpgradeCmd() *cli.Command {
}, },
}, },
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
err := utils.DropCapsToAlrUser() if err := utils.ExitIfNotRoot(); err != nil {
if err != nil { return err
slog.Error(gotext.Get("Error dropping capabilities"), "err", err)
os.Exit(1)
} }
if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil {
return err
}
installer, installerClose, err := build.GetSafeInstaller()
if err != nil {
return err
}
defer installerClose()
if err := utils.ExitIfCantSetNoNewPrivs(); err != nil {
return err
}
scripter, scripterClose, err := build.GetSafeScriptExecutor()
if err != nil {
return err
}
defer scripterClose()
ctx := c.Context ctx := c.Context
cfg := config.New() deps, err := appbuilder.
err = cfg.Load() New(ctx).
WithConfig().
WithDB().
WithRepos().
WithDistroInfo().
WithManager().
Build()
if err != nil { if err != nil {
slog.Error(gotext.Get("Error loading config"), "err", err) return err
os.Exit(1)
} }
defer deps.Defer()
db := database.New(cfg) builder, err := build.NewMainBuilder(
rs := repos.New(cfg, db) deps.Cfg,
err = db.Init(ctx) deps.Manager,
if err != nil { deps.Repos,
slog.Error(gotext.Get("Error initialization database"), "err", err) scripter,
os.Exit(1) installer,
}
slog.Debug("builder setup")
builder := build.NewMainBuilder(
cfg,
rs,
) )
info, err := distro.ParseOSRelease(ctx)
slog.Debug("ParseOSRelease", "err", err)
if err != nil { if err != nil {
slog.Error(gotext.Get("Error parsing os-release file"), "err", err) return err
os.Exit(1)
} }
mgr := manager.Detect() updates, err := checkForUpdates(ctx, deps.Manager, deps.DB, deps.Info)
if mgr == nil {
slog.Error(gotext.Get("Unable to detect a supported package manager on the system"))
os.Exit(1)
}
if cfg.AutoPull() {
slog.Debug("autopull")
err = rs.Pull(ctx, cfg.Repos())
if err != nil {
slog.Error(gotext.Get("Error pulling repos"), "err", err)
os.Exit(1)
}
}
updates, err := checkForUpdates(ctx, mgr, cfg, db, rs, info)
if err != nil { if err != nil {
slog.Error(gotext.Get("Error checking for updates"), "err", err) return cliutils.FormatCliExit(gotext.Get("Error checking for updates"), err)
os.Exit(1)
} }
if len(updates) > 0 { if len(updates) > 0 {
@@ -120,14 +117,13 @@ func UpgradeCmd() *cli.Command {
Clean: c.Bool("clean"), Clean: c.Bool("clean"),
Interactive: c.Bool("interactive"), Interactive: c.Bool("interactive"),
}, },
Info: info, Info: deps.Info,
PkgFormat_: build.GetPkgFormat(mgr), PkgFormat_: build.GetPkgFormat(deps.Manager),
}, },
updates, updates,
) )
if err != nil { if err != nil {
slog.Error(gotext.Get("Error checking for updates"), "err", err) return cliutils.FormatCliExit(gotext.Get("Error checking for updates"), err)
os.Exit(1)
} }
} else { } else {
slog.Info(gotext.Get("There is nothing to do.")) slog.Info(gotext.Get("There is nothing to do."))
@@ -141,9 +137,7 @@ func UpgradeCmd() *cli.Command {
func checkForUpdates( func checkForUpdates(
ctx context.Context, ctx context.Context,
mgr manager.Manager, mgr manager.Manager,
cfg *config.ALRConfig,
db *database.Database, db *database.Database,
rs *repos.Repos,
info *distro.OSRelease, info *distro.OSRelease,
) ([]database.Package, error) { ) ([]database.Package, error) {
installed, err := mgr.ListInstalled(nil) installed, err := mgr.ListInstalled(nil)