Compare commits

...

22 Commits

Author SHA1 Message Date
3ce9f0db35 Merge pull request 'fix install bash completion' (#63) from Maks1mS/ALR:fix-install-bash-complete into master
Reviewed-on: #63
2025-03-30 12:51:36 +00:00
469a05105a test: add bash completion test 2025-03-30 13:10:48 +03:00
70aca61750 fix install bash complete 2025-03-30 13:09:34 +03:00
4b35f5e4e6 Исправление фильтрации пакетов в зависимости от дистрибутива. 2025-03-28 11:17:58 +03:00
7770675a8d Merge pull request 'fix: add interactive=false handling in remove command' (#60) from Maks1mS/ALR:fix/32 into master
Reviewed-on: #60
2025-03-26 07:28:06 +00:00
bd79d56776 fix: add interactive=false handling in remove command 2025-03-26 10:22:56 +03:00
fff8b777fe Merge pull request 'fix installing multiple packages' (#58) from Maks1mS/ALR:fix/50 into master
Reviewed-on: #58
2025-03-26 07:14:18 +00:00
6bee268ea9 fix installing multiple packages 2025-03-26 10:11:24 +03:00
4b53e819d8 Merge pull request 'chore: enable autoPull' (#57) from Maks1mS/ALR:enable-autopull into master
Reviewed-on: #57
 [Maks1mS]
chore: enable autoPull
2025-03-23 10:30:34 +00:00
6c0e8aeb3f chore: enable autoPull 2025-03-23 13:28:33 +03:00
cbc6b9f452 Merge pull request 'Update config module' (#56) from Maks1mS/ALR:fix/update-config-module into master
Reviewed-on: #56

    Конфигурация явно загружается методом Load, убрана лишняя передача контекста в методы config.

    Конфигурация определяется как комбинация из нескольких источников (частично решает задачу #55):

    defaultConfig

    /etc/alr/alr.toml

    $HOME/.config/alr.toml

    ENV переменных

    2.1. addrepo / removerepo сохраняют конфигурацию в $HOME/.config/alr.toml

    Добавлена возможность задать уровень логов (закрывает #49). Теперь при дальнейшей работе в старые и новые методы желательно добавлять debug логирование.
2025-03-22 12:47:16 +00:00
c705c25613 fix: add config load and directory creation 2025-03-22 13:41:41 +03:00
8f4b021a93 update config module 2025-03-22 12:58:10 +03:00
5e7d4033e4 Merge pull request 'fix: save config after removerepo and LC_ALL=C for info command' (#54) from Maks1mS/ALR:fix-53-and-removerepo into master
Reviewed-on: #54
2025-03-19 05:46:17 +00:00
4ac2432770 fix: removerepo and LC_ALL=C for info command 2025-03-19 08:26:53 +03:00
3c37310f0d Merge pull request 'test: add e2e tests' (#51) from Maks1mS/ALR:tests/add-e2e-test into master
Reviewed-on: #51
2025-03-15 10:17:38 +00:00
d300ab197b test: add e2e tests 2025-03-15 12:52:56 +03:00
eb2cc3c1e6 Merge pull request 'fix: add handling len(pkgs) == 0' (#48) from Maks1mS/ALR:fix/handle-notfound-package into master
Reviewed-on: #48
2025-03-14 16:40:24 +00:00
7a3acfe5c1 fix: add handling len(pkgs) == 0 2025-03-14 19:38:58 +03:00
9cf8af08ab Merge pull request 'fix: remove duplicates correctly' (#46) from Maks1mS/ALR:fix/remove-duplicates-correctly into master
Reviewed-on: #46
2025-03-13 13:44:40 +00:00
86940e8962 tests: add TestRemoveDuplicatesSources 2025-03-13 16:38:36 +03:00
db244204c7 fix: remove duplicates correctly 2025-03-13 16:24:37 +03:00
49 changed files with 1206 additions and 432 deletions

4
.gitignore vendored

@ -7,4 +7,6 @@
.idea .idea
.gigaide .gigaide
*.out *.out
e2e-tests/alr

@ -71,3 +71,8 @@ i18n:
test-coverage: 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
e2e-test: clean build
rm -f ./e2e-tests/alr
cp alr e2e-tests
go test -tags=e2e ./...

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

Before

Width:  |  Height:  |  Size: 926 B

After

Width:  |  Height:  |  Size: 926 B

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

Before

Width:  |  Height:  |  Size: 942 B

After

Width:  |  Height:  |  Size: 940 B

@ -68,9 +68,15 @@ func BuildCmd() *cli.Command {
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
ctx := c.Context ctx := c.Context
cfg := config.New() 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) db := database.New(cfg)
rs := repos.New(cfg, db) rs := repos.New(cfg, db)
err := db.Init(ctx) err = db.Init(ctx)
if err != nil { if err != nil {
slog.Error(gotext.Get("Error initialization database"), "err", err) slog.Error(gotext.Get("Error initialization database"), "err", err)
os.Exit(1) os.Exit(1)
@ -80,7 +86,7 @@ func BuildCmd() *cli.Command {
var packages []string var packages []string
repository := "default" repository := "default"
repoDir := cfg.GetPaths(ctx).RepoDir repoDir := cfg.GetPaths().RepoDir
switch { switch {
case c.IsSet("script"): case c.IsSet("script"):
@ -118,8 +124,8 @@ func BuildCmd() *cli.Command {
} }
// Проверка автоматического пулла репозиториев // Проверка автоматического пулла репозиториев
if cfg.AutoPull(ctx) { if cfg.AutoPull() {
err := rs.Pull(ctx, cfg.Repos(ctx)) err := rs.Pull(ctx, cfg.Repos())
if err != nil { if err != nil {
slog.Error(gotext.Get("Error pulling repositories"), "err", err) slog.Error(gotext.Get("Error pulling repositories"), "err", err)
os.Exit(1) os.Exit(1)

70
e2e-tests/addrepo_test.go Normal file

@ -0,0 +1,70 @@
// 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 (
"bytes"
"testing"
"github.com/efficientgo/e2e"
"github.com/stretchr/testify/assert"
)
func TestE2EAlrAddRepo(t *testing.T) {
dockerMultipleRun(
t,
"add-repo-remove-repo",
COMMON_SYSTEMS,
func(t *testing.T, r e2e.Runnable) {
err := r.Exec(e2e.NewCommand(
"alr",
"addrepo",
"--name",
"alr-repo",
"--url",
"https://gitea.plemya-x.ru/Plemya-x/alr-repo.git",
))
assert.NoError(t, err)
err = r.Exec(e2e.NewCommand(
"bash",
"-c",
"cat $HOME/.config/alr/alr.toml",
))
assert.NoError(t, err)
err = r.Exec(e2e.NewCommand(
"alr",
"removerepo",
"--name",
"alr-repo",
))
assert.NoError(t, err)
var buf bytes.Buffer
err = r.Exec(e2e.NewCommand(
"bash",
"-c",
"cat $HOME/.config/alr/alr.toml",
), e2e.WithExecOptionStdout(&buf))
assert.NoError(t, err)
assert.Contains(t, buf.String(), "rootCmd")
},
)
}

@ -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/>.
//go:build e2e
package e2etests_test
import (
"testing"
"github.com/alecthomas/assert/v2"
"github.com/efficientgo/e2e"
)
func TestE2EBashCompletion(t *testing.T) {
dockerMultipleRun(
t,
"bash-completion",
COMMON_SYSTEMS,
func(t *testing.T, r e2e.Runnable) {
err := r.Exec(e2e.NewCommand(
"alr", "install", "--generate-bash-completion",
))
assert.NoError(t, err)
},
)
}

180
e2e-tests/common_test.go Normal file

@ -0,0 +1,180 @@
// 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 (
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"log"
"os"
"os/exec"
"testing"
"time"
"github.com/efficientgo/e2e"
"github.com/stretchr/testify/assert"
expect "github.com/tailscale/goexpect"
)
// DebugWriter оборачивает io.Writer и логирует все записываемые данные.
type DebugWriter struct {
prefix string
writer io.Writer
}
func (d *DebugWriter) Write(p []byte) (n int, err error) {
log.Printf("%s: Writing data: %q", d.prefix, p) // Логируем данные
return d.writer.Write(p)
}
// DebugReader оборачивает io.Reader и логирует все читаемые данные.
type DebugReader struct {
prefix string
reader io.Reader
}
func (d *DebugReader) Read(p []byte) (n int, err error) {
n, err = d.reader.Read(p)
if n > 0 {
log.Printf("%s: Read data: %q", d.prefix, p[:n]) // Логируем данные
}
return n, err
}
func e2eSpawn(runnable e2e.Runnable, command e2e.Command, timeout time.Duration, opts ...expect.Option) (expect.Expecter, <-chan error, error, *io.PipeWriter) {
resCh := make(chan error)
// Создаем pipe для stdin и stdout
stdinReader, stdinWriter := io.Pipe()
stdoutReader, stdoutWriter := io.Pipe()
debugStdinReader := &DebugReader{prefix: "STDIN", reader: stdinReader}
debugStdoutWriter := &DebugWriter{prefix: "STDOUT", writer: stdoutWriter}
go func() {
err := runnable.Exec(
command,
e2e.WithExecOptionStdout(debugStdoutWriter),
e2e.WithExecOptionStdin(debugStdinReader),
e2e.WithExecOptionStderr(debugStdoutWriter),
)
resCh <- err
}()
exp, chnErr, err := expect.SpawnGeneric(&expect.GenOptions{
In: stdinWriter,
Out: stdoutReader,
Wait: func() error {
return <-resCh
},
Close: func() error {
stdinWriter.Close()
stdoutReader.Close()
return nil
},
Check: func() bool { return true },
}, timeout, expect.Verbose(true), expect.VerboseWriter(os.Stdout))
return exp, chnErr, err, stdinWriter
}
var ALL_SYSTEMS []string = []string{
"ubuntu-24.04",
// "alt-sisyphus",
// "archlinux",
// "alpine",
// "opensuse-leap",
// "redos-8",
}
var COMMON_SYSTEMS []string = []string{
"ubuntu-24.04",
}
func init() {
for _, id := range ALL_SYSTEMS {
buildAlrTestImage(id)
}
}
func buildAlrTestImage(id string) {
cmd := exec.Command(
"docker",
"build",
"-t", fmt.Sprintf("alr-testimage-%s", id),
"-f", fmt.Sprintf("images/Dockerfile.%s", id),
".",
)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
fmt.Println("Error:", err)
return
}
}
func dockerMultipleRun(t *testing.T, name string, ids []string, f func(t *testing.T, runnable e2e.Runnable)) {
t.Run(name, func(t *testing.T) {
for _, id := range ids {
t.Run(id, func(t *testing.T) {
t.Parallel()
dockerName := fmt.Sprintf("alr-test-%s-%s", name, id)
hash := sha256.New()
hash.Write([]byte(dockerName))
hashSum := hash.Sum(nil)
hashString := hex.EncodeToString(hashSum)
truncatedHash := hashString[:8]
e, err := e2e.New(e2e.WithVerbose(), e2e.WithName(fmt.Sprintf("alr-%s", truncatedHash)))
assert.NoError(t, err)
t.Cleanup(e.Close)
imageId := fmt.Sprintf("alr-testimage-%s", id)
runnable := e.Runnable(dockerName).Init(
e2e.StartOptions{
Image: imageId,
Volumes: []string{
"./alr:/usr/bin/alr",
},
Privileged: true,
},
)
assert.NoError(t, e2e.StartAndWaitReady(runnable))
f(t, runnable)
})
}
})
}
func runTestCommands(t *testing.T, r e2e.Runnable, timeout time.Duration, expects []expect.Batcher) {
exp, _, err, _ := e2eSpawn(
r,
e2e.NewCommand("/bin/bash"), 25*time.Second,
expect.Verbose(true),
)
assert.NoError(t, err)
_, err = exp.ExpectBatch(
expects,
timeout,
)
assert.NoError(t, err)
}

43
e2e-tests/fix_test.go Normal file

@ -0,0 +1,43 @@
// 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"
"time"
"github.com/efficientgo/e2e"
expect "github.com/tailscale/goexpect"
)
func TestE2EAlrFix(t *testing.T) {
dockerMultipleRun(
t,
"run-fix",
COMMON_SYSTEMS,
func(t *testing.T, r e2e.Runnable) {
runTestCommands(t, r, time.Second*30, []expect.Batcher{
&expect.BSnd{S: "alr fix\n"},
&expect.BExp{R: `--> Done`},
&expect.BSnd{S: "echo $?\n"},
&expect.BExp{R: `^0\n$`},
})
},
)
}

@ -0,0 +1,4 @@
FROM alpine:latest
RUN adduser -s /bin/bash alr-user
USER alr-user
ENTRYPOINT ["tail", "-f", "/dev/null"]

@ -0,0 +1,5 @@
FROM registry.altlinux.org/sisyphus/alt:latest
RUN apt-get update && apt-get install -y ca-certificates
RUN useradd -m -s /bin/bash alr-user
USER alr-user
ENTRYPOINT ["tail", "-f", "/dev/null"]

@ -0,0 +1,4 @@
FROM archlinux:latest
RUN useradd -m -s /bin/bash alr-user
USER alr-user
ENTRYPOINT ["tail", "-f", "/dev/null"]

@ -0,0 +1,4 @@
FROM opensuse/leap:latest
RUN useradd -m -s /bin/bash alr-user
USER alr-user
ENTRYPOINT ["tail", "-f", "/dev/null"]

@ -0,0 +1,4 @@
FROM registry.red-soft.ru/ubi8/ubi:latest
RUN useradd -m -s /bin/bash alr-user
USER alr-user
ENTRYPOINT ["tail", "-f", "/dev/null"]

@ -0,0 +1,7 @@
FROM ubuntu:24.10
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates sudo
RUN useradd -m -s /bin/bash alr-user && \
echo "alr-user ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/alr-user && \
chmod 0440 /etc/sudoers.d/alr-user
USER alr-user
ENTRYPOINT ["tail", "-f", "/dev/null"]

@ -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/>.
//go:build e2e
package e2etests_test
import (
"testing"
"github.com/alecthomas/assert/v2"
"github.com/efficientgo/e2e"
)
func TestE2EIssue32Interactive(t *testing.T) {
dockerMultipleRun(
t,
"issue-32-interactive",
COMMON_SYSTEMS,
func(t *testing.T, r e2e.Runnable) {
err := r.Exec(e2e.NewCommand(
"alr", "--interactive=false", "remove", "ca-certificates",
))
assert.NoError(t, err)
},
)
}

@ -0,0 +1,55 @@
// 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 TestE2EIssue50InstallMultiple(t *testing.T) {
dockerMultipleRun(
t,
"issue-50-install-multiple",
COMMON_SYSTEMS,
func(t *testing.T, r e2e.Runnable) {
err := r.Exec(e2e.NewCommand(
"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(
"alr", "in", "foo-pkg", "bar-pkg",
))
assert.NoError(t, err)
err = r.Exec(e2e.NewCommand("cat", "/opt/foo"))
assert.NoError(t, err)
err = r.Exec(e2e.NewCommand("cat", "/opt/bar"))
assert.NoError(t, err)
},
)
}

@ -0,0 +1,52 @@
// 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 TestE2EIssue53LcAllCInfo(t *testing.T) {
dockerMultipleRun(
t,
"issue-53-lc-all-c-info",
COMMON_SYSTEMS,
func(t *testing.T, r e2e.Runnable) {
err := r.Exec(e2e.NewCommand(
"alr",
"addrepo",
"--name",
"alr-repo",
"--url",
"https://gitea.plemya-x.ru/Plemya-x/alr-repo.git",
))
assert.NoError(t, err)
err = r.Exec(e2e.NewCommand(
"bash",
"-c",
"LANG=C alr info alr-bin",
))
assert.NoError(t, err)
},
)
}

44
e2e-tests/version_test.go Normal file

@ -0,0 +1,44 @@
// 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"
"time"
"github.com/efficientgo/e2e"
expect "github.com/tailscale/goexpect"
)
func TestE2EAlrVersion(t *testing.T) {
dockerMultipleRun(
t,
"check-version",
COMMON_SYSTEMS,
func(t *testing.T, r e2e.Runnable) {
runTestCommands(t, r, time.Second*10, []expect.Batcher{
&expect.BSnd{S: "alr version\n"},
&expect.BExp{R: `^v\d+\.\d+\.\d+(?:-\d+-g[a-f0-9]+)?\n$`},
&expect.BSnd{S: "echo $?\n"},
&expect.BExp{R: `^0\n$`},
})
},
)
}

18
fix.go

@ -38,11 +38,17 @@ func FixCmd() *cli.Command {
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
ctx := c.Context ctx := c.Context
cfg := config.New() cfg := config.New()
paths := cfg.GetPaths(ctx) err := cfg.Load()
if err != nil {
slog.Error(gotext.Get("Error loading config"), "err", err)
os.Exit(1)
}
paths := cfg.GetPaths()
slog.Info(gotext.Get("Removing cache directory")) slog.Info(gotext.Get("Removing cache directory"))
err := os.RemoveAll(paths.CacheDir) err = os.RemoveAll(paths.CacheDir)
if err != nil { if err != nil {
slog.Error(gotext.Get("Unable to remove cache directory"), "err", err) slog.Error(gotext.Get("Unable to remove cache directory"), "err", err)
os.Exit(1) os.Exit(1)
@ -57,6 +63,12 @@ func FixCmd() *cli.Command {
} }
cfg = config.New() 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) db := database.New(cfg)
err = db.Init(ctx) err = db.Init(ctx)
if err != nil { if err != nil {
@ -64,7 +76,7 @@ func FixCmd() *cli.Command {
os.Exit(1) os.Exit(1)
} }
rs := repos.New(cfg, db) rs := repos.New(cfg, db)
err = rs.Pull(ctx, cfg.Repos(ctx)) err = rs.Pull(ctx, cfg.Repos())
if err != nil { if err != nil {
slog.Error(gotext.Get("Error pulling repos"), "err", err) slog.Error(gotext.Get("Error pulling repos"), "err", err)
os.Exit(1) os.Exit(1)

8
go.mod

@ -8,11 +8,14 @@ require (
gitea.plemya-x.ru/Plemya-x/fakeroot v0.0.1 gitea.plemya-x.ru/Plemya-x/fakeroot v0.0.1
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/chroma/v2 v2.9.1 github.com/alecthomas/chroma/v2 v2.9.1
github.com/caarlos0/env v3.5.0+incompatible
github.com/charmbracelet/bubbles v0.20.0 github.com/charmbracelet/bubbles v0.20.0
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/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.5.0
github.com/go-git/go-git/v5 v5.12.0 github.com/go-git/go-git/v5 v5.12.0
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
@ -26,6 +29,7 @@ require (
github.com/muesli/reflow v0.3.0 github.com/muesli/reflow v0.3.0
github.com/pelletier/go-toml/v2 v2.1.0 github.com/pelletier/go-toml/v2 v2.1.0
github.com/stretchr/testify v1.10.0 github.com/stretchr/testify v1.10.0
github.com/tailscale/goexpect v0.0.0-20210902213824-6e8c725cea41
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
@ -46,6 +50,7 @@ require (
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.0.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
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb // indirect github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb // indirect
@ -64,6 +69,7 @@ require (
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
github.com/dustin/go-humanize v1.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect
github.com/efficientgo/core v1.0.0-rc.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
@ -71,12 +77,14 @@ require (
github.com/gobwas/glob v0.2.3 // indirect github.com/gobwas/glob v0.2.3 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/snappy v0.0.4 // indirect github.com/golang/snappy v0.0.4 // indirect
github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f // indirect
github.com/google/rpmpack v0.6.1-0.20240329070804-c2247cbb881a // indirect github.com/google/rpmpack v0.6.1-0.20240329070804-c2247cbb881a // indirect
github.com/google/uuid v1.4.0 // indirect github.com/google/uuid v1.4.0 // indirect
github.com/goreleaser/chglog v0.6.1 // indirect github.com/goreleaser/chglog v0.6.1 // indirect
github.com/goreleaser/fileglob v1.3.0 // indirect github.com/goreleaser/fileglob v1.3.0 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hexops/gotextdiff v1.0.3 // indirect
github.com/huandu/xstrings v1.3.3 // indirect github.com/huandu/xstrings v1.3.3 // indirect
github.com/imdario/mergo v0.3.16 // indirect github.com/imdario/mergo v0.3.16 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect

37
go.sum

@ -61,6 +61,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4= github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4=
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI= github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
github.com/bodgit/plumbing v1.2.0 h1:gg4haxoKphLjml+tgnecR4yLBV5zo4HAZGCtAh3xCzM= github.com/bodgit/plumbing v1.2.0 h1:gg4haxoKphLjml+tgnecR4yLBV5zo4HAZGCtAh3xCzM=
@ -70,11 +72,15 @@ github.com/bodgit/sevenzip v1.3.0/go.mod h1:omwNcgZTEooWM8gA/IJ2Nk/+ZQ94+GsytRzO
github.com/bodgit/windows v1.0.0 h1:rLQ/XjsleZvx4fR1tB/UxQrK+SJ2OFHzfPjLWWOhDIA= 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/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= 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/go.mod h1:tdCsowwCzMLdkqRYDlHpZCp2UooDD3MspDBjZ2AD02Y=
github.com/caarlos0/testfs v0.4.4 h1:3PHvzHi5Lt+g332CiShwS8ogTgS3HjrmzZxCm6JCDr8= github.com/caarlos0/testfs v0.4.4 h1:3PHvzHi5Lt+g332CiShwS8ogTgS3HjrmzZxCm6JCDr8=
github.com/caarlos0/testfs v0.4.4/go.mod h1:bRN55zgG4XCUVVHZCeU+/Tz1Q6AxEJOEJTliBy+1DMk= github.com/caarlos0/testfs v0.4.4/go.mod h1:bRN55zgG4XCUVVHZCeU+/Tz1Q6AxEJOEJTliBy+1DMk=
github.com/cavaliergopher/cpio v1.0.1 h1:KQFSeKmZhv0cr+kawA3a0xTQCU4QxXF1vhU7P7av2KM= github.com/cavaliergopher/cpio v1.0.1 h1:KQFSeKmZhv0cr+kawA3a0xTQCU4QxXF1vhU7P7av2KM=
github.com/cavaliergopher/cpio v1.0.1/go.mod h1:pBdaqQjnvXxdS/6CvNDwIANIFSP0xRKI16PX4xejRQc= github.com/cavaliergopher/cpio v1.0.1/go.mod h1:pBdaqQjnvXxdS/6CvNDwIANIFSP0xRKI16PX4xejRQc=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE= github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE=
github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU=
github.com/charmbracelet/bubbletea v1.2.4 h1:KN8aCViA0eps9SCOThb2/XPIlea3ANJLUkv3KnQRNCE= github.com/charmbracelet/bubbletea v1.2.4 h1:KN8aCViA0eps9SCOThb2/XPIlea3ANJLUkv3KnQRNCE=
@ -115,6 +121,10 @@ github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5Jflh
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/efficientgo/core v1.0.0-rc.0 h1:jJoA0N+C4/knWYVZ6GrdHOtDyrg8Y/TR4vFpTaqTsqs=
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/go.mod h1:plsKU0YHE9uX+7utvr7SiDtVBSHJyEfHRO4UnUgDmts=
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
@ -161,6 +171,8 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@ -171,6 +183,8 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f h1:5CjVwnuUcp5adK4gmY6i72gpVFVnZDP2h5TmPScB6u4=
github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f/go.mod h1:nOFQdrUlIlx6M6ODdSpBj1NVA+VgLC6kmw60mkw34H4=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@ -217,6 +231,8 @@ github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08 h1:wMeVzrPO3m
github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o= github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o=
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
@ -262,6 +278,8 @@ github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mholt/archiver/v4 v4.0.0-alpha.8 h1:tRGQuDVPh66WCOelqe6LIGh0gwmfwxUrSSDunscGsRM= github.com/mholt/archiver/v4 v4.0.0-alpha.8 h1:tRGQuDVPh66WCOelqe6LIGh0gwmfwxUrSSDunscGsRM=
@ -282,6 +300,8 @@ github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nwaples/rardecode/v2 v2.0.0-beta.2 h1:e3mzJFJs4k83GXBEiTaQ5HgSc/kOK8q0rDaRO0MPaOk= github.com/nwaples/rardecode/v2 v2.0.0-beta.2 h1:e3mzJFJs4k83GXBEiTaQ5HgSc/kOK8q0rDaRO0MPaOk=
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/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
@ -296,7 +316,15 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk=
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.36.0 h1:78hJTing+BLYLjhXE+Z2BubeEymH5Lr0/Mt8FKkxxYo=
github.com/prometheus/common v0.36.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
@ -338,6 +366,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tailscale/goexpect v0.0.0-20210902213824-6e8c725cea41 h1:/V2rCMMWcsjYaYO2MeovLw+ClP63OtXgCF2Y1eb8+Ns=
github.com/tailscale/goexpect v0.0.0-20210902213824-6e8c725cea41/go.mod h1:/roCdA6gg6lQyw/Oz6gIIGu3ggJKYhF+WC/AQReE5XQ=
github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw= github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY= github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
@ -435,6 +465,8 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr
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=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -540,6 +572,8 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@ -560,6 +594,8 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@ -570,6 +606,7 @@ gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

23
info.go

@ -51,8 +51,14 @@ func InfoCmd() *cli.Command {
BashComplete: func(c *cli.Context) { BashComplete: func(c *cli.Context) {
ctx := c.Context ctx := c.Context
cfg := config.New() 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) db := database.New(cfg)
err := db.Init(ctx) err = db.Init(ctx)
if err != nil { if err != nil {
slog.Error(gotext.Get("Error initialization database"), "err", err) slog.Error(gotext.Get("Error initialization database"), "err", err)
os.Exit(1) os.Exit(1)
@ -80,8 +86,14 @@ func InfoCmd() *cli.Command {
ctx := c.Context ctx := c.Context
cfg := config.New() 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) db := database.New(cfg)
err := db.Init(ctx) err = db.Init(ctx)
if err != nil { if err != nil {
slog.Error(gotext.Get("Error initialization database"), "err", err) slog.Error(gotext.Get("Error initialization database"), "err", err)
os.Exit(1) os.Exit(1)
@ -94,8 +106,8 @@ func InfoCmd() *cli.Command {
os.Exit(1) os.Exit(1)
} }
if cfg.AutoPull(ctx) { if cfg.AutoPull() {
err := rs.Pull(ctx, cfg.Repos(ctx)) err := rs.Pull(ctx, cfg.Repos())
if err != nil { if err != nil {
slog.Error(gotext.Get("Error pulling repos"), "err", err) slog.Error(gotext.Get("Error pulling repos"), "err", err)
os.Exit(1) os.Exit(1)
@ -122,6 +134,9 @@ func InfoCmd() *cli.Command {
slog.Error("Can't detect system language", "err", err) slog.Error("Can't detect system language", "err", err)
os.Exit(1) os.Exit(1)
} }
if systemLang == "" {
systemLang = "en"
}
if !all { if !all {
info, err := distro.ParseOSRelease(ctx) info, err := distro.ParseOSRelease(ctx)

@ -65,16 +65,22 @@ func InstallCmd() *cli.Command {
} }
cfg := config.New() 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) db := database.New(cfg)
rs := repos.New(cfg, db) rs := repos.New(cfg, db)
err := db.Init(ctx) err = db.Init(ctx)
if err != nil { if err != nil {
slog.Error(gotext.Get("Error initialization database"), "err", err) slog.Error(gotext.Get("Error initialization database"), "err", err)
os.Exit(1) os.Exit(1)
} }
if cfg.AutoPull(ctx) { if cfg.AutoPull() {
err := rs.Pull(ctx, cfg.Repos(ctx)) err := rs.Pull(ctx, cfg.Repos())
if err != nil { if err != nil {
slog.Error(gotext.Get("Error pulling repositories"), "err", err) slog.Error(gotext.Get("Error pulling repositories"), "err", err)
os.Exit(1) os.Exit(1)
@ -118,8 +124,14 @@ func InstallCmd() *cli.Command {
}, },
BashComplete: func(c *cli.Context) { BashComplete: func(c *cli.Context) {
cfg := config.New() 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) db := database.New(cfg)
err := db.Init(c.Context) err = db.Init(c.Context)
if err != nil { if err != nil {
slog.Error(gotext.Get("Error initialization database"), "err", err) slog.Error(gotext.Get("Error initialization database"), "err", err)
os.Exit(1) os.Exit(1)
@ -163,7 +175,10 @@ func RemoveCmd() *cli.Command {
os.Exit(1) os.Exit(1)
} }
err := mgr.Remove(nil, c.Args().Slice()...) 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) slog.Error(gotext.Get("Error removing packages"), "err", err)
os.Exit(1) os.Exit(1)

@ -20,15 +20,14 @@
package config package config
import ( import (
"context"
"log/slog" "log/slog"
"os" "os"
"path/filepath" "path/filepath"
"sync" "reflect"
"github.com/pelletier/go-toml/v2"
"github.com/caarlos0/env"
"github.com/leonelquinteros/gotext" "github.com/leonelquinteros/gotext"
"github.com/pelletier/go-toml/v2"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" "gitea.plemya-x.ru/Plemya-x/ALR/internal/types"
) )
@ -36,16 +35,13 @@ import (
type ALRConfig struct { type ALRConfig struct {
cfg *types.Config cfg *types.Config
paths *Paths paths *Paths
cfgOnce sync.Once
pathsOnce sync.Once
} }
var defaultConfig = &types.Config{ var defaultConfig = &types.Config{
RootCmd: "sudo", RootCmd: "sudo",
PagerStyle: "native", PagerStyle: "native",
IgnorePkgUpdates: []string{}, IgnorePkgUpdates: []string{},
AutoPull: false, AutoPull: true,
Repos: []types.Repo{}, Repos: []types.Repo{},
} }
@ -53,147 +49,152 @@ func New() *ALRConfig {
return &ALRConfig{} return &ALRConfig{}
} }
func (c *ALRConfig) Load(ctx context.Context) { func readConfig(path string) (*types.Config, error) {
cfgFl, err := os.Open(c.GetPaths(ctx).ConfigPath) file, err := os.Open(path)
if err != nil { if err != nil {
slog.Warn(gotext.Get("Error opening config file, using defaults"), "err", err) return nil, err
c.cfg = defaultConfig
return
} }
defer cfgFl.Close() defer file.Close()
// Copy the default configuration into config config := types.Config{}
defCopy := *defaultConfig
config := &defCopy
config.Repos = nil
err = toml.NewDecoder(cfgFl).Decode(config) if err := toml.NewDecoder(file).Decode(&config); err != nil {
if err != nil { return nil, err
slog.Warn(gotext.Get("Error decoding config file, using defaults"), "err", err)
c.cfg = defaultConfig
return
} }
c.cfg = config
return &config, nil
} }
func (c *ALRConfig) initPaths() { func mergeStructs(dst, src interface{}) {
paths := &Paths{} srcVal := reflect.ValueOf(src)
if srcVal.IsNil() {
return
}
srcVal = srcVal.Elem()
dstVal := reflect.ValueOf(dst).Elem()
for i := range srcVal.NumField() {
srcField := srcVal.Field(i)
srcFieldName := srcVal.Type().Field(i).Name
dstField := dstVal.FieldByName(srcFieldName)
if dstField.IsValid() && dstField.CanSet() {
dstField.Set(srcField)
}
}
}
const systemConfigPath = "/etc/alr/alr.toml"
func (c *ALRConfig) Load() error {
systemConfig, err := readConfig(
systemConfigPath,
)
if err != nil {
slog.Debug("Cannot read system config", "err", err)
}
cfgDir, err := os.UserConfigDir() cfgDir, err := os.UserConfigDir()
if err != nil { if err != nil {
slog.Error(gotext.Get("Unable to detect user config directory"), "err", err) slog.Debug("Cannot read user config directory")
os.Exit(1)
} }
userConfigPath := filepath.Join(cfgDir, "alr", "alr.toml")
paths.ConfigDir = filepath.Join(cfgDir, "alr") userConfig, err := readConfig(
userConfigPath,
err = os.MkdirAll(paths.ConfigDir, 0o755) )
if err != nil { if err != nil {
slog.Error(gotext.Get("Unable to create ALR config directory"), "err", err) slog.Debug("Cannot read user config")
os.Exit(1)
} }
paths.ConfigPath = filepath.Join(paths.ConfigDir, "alr.toml") config := &types.Config{}
if _, err := os.Stat(paths.ConfigPath); err != nil { mergeStructs(config, defaultConfig)
cfgFl, err := os.Create(paths.ConfigPath) mergeStructs(config, systemConfig)
if err != nil { mergeStructs(config, userConfig)
slog.Error(gotext.Get("Unable to create ALR config file"), "err", err) err = env.Parse(config)
os.Exit(1) if err != nil {
} return err
err = toml.NewEncoder(cfgFl).Encode(&defaultConfig)
if err != nil {
slog.Error(gotext.Get("Error encoding default configuration"), "err", err)
os.Exit(1)
}
cfgFl.Close()
} }
c.cfg = config
cacheDir, err := os.UserCacheDir() cacheDir, err := os.UserCacheDir()
if err != nil { if err != nil {
slog.Error(gotext.Get("Unable to detect cache directory"), "err", err) return err
}
c.paths = &Paths{}
c.paths.UserConfigPath = userConfigPath
c.paths.CacheDir = filepath.Join(cacheDir, "alr")
c.paths.RepoDir = filepath.Join(c.paths.CacheDir, "repo")
c.paths.PkgsDir = filepath.Join(c.paths.CacheDir, "pkgs")
c.paths.DBPath = filepath.Join(c.paths.CacheDir, "db")
c.initPaths()
return nil
}
func (c *ALRConfig) RootCmd() string {
return c.cfg.RootCmd
}
func (c *ALRConfig) PagerStyle() string {
return c.cfg.PagerStyle
}
func (c *ALRConfig) AutoPull() bool {
return c.cfg.AutoPull
}
func (c *ALRConfig) AllowRunAsRoot() bool {
return c.cfg.Unsafe.AllowRunAsRoot
}
func (c *ALRConfig) Repos() []types.Repo {
return c.cfg.Repos
}
func (c *ALRConfig) SetRepos(repos []types.Repo) {
c.cfg.Repos = repos
}
func (c *ALRConfig) IgnorePkgUpdates() []string {
return c.cfg.IgnorePkgUpdates
}
func (c *ALRConfig) LogLevel() string {
return c.cfg.LogLevel
}
func (c *ALRConfig) GetPaths() *Paths {
return c.paths
}
func (c *ALRConfig) initPaths() {
err := os.MkdirAll(filepath.Dir(c.paths.UserConfigPath), 0o755)
if err != nil {
slog.Error(gotext.Get("Unable to create config directory"), "err", err)
os.Exit(1) os.Exit(1)
} }
paths.CacheDir = filepath.Join(cacheDir, "alr") err = os.MkdirAll(c.paths.RepoDir, 0o755)
paths.RepoDir = filepath.Join(paths.CacheDir, "repo")
paths.PkgsDir = filepath.Join(paths.CacheDir, "pkgs")
err = os.MkdirAll(paths.RepoDir, 0o755)
if err != nil { if err != nil {
slog.Error(gotext.Get("Unable to create repo cache directory"), "err", err) slog.Error(gotext.Get("Unable to create repo cache directory"), "err", err)
os.Exit(1) os.Exit(1)
} }
err = os.MkdirAll(paths.PkgsDir, 0o755) err = os.MkdirAll(c.paths.PkgsDir, 0o755)
if err != nil { if err != nil {
slog.Error(gotext.Get("Unable to create package cache directory"), "err", err) slog.Error(gotext.Get("Unable to create package cache directory"), "err", err)
os.Exit(1) os.Exit(1)
} }
paths.DBPath = filepath.Join(paths.CacheDir, "db")
c.paths = paths
} }
func (c *ALRConfig) GetPaths(ctx context.Context) *Paths { func (c *ALRConfig) SaveUserConfig() error {
c.pathsOnce.Do(func() { f, err := os.Create(c.paths.UserConfigPath)
c.initPaths() if err != nil {
}) return err
return c.paths }
}
func (c *ALRConfig) Repos(ctx context.Context) []types.Repo {
c.cfgOnce.Do(func() {
c.Load(ctx)
})
return c.cfg.Repos
}
func (c *ALRConfig) SetRepos(ctx context.Context, repos []types.Repo) {
c.cfgOnce.Do(func() {
c.Load(ctx)
})
c.cfg.Repos = repos
}
func (c *ALRConfig) IgnorePkgUpdates(ctx context.Context) []string {
c.cfgOnce.Do(func() {
c.Load(ctx)
})
return c.cfg.IgnorePkgUpdates
}
func (c *ALRConfig) AutoPull(ctx context.Context) bool {
c.cfgOnce.Do(func() {
c.Load(ctx)
})
return c.cfg.AutoPull
}
func (c *ALRConfig) PagerStyle(ctx context.Context) string {
c.cfgOnce.Do(func() {
c.Load(ctx)
})
return c.cfg.PagerStyle
}
func (c *ALRConfig) AllowRunAsRoot(ctx context.Context) bool {
c.cfgOnce.Do(func() {
c.Load(ctx)
})
return c.cfg.Unsafe.AllowRunAsRoot
}
func (c *ALRConfig) RootCmd(ctx context.Context) string {
c.cfgOnce.Do(func() {
c.Load(ctx)
})
return c.cfg.RootCmd
}
func (c *ALRConfig) Save(f *os.File) error {
return toml.NewEncoder(f).Encode(c.cfg) return toml.NewEncoder(f).Encode(c.cfg)
} }

@ -21,10 +21,9 @@ package config
// Paths contains various paths used by ALR // Paths contains various paths used by ALR
type Paths struct { type Paths struct {
ConfigDir string UserConfigPath string
ConfigPath string CacheDir string
CacheDir string RepoDir string
RepoDir string PkgsDir string
PkgsDir string DBPath string
DBPath string
} }

@ -59,7 +59,7 @@ type version struct {
} }
type Config interface { type Config interface {
GetPaths(ctx context.Context) *config.Paths GetPaths() *config.Paths
} }
type Database struct { type Database struct {
@ -82,7 +82,7 @@ func (d *Database) Init(ctx context.Context) error {
} }
func (d *Database) Connect(ctx context.Context) error { func (d *Database) Connect(ctx context.Context) error {
dsn := d.config.GetPaths(ctx).DBPath dsn := d.config.GetPaths().DBPath
db, err := sqlx.Open("sqlite", dsn) db, err := sqlx.Open("sqlite", dsn)
if err != nil { if err != nil {
return err return err

@ -33,7 +33,7 @@ import (
type TestALRConfig struct{} type TestALRConfig struct{}
func (c *TestALRConfig) GetPaths(ctx context.Context) *config.Paths { func (c *TestALRConfig) GetPaths() *config.Paths {
return &config.Paths{ return &config.Paths{
DBPath: ":memory:", DBPath: ":memory:",
} }

@ -38,7 +38,7 @@ import (
type TestALRConfig struct{} type TestALRConfig struct{}
func (c *TestALRConfig) GetPaths(ctx context.Context) *config.Paths { func (c *TestALRConfig) GetPaths() *config.Paths {
return &config.Paths{ return &config.Paths{
CacheDir: "/tmp", CacheDir: "/tmp",
} }

@ -28,7 +28,7 @@ import (
) )
type Config interface { type Config interface {
GetPaths(ctx context.Context) *config.Paths GetPaths() *config.Paths
} }
type DownloadCache struct { type DownloadCache struct {
@ -43,7 +43,7 @@ func New(cfg Config) *DownloadCache {
func (dc *DownloadCache) BasePath(ctx context.Context) string { func (dc *DownloadCache) BasePath(ctx context.Context) string {
return filepath.Join( return filepath.Join(
dc.cfg.GetPaths(ctx).CacheDir, "dl", dc.cfg.GetPaths().CacheDir, "dl",
) )
} }

@ -36,7 +36,7 @@ type TestALRConfig struct {
CacheDir string CacheDir string
} }
func (c *TestALRConfig) GetPaths(ctx context.Context) *config.Paths { func (c *TestALRConfig) GetPaths() *config.Paths {
return &config.Paths{ return &config.Paths{
CacheDir: c.CacheDir, CacheDir: c.CacheDir,
} }

@ -62,6 +62,25 @@ func New() *Logger {
} }
} }
func slogLevelToLog(level slog.Level) log.Level {
switch level {
case slog.LevelDebug:
return log.DebugLevel
case slog.LevelInfo:
return log.InfoLevel
case slog.LevelWarn:
return log.WarnLevel
case slog.LevelError:
return log.ErrorLevel
}
return log.FatalLevel
}
func (l *Logger) SetLevel(level slog.Level) {
l.lOut.(*log.Logger).SetLevel(slogLevelToLog(level))
l.lErr.(*log.Logger).SetLevel(slogLevelToLog(level))
}
func (l *Logger) Enabled(ctx context.Context, level slog.Level) bool { func (l *Logger) Enabled(ctx context.Context, level slog.Level) bool {
if level <= slog.LevelInfo { if level <= slog.LevelInfo {
return l.lOut.Enabled(ctx, level) return l.lOut.Enabled(ctx, level)
@ -90,7 +109,9 @@ func (l *Logger) WithGroup(name string) slog.Handler {
return &sl return &sl
} }
func SetupDefault() { func SetupDefault() *Logger {
logger := slog.New(New()) l := New()
logger := slog.New(l)
slog.SetDefault(logger) slog.SetDefault(logger)
return l
} }

@ -30,35 +30,39 @@ 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:75 #: build.go:73
msgid "Error loading config"
msgstr ""
#: build.go:81
msgid "Error initialization database" msgid "Error initialization database"
msgstr "" msgstr ""
#: build.go:104 #: build.go:110
msgid "Package not found" msgid "Package not found"
msgstr "" msgstr ""
#: build.go:124 #: build.go:130
msgid "Error pulling repositories" msgid "Error pulling repositories"
msgstr "" msgstr ""
#: build.go:132 #: build.go:138
msgid "Unable to detect a supported package manager on the system" msgid "Unable to detect a supported package manager on the system"
msgstr "" msgstr ""
#: build.go:138 #: build.go:144
msgid "Error parsing os release" msgid "Error parsing os release"
msgstr "" msgstr ""
#: build.go:160 #: build.go:166
msgid "Error building package" msgid "Error building package"
msgstr "" msgstr ""
#: build.go:167 #: build.go:173
msgid "Error getting working directory" msgid "Error getting working directory"
msgstr "" msgstr ""
#: build.go:176 #: build.go:182
msgid "Error moving the package" msgid "Error moving the package"
msgstr "" msgstr ""
@ -66,27 +70,27 @@ msgstr ""
msgid "Attempt to fix problems with ALR" msgid "Attempt to fix problems with ALR"
msgstr "" msgstr ""
#: fix.go:43 #: fix.go:49
msgid "Removing cache directory" msgid "Removing cache directory"
msgstr "" msgstr ""
#: fix.go:47 #: fix.go:53
msgid "Unable to remove cache directory" msgid "Unable to remove cache directory"
msgstr "" msgstr ""
#: fix.go:51 #: fix.go:57
msgid "Rebuilding cache" msgid "Rebuilding cache"
msgstr "" msgstr ""
#: fix.go:55 #: fix.go:61
msgid "Unable to create new cache directory" msgid "Unable to create new cache directory"
msgstr "" msgstr ""
#: fix.go:69 #: fix.go:81
msgid "Error pulling repos" msgid "Error pulling repos"
msgstr "" msgstr ""
#: fix.go:73 #: fix.go:85
msgid "Done" msgid "Done"
msgstr "" msgstr ""
@ -122,31 +126,31 @@ msgstr ""
msgid "Show all information, not just for the current distro" msgid "Show all information, not just for the current distro"
msgstr "" msgstr ""
#: info.go:63 #: info.go:69
msgid "Error getting packages" msgid "Error getting packages"
msgstr "" msgstr ""
#: info.go:72 #: info.go:78
msgid "Error iterating over packages" msgid "Error iterating over packages"
msgstr "" msgstr ""
#: info.go:93 #: info.go:105
msgid "Command info expected at least 1 argument, got %d" msgid "Command info expected at least 1 argument, got %d"
msgstr "" msgstr ""
#: info.go:107 #: info.go:119
msgid "Error finding packages" msgid "Error finding packages"
msgstr "" msgstr ""
#: info.go:129 #: info.go:144
msgid "Error parsing os-release file" msgid "Error parsing os-release file"
msgstr "" msgstr ""
#: info.go:138 #: info.go:153
msgid "Error resolving overrides" msgid "Error resolving overrides"
msgstr "" msgstr ""
#: info.go:147 info.go:153 #: info.go:162 info.go:168
msgid "Error encoding script variables" msgid "Error encoding script variables"
msgstr "" msgstr ""
@ -158,15 +162,15 @@ msgstr ""
msgid "Command install expected at least 1 argument, got %d" msgid "Command install expected at least 1 argument, got %d"
msgstr "" msgstr ""
#: install.go:151 #: install.go:163
msgid "Remove an installed package" msgid "Remove an installed package"
msgstr "" msgstr ""
#: install.go:156 #: install.go:168
msgid "Command remove expected at least 1 argument, got %d" msgid "Command remove expected at least 1 argument, got %d"
msgstr "" msgstr ""
#: install.go:168 #: install.go:183
msgid "Error removing packages" msgid "Error removing packages"
msgstr "" msgstr ""
@ -250,39 +254,15 @@ msgstr ""
msgid "OPTIONS" msgid "OPTIONS"
msgstr "" msgstr ""
#: internal/config/config.go:59 #: internal/config/config.go:176
msgid "Error opening config file, using defaults" msgid "Unable to create config directory"
msgstr "" msgstr ""
#: internal/config/config.go:72 #: internal/config/config.go:182
msgid "Error decoding config file, using defaults"
msgstr ""
#: internal/config/config.go:84
msgid "Unable to detect user config directory"
msgstr ""
#: internal/config/config.go:92
msgid "Unable to create ALR config directory"
msgstr ""
#: internal/config/config.go:101
msgid "Unable to create ALR config file"
msgstr ""
#: internal/config/config.go:107
msgid "Error encoding default configuration"
msgstr ""
#: internal/config/config.go:116
msgid "Unable to detect cache directory"
msgstr ""
#: internal/config/config.go:126
msgid "Unable to create repo cache directory" msgid "Unable to create repo cache directory"
msgstr "" msgstr ""
#: internal/config/config.go:132 #: internal/config/config.go:188
msgid "Unable to create package cache directory" msgid "Unable to create package cache directory"
msgstr "" msgstr ""
@ -327,7 +307,7 @@ msgstr ""
msgid "List ALR repo packages" msgid "List ALR repo packages"
msgstr "" msgstr ""
#: list.go:92 #: list.go:98
msgid "Error listing installed packages" msgid "Error listing installed packages"
msgstr "" msgstr ""
@ -343,17 +323,17 @@ msgstr ""
msgid "Enable interactive questions and prompts" msgid "Enable interactive questions and prompts"
msgstr "" msgstr ""
#: main.go:92 #: main.go:96
msgid "" msgid ""
"Running ALR as root is forbidden as it may cause catastrophic damage to your " "Running ALR as root is forbidden as it may cause catastrophic damage to your "
"system" "system"
msgstr "" msgstr ""
#: main.go:125 #: main.go:154
msgid "Show help" msgid "Show help"
msgstr "" msgstr ""
#: main.go:129 #: main.go:158
msgid "Error while running app" msgid "Error while running app"
msgstr "" msgstr ""
@ -365,69 +345,69 @@ msgstr ""
msgid "Building package" msgid "Building package"
msgstr "" msgstr ""
#: pkg/build/build.go:231 #: pkg/build/build.go:208
msgid "The checksums array must be the same length as sources"
msgstr ""
#: pkg/build/build.go:235
msgid "Downloading sources" msgid "Downloading sources"
msgstr "" msgstr ""
#: pkg/build/build.go:253 #: pkg/build/build.go:257
msgid "Building package metadata" msgid "Building package metadata"
msgstr "" msgstr ""
#: pkg/build/build.go:275 #: pkg/build/build.go:279
msgid "Compressing package" msgid "Compressing package"
msgstr "" msgstr ""
#: pkg/build/build.go:434 #: pkg/build/build.go:438
msgid "" msgid ""
"Your system's CPU architecture doesn't match this package. Do you want to " "Your system's CPU architecture doesn't match this package. Do you want to "
"build anyway?" "build anyway?"
msgstr "" msgstr ""
#: pkg/build/build.go:448 #: pkg/build/build.go:452
msgid "This package is already installed" msgid "This package is already installed"
msgstr "" msgstr ""
#: pkg/build/build.go:472 #: pkg/build/build.go:476
msgid "Installing build dependencies" msgid "Installing build dependencies"
msgstr "" msgstr ""
#: pkg/build/build.go:513 #: pkg/build/build.go:521
msgid "Installing dependencies" msgid "Installing dependencies"
msgstr "" msgstr ""
#: pkg/build/build.go:548 #: pkg/build/build.go:602
msgid "The checksums array must be the same length as sources"
msgstr ""
#: pkg/build/build.go:599
msgid "Would you like to remove the build dependencies?" msgid "Would you like to remove the build dependencies?"
msgstr "" msgstr ""
#: pkg/build/build.go:662 #: pkg/build/build.go:665
msgid "Executing prepare()" msgid "Executing prepare()"
msgstr "" msgstr ""
#: pkg/build/build.go:672 #: pkg/build/build.go:675
msgid "Executing build()" msgid "Executing build()"
msgstr "" msgstr ""
#: pkg/build/build.go:702 pkg/build/build.go:722 #: pkg/build/build.go:705 pkg/build/build.go:725
msgid "Executing %s()" msgid "Executing %s()"
msgstr "" msgstr ""
#: pkg/build/build.go:781 #: pkg/build/build.go:784
msgid "Error installing native packages" msgid "Error installing native packages"
msgstr "" msgstr ""
#: pkg/build/build.go:805 #: pkg/build/build.go:808
msgid "Error installing package" msgid "Error installing package"
msgstr "" msgstr ""
#: pkg/build/build.go:864 #: pkg/build/build.go:867
msgid "AutoProv is not implemented for this package format, so it's skipped" msgid "AutoProv is not implemented for this package format, so it's skipped"
msgstr "" msgstr ""
#: pkg/build/build.go:875 #: pkg/build/build.go:878
msgid "AutoReq is not implemented for this package format, so it's skipped" msgid "AutoReq is not implemented for this package format, so it's skipped"
msgstr "" msgstr ""
@ -461,47 +441,43 @@ msgid ""
"updating ALR if something doesn't work." "updating ALR if something doesn't work."
msgstr "" msgstr ""
#: repo.go:41 #: repo.go:40
msgid "Add a new repository" msgid "Add a new repository"
msgstr "" msgstr ""
#: repo.go:48 #: repo.go:47
msgid "Name of the new repo" msgid "Name of the new repo"
msgstr "" msgstr ""
#: repo.go:54 #: repo.go:53
msgid "URL of the new repo" msgid "URL of the new repo"
msgstr "" msgstr ""
#: repo.go:82 repo.go:147 #: repo.go:86 repo.go:156
msgid "Error opening config file" msgid "Error saving config"
msgstr "" msgstr ""
#: repo.go:88 repo.go:153 #: repo.go:111
msgid "Error encoding config"
msgstr ""
#: repo.go:113
msgid "Remove an existing repository" msgid "Remove an existing repository"
msgstr "" msgstr ""
#: repo.go:120 #: repo.go:118
msgid "Name of the repo to be deleted" msgid "Name of the repo to be deleted"
msgstr "" msgstr ""
#: repo.go:139 #: repo.go:142
msgid "Repo does not exist" msgid "Repo does not exist"
msgstr "" msgstr ""
#: repo.go:159 #: repo.go:150
msgid "Error removing repo directory" msgid "Error removing repo directory"
msgstr "" msgstr ""
#: repo.go:170 #: repo.go:167
msgid "Error removing packages from database" msgid "Error removing packages from database"
msgstr "" msgstr ""
#: repo.go:182 #: repo.go:179
msgid "Pull all repositories that have changed" msgid "Pull all repositories that have changed"
msgstr "" msgstr ""
@ -529,11 +505,11 @@ msgstr ""
msgid "Format output using a Go template" msgid "Format output using a Go template"
msgstr "" msgstr ""
#: search.go:82 search.go:99 #: search.go:88 search.go:105
msgid "Error parsing format template" msgid "Error parsing format template"
msgstr "" msgstr ""
#: search.go:107 #: search.go:113
msgid "Error executing template" msgid "Error executing template"
msgstr "" msgstr ""
@ -541,10 +517,10 @@ msgstr ""
msgid "Upgrade all installed packages" msgid "Upgrade all installed packages"
msgstr "" msgstr ""
#: upgrade.go:90 #: upgrade.go:96
msgid "Error checking for updates" msgid "Error checking for updates"
msgstr "" msgstr ""
#: upgrade.go:112 #: upgrade.go:118
msgid "There is nothing to do." msgid "There is nothing to do."
msgstr "" msgstr ""

@ -12,8 +12,8 @@ msgstr ""
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"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 && " "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"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 47.1\n"
#: build.go:44 #: build.go:44
@ -37,35 +37,40 @@ 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:75 #: build.go:73
#, fuzzy
msgid "Error loading config"
msgstr "Ошибка при кодировании конфигурации"
#: build.go:81
msgid "Error initialization database" msgid "Error initialization database"
msgstr "Ошибка инициализации базы данных" msgstr "Ошибка инициализации базы данных"
#: build.go:104 #: build.go:110
msgid "Package not found" msgid "Package not found"
msgstr "Пакет не найден" msgstr "Пакет не найден"
#: build.go:124 #: build.go:130
msgid "Error pulling repositories" msgid "Error pulling repositories"
msgstr "Ошибка при извлечении репозиториев" msgstr "Ошибка при извлечении репозиториев"
#: build.go:132 #: build.go:138
msgid "Unable to detect a supported package manager on the system" msgid "Unable to detect a supported package manager on the system"
msgstr "Не удалось обнаружить поддерживаемый менеджер пакетов в системе" msgstr "Не удалось обнаружить поддерживаемый менеджер пакетов в системе"
#: build.go:138 #: build.go:144
msgid "Error parsing os release" msgid "Error parsing os release"
msgstr "Ошибка при разборе файла выпуска операционной системы" msgstr "Ошибка при разборе файла выпуска операционной системы"
#: build.go:160 #: build.go:166
msgid "Error building package" msgid "Error building package"
msgstr "Ошибка при сборке пакета" msgstr "Ошибка при сборке пакета"
#: build.go:167 #: build.go:173
msgid "Error getting working directory" msgid "Error getting working directory"
msgstr "Ошибка при получении рабочего каталога" msgstr "Ошибка при получении рабочего каталога"
#: build.go:176 #: build.go:182
msgid "Error moving the package" msgid "Error moving the package"
msgstr "Ошибка при перемещении пакета" msgstr "Ошибка при перемещении пакета"
@ -73,27 +78,27 @@ msgstr "Ошибка при перемещении пакета"
msgid "Attempt to fix problems with ALR" msgid "Attempt to fix problems with ALR"
msgstr "Попытка устранить проблемы с ALR" msgstr "Попытка устранить проблемы с ALR"
#: fix.go:43 #: fix.go:49
msgid "Removing cache directory" msgid "Removing cache directory"
msgstr "Удаление каталога кэша" msgstr "Удаление каталога кэша"
#: fix.go:47 #: fix.go:53
msgid "Unable to remove cache directory" msgid "Unable to remove cache directory"
msgstr "Не удалось удалить каталог кэша" msgstr "Не удалось удалить каталог кэша"
#: fix.go:51 #: fix.go:57
msgid "Rebuilding cache" msgid "Rebuilding cache"
msgstr "Восстановление кэша" msgstr "Восстановление кэша"
#: fix.go:55 #: fix.go:61
msgid "Unable to create new cache directory" msgid "Unable to create new cache directory"
msgstr "Не удалось создать новый каталог кэша" msgstr "Не удалось создать новый каталог кэша"
#: fix.go:69 #: fix.go:81
msgid "Error pulling repos" msgid "Error pulling repos"
msgstr "Ошибка при извлечении репозиториев" msgstr "Ошибка при извлечении репозиториев"
#: fix.go:73 #: fix.go:85
msgid "Done" msgid "Done"
msgstr "Сделано" msgstr "Сделано"
@ -129,31 +134,31 @@ msgstr "Отобразить информацию о пакете"
msgid "Show all information, not just for the current distro" msgid "Show all information, not just for the current distro"
msgstr "Показывать всю информацию, не только для текущего дистрибутива" msgstr "Показывать всю информацию, не только для текущего дистрибутива"
#: info.go:63 #: info.go:69
msgid "Error getting packages" msgid "Error getting packages"
msgstr "Ошибка при получении пакетов" msgstr "Ошибка при получении пакетов"
#: info.go:72 #: info.go:78
msgid "Error iterating over packages" msgid "Error iterating over packages"
msgstr "Ошибка при переборе пакетов" msgstr "Ошибка при переборе пакетов"
#: info.go:93 #: info.go:105
msgid "Command info expected at least 1 argument, got %d" msgid "Command info expected at least 1 argument, got %d"
msgstr "Для команды info ожидался хотя бы 1 аргумент, получено %d" msgstr "Для команды info ожидался хотя бы 1 аргумент, получено %d"
#: info.go:107 #: info.go:119
msgid "Error finding packages" msgid "Error finding packages"
msgstr "Ошибка при поиске пакетов" msgstr "Ошибка при поиске пакетов"
#: info.go:129 #: info.go:144
msgid "Error parsing os-release file" msgid "Error parsing os-release file"
msgstr "Ошибка при разборе файла выпуска операционной системы" msgstr "Ошибка при разборе файла выпуска операционной системы"
#: info.go:138 #: info.go:153
msgid "Error resolving overrides" msgid "Error resolving overrides"
msgstr "Ошибка устранения переорпеделений" msgstr "Ошибка устранения переорпеделений"
#: info.go:147 info.go:153 #: info.go:162 info.go:168
msgid "Error encoding script variables" msgid "Error encoding script variables"
msgstr "Ошибка кодирования переменных скрита" msgstr "Ошибка кодирования переменных скрита"
@ -165,15 +170,15 @@ msgstr "Установить новый пакет"
msgid "Command install expected at least 1 argument, got %d" msgid "Command install expected at least 1 argument, got %d"
msgstr "Для команды install ожидался хотя бы 1 аргумент, получено %d" msgstr "Для команды install ожидался хотя бы 1 аргумент, получено %d"
#: install.go:151 #: install.go:163
msgid "Remove an installed package" msgid "Remove an installed package"
msgstr "Удалить установленный пакет" msgstr "Удалить установленный пакет"
#: install.go:156 #: install.go:168
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:168 #: install.go:183
msgid "Error removing packages" msgid "Error removing packages"
msgstr "Ошибка при удалении пакетов" msgstr "Ошибка при удалении пакетов"
@ -257,43 +262,16 @@ msgstr "КАТЕГОРИЯ"
msgid "OPTIONS" msgid "OPTIONS"
msgstr "ПАРАМЕТРЫ" msgstr "ПАРАМЕТРЫ"
#: internal/config/config.go:59 #: internal/config/config.go:176
msgid "Error opening config file, using defaults" #, fuzzy
msgstr "" msgid "Unable to create config directory"
"Ошибка при открытии конфигурационного файла, используются значения по "
"умолчанию"
#: internal/config/config.go:72
msgid "Error decoding config file, using defaults"
msgstr ""
"Ошибка при декодировании конфигурационного файла, используются значения по "
"умолчанию"
#: internal/config/config.go:84
msgid "Unable to detect user config directory"
msgstr "Не удалось обнаружить каталог конфигурации пользователя"
#: internal/config/config.go:92
msgid "Unable to create ALR config directory"
msgstr "Не удалось создать каталог конфигурации ALR" msgstr "Не удалось создать каталог конфигурации ALR"
#: internal/config/config.go:101 #: internal/config/config.go:182
msgid "Unable to create ALR config file"
msgstr "Не удалось создать конфигурационный файл ALR"
#: internal/config/config.go:107
msgid "Error encoding default configuration"
msgstr "Ошибка кодирования конфигурации по умолчанию"
#: internal/config/config.go:116
msgid "Unable to detect cache directory"
msgstr "Не удалось обнаружить каталог кэша"
#: internal/config/config.go:126
msgid "Unable to create repo cache directory" msgid "Unable to create repo cache directory"
msgstr "Не удалось создать каталог кэша репозитория" msgstr "Не удалось создать каталог кэша репозитория"
#: internal/config/config.go:132 #: internal/config/config.go:188
msgid "Unable to create package cache directory" msgid "Unable to create package cache directory"
msgstr "Не удалось создать каталог кэша пакетов" msgstr "Не удалось создать каталог кэша пакетов"
@ -339,7 +317,7 @@ msgstr "ОШИБКА"
msgid "List ALR repo packages" msgid "List ALR repo packages"
msgstr "Список пакетов репозитория ALR" msgstr "Список пакетов репозитория ALR"
#: list.go:92 #: list.go:98
msgid "Error listing installed packages" msgid "Error listing installed packages"
msgstr "Ошибка при составлении списка установленных пакетов" msgstr "Ошибка при составлении списка установленных пакетов"
@ -355,7 +333,7 @@ msgstr "Аргументы, которые будут переданы мене
msgid "Enable interactive questions and prompts" msgid "Enable interactive questions and prompts"
msgstr "Включение интерактивных вопросов и запросов" msgstr "Включение интерактивных вопросов и запросов"
#: main.go:92 #: main.go:96
msgid "" msgid ""
"Running ALR as root is forbidden as it may cause catastrophic damage to your " "Running ALR as root is forbidden as it may cause catastrophic damage to your "
"system" "system"
@ -363,11 +341,11 @@ msgstr ""
"Запуск ALR от имени root запрещён, так как это может привести к " "Запуск ALR от имени root запрещён, так как это может привести к "
"катастрофическому повреждению вашей системы" "катастрофическому повреждению вашей системы"
#: main.go:125 #: main.go:154
msgid "Show help" msgid "Show help"
msgstr "Показать справку" msgstr "Показать справку"
#: main.go:129 #: main.go:158
msgid "Error while running app" msgid "Error while running app"
msgstr "Ошибка при запуске приложения" msgstr "Ошибка при запуске приложения"
@ -379,19 +357,23 @@ msgstr "Не удалось предложить пользователю про
msgid "Building package" msgid "Building package"
msgstr "Сборка пакета" msgstr "Сборка пакета"
#: pkg/build/build.go:231 #: pkg/build/build.go:208
msgid "The checksums array must be the same length as sources"
msgstr "Массив контрольных сумм должен быть той же длины, что и источники"
#: pkg/build/build.go:235
msgid "Downloading sources" msgid "Downloading sources"
msgstr "Скачивание источников" msgstr "Скачивание источников"
#: pkg/build/build.go:253 #: pkg/build/build.go:257
msgid "Building package metadata" msgid "Building package metadata"
msgstr "Сборка метаданных пакета" msgstr "Сборка метаданных пакета"
#: pkg/build/build.go:275 #: pkg/build/build.go:279
msgid "Compressing package" msgid "Compressing package"
msgstr "Сжатие пакета" msgstr "Сжатие пакета"
#: pkg/build/build.go:434 #: pkg/build/build.go:438
msgid "" msgid ""
"Your system's CPU architecture doesn't match this package. Do you want to " "Your system's CPU architecture doesn't match this package. Do you want to "
"build anyway?" "build anyway?"
@ -399,52 +381,48 @@ msgstr ""
"Архитектура процессора вашей системы не соответствует этому пакету. Вы все " "Архитектура процессора вашей системы не соответствует этому пакету. Вы все "
"равно хотите выполнить сборку?" "равно хотите выполнить сборку?"
#: pkg/build/build.go:448 #: pkg/build/build.go:452
msgid "This package is already installed" msgid "This package is already installed"
msgstr "Этот пакет уже установлен" msgstr "Этот пакет уже установлен"
#: pkg/build/build.go:472 #: pkg/build/build.go:476
msgid "Installing build dependencies" msgid "Installing build dependencies"
msgstr "Установка зависимостей сборки" msgstr "Установка зависимостей сборки"
#: pkg/build/build.go:513 #: pkg/build/build.go:521
msgid "Installing dependencies" msgid "Installing dependencies"
msgstr "Установка зависимостей" msgstr "Установка зависимостей"
#: pkg/build/build.go:548 #: pkg/build/build.go:602
msgid "The checksums array must be the same length as sources"
msgstr "Массив контрольных сумм должен быть той же длины, что и источники"
#: pkg/build/build.go:599
msgid "Would you like to remove the build dependencies?" msgid "Would you like to remove the build dependencies?"
msgstr "Хотели бы вы удалить зависимости сборки?" msgstr "Хотели бы вы удалить зависимости сборки?"
#: pkg/build/build.go:662 #: pkg/build/build.go:665
msgid "Executing prepare()" msgid "Executing prepare()"
msgstr "Исполнение prepare()" msgstr "Исполнение prepare()"
#: pkg/build/build.go:672 #: pkg/build/build.go:675
msgid "Executing build()" msgid "Executing build()"
msgstr "Исполнение build()" msgstr "Исполнение build()"
#: pkg/build/build.go:702 pkg/build/build.go:722 #: pkg/build/build.go:705 pkg/build/build.go:725
msgid "Executing %s()" msgid "Executing %s()"
msgstr "Исполнение %s()" msgstr "Исполнение %s()"
#: pkg/build/build.go:781 #: pkg/build/build.go:784
msgid "Error installing native packages" msgid "Error installing native packages"
msgstr "Ошибка при установке нативных пакетов" msgstr "Ошибка при установке нативных пакетов"
#: pkg/build/build.go:805 #: pkg/build/build.go:808
msgid "Error installing package" msgid "Error installing package"
msgstr "Ошибка при установке пакета" msgstr "Ошибка при установке пакета"
#: pkg/build/build.go:864 #: pkg/build/build.go:867
msgid "AutoProv is not implemented for this package format, so it's skipped" msgid "AutoProv is not implemented for this package format, so it's skipped"
msgstr "" msgstr ""
"AutoProv не реализовано для этого формата пакета, поэтому будет пропущено" "AutoProv не реализовано для этого формата пакета, поэтому будет пропущено"
#: pkg/build/build.go:875 #: pkg/build/build.go:878
msgid "AutoReq is not implemented for this package format, so it's skipped" msgid "AutoReq is not implemented for this package format, so it's skipped"
msgstr "" msgstr ""
"AutoReq не реализовано для этого формата пакета, поэтому будет пропущено" "AutoReq не реализовано для этого формата пакета, поэтому будет пропущено"
@ -481,47 +459,44 @@ msgstr ""
"Минимальная версия ALR для ALR-репозитория выше текущей версии. Попробуйте " "Минимальная версия ALR для ALR-репозитория выше текущей версии. Попробуйте "
"обновить ALR, если что-то не работает." "обновить ALR, если что-то не работает."
#: repo.go:41 #: repo.go:40
msgid "Add a new repository" msgid "Add a new repository"
msgstr "Добавить новый репозиторий" msgstr "Добавить новый репозиторий"
#: repo.go:48 #: repo.go:47
msgid "Name of the new repo" msgid "Name of the new repo"
msgstr "Название нового репозитория" msgstr "Название нового репозитория"
#: repo.go:54 #: repo.go:53
msgid "URL of the new repo" msgid "URL of the new repo"
msgstr "URL-адрес нового репозитория" msgstr "URL-адрес нового репозитория"
#: repo.go:82 repo.go:147 #: repo.go:86 repo.go:156
msgid "Error opening config file" #, fuzzy
msgstr "Ошибка при открытии конфигурационного файла" msgid "Error saving config"
#: repo.go:88 repo.go:153
msgid "Error encoding config"
msgstr "Ошибка при кодировании конфигурации" msgstr "Ошибка при кодировании конфигурации"
#: repo.go:113 #: repo.go:111
msgid "Remove an existing repository" msgid "Remove an existing repository"
msgstr "Удалить существующий репозиторий" msgstr "Удалить существующий репозиторий"
#: repo.go:120 #: repo.go:118
msgid "Name of the repo to be deleted" msgid "Name of the repo to be deleted"
msgstr "Название репозитория удалён" msgstr "Название репозитория удалён"
#: repo.go:139 #: repo.go:142
msgid "Repo does not exist" msgid "Repo does not exist"
msgstr "Репозитория не существует" msgstr "Репозитория не существует"
#: repo.go:159 #: repo.go:150
msgid "Error removing repo directory" msgid "Error removing repo directory"
msgstr "Ошибка при удалении каталога репозитория" msgstr "Ошибка при удалении каталога репозитория"
#: repo.go:170 #: repo.go:167
msgid "Error removing packages from database" msgid "Error removing packages from database"
msgstr "Ошибка при удалении пакетов из базы данных" msgstr "Ошибка при удалении пакетов из базы данных"
#: repo.go:182 #: repo.go:179
msgid "Pull all repositories that have changed" msgid "Pull all repositories that have changed"
msgstr "Скачать все изменённые репозитории" msgstr "Скачать все изменённые репозитории"
@ -549,11 +524,11 @@ msgstr "Иcкать по provides"
msgid "Format output using a Go template" msgid "Format output using a Go template"
msgstr "Формат выходных данных с использованием шаблона Go" msgstr "Формат выходных данных с использованием шаблона Go"
#: search.go:82 search.go:99 #: search.go:88 search.go:105
msgid "Error parsing format template" msgid "Error parsing format template"
msgstr "Ошибка при разборе шаблона" msgstr "Ошибка при разборе шаблона"
#: search.go:107 #: search.go:113
msgid "Error executing template" msgid "Error executing template"
msgstr "Ошибка при выполнении шаблона" msgstr "Ошибка при выполнении шаблона"
@ -561,14 +536,39 @@ msgstr "Ошибка при выполнении шаблона"
msgid "Upgrade all installed packages" msgid "Upgrade all installed packages"
msgstr "Обновить все установленные пакеты" msgstr "Обновить все установленные пакеты"
#: upgrade.go:90 #: upgrade.go:96
msgid "Error checking for updates" msgid "Error checking for updates"
msgstr "Ошибка при проверке обновлений" msgstr "Ошибка при проверке обновлений"
#: upgrade.go:112 #: upgrade.go:118
msgid "There is nothing to do." msgid "There is nothing to do."
msgstr "Здесь нечего делать." msgstr "Здесь нечего делать."
#~ msgid "Error opening config file, using defaults"
#~ msgstr ""
#~ "Ошибка при открытии конфигурационного файла, используются значения по "
#~ "умолчанию"
#~ msgid "Error decoding config file, using defaults"
#~ msgstr ""
#~ "Ошибка при декодировании конфигурационного файла, используются значения "
#~ "по умолчанию"
#~ msgid "Unable to detect user config directory"
#~ msgstr "Не удалось обнаружить каталог конфигурации пользователя"
#~ msgid "Unable to create ALR config file"
#~ msgstr "Не удалось создать конфигурационный файл ALR"
#~ msgid "Error encoding default configuration"
#~ msgstr "Ошибка кодирования конфигурации по умолчанию"
#~ msgid "Unable to detect cache directory"
#~ msgstr "Не удалось обнаружить каталог кэша"
#~ msgid "Error opening config file"
#~ msgstr "Ошибка при открытии конфигурационного файла"
#~ msgid "Error parsing system language" #~ msgid "Error parsing system language"
#~ msgstr "Ошибка при парсинге языка системы" #~ msgstr "Ошибка при парсинге языка системы"

@ -21,12 +21,13 @@ package types
// Config represents the ALR configuration file // Config represents the ALR configuration file
type Config struct { type Config struct {
RootCmd string `toml:"rootCmd"` RootCmd string `toml:"rootCmd" env:"ALR_ROOT_CMD"`
PagerStyle string `toml:"pagerStyle"` 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"` Unsafe Unsafe `toml:"unsafe"`
AutoPull bool `toml:"autoPull"` AutoPull bool `toml:"autoPull" env:"ALR_AUTOPULL"`
LogLevel string `toml:"logLevel" env:"ALR_LOG_LEVEL"`
} }
// Repo represents a ALR repo within a configuration file // Repo represents a ALR repo within a configuration file
@ -36,5 +37,5 @@ type Repo struct {
} }
type Unsafe struct { type Unsafe struct {
AllowRunAsRoot bool `toml:"allowRunAsRoot"` AllowRunAsRoot bool `toml:"allowRunAsRoot" env:"ALR_UNSAFE_ALLOW_RUN_AS_ROOT"`
} }

14
list.go

@ -49,16 +49,22 @@ func ListCmd() *cli.Command {
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
ctx := c.Context ctx := c.Context
cfg := config.New() 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) db := database.New(cfg)
err := db.Init(ctx) err = db.Init(ctx)
if err != nil { if err != nil {
slog.Error(gotext.Get("Error initialization database"), "err", err) slog.Error(gotext.Get("Error initialization database"), "err", err)
os.Exit(1) os.Exit(1)
} }
rs := repos.New(cfg, db) rs := repos.New(cfg, db)
if cfg.AutoPull(ctx) { if cfg.AutoPull() {
err = rs.Pull(ctx, cfg.Repos(ctx)) err = rs.Pull(ctx, cfg.Repos())
if err != nil { if err != nil {
slog.Error(gotext.Get("Error pulling repositories"), "err", err) slog.Error(gotext.Get("Error pulling repositories"), "err", err)
os.Exit(1) os.Exit(1)
@ -110,7 +116,7 @@ func ListCmd() *cli.Command {
return err return err
} }
if slices.Contains(cfg.IgnorePkgUpdates(ctx), pkg.Name) { if slices.Contains(cfg.IgnorePkgUpdates(), pkg.Name) {
continue continue
} }

47
main.go

@ -84,11 +84,15 @@ func GetApp() *cli.App {
SearchCmd(), SearchCmd(),
}, },
Before: func(c *cli.Context) error { Before: func(c *cli.Context) error {
ctx := c.Context
cfg := config.New() cfg := config.New()
err := cfg.Load()
if err != nil {
slog.Error(gotext.Get("Error loading config"), "err", err)
os.Exit(1)
}
cmd := c.Args().First() cmd := c.Args().First()
if cmd != "helper" && !cfg.AllowRunAsRoot(ctx) && os.Geteuid() == 0 { 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")) slog.Error(gotext.Get("Running ALR as root is forbidden as it may cause catastrophic damage to your system"))
os.Exit(1) os.Exit(1)
} }
@ -104,17 +108,42 @@ func GetApp() *cli.App {
} }
} }
func main() { func setLogLevel(newLevel string) {
translations.Setup() level := slog.LevelInfo
logger.SetupDefault() switch newLevel {
case "DEBUG":
level = slog.LevelDebug
case "INFO":
level = slog.LevelInfo
case "WARN":
level = slog.LevelWarn
case "ERROR":
level = slog.LevelError
}
logger, ok := slog.Default().Handler().(*logger.Logger)
if !ok {
panic("unexpected")
}
logger.SetLevel(level)
}
app := GetApp() func main() {
cfg := config.New() logger.SetupDefault()
setLogLevel(os.Getenv("ALR_LOG_LEVEL"))
translations.Setup()
ctx := context.Background() ctx := context.Background()
app := GetApp()
cfg := config.New()
err := cfg.Load()
if err != nil {
slog.Error(gotext.Get("Error loading config"), "err", err)
os.Exit(1)
}
setLogLevel(cfg.LogLevel())
// Set the root command to the one set in the ALR config // Set the root command to the one set in the ALR config
manager.DefaultRootCmd = cfg.RootCmd(ctx) 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()
@ -124,7 +153,7 @@ func main() {
cli.CommandHelpTemplate = cliutils.GetCommandHelpTemplate() cli.CommandHelpTemplate = cliutils.GetCommandHelpTemplate()
cli.HelpFlag.(*cli.BoolFlag).Usage = gotext.Get("Show help") cli.HelpFlag.(*cli.BoolFlag).Usage = gotext.Get("Show help")
err := app.RunContext(ctx, os.Args) err = app.RunContext(ctx, os.Args)
if err != nil { if err != nil {
slog.Error(gotext.Get("Error while running app"), "err", err) slog.Error(gotext.Get("Error while running app"), "err", err)
} }

@ -59,8 +59,8 @@ type PackageFinder interface {
} }
type Config interface { type Config interface {
GetPaths(ctx context.Context) *config.Paths GetPaths() *config.Paths
PagerStyle(ctx context.Context) string PagerStyle() string
} }
type Builder struct { type Builder struct {
@ -88,7 +88,7 @@ func NewBuilder(
} }
func (b *Builder) UpdateOptsFromPkg(pkg *db.Package, packages []string) { func (b *Builder) UpdateOptsFromPkg(pkg *db.Package, packages []string) {
repodir := b.config.GetPaths(b.ctx).RepoDir repodir := b.config.GetPaths().RepoDir
b.opts.Repository = pkg.Repository b.opts.Repository = pkg.Repository
if pkg.BasePkgName != "" { if pkg.BasePkgName != "" {
b.opts.Script = filepath.Join(repodir, pkg.Repository, pkg.BasePkgName, "alr.sh") b.opts.Script = filepath.Join(repodir, pkg.Repository, pkg.BasePkgName, "alr.sh")
@ -149,7 +149,7 @@ func (b *Builder) BuildPackage(ctx context.Context) ([]string, []string, error)
ctx, ctx,
b.opts.Script, b.opts.Script,
basePkg, basePkg,
b.config.PagerStyle(ctx), b.config.PagerStyle(),
b.opts.Interactive, b.opts.Interactive,
) )
if err != nil { if err != nil {
@ -203,8 +203,12 @@ func (b *Builder) BuildPackage(ctx context.Context) ([]string, []string, error)
buildDepends = removeDuplicates(buildDepends) buildDepends = removeDuplicates(buildDepends)
optDepends = removeDuplicates(optDepends) optDepends = removeDuplicates(optDepends)
depends = removeDuplicates(depends) depends = removeDuplicates(depends)
sources = removeDuplicates(sources)
checksums = removeDuplicates(checksums) if len(sources) != len(checksums) {
slog.Error(gotext.Get("The checksums array must be the same length as sources"))
os.Exit(1)
}
sources, checksums = removeDuplicatesSources(sources, checksums)
mergedVars := types.BuildVars{ mergedVars := types.BuildVars{
Sources: sources, Sources: sources,
@ -388,7 +392,7 @@ func (b *Builder) getDirs(basePkg string) (types.Directories, error) {
return types.Directories{}, err return types.Directories{}, err
} }
baseDir := filepath.Join(b.config.GetPaths(b.ctx).PkgsDir, basePkg) // Определяем базовую директорию baseDir := filepath.Join(b.config.GetPaths().PkgsDir, basePkg) // Определяем базовую директорию
return types.Directories{ return types.Directories{
BaseDir: baseDir, BaseDir: baseDir,
SrcDir: filepath.Join(baseDir, "src"), SrcDir: filepath.Join(baseDir, "src"),
@ -485,13 +489,17 @@ func (b *Builder) getBuildersForPackages(pkgs []db.Package) []*Builder {
} }
pkgsMap := make(map[string]*item) pkgsMap := make(map[string]*item)
for _, pkg := range pkgs { for _, pkg := range pkgs {
if pkgsMap[pkg.BasePkgName] == nil { name := pkg.BasePkgName
pkgsMap[pkg.BasePkgName] = &item{ if name == "" {
name = pkg.Name
}
if pkgsMap[name] == nil {
pkgsMap[name] = &item{
pkg: &pkg, pkg: &pkg,
} }
} }
pkgsMap[pkg.BasePkgName].packages = append( pkgsMap[name].packages = append(
pkgsMap[pkg.BasePkgName].packages, pkgsMap[name].packages,
pkg.Name, pkg.Name,
) )
} }
@ -544,11 +552,6 @@ func (b *Builder) buildALRDeps(ctx context.Context, depends []string) (builtPath
} }
func (b *Builder) getSources(ctx context.Context, dirs types.Directories, bv *types.BuildVars) error { func (b *Builder) getSources(ctx context.Context, dirs types.Directories, bv *types.BuildVars) error {
if len(bv.Sources) != len(bv.Checksums) {
slog.Error(gotext.Get("The checksums array must be the same length as sources"))
os.Exit(1)
}
for i, src := range bv.Sources { for i, src := range bv.Sources {
opts := dl.Options{ opts := dl.Options{
Name: fmt.Sprintf("%s[%d]", bv.Name, i), Name: fmt.Sprintf("%s[%d]", bv.Name, i),

@ -144,11 +144,11 @@ func (m *TestManager) IsInstalled(pkg string) (bool, error) {
type TestConfig struct{} type TestConfig struct{}
func (c *TestConfig) PagerStyle(ctx context.Context) string { func (c *TestConfig) PagerStyle() string {
return "native" return "native"
} }
func (c *TestConfig) GetPaths(ctx context.Context) *config.Paths { func (c *TestConfig) GetPaths() *config.Paths {
return &config.Paths{ return &config.Paths{
CacheDir: "/tmp", CacheDir: "/tmp",
} }

@ -313,3 +313,24 @@ func removeDuplicates(slice []string) []string {
return result return result
} }
func removeDuplicatesSources(sources, checksums []string) ([]string, []string) {
seen := map[string]string{}
keys := make([]string, 0)
for i, s := range sources {
if val, ok := seen[s]; !ok || strings.EqualFold(val, "SKIP") {
if !ok {
keys = append(keys, s)
}
seen[s] = checksums[i]
}
}
newSources := make([]string, len(keys))
newChecksums := make([]string, len(keys))
for i, k := range keys {
newSources[i] = k
newChecksums[i] = seen[k]
}
return newSources, newChecksums
}

@ -0,0 +1,47 @@
// 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 (
"testing"
"github.com/stretchr/testify/assert"
)
func TestRemoveDuplicatesSources(t *testing.T) {
type testCase struct {
Name string
Sources []string
Checksums []string
NewSources []string
NewChecksums []string
}
for _, tc := range []testCase{{
Name: "prefer non-skip values",
Sources: []string{"a", "b", "c", "a"},
Checksums: []string{"skip", "skip", "skip", "1"},
NewSources: []string{"a", "b", "c"},
NewChecksums: []string{"1", "skip", "skip"},
}} {
t.Run(tc.Name, func(t *testing.T) {
s, c := removeDuplicatesSources(tc.Sources, tc.Checksums)
assert.Equal(t, s, tc.NewSources)
assert.Equal(t, c, tc.NewChecksums)
})
}
}

@ -67,7 +67,7 @@ type action struct {
// If repos is set to nil, the repos in the ALR config will be used. // If repos is set to nil, the repos in the ALR config will be used.
func (rs *Repos) Pull(ctx context.Context, repos []types.Repo) error { func (rs *Repos) Pull(ctx context.Context, repos []types.Repo) error {
if repos == nil { if repos == nil {
repos = rs.cfg.Repos(ctx) repos = rs.cfg.Repos()
} }
for _, repo := range repos { for _, repo := range repos {
@ -77,7 +77,7 @@ func (rs *Repos) Pull(ctx context.Context, repos []types.Repo) error {
} }
slog.Info(gotext.Get("Pulling repository"), "name", repo.Name) slog.Info(gotext.Get("Pulling repository"), "name", repo.Name)
repoDir := filepath.Join(rs.cfg.GetPaths(ctx).RepoDir, repo.Name) repoDir := filepath.Join(rs.cfg.GetPaths().RepoDir, repo.Name)
var repoFS billy.Filesystem var repoFS billy.Filesystem
gitDir := filepath.Join(repoDir, ".git") gitDir := filepath.Join(repoDir, ".git")

@ -32,13 +32,13 @@ import (
type TestALRConfig struct{} type TestALRConfig struct{}
func (c *TestALRConfig) GetPaths(ctx context.Context) *config.Paths { func (c *TestALRConfig) GetPaths() *config.Paths {
return &config.Paths{ return &config.Paths{
DBPath: ":memory:", DBPath: ":memory:",
} }
} }
func (c *TestALRConfig) Repos(ctx context.Context) []types.Repo { func (c *TestALRConfig) Repos() []types.Repo {
return []types.Repo{ return []types.Repo{
{ {
Name: "test", Name: "test",

@ -44,7 +44,7 @@ type TestALRConfig struct {
PkgsDir string PkgsDir string
} }
func (c *TestALRConfig) GetPaths(ctx context.Context) *config.Paths { func (c *TestALRConfig) GetPaths() *config.Paths {
return &config.Paths{ return &config.Paths{
DBPath: ":memory:", DBPath: ":memory:",
CacheDir: c.CacheDir, CacheDir: c.CacheDir,
@ -53,7 +53,7 @@ func (c *TestALRConfig) GetPaths(ctx context.Context) *config.Paths {
} }
} }
func (c *TestALRConfig) Repos(ctx context.Context) []types.Repo { func (c *TestALRConfig) Repos() []types.Repo {
return []types.Repo{} return []types.Repo{}
} }

@ -17,16 +17,14 @@
package repos package repos
import ( import (
"context"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" "gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db" database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/types" "gitea.plemya-x.ru/Plemya-x/ALR/internal/types"
) )
type Config interface { type Config interface {
GetPaths(ctx context.Context) *config.Paths GetPaths() *config.Paths
Repos(ctx context.Context) []types.Repo Repos() []types.Repo
} }
type Repos struct { type Repos struct {

63
repo.go

@ -25,7 +25,6 @@ import (
"path/filepath" "path/filepath"
"github.com/leonelquinteros/gotext" "github.com/leonelquinteros/gotext"
"github.com/pelletier/go-toml/v2"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
@ -61,7 +60,13 @@ func AddRepoCmd() *cli.Command {
repoURL := c.String("url") repoURL := c.String("url")
cfg := config.New() cfg := config.New()
reposSlice := cfg.Repos(ctx) err := cfg.Load()
if err != nil {
slog.Error(gotext.Get("Error loading config"), "err", err)
os.Exit(1)
}
reposSlice := cfg.Repos()
for _, repo := range reposSlice { for _, repo := range reposSlice {
if repo.URL == repoURL { if repo.URL == repoURL {
@ -74,18 +79,11 @@ func AddRepoCmd() *cli.Command {
Name: name, Name: name,
URL: repoURL, URL: repoURL,
}) })
cfg.SetRepos(reposSlice)
cfg.SetRepos(ctx, reposSlice) err = cfg.SaveUserConfig()
cfgFl, err := os.Create(cfg.GetPaths(ctx).ConfigPath)
if err != nil { if err != nil {
slog.Error(gotext.Get("Error opening config file"), "err", err) slog.Error(gotext.Get("Error saving config"), "err", err)
os.Exit(1)
}
err = cfg.Save(cfgFl)
if err != nil {
slog.Error(gotext.Get("Error encoding config"), "err", err)
os.Exit(1) os.Exit(1)
} }
@ -96,7 +94,7 @@ func AddRepoCmd() *cli.Command {
} }
rs := repos.New(cfg, db) rs := repos.New(cfg, db)
err = rs.Pull(ctx, cfg.Repos(ctx)) err = rs.Pull(ctx, cfg.Repos())
if err != nil { if err != nil {
slog.Error(gotext.Get("Error pulling repos"), "err", err) slog.Error(gotext.Get("Error pulling repos"), "err", err)
os.Exit(1) os.Exit(1)
@ -125,10 +123,15 @@ func RemoveRepoCmd() *cli.Command {
name := c.String("name") name := c.String("name")
cfg := config.New() cfg := config.New()
err := cfg.Load()
if err != nil {
slog.Error(gotext.Get("Error loading config"), "err", err)
os.Exit(1)
}
found := false found := false
index := 0 index := 0
reposSlice := cfg.Repos(ctx) reposSlice := cfg.Repos()
for i, repo := range reposSlice { for i, repo := range reposSlice {
if repo.Name == name { if repo.Name == name {
index = i index = i
@ -140,26 +143,20 @@ func RemoveRepoCmd() *cli.Command {
os.Exit(1) os.Exit(1)
} }
cfg.SetRepos(ctx, slices.Delete(reposSlice, index, index+1)) cfg.SetRepos(slices.Delete(reposSlice, index, index+1))
cfgFl, err := os.Create(cfg.GetPaths(ctx).ConfigPath) err = os.RemoveAll(filepath.Join(cfg.GetPaths().RepoDir, name))
if err != nil {
slog.Error(gotext.Get("Error opening config file"), "err", err)
os.Exit(1)
}
err = toml.NewEncoder(cfgFl).Encode(&cfg)
if err != nil {
slog.Error(gotext.Get("Error encoding config"), "err", err)
os.Exit(1)
}
err = os.RemoveAll(filepath.Join(cfg.GetPaths(ctx).RepoDir, name))
if err != nil { if err != nil {
slog.Error(gotext.Get("Error removing repo directory"), "err", err) slog.Error(gotext.Get("Error removing repo directory"), "err", err)
os.Exit(1) os.Exit(1)
} }
err = cfg.SaveUserConfig()
if err != nil {
slog.Error(gotext.Get("Error saving config"), "err", err)
os.Exit(1)
}
db := database.New(cfg) db := database.New(cfg)
err = db.Init(ctx) err = db.Init(ctx)
if err != nil { if err != nil {
@ -184,13 +181,19 @@ func RefreshCmd() *cli.Command {
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
ctx := c.Context ctx := c.Context
cfg := config.New() 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) db := database.New(cfg)
err := db.Init(ctx) err = db.Init(ctx)
if err != nil { if err != nil {
os.Exit(1) os.Exit(1)
} }
rs := repos.New(cfg, db) rs := repos.New(cfg, db)
err = rs.Pull(ctx, cfg.Repos(ctx)) err = rs.Pull(ctx, cfg.Repos())
if err != nil { if err != nil {
slog.Error(gotext.Get("Error pulling repos"), "err", err) slog.Error(gotext.Get("Error pulling repos"), "err", err)
os.Exit(1) os.Exit(1)

@ -97,14 +97,15 @@ 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 elif [ "$pkgMgr" == "apt-get" ]; then
latestFile=$(echo "$fileList" | grep -E 'alr-bin-.*\.x86_64\.rpm' | grep -v 'alt1' | 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" == "apt-get" ]]; then elif [[ "$pkgMgr" == "dnf" || "$pkgMgr" == "yum" || "$pkgMgr" == "zypper" ]]; then
latestFile=$(echo "$fileList" | grep -E 'alr-bin-.*alt1.x86_64.rpm' | sort -V | tail -n 1) latestFile=$(echo "$fileList" | grep -E 'alr-bin-.*\.x86_64\.rpm' | sort -V | tail -n 1)
fi
else else
error "Не поддерживаемый менеджер пакетов для автоматической установки" error "Не поддерживаемый менеджер пакетов для автоматической установки"

@ -65,8 +65,14 @@ func SearchCmd() *cli.Command {
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
ctx := c.Context ctx := c.Context
cfg := config.New() 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) db := database.New(cfg)
err := db.Init(ctx) err = db.Init(ctx)
defer db.Close() defer db.Close()
if err != nil { if err != nil {

@ -57,9 +57,15 @@ func UpgradeCmd() *cli.Command {
ctx := c.Context ctx := c.Context
cfg := config.New() 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) db := database.New(cfg)
rs := repos.New(cfg, db) rs := repos.New(cfg, db)
err := db.Init(ctx) err = db.Init(ctx)
if err != nil { if err != nil {
slog.Error(gotext.Get("Error initialization database"), "err", err) slog.Error(gotext.Get("Error initialization database"), "err", err)
os.Exit(1) os.Exit(1)
@ -77,8 +83,8 @@ func UpgradeCmd() *cli.Command {
os.Exit(1) os.Exit(1)
} }
if cfg.AutoPull(ctx) { if cfg.AutoPull() {
err = rs.Pull(ctx, cfg.Repos(ctx)) err = rs.Pull(ctx, cfg.Repos())
if err != nil { if err != nil {
slog.Error(gotext.Get("Error pulling repos"), "err", err) slog.Error(gotext.Get("Error pulling repos"), "err", err)
os.Exit(1) os.Exit(1)
@ -152,6 +158,10 @@ func checkForUpdates(
return nil, err return nil, err
} }
if len(pkgs) == 0 {
continue
}
pkg := pkgs[0] pkg := pkgs[0]
repoVer := pkg.Version repoVer := pkg.Version