40 Commits

Author SHA1 Message Date
b9bf908007 chore: remove legacy translation system 2025-01-22 16:53:30 +03:00
a6076b1253 chore: replace old logger with new 2025-01-22 16:37:16 +03:00
ac35b4d71d feat: add new logger and translation 2025-01-22 14:40:11 +03:00
945f920654 Merge pull request 'fix: rename module from plemya-x.ru/alr to gitea.plemya-x.ru/Plemya-x/ALR' (#12) from Maks1mS/ALR:fix/rename-module into master
Reviewed-on: Plemya-x/ALR#12
2025-01-20 21:17:58 +00:00
84ac2377fb fix: rename module from plemya-x.ru/alr to gitea.plemya-x.ru/Plemya-x/ALR 2025-01-20 19:58:24 +03:00
de1db25202 Merge pull request 'fix: removeAlreadyInstalled before FindPkgs' (#11) from Maks1mS/ALR:fix/resolve-deps into master
Reviewed-on: Plemya-x/ALR#11
2025-01-19 08:55:09 +00:00
2d6504b329 fix: removeAlreadyInstalled before FindPkgs 2025-01-19 11:49:00 +03:00
4ca557402a Merge pull request 'chore: add fmt and update-license' (#10) from Maks1mS/ALR:chore/linting into master
Reviewed-on: Plemya-x/ALR#10
2025-01-19 07:41:15 +00:00
e497d41030 chore: run make update-license fmt 2025-01-18 19:30:02 +03:00
d46414a67c Merge branch 'master' into chore/linting 2025-01-18 19:22:18 +03:00
29e2f85eeb Исправление ссылки на скрипт установки 2025-01-18 18:30:26 +03:00
c9c872abbc Merge remote-tracking branch 'gitea/master' 2025-01-18 18:29:50 +03:00
fb93864d09 Revert "Исправление ссылки на скрипт установки"
This reverts commit 9fcd618a83.
2025-01-18 18:29:42 +03:00
9fcd618a83 Исправление ссылки на скрипт установки 2025-01-18 18:29:17 +03:00
1fb9c6b574 Merge pull request 'refactor(db, config, repos): migrate from functions to struct' (#9) from Maks1mS/ALR:refactor/db into master
Reviewed-on: Plemya-x/ALR#9
2025-01-18 15:27:10 +00:00
fb5c875713 Merge pull request 'fix: add auto_req and auto_prov' (#7) from Maks1mS/ALR:fix/make-findprovides-findrequires-optional into master
Reviewed-on: Plemya-x/ALR#7
2025-01-18 15:24:07 +00:00
3f428ab7b5 chore: add golangci-lint 2025-01-14 15:45:29 +03:00
5b7af1f6b5 chore: refactor license update script 2025-01-14 15:28:54 +03:00
3224d7c6e4 chore: add license update script 2025-01-14 15:24:24 +03:00
e1829c4824 refactor: migrate repos find to struct 2025-01-14 13:18:51 +03:00
12d83f2015 test: fix decoder and handlers tests 2025-01-14 13:04:10 +03:00
6bc6bfdcd9 refactor: migrate dlcache to struct 2025-01-14 12:59:00 +03:00
eeb25c239b refactor: move defaultConfig from config_legacy to config 2025-01-14 11:54:00 +03:00
91937a1fc5 refactor: migrate repo to struct 2025-01-14 11:49:42 +03:00
e827fb8049 refactor: use context logger 2025-01-14 10:41:38 +03:00
a13acc5ed0 refactor: migrate list command to struct API 2025-01-14 10:35:23 +03:00
52d3ab7791 refactor: migrate db and config packages to use struct-based API
Removed global variables in favor of instance variables. This makes the code more maintainable and making it easier to write unit tests without relying on global state.

Marked the old functions with global state as obsolete, redirecting them to use a new API based on struct in order to rewrite the code using these functions gradually.
2025-01-14 10:11:17 +03:00
a345a24b95 fix: add auto_req and auto_prov 2024-12-27 21:15:37 +03:00
5d1d3d7c45 Merge pull request 'feat: add find-provides and find-requires (rpm only)' (#5) from Maks1mS/ALR:feat/auto-provides-requires into master
Reviewed-on: Plemya-x/ALR#5
2024-12-26 08:07:15 +00:00
a711edbcc0 fix: ignore empty dependencies 2024-12-23 21:12:08 +03:00
d5636e8094 feat: add find-provides and find-requires (rpm only) 2024-12-19 19:21:41 +03:00
5d17875813 Merge pull request 'Исправление file exists при распаковке архива' (#2) from Maks1mS/ALR:fix/unpack-file-exists-error into master
Reviewed-on: xpamych/ALR#2
2024-11-17 18:25:29 +00:00
41eec2fc98 fix: use MkdirAll instead Mkdir to ignore existing dirs 2024-11-17 21:11:54 +03:00
1273aeae39 Merge branch 'Maks1mS-master'
# Conflicts:
#	pkg/build/build.go
2024-11-16 11:55:20 +03:00
49785d4dc8 Комментирование кода, добавление возможности сборки нескольких пакетов package_* из одного alr.sh 2024-11-16 11:32:47 +03:00
1890311d11 fix: add alt only for rpm on altlinux 2024-11-15 16:35:31 +03:00
eb1c1a1d8c fix: add alt prefix for release 2024-11-15 16:25:09 +03:00
c105cf2cbb chore: go mod tidy 2024-11-15 16:21:57 +03:00
94048184c1 chore: update goreleaser/nfpm 2024-11-15 15:50:45 +03:00
3f1c1b6795 feat: add apt-rpm manager 2024-11-15 15:21:34 +03:00
90 changed files with 4557 additions and 2512 deletions

3
.gitignore vendored
View File

@@ -4,4 +4,5 @@
/dist/
/internal/config/version.txt
.fleet
.idea
.idea
.gigaide

46
.golangci.yml Normal file
View File

@@ -0,0 +1,46 @@
# 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/>.
run:
timeout: 5m
linters-settings:
goimports:
local-prefixes: "gitea.plemya-x.ru/Plemya-x/ALR"
gofmt:
simplify: true
gofumpt:
extra-rules: true
linters:
enable:
- gofmt
- gofumpt
- goimports
- gocritic
- govet
- staticcheck
- unused
- errcheck
- typecheck
# - forbidigo
issues:
fix: true
exclude-rules:
- path: _test\.go
linters:
- errcheck

View File

@@ -1,3 +1,22 @@
# This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
# It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
#
# ALR - Any Linux Repository
# Copyright (C) 2025 Евгений Храмов
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
before:
hooks:
- go mod tidy

View File

@@ -1,3 +1,22 @@
# This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
# It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
#
# ALR - Any Linux Repository
# Copyright (C) 2025 Евгений Храмов
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
platform: linux/amd64
pipeline:
release:

View File

@@ -11,6 +11,10 @@ ZSH_COMPLETION := $(COMPLETIONS_DIR)/zsh
INSTALLED_BASH_COMPLETION := $(DESTDIR)$(PREFIX)/share/bash-completion/completions/$(NAME)
INSTALLED_ZSH_COMPLETION := $(DESTDIR)$(PREFIX)/share/zsh/site-functions/_$(NAME)
ADD_LICENSE_BIN := go run github.com/google/addlicense@4caba19b7ed7818bb86bc4cd20411a246aa4a524
GOLANGCI_LINT_BIN := go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.2
XGOTEXT_BIN := go run github.com/Tom5521/xgotext@v1.2.0
.PHONY: build install clean clear uninstall check-no-root
build: check-no-root $(BIN)
@@ -48,3 +52,17 @@ uninstall:
clean clear:
rm -f $(BIN)
OLD_FILES=$$(< old-files)
IGNORE_OLD_FILES := $(foreach file,$(shell cat old-files),-ignore $(file))
update-license:
$(ADD_LICENSE_BIN) -v -f license-header-old-files.tmpl $(OLD_FILES)
$(ADD_LICENSE_BIN) -v -f license-header.tmpl $(IGNORE_OLD_FILES) .
fmt:
$(GOLANGCI_LINT_BIN) run --fix
i18n:
$(XGOTEXT_BIN) --output ./internal/translations/default.pot
msguniq --use-first -o ./internal/translations/default.pot ./internal/translations/default.pot
msgmerge --backup=off -U ./internal/translations/po/ru/default.po ./internal/translations/default.pot

View File

@@ -21,7 +21,7 @@ ALR написан на чистом Go и после сборки не имее
curl -fsSL plemya-x.ru/alr/install.sh | bash
```
**ВАЖНО**: При этом скрипт будет загружен и запущен с <https://gitea.plemya-x.ru/xpamych/ALR/install>. Пожалуйста, просматривайте любые скрипты, которые вы скачиваете из Интернета (включая этот), прежде чем запускать их.
**ВАЖНО**: При этом скрипт будет загружен и запущен с <https://gitea.plemya-x.ru/Plemya-x/ALR/src/branch/master/scripts/install.sh>. Пожалуйста, просматривайте любые скрипты, которые вы скачиваете из Интернета (включая этот), прежде чем запускать их.
### Сборка из исходного кода

View File

@@ -1,35 +1,38 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"log/slog"
"os"
"path/filepath"
"github.com/leonelquinteros/gotext"
"github.com/urfave/cli/v2"
"plemya-x.ru/alr/internal/config"
"plemya-x.ru/alr/internal/osutils"
"plemya-x.ru/alr/internal/types"
"plemya-x.ru/alr/pkg/build"
"plemya-x.ru/alr/pkg/loggerctx"
"plemya-x.ru/alr/pkg/manager"
"plemya-x.ru/alr/pkg/repos"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/osutils"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/types"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/build"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos"
)
var buildCmd = &cli.Command{
@@ -55,7 +58,6 @@ var buildCmd = &cli.Command{
},
Action: func(c *cli.Context) error {
ctx := c.Context
log := loggerctx.From(ctx)
script := c.String("script")
if c.String("package") != "" {
@@ -64,12 +66,14 @@ var buildCmd = &cli.Command{
err := repos.Pull(ctx, config.Config(ctx).Repos)
if err != nil {
log.Fatal("Error pulling repositories").Err(err).Send()
slog.Error(gotext.Get("Error pulling repositories"), "err", err)
os.Exit(1)
}
mgr := manager.Detect()
if mgr == nil {
log.Fatal("Unable to detect a supported package manager on the system").Send()
slog.Error(gotext.Get("Unable to detect a supported package manager on the system"))
os.Exit(1)
}
pkgPaths, _, err := build.BuildPackage(ctx, types.BuildOpts{
@@ -79,19 +83,22 @@ var buildCmd = &cli.Command{
Interactive: c.Bool("interactive"),
})
if err != nil {
log.Fatal("Error building package").Err(err).Send()
slog.Error(gotext.Get("Error building package"), "err", err)
os.Exit(1)
}
wd, err := os.Getwd()
if err != nil {
log.Fatal("Error getting working directory").Err(err).Send()
slog.Error(gotext.Get("Error getting working directory"), "err", err)
os.Exit(1)
}
for _, pkgPath := range pkgPaths {
name := filepath.Base(pkgPath)
err = osutils.Move(pkgPath, filepath.Join(wd, name))
if err != nil {
log.Fatal("Error moving the package").Err(err).Send()
slog.Error(gotext.Get("Error moving the package"), "err", err)
os.Exit(1)
}
}

61
fix.go
View File

@@ -1,31 +1,34 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"log/slog"
"os"
"github.com/leonelquinteros/gotext"
"github.com/urfave/cli/v2"
"plemya-x.ru/alr/internal/config"
"plemya-x.ru/alr/internal/db"
"plemya-x.ru/alr/pkg/loggerctx"
"plemya-x.ru/alr/pkg/repos"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos"
)
var fixCmd = &cli.Command{
@@ -33,31 +36,33 @@ var fixCmd = &cli.Command{
Usage: "Attempt to fix problems with ALR",
Action: func(c *cli.Context) error {
ctx := c.Context
log := loggerctx.From(ctx)
db.Close()
paths := config.GetPaths(ctx)
log.Info("Removing cache directory").Send()
slog.Info(gotext.Get("Removing cache directory"))
err := os.RemoveAll(paths.CacheDir)
if err != nil {
log.Fatal("Unable to remove cache directory").Err(err).Send()
slog.Error(gotext.Get("Unable to remove cache directory"), "err", err)
os.Exit(1)
}
log.Info("Rebuilding cache").Send()
slog.Info(gotext.Get("Rebuilding cache"))
err = os.MkdirAll(paths.CacheDir, 0o755)
if err != nil {
log.Fatal("Unable to create new cache directory").Err(err).Send()
slog.Error(gotext.Get("Unable to create new cache directory"), "err", err)
os.Exit(1)
}
err = repos.Pull(ctx, config.Config(ctx).Repos)
if err != nil {
log.Fatal("Error pulling repos").Err(err).Send()
slog.Error(gotext.Get("Error pulling repos"), "err", err)
os.Exit(1)
}
log.Info("Done").Send()
slog.Info(gotext.Get("Done"))
return nil
},

22
gen.go
View File

@@ -1,10 +1,30 @@
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"os"
"github.com/urfave/cli/v2"
"plemya-x.ru/alr/pkg/gen"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/gen"
)
var genCmd = &cli.Command{

64
go.mod
View File

@@ -1,4 +1,4 @@
module plemya-x.ru/alr
module gitea.plemya-x.ru/Plemya-x/ALR
go 1.21
@@ -10,26 +10,30 @@ require (
github.com/alecthomas/chroma/v2 v2.9.1
github.com/charmbracelet/bubbles v0.16.1
github.com/charmbracelet/bubbletea v0.24.2
github.com/charmbracelet/lipgloss v0.8.0
github.com/charmbracelet/lipgloss v0.10.0
github.com/charmbracelet/log v0.4.0
github.com/go-git/go-billy/v5 v5.5.0
github.com/go-git/go-git/v5 v5.9.0
github.com/goreleaser/nfpm/v2 v2.33.0
github.com/go-git/go-git/v5 v5.12.0
github.com/goreleaser/nfpm/v2 v2.41.0
github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08
github.com/jmoiron/sqlx v1.3.5
github.com/leonelquinteros/gotext v1.7.0
github.com/mattn/go-isatty v0.0.19
github.com/mholt/archiver/v4 v4.0.0-alpha.8
github.com/mitchellh/mapstructure v1.5.0
github.com/muesli/reflow v0.3.0
github.com/pelletier/go-toml/v2 v2.1.0
github.com/schollz/progressbar/v3 v3.13.1
github.com/stretchr/testify v1.10.0
github.com/urfave/cli/v2 v2.25.7
github.com/vmihailenco/msgpack/v5 v5.3.5
go.elara.ws/logger v0.0.0-20230421022458-e80700db2090
go.elara.ws/translate v0.0.0-20230421025926-32ccfcd110e6
go.elara.ws/vercmp v0.0.0-20230622214216-0b2b067575c4
golang.org/x/crypto v0.13.0
golang.org/x/exp v0.0.0-20230905200255-921286631fa9
golang.org/x/sys v0.12.0
golang.org/x/text v0.13.0
golang.org/x/crypto v0.27.0
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb
golang.org/x/sys v0.27.0
golang.org/x/text v0.21.0
gopkg.in/yaml.v3 v3.0.1
modernc.org/sqlite v1.25.0
mvdan.cc/sh/v3 v3.7.0
@@ -37,14 +41,13 @@ require (
)
require (
dario.cat/mergo v1.0.0 // indirect
dario.cat/mergo v1.0.1 // indirect
github.com/AlekSi/pointer v1.2.0 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.2.1 // indirect
github.com/Masterminds/semver/v3 v3.3.0 // indirect
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect
github.com/acomagu/bufpipe v1.0.4 // indirect
github.com/ProtonMail/go-crypto v1.0.0 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb // indirect
@@ -52,23 +55,25 @@ require (
github.com/bodgit/sevenzip v1.3.0 // indirect
github.com/bodgit/windows v1.0.0 // indirect
github.com/cavaliergopher/cpio v1.0.1 // indirect
github.com/cloudflare/circl v1.3.3 // indirect
github.com/cloudflare/circl v1.3.8 // indirect
github.com/connesc/cipherio v0.2.1 // indirect
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dlclark/regexp2 v1.10.0 // indirect
github.com/dsnet/compress v0.0.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/rpmpack v0.5.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/google/rpmpack v0.6.1-0.20240329070804-c2247cbb881a // indirect
github.com/google/uuid v1.4.0 // indirect
github.com/gookit/color v1.5.1 // indirect
github.com/goreleaser/chglog v0.5.0 // indirect
github.com/goreleaser/chglog v0.6.1 // indirect
github.com/goreleaser/fileglob v1.3.0 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
@@ -77,10 +82,10 @@ require (
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/compress v1.17.0 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-colorable v0.1.2 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
@@ -93,26 +98,27 @@ require (
github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect
github.com/pierrec/lz4/v4 v4.1.15 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sergi/go-diff v1.2.0 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/shopspring/decimal v1.2.0 // indirect
github.com/skeema/knownhosts v1.2.0 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/skeema/knownhosts v1.2.2 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/therootcompany/xz v1.0.1 // indirect
github.com/ulikunitz/xz v0.5.11 // indirect
github.com/ulikunitz/xz v0.5.12 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
gitlab.com/digitalxero/go-conventional-commit v1.0.7 // indirect
go4.org v0.0.0-20200411211856-f5505b9728dd // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.15.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/term v0.12.0 // indirect
golang.org/x/tools v0.13.0 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/term v0.24.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
lukechampine.com/uint128 v1.2.0 // indirect
modernc.org/cc/v3 v3.40.0 // indirect

155
go.sum
View File

@@ -14,8 +14,8 @@ cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2k
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
@@ -23,13 +23,13 @@ github.com/AlekSi/pointer v1.2.0 h1:glcy/gc4h8HnG2Z3ZECSzZ1IX1x2JxRVuDzaJwQE0+w=
github.com/AlekSi/pointer v1.2.0/go.mod h1:gZGfd3dpW4vEc/UlyfKKi1roIqcCgwOIvb0tSNSBle0=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ=
github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
@@ -37,16 +37,14 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg=
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k=
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw=
github.com/ProtonMail/gopenpgp/v2 v2.7.1 h1:Awsg7MPc2gD3I7IFac2qE3Gdls0lZW8SzrFZ3k1oz0s=
github.com/ProtonMail/gopenpgp/v2 v2.7.1/go.mod h1:/BU5gfAVwqyd8EfC3Eu7zmuhwYQpKs+cGD8M//iiaxs=
github.com/PuerkitoBio/purell v1.2.0 h1:/Jdm5QfyM8zdlqT6WVZU4cfP23sot6CEHA4CS49Ezig=
github.com/PuerkitoBio/purell v1.2.0/go.mod h1:OhLRTaaIzhvIyofkJfB24gokC7tM42Px5UhoT32THBk=
github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ=
github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink=
github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ=
github.com/alecthomas/chroma/v2 v2.9.1 h1:0O3lTQh9FxazJ4BYE/MOi/vDGuHn7B+6Bu902N2UZvU=
@@ -70,8 +68,6 @@ 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/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM=
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/caarlos0/go-rpmutils v0.2.1-0.20211112020245-2cd62ff89b11 h1:IRrDwVlWQr6kS1U8/EtyA1+EHcc4yl8pndcqXWrEamg=
github.com/caarlos0/go-rpmutils v0.2.1-0.20211112020245-2cd62ff89b11/go.mod h1:je2KZ+LxaCNvCoKg32jtOIULcFogJKcL1ZWUaIBjKj0=
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/cavaliergopher/cpio v1.0.1 h1:KQFSeKmZhv0cr+kawA3a0xTQCU4QxXF1vhU7P7av2KM=
@@ -81,20 +77,23 @@ github.com/charmbracelet/bubbles v0.16.1 h1:6uzpAAaT9ZqKssntbvZMlksWHruQLNxg49H5
github.com/charmbracelet/bubbles v0.16.1/go.mod h1:2QCp9LFlEsBQMvIYERr7Ww2H2bA7xen1idUDIzm/+Xc=
github.com/charmbracelet/bubbletea v0.24.2 h1:uaQIKx9Ai6Gdh5zpTbGiWpytMU+CfsPp06RaW2cx/SY=
github.com/charmbracelet/bubbletea v0.24.2/go.mod h1:XdrNrV4J8GiyshTtx3DNuYkR1FDaJmO3l2nejekbsgg=
github.com/charmbracelet/lipgloss v0.8.0 h1:IS00fk4XAHcf8uZKc3eHeMUTCxUH6NkaTrdyCQk84RU=
github.com/charmbracelet/lipgloss v0.8.0/go.mod h1:p4eYUZZJ/0oXTuCQKFF8mqyKCz0ja6y+7DniDDw5KKU=
github.com/charmbracelet/lipgloss v0.10.0 h1:KWeXFSexGcfahHX+54URiZGkBFazf70JNMtwg/AFW3s=
github.com/charmbracelet/lipgloss v0.10.0/go.mod h1:Wig9DSfvANsxqkRsqj6x87irdy123SR4dOXlKa91ciE=
github.com/charmbracelet/log v0.4.0 h1:G9bQAcx8rWA2T3pWvx7YtPTPwgqpk7D68BX21IRW8ZM=
github.com/charmbracelet/log v0.4.0/go.mod h1:63bXt/djrizTec0l11H20t8FDSvA4CRZJ1KH22MdptM=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs=
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/cloudflare/circl v1.3.8 h1:j+V8jJt09PoeMFIu2uh5JUyEaIHTXVOHslFoLNAKqwI=
github.com/cloudflare/circl v1.3.8/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
github.com/connesc/cipherio v0.2.1 h1:FGtpTPMbKNNWByNrr9aEBtaJtXjqOzkIXNYJp6OEycw=
github.com/connesc/cipherio v0.2.1/go.mod h1:ukY0MWJDFnJEbXMQtOcn2VmTpRfzcTz4OoVrWGGJZcA=
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY=
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
@@ -116,20 +115,22 @@ github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo=
github.com/go-git/go-git/v5 v5.9.0 h1:cD9SFA7sHVRdJ7AYck1ZaAa/yeuBvGPxwXDL8cxrObY=
github.com/go-git/go-git/v5 v5.9.0/go.mod h1:RKIqga24sWdMGZF+1Ekv9kylsDz6LzdTSI2s/OsZWE0=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys=
github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
@@ -158,8 +159,8 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
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/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-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@@ -167,23 +168,23 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/rpmpack v0.5.0 h1:L16KZ3QvkFGpYhmp23iQip+mx1X39foEsqszjMNBm8A=
github.com/google/rpmpack v0.5.0/go.mod h1:uqVAUVQLq8UY2hCDfmJ/+rtO3aw7qyhc90rCVEabEfI=
github.com/google/rpmpack v0.6.1-0.20240329070804-c2247cbb881a h1:JJBdjSfqSy3mnDT0940ASQFghwcZ4y4cb6ttjAoXqwE=
github.com/google/rpmpack v0.6.1-0.20240329070804-c2247cbb881a/go.mod h1:uqVAUVQLq8UY2hCDfmJ/+rtO3aw7qyhc90rCVEabEfI=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gookit/color v1.5.1 h1:Vjg2VEcdHpwq+oY63s/ksHrgJYCTo0bwWvmmYWdE9fQ=
github.com/gookit/color v1.5.1/go.mod h1:wZFzea4X8qN6vHOSP2apMb4/+w/orMznEzYsIHPaqKM=
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
github.com/goreleaser/chglog v0.5.0 h1:Sk6BMIpx8+vpAf8KyPit34OgWui8c7nKTMHhYx88jJ4=
github.com/goreleaser/chglog v0.5.0/go.mod h1:Ri46M3lrMuv76FHszs3vtABR8J8k1w9JHYAzxeeOl28=
github.com/goreleaser/chglog v0.6.1 h1:NZKiX8l0FTQPRzBgKST7knvNZmZ04f7PEGkN2wInfhE=
github.com/goreleaser/chglog v0.6.1/go.mod h1:Bnnfo07jMZkaAb0uRNASMZyOsX6ROW6X1qbXqN3guUo=
github.com/goreleaser/fileglob v1.3.0 h1:/X6J7U8lbDpQtBvGcwwPS6OpzkNVlVEsFUVRx9+k+7I=
github.com/goreleaser/fileglob v1.3.0/go.mod h1:Jx6BoXv3mbYkEzwm9THo7xbr5egkAraxkGorbJb4RxU=
github.com/goreleaser/nfpm/v2 v2.33.0 h1:yBv6jgkPwih4va/S42rceSjJ2Znt3Og/Ntc76oP0tfI=
github.com/goreleaser/nfpm/v2 v2.33.0/go.mod h1:8wwWWvJWmn84xo/Sqiv0aMvEGTHlHZTXTEuVSgQpkIM=
github.com/goreleaser/nfpm/v2 v2.41.0 h1:JyMzS/EwqaWbFs+7Z9oZ4Hkk4or00gUTqwm9Dgr8QYg=
github.com/goreleaser/nfpm/v2 v2.41.0/go.mod h1:VPc5kF5OgfA+BosV/A2aB+Vg34honjWvp0Vt8ogsSi0=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
@@ -202,6 +203,8 @@ github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jeandeaual/go-locale v0.0.0-20241217141322-fcc2cadd6f08 h1:wMeVzrPO3mfHIWLZtDcSaGAe2I4PW9B/P5nMkRSwCAc=
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/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
@@ -215,8 +218,8 @@ github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
@@ -227,16 +230,19 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leonelquinteros/gotext v1.7.0 h1:jcJmF4AXqyamP7vuw2MMIKs+O3jAEmvrc5JQiI8Ht/8=
github.com/leonelquinteros/gotext v1.7.0/go.mod h1:qJdoQuERPpccw7L70uoU+K/BvTfRBHYsisCQyFLXyvw=
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
@@ -291,30 +297,32 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
github.com/sassoftware/go-rpmutils v0.4.0 h1:ojND82NYBxgwrV+mX1CWsd5QJvvEZTKddtCdFLPWhpg=
github.com/sassoftware/go-rpmutils v0.4.0/go.mod h1:3goNWi7PGAT3/dlql2lv3+MSN5jNYPjT5mVcQcIsYzI=
github.com/schollz/progressbar/v3 v3.13.1 h1:o8rySDYiQ59Mwzy2FELeHY5ZARXZTVJC7iHD6PEFUiE=
github.com/schollz/progressbar/v3 v3.13.1/go.mod h1:xvrbki8kfT1fzWzBT/UZd9L6GA+jdL7HAgq2RFnO6fQ=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/skeema/knownhosts v1.2.0 h1:h9r9cf0+u7wSE+M183ZtMGgOJKiL96brpaz5ekfJCpM=
github.com/skeema/knownhosts v1.2.0/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo=
github.com/smartystreets/assertions v1.13.1 h1:Ef7KhSmjZcK6AVf9YbJdvPYG9avaF0ZxudX+ThRdWfU=
github.com/smartystreets/assertions v1.13.1/go.mod h1:cXr/IwVfSo/RbCSPhoAPv73p3hlSdrBH/b3SdnW/LMY=
github.com/smartystreets/goconvey v1.8.0 h1:Oi49ha/2MURE0WexF052Z0m+BNSGirfjg5RL+JXWq3w=
github.com/smartystreets/goconvey v1.8.0/go.mod h1:EdX8jtrTIj26jmjCOVNMVSIYAtgexqXKHOXW2Dx9JLg=
github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A=
github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@@ -326,13 +334,14 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
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/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
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.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8=
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
@@ -371,8 +380,8 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -381,8 +390,8 @@ golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb h1:c0vyKkb6yr3KR7jEfJaOSv4lG7xPkbN6r52aJz1d8a8=
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -402,8 +411,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -424,8 +433,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -439,8 +448,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -470,15 +479,15 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -489,8 +498,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -519,8 +528,8 @@ golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapK
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -566,8 +575,8 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
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.4/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/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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -1,17 +1,38 @@
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"fmt"
"log/slog"
"os"
"strings"
"github.com/leonelquinteros/gotext"
"github.com/urfave/cli/v2"
"plemya-x.ru/alr/internal/cpu"
"plemya-x.ru/alr/internal/shutils/helpers"
"plemya-x.ru/alr/pkg/distro"
"plemya-x.ru/alr/pkg/loggerctx"
"mvdan.cc/sh/v3/expand"
"mvdan.cc/sh/v3/interp"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/cpu"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/helpers"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
)
var helperCmd = &cli.Command{
@@ -29,7 +50,6 @@ var helperCmd = &cli.Command{
},
Action: func(c *cli.Context) error {
ctx := c.Context
log := loggerctx.From(ctx)
if c.Args().Len() < 1 {
cli.ShowSubcommandHelpAndExit(c, 1)
@@ -37,17 +57,20 @@ var helperCmd = &cli.Command{
helper, ok := helpers.Helpers[c.Args().First()]
if !ok {
log.Fatal("No such helper command").Str("name", c.Args().First()).Send()
slog.Error(gotext.Get("No such helper command"), "name", c.Args().First())
os.Exit(1)
}
wd, err := os.Getwd()
if err != nil {
log.Fatal("Error getting working directory").Err(err).Send()
slog.Error(gotext.Get("Error getting working directory"), "err", err)
os.Exit(1)
}
info, err := distro.ParseOSRelease(ctx)
if err != nil {
log.Fatal("Error getting working directory").Err(err).Send()
slog.Error(gotext.Get("Error getting working directory"), "err", err)
os.Exit(1)
}
hc := interp.HandlerContext{

178
info.go
View File

@@ -1,106 +1,122 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"fmt"
"log/slog"
"os"
"github.com/leonelquinteros/gotext"
"github.com/urfave/cli/v2"
"plemya-x.ru/alr/internal/cliutils"
"plemya-x.ru/alr/internal/config"
"plemya-x.ru/alr/internal/overrides"
"plemya-x.ru/alr/pkg/distro"
"plemya-x.ru/alr/pkg/loggerctx"
"plemya-x.ru/alr/pkg/repos"
"gopkg.in/yaml.v3"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/overrides"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos"
)
var infoCmd = &cli.Command{
Name: "info",
Usage: "Print information about a package",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "all",
Aliases: []string{"a"},
Usage: "Show all information, not just for the current distro",
func GetInfoCmd() *cli.Command {
return &cli.Command{
Name: "info",
Usage: gotext.Get("Print information about a package"),
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "all",
Aliases: []string{"a"},
Usage: gotext.Get("Show all information, not just for the current distro"),
},
},
},
Action: func(c *cli.Context) error {
ctx := c.Context
log := loggerctx.From(ctx)
Action: func(c *cli.Context) error {
ctx := c.Context
args := c.Args()
if args.Len() < 1 {
log.Fatalf("Command info expected at least 1 argument, got %d", args.Len()).Send()
}
cfg := config.New()
db := db.New(cfg)
rs := repos.New(cfg, db)
err := repos.Pull(ctx, config.Config(ctx).Repos)
if err != nil {
log.Fatal("Error pulling repositories").Err(err).Send()
}
found, _, err := repos.FindPkgs(ctx, args.Slice())
if err != nil {
log.Fatal("Error finding packages").Err(err).Send()
}
if len(found) == 0 {
os.Exit(1)
}
pkgs := cliutils.FlattenPkgs(ctx, found, "show", c.Bool("interactive"))
var names []string
all := c.Bool("all")
if !all {
info, err := distro.ParseOSRelease(ctx)
if err != nil {
log.Fatal("Error parsing os-release file").Err(err).Send()
args := c.Args()
if args.Len() < 1 {
slog.Error(gotext.Get("Command info expected at least 1 argument, got %d", args.Len()))
os.Exit(1)
}
names, err = overrides.Resolve(
info,
overrides.DefaultOpts.
WithLanguages([]string{config.SystemLang()}),
)
if err != nil {
log.Fatal("Error resolving overrides").Err(err).Send()
}
}
for _, pkg := range pkgs {
err := rs.Pull(ctx, cfg.Repos(ctx))
if err != nil {
slog.Error(gotext.Get("Error pulling repositories"))
os.Exit(1)
}
found, _, err := rs.FindPkgs(ctx, args.Slice())
if err != nil {
slog.Error(gotext.Get("Error finding packages"), "err", err)
os.Exit(1)
}
if len(found) == 0 {
os.Exit(1)
}
pkgs := cliutils.FlattenPkgs(ctx, found, "show", c.Bool("interactive"))
var names []string
all := c.Bool("all")
if !all {
err = yaml.NewEncoder(os.Stdout).Encode(overrides.ResolvePackage(&pkg, names))
info, err := distro.ParseOSRelease(ctx)
if err != nil {
log.Fatal("Error encoding script variables").Err(err).Send()
slog.Error(gotext.Get("Error parsing os-release file"), "err", err)
os.Exit(1)
}
} else {
err = yaml.NewEncoder(os.Stdout).Encode(pkg)
names, err = overrides.Resolve(
info,
overrides.DefaultOpts.
WithLanguages([]string{config.SystemLang()}),
)
if err != nil {
log.Fatal("Error encoding script variables").Err(err).Send()
slog.Error(gotext.Get("Error resolving overrides"), "err", err)
os.Exit(1)
}
}
fmt.Println("---")
}
for _, pkg := range pkgs {
if !all {
err = yaml.NewEncoder(os.Stdout).Encode(overrides.ResolvePackage(&pkg, names))
if err != nil {
slog.Error(gotext.Get("Error encoding script variables"), "err", err)
os.Exit(1)
}
} else {
err = yaml.NewEncoder(os.Stdout).Encode(pkg)
if err != nil {
slog.Error(gotext.Get("Error encoding script variables"), "err", err)
os.Exit(1)
}
}
return nil
},
fmt.Println("---")
}
return nil
},
}
}

View File

@@ -1,35 +1,39 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"fmt"
"log/slog"
"os"
"github.com/leonelquinteros/gotext"
"github.com/urfave/cli/v2"
"plemya-x.ru/alr/internal/cliutils"
"plemya-x.ru/alr/internal/config"
"plemya-x.ru/alr/internal/db"
"plemya-x.ru/alr/internal/types"
"plemya-x.ru/alr/pkg/build"
"plemya-x.ru/alr/pkg/loggerctx"
"plemya-x.ru/alr/pkg/manager"
"plemya-x.ru/alr/pkg/repos"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/types"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/build"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos"
)
var installCmd = &cli.Command{
@@ -45,26 +49,29 @@ var installCmd = &cli.Command{
},
Action: func(c *cli.Context) error {
ctx := c.Context
log := loggerctx.From(ctx)
args := c.Args()
if args.Len() < 1 {
log.Fatalf("Command install expected at least 1 argument, got %d", args.Len()).Send()
slog.Error(gotext.Get("Command install expected at least 1 argument, got %d", args.Len()))
os.Exit(1)
}
mgr := manager.Detect()
if mgr == nil {
log.Fatal("Unable to detect a supported package manager on the system").Send()
slog.Error(gotext.Get("Unable to detect a supported package manager on the system"))
os.Exit(1)
}
err := repos.Pull(ctx, config.Config(ctx).Repos)
if err != nil {
log.Fatal("Error pulling repositories").Err(err).Send()
slog.Error(gotext.Get("Error pulling repositories"), "err", err)
os.Exit(1)
}
found, notFound, err := repos.FindPkgs(ctx, args.Slice())
if err != nil {
log.Fatal("Error finding packages").Err(err).Send()
slog.Error(gotext.Get("Error finding packages"), "err", err)
os.Exit(1)
}
pkgs := cliutils.FlattenPkgs(ctx, found, "install", c.Bool("interactive"))
@@ -76,10 +83,10 @@ var installCmd = &cli.Command{
return nil
},
BashComplete: func(c *cli.Context) {
log := loggerctx.From(c.Context)
result, err := db.GetPkgs(c.Context, "true")
if err != nil {
log.Fatal("Error getting packages").Err(err).Send()
slog.Error(gotext.Get("Error getting packages"), "err", err)
os.Exit(1)
}
defer result.Close()
@@ -87,7 +94,8 @@ var installCmd = &cli.Command{
var pkg db.Package
err = result.StructScan(&pkg)
if err != nil {
log.Fatal("Error iterating over packages").Err(err).Send()
slog.Error(gotext.Get("Error iterating over packages"), "err", err)
os.Exit(1)
}
fmt.Println(pkg.Name)
@@ -100,21 +108,22 @@ var removeCmd = &cli.Command{
Usage: "Remove an installed package",
Aliases: []string{"rm"},
Action: func(c *cli.Context) error {
log := loggerctx.From(c.Context)
args := c.Args()
if args.Len() < 1 {
log.Fatalf("Command remove expected at least 1 argument, got %d", args.Len()).Send()
slog.Error(gotext.Get("Command remove expected at least 1 argument, got %d", args.Len()))
os.Exit(1)
}
mgr := manager.Detect()
if mgr == nil {
log.Fatal("Unable to detect a supported package manager on the system").Send()
slog.Error(gotext.Get("Unable to detect a supported package manager on the system"))
os.Exit(1)
}
err := mgr.Remove(nil, c.Args().Slice()...)
if err != nil {
log.Fatal("Error removing packages").Err(err).Send()
slog.Error(gotext.Get("Error removing packages"), "err", err)
os.Exit(1)
}
return nil

View File

@@ -1,34 +1,35 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package cliutils
import (
"context"
"log/slog"
"os"
"strings"
"github.com/AlecAivazis/survey/v2"
"plemya-x.ru/alr/internal/config"
"plemya-x.ru/alr/internal/db"
"plemya-x.ru/alr/internal/pager"
"plemya-x.ru/alr/internal/translations"
"plemya-x.ru/alr/pkg/loggerctx"
"github.com/leonelquinteros/gotext"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/pager"
)
// YesNoPrompt asks the user a yes or no question, using def as the default answer
@@ -37,7 +38,7 @@ func YesNoPrompt(ctx context.Context, msg string, interactive, def bool) (bool,
var answer bool
err := survey.AskOne(
&survey.Confirm{
Message: translations.Translator(ctx).TranslateTo(msg, config.Language(ctx)),
Message: msg,
Default: def,
},
&answer,
@@ -52,14 +53,11 @@ func YesNoPrompt(ctx context.Context, msg string, interactive, def bool) (bool,
// shows it if they answer yes, then asks if they'd still like to
// continue, and exits if they answer no.
func PromptViewScript(ctx context.Context, script, name, style string, interactive bool) error {
log := loggerctx.From(ctx)
if !interactive {
return nil
}
scriptPrompt := translations.Translator(ctx).TranslateTo("Would you like to view the build script for", config.Language(ctx)) + " " + name
view, err := YesNoPrompt(ctx, scriptPrompt, interactive, false)
view, err := YesNoPrompt(ctx, gotext.Get("Would you like to view the build script for %s", name), interactive, false)
if err != nil {
return err
}
@@ -70,13 +68,14 @@ func PromptViewScript(ctx context.Context, script, name, style string, interacti
return err
}
cont, err := YesNoPrompt(ctx, "Would you still like to continue?", interactive, false)
cont, err := YesNoPrompt(ctx, gotext.Get("Would you still like to continue?"), interactive, false)
if err != nil {
return err
}
if !cont {
log.Fatal(translations.Translator(ctx).TranslateTo("User chose not to continue after reading script", config.Language(ctx))).Send()
slog.Error(gotext.Get("User chose not to continue after reading script"))
os.Exit(1)
}
}
@@ -104,13 +103,13 @@ func ShowScript(path, name, style string) error {
// FlattenPkgs attempts to flatten the a map of slices of packages into a single slice
// of packages by prompting the user if multiple packages match.
func FlattenPkgs(ctx context.Context, found map[string][]db.Package, verb string, interactive bool) []db.Package {
log := loggerctx.From(ctx)
var outPkgs []db.Package
for _, pkgs := range found {
if len(pkgs) > 1 && interactive {
choice, err := PkgPrompt(ctx, pkgs, verb, interactive)
if err != nil {
log.Fatal("Error prompting for choice of package").Send()
slog.Error(gotext.Get("Error prompting for choice of package"))
os.Exit(1)
}
outPkgs = append(outPkgs, choice)
} else if len(pkgs) == 1 || !interactive {
@@ -133,7 +132,7 @@ func PkgPrompt(ctx context.Context, options []db.Package, verb string, interacti
prompt := &survey.Select{
Options: names,
Message: translations.Translator(ctx).TranslateTo("Choose which package to "+verb, config.Language(ctx)),
Message: gotext.Get("Choose which package to %s", verb),
}
var choice int
@@ -154,7 +153,7 @@ func ChooseOptDepends(ctx context.Context, options []string, verb string, intera
prompt := &survey.MultiSelect{
Options: options,
Message: translations.Translator(ctx).TranslateTo("Choose which optional package(s) to install", config.Language(ctx)),
Message: gotext.Get("Choose which optional package(s) to install"),
}
var choices []int

View File

@@ -1,33 +1,46 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package config
import (
"context"
"log/slog"
"os"
"path/filepath"
"sync"
"github.com/pelletier/go-toml/v2"
"plemya-x.ru/alr/internal/types"
"plemya-x.ru/alr/pkg/loggerctx"
"github.com/leonelquinteros/gotext"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/types"
)
type ALRConfig struct {
cfg *types.Config
paths *Paths
cfgOnce sync.Once
pathsOnce sync.Once
}
var defaultConfig = &types.Config{
RootCmd: "sudo",
PagerStyle: "native",
@@ -40,40 +53,112 @@ var defaultConfig = &types.Config{
},
}
var (
configMtx sync.Mutex
config *types.Config
)
func New() *ALRConfig {
return &ALRConfig{}
}
// Config returns a ALR configuration struct.
// The first time it's called, it'll load the config from a file.
// Subsequent calls will just return the same value.
func Config(ctx context.Context) *types.Config {
configMtx.Lock()
defer configMtx.Unlock()
log := loggerctx.From(ctx)
func (c *ALRConfig) Load(ctx context.Context) {
cfgFl, err := os.Open(c.GetPaths(ctx).ConfigPath)
if err != nil {
slog.Warn(gotext.Get("Error opening config file, using defaults"), "err", err)
c.cfg = defaultConfig
return
}
defer cfgFl.Close()
if config == nil {
cfgFl, err := os.Open(GetPaths(ctx).ConfigPath)
if err != nil {
log.Warn("Error opening config file, using defaults").Err(err).Send()
return defaultConfig
}
defer cfgFl.Close()
// Copy the default configuration into config
defCopy := *defaultConfig
config := &defCopy
config.Repos = nil
// Copy the default configuration into config
defCopy := *defaultConfig
config = &defCopy
config.Repos = nil
err = toml.NewDecoder(cfgFl).Decode(config)
if err != nil {
slog.Warn(gotext.Get("Error decoding config file, using defaults"), "err", err)
c.cfg = defaultConfig
return
}
c.cfg = config
}
err = toml.NewDecoder(cfgFl).Decode(config)
if err != nil {
log.Warn("Error decoding config file, using defaults").Err(err).Send()
// Set config back to nil so that we try again next time
config = nil
return defaultConfig
}
func (c *ALRConfig) initPaths() {
paths := &Paths{}
cfgDir, err := os.UserConfigDir()
if err != nil {
slog.Error(gotext.Get("Unable to detect user config directory"), "err", err)
os.Exit(1)
}
return config
paths.ConfigDir = filepath.Join(cfgDir, "alr")
err = os.MkdirAll(paths.ConfigDir, 0o755)
if err != nil {
slog.Error(gotext.Get("Unable to create ALR config directory"), "err", err)
os.Exit(1)
}
paths.ConfigPath = filepath.Join(paths.ConfigDir, "alr.toml")
if _, err := os.Stat(paths.ConfigPath); err != nil {
cfgFl, err := os.Create(paths.ConfigPath)
if err != nil {
slog.Error(gotext.Get("Unable to create ALR config file"), "err", err)
os.Exit(1)
}
err = toml.NewEncoder(cfgFl).Encode(&defaultConfig)
if err != nil {
slog.Error(gotext.Get("Error encoding default configuration"), "err", err)
os.Exit(1)
}
cfgFl.Close()
}
cacheDir, err := os.UserCacheDir()
if err != nil {
slog.Error(gotext.Get("Unable to detect cache directory"), "err", err)
os.Exit(1)
}
paths.CacheDir = filepath.Join(cacheDir, "alr")
paths.RepoDir = filepath.Join(paths.CacheDir, "repo")
paths.PkgsDir = filepath.Join(paths.CacheDir, "pkgs")
err = os.MkdirAll(paths.RepoDir, 0o755)
if err != nil {
slog.Error(gotext.Get("Unable to create repo cache directory"), "err", err)
os.Exit(1)
}
err = os.MkdirAll(paths.PkgsDir, 0o755)
if err != nil {
slog.Error(gotext.Get("Unable to create package cache directory"), "err", err)
os.Exit(1)
}
paths.DBPath = filepath.Join(paths.CacheDir, "db")
c.paths = paths
}
func (c *ALRConfig) GetPaths(ctx context.Context) *Paths {
c.pathsOnce.Do(func() {
c.initPaths()
})
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) IgnorePkgUpdates(ctx context.Context) []string {
c.cfgOnce.Do(func() {
c.Load(ctx)
})
return c.cfg.IgnorePkgUpdates
}

View File

@@ -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/>.
package config
import (
"context"
"sync"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/types"
)
// Config returns a ALR configuration struct.
// The first time it's called, it'll load the config from a file.
// Subsequent calls will just return the same value.
//
// Deprecated: use struct method
func Config(ctx context.Context) *types.Config {
return GetInstance(ctx).cfg
}
// =======================
// FOR LEGACY ONLY
// =======================
var (
alrConfig *ALRConfig
alrConfigOnce sync.Once
)
// Deprecated: For legacy only
func GetInstance(ctx context.Context) *ALRConfig {
alrConfigOnce.Do(func() {
alrConfig = New()
alrConfig.Load(ctx)
})
return alrConfig
}

View File

@@ -1,30 +1,32 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package config
import (
"context"
"log/slog"
"os"
"strings"
"sync"
"plemya-x.ru/alr/pkg/loggerctx"
"github.com/leonelquinteros/gotext"
"golang.org/x/text/language"
)
@@ -41,12 +43,12 @@ var (
func Language(ctx context.Context) language.Tag {
langMtx.Lock()
defer langMtx.Unlock()
log := loggerctx.From(ctx)
if !langSet {
syslang := SystemLang()
tag, err := language.Parse(syslang)
if err != nil {
log.Fatal("Error parsing system language").Err(err).Send()
slog.Error(gotext.Get("Error parsing system language"), "err", err)
os.Exit(1)
}
base, _ := tag.Base()
lang = language.Make(base.String())

View File

@@ -1,31 +1,26 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package config
import (
"context"
"os"
"path/filepath"
"sync"
"github.com/pelletier/go-toml/v2"
"plemya-x.ru/alr/pkg/loggerctx"
)
// Paths contains various paths used by ALR
@@ -38,71 +33,13 @@ type Paths struct {
DBPath string
}
var (
pathsMtx sync.Mutex
paths *Paths
)
// GetPaths returns a Paths struct.
// The first time it's called, it'll generate the struct
// using information from the system.
// Subsequent calls will return the same value.
//
// Depreacted: use struct API
func GetPaths(ctx context.Context) *Paths {
pathsMtx.Lock()
defer pathsMtx.Unlock()
log := loggerctx.From(ctx)
if paths == nil {
paths = &Paths{}
cfgDir, err := os.UserConfigDir()
if err != nil {
log.Fatal("Unable to detect user config directory").Err(err).Send()
}
paths.ConfigDir = filepath.Join(cfgDir, "alr")
err = os.MkdirAll(paths.ConfigDir, 0o755)
if err != nil {
log.Fatal("Unable to create ALR config directory").Err(err).Send()
}
paths.ConfigPath = filepath.Join(paths.ConfigDir, "alr.toml")
if _, err := os.Stat(paths.ConfigPath); err != nil {
cfgFl, err := os.Create(paths.ConfigPath)
if err != nil {
log.Fatal("Unable to create ALR config file").Err(err).Send()
}
err = toml.NewEncoder(cfgFl).Encode(&defaultConfig)
if err != nil {
log.Fatal("Error encoding default configuration").Err(err).Send()
}
cfgFl.Close()
}
cacheDir, err := os.UserCacheDir()
if err != nil {
log.Fatal("Unable to detect cache directory").Err(err).Send()
}
paths.CacheDir = filepath.Join(cacheDir, "alr")
paths.RepoDir = filepath.Join(paths.CacheDir, "repo")
paths.PkgsDir = filepath.Join(paths.CacheDir, "pkgs")
err = os.MkdirAll(paths.RepoDir, 0o755)
if err != nil {
log.Fatal("Unable to create repo cache directory").Err(err).Send()
}
err = os.MkdirAll(paths.PkgsDir, 0o755)
if err != nil {
log.Fatal("Unable to create package cache directory").Err(err).Send()
}
paths.DBPath = filepath.Join(paths.CacheDir, "db")
}
return paths
alrConfig := GetInstance(ctx)
return alrConfig.GetPaths(ctx)
}

View File

@@ -1,3 +1,22 @@
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package config
// Version contains the version of ALR. If the version

View File

@@ -1,20 +1,21 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package cpu

View File

@@ -1,47 +1,38 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package db
import (
"context"
"database/sql"
"database/sql/driver"
"encoding/json"
"errors"
"fmt"
"sync"
"log/slog"
"github.com/jmoiron/sqlx"
"plemya-x.ru/alr/internal/config"
"plemya-x.ru/alr/pkg/loggerctx"
"golang.org/x/exp/slices"
"modernc.org/sqlite"
"github.com/leonelquinteros/gotext"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
)
// CurrentVersion is the current version of the database.
// The database is reset if its version doesn't match this.
const CurrentVersion = 2
func init() {
sqlite.MustRegisterScalarFunction("json_array_contains", 2, jsonArrayContains)
}
// Package is a ALR package's database representation
type Package struct {
Name string `sh:"name,required" db:"name"`
@@ -66,66 +57,46 @@ type version struct {
Version int `db:"version"`
}
var (
mu sync.Mutex
type Config interface {
GetPaths(ctx context.Context) *config.Paths
}
type Database struct {
conn *sqlx.DB
closed = true
)
config Config
}
// DB returns the ALR database.
// The first time it's called, it opens the SQLite database file.
// Subsequent calls return the same connection.
func DB(ctx context.Context) *sqlx.DB {
log := loggerctx.From(ctx)
if conn != nil && !closed {
return getConn()
func New(config Config) *Database {
return &Database{
config: config,
}
_, err := open(ctx, config.GetPaths(ctx).DBPath)
}
func (d *Database) Init(ctx context.Context) error {
err := d.Connect(ctx)
if err != nil {
log.Fatal("Error opening database").Err(err).Send()
return err
}
return getConn()
return d.initDB(ctx)
}
func getConn() *sqlx.DB {
mu.Lock()
defer mu.Unlock()
return conn
}
func open(ctx context.Context, dsn string) (*sqlx.DB, error) {
func (d *Database) Connect(ctx context.Context) error {
dsn := d.config.GetPaths(ctx).DBPath
db, err := sqlx.Open("sqlite", dsn)
if err != nil {
return nil, err
return err
}
mu.Lock()
conn = db
closed = false
mu.Unlock()
err = initDB(ctx, dsn)
if err != nil {
return nil, err
}
return db, nil
d.conn = db
return nil
}
// Close closes the database
func Close() error {
closed = true
if conn != nil {
return conn.Close()
} else {
return nil
}
func (d *Database) GetConn() *sqlx.DB {
return d.conn
}
// initDB initializes the database
func initDB(ctx context.Context, dsn string) error {
log := loggerctx.From(ctx)
conn = conn.Unsafe()
func (d *Database) initDB(ctx context.Context) error {
d.conn = d.conn.Unsafe()
conn := d.conn
_, err := conn.ExecContext(ctx, `
CREATE TABLE IF NOT EXISTS pkgs (
name TEXT NOT NULL,
@@ -155,58 +126,72 @@ func initDB(ctx context.Context, dsn string) error {
return err
}
ver, ok := GetVersion(ctx)
ver, ok := d.GetVersion(ctx)
if ok && ver != CurrentVersion {
log.Warn("Database version mismatch; resetting").Int("version", ver).Int("expected", CurrentVersion).Send()
reset(ctx)
return initDB(ctx, dsn)
slog.Warn(gotext.Get("Database version mismatch; resetting"), "version", ver, "expected", CurrentVersion)
d.reset(ctx)
return d.initDB(ctx)
} else if !ok {
log.Warn("Database version does not exist. Run alr fix if something isn't working.").Send()
return addVersion(ctx, CurrentVersion)
slog.Warn(gotext.Get("Database version does not exist. Run alr fix if something isn't working."), "version", ver, "expected", CurrentVersion)
return d.addVersion(ctx, CurrentVersion)
}
return nil
}
// reset drops all the database tables
func reset(ctx context.Context) error {
_, err := DB(ctx).ExecContext(ctx, "DROP TABLE IF EXISTS pkgs;")
if err != nil {
return err
}
_, err = DB(ctx).ExecContext(ctx, "DROP TABLE IF EXISTS alr_db_version;")
return err
}
// IsEmpty returns true if the database has no packages in it, otherwise it returns false.
func IsEmpty(ctx context.Context) bool {
var count int
err := DB(ctx).GetContext(ctx, &count, "SELECT count(1) FROM pkgs;")
if err != nil {
return true
}
return count == 0
}
// GetVersion returns the database version and a boolean indicating
// whether the database contained a version number
func GetVersion(ctx context.Context) (int, bool) {
func (d *Database) GetVersion(ctx context.Context) (int, bool) {
var ver version
err := DB(ctx).GetContext(ctx, &ver, "SELECT * FROM alr_db_version LIMIT 1;")
err := d.conn.GetContext(ctx, &ver, "SELECT * FROM alr_db_version LIMIT 1;")
if err != nil {
return 0, false
}
return ver.Version, true
}
func addVersion(ctx context.Context, ver int) error {
_, err := DB(ctx).ExecContext(ctx, `INSERT INTO alr_db_version(version) VALUES (?);`, ver)
func (d *Database) addVersion(ctx context.Context, ver int) error {
_, err := d.conn.ExecContext(ctx, `INSERT INTO alr_db_version(version) VALUES (?);`, ver)
return err
}
// InsertPackage adds a package to the database
func InsertPackage(ctx context.Context, pkg Package) error {
_, err := DB(ctx).NamedExecContext(ctx, `
func (d *Database) reset(ctx context.Context) error {
_, err := d.conn.ExecContext(ctx, "DROP TABLE IF EXISTS pkgs;")
if err != nil {
return err
}
_, err = d.conn.ExecContext(ctx, "DROP TABLE IF EXISTS alr_db_version;")
return err
}
func (d *Database) GetPkgs(ctx context.Context, where string, args ...any) (*sqlx.Rows, error) {
stream, err := d.conn.QueryxContext(ctx, "SELECT * FROM pkgs WHERE "+where, args...)
if err != nil {
return nil, err
}
return stream, nil
}
func (d *Database) GetPkg(ctx context.Context, where string, args ...any) (*Package, error) {
out := &Package{}
err := d.conn.GetContext(ctx, out, "SELECT * FROM pkgs WHERE "+where+" LIMIT 1", args...)
return out, err
}
func (d *Database) DeletePkgs(ctx context.Context, where string, args ...any) error {
_, err := d.conn.ExecContext(ctx, "DELETE FROM pkgs WHERE "+where, args...)
return err
}
func (d *Database) IsEmpty(ctx context.Context) bool {
var count int
err := d.conn.GetContext(ctx, &count, "SELECT count(1) FROM pkgs;")
if err != nil {
return true
}
return count == 0
}
func (d *Database) InsertPackage(ctx context.Context, pkg Package) error {
_, err := d.conn.NamedExecContext(ctx, `
INSERT OR REPLACE INTO pkgs (
name,
repository,
@@ -246,101 +231,10 @@ func InsertPackage(ctx context.Context, pkg Package) error {
return err
}
// GetPkgs returns a result containing packages that match the where conditions
func GetPkgs(ctx context.Context, where string, args ...any) (*sqlx.Rows, error) {
stream, err := DB(ctx).QueryxContext(ctx, "SELECT * FROM pkgs WHERE "+where, args...)
if err != nil {
return nil, err
}
return stream, nil
}
// GetPkg returns a single package that matches the where conditions
func GetPkg(ctx context.Context, where string, args ...any) (*Package, error) {
out := &Package{}
err := DB(ctx).GetContext(ctx, out, "SELECT * FROM pkgs WHERE "+where+" LIMIT 1", args...)
return out, err
}
// DeletePkgs deletes all packages matching the where conditions
func DeletePkgs(ctx context.Context, where string, args ...any) error {
_, err := DB(ctx).ExecContext(ctx, "DELETE FROM pkgs WHERE "+where, args...)
return err
}
// jsonArrayContains is an SQLite function that checks if a JSON array
// in the database contains a given value
func jsonArrayContains(ctx *sqlite.FunctionContext, args []driver.Value) (driver.Value, error) {
value, ok := args[0].(string)
if !ok {
return nil, errors.New("both arguments to json_array_contains must be strings")
}
item, ok := args[1].(string)
if !ok {
return nil, errors.New("both arguments to json_array_contains must be strings")
}
var array []string
err := json.Unmarshal([]byte(value), &array)
if err != nil {
return nil, err
}
return slices.Contains(array, item), nil
}
// JSON represents a JSON value in the database
type JSON[T any] struct {
Val T
}
// NewJSON creates a new database JSON value
func NewJSON[T any](v T) JSON[T] {
return JSON[T]{Val: v}
}
func (s *JSON[T]) Scan(val any) error {
if val == nil {
func (d *Database) Close() error {
if d.conn != nil {
return d.conn.Close()
} else {
return nil
}
switch val := val.(type) {
case string:
err := json.Unmarshal([]byte(val), &s.Val)
if err != nil {
return err
}
case sql.NullString:
if val.Valid {
err := json.Unmarshal([]byte(val.String), &s.Val)
if err != nil {
return err
}
}
default:
return errors.New("sqlite json types must be strings")
}
return nil
}
func (s JSON[T]) Value() (driver.Value, error) {
data, err := json.Marshal(s.Val)
if err != nil {
return nil, err
}
return string(data), nil
}
func (s JSON[T]) MarshalYAML() (any, error) {
return s.Val, nil
}
func (s JSON[T]) String() string {
return fmt.Sprint(s.Val)
}
func (s JSON[T]) GoString() string {
return fmt.Sprintf("%#v", s.Val)
}

106
internal/db/db_legacy.go Normal file
View File

@@ -0,0 +1,106 @@
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package db
import (
"context"
"log/slog"
"os"
"sync"
"github.com/jmoiron/sqlx"
"github.com/leonelquinteros/gotext"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
)
// DB returns the ALR database.
// The first time it's called, it opens the SQLite database file.
// Subsequent calls return the same connection.
//
// Deprecated: use struct method
func DB(ctx context.Context) *sqlx.DB {
return GetInstance(ctx).GetConn()
}
// Close closes the database
//
// Deprecated: use struct method
func Close() error {
if database != nil {
return database.Close()
}
return nil
}
// IsEmpty returns true if the database has no packages in it, otherwise it returns false.
//
// Deprecated: use struct method
func IsEmpty(ctx context.Context) bool {
return GetInstance(ctx).IsEmpty(ctx)
}
// InsertPackage adds a package to the database
//
// Deprecated: use struct method
func InsertPackage(ctx context.Context, pkg Package) error {
return GetInstance(ctx).InsertPackage(ctx, pkg)
}
// GetPkgs returns a result containing packages that match the where conditions
//
// Deprecated: use struct method
func GetPkgs(ctx context.Context, where string, args ...any) (*sqlx.Rows, error) {
return GetInstance(ctx).GetPkgs(ctx, where, args...)
}
// GetPkg returns a single package that matches the where conditions
//
// Deprecated: use struct method
func GetPkg(ctx context.Context, where string, args ...any) (*Package, error) {
return GetInstance(ctx).GetPkg(ctx, where, args...)
}
// DeletePkgs deletes all packages matching the where conditions
//
// Deprecated: use struct method
func DeletePkgs(ctx context.Context, where string, args ...any) error {
return GetInstance(ctx).DeletePkgs(ctx, where, args...)
}
// =======================
// FOR LEGACY ONLY
// =======================
var (
dbOnce sync.Once
database *Database
)
// Deprecated: For legacy only
func GetInstance(ctx context.Context) *Database {
dbOnce.Do(func() {
cfg := config.GetInstance(ctx)
database = New(cfg)
err := database.Init(ctx)
if err != nil {
slog.Error(gotext.Get("Error opening database"), "err", err)
os.Exit(1)
}
})
return database
}

View File

@@ -1,32 +1,50 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package db_test
import (
"context"
"reflect"
"strings"
"testing"
"github.com/jmoiron/sqlx"
"plemya-x.ru/alr/internal/db"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
)
type TestALRConfig struct{}
func (c *TestALRConfig) GetPaths(ctx context.Context) *config.Paths {
return &config.Paths{
DBPath: ":memory:",
}
}
func prepareDb() *db.Database {
database := db.New(&TestALRConfig{})
database.Init(context.Background())
return database
}
var testPkg = db.Package{
Name: "test",
Version: "0.0.1",
@@ -59,18 +77,11 @@ var testPkg = db.Package{
}
func TestInit(t *testing.T) {
_, err := db.Open(":memory:")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
defer db.Close()
ctx := context.Background()
database := prepareDb()
defer database.Close()
_, err = db.DB().Exec("SELECT * FROM pkgs")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
ver, ok := db.GetVersion()
ver, ok := database.GetVersion(ctx)
if !ok {
t.Errorf("Expected version to be present")
} else if ver != db.CurrentVersion {
@@ -79,19 +90,17 @@ func TestInit(t *testing.T) {
}
func TestInsertPackage(t *testing.T) {
_, err := db.Open(":memory:")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
defer db.Close()
ctx := context.Background()
database := prepareDb()
defer database.Close()
err = db.InsertPackage(testPkg)
err := database.InsertPackage(ctx, testPkg)
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
dbPkg := db.Package{}
err = sqlx.Get(db.DB(), &dbPkg, "SELECT * FROM pkgs WHERE name = 'test' AND repository = 'default'")
err = sqlx.Get(database.GetConn(), &dbPkg, "SELECT * FROM pkgs WHERE name = 'test' AND repository = 'default'")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
@@ -102,28 +111,26 @@ func TestInsertPackage(t *testing.T) {
}
func TestGetPkgs(t *testing.T) {
_, err := db.Open(":memory:")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
defer db.Close()
ctx := context.Background()
database := prepareDb()
defer database.Close()
x1 := testPkg
x1.Name = "x1"
x2 := testPkg
x2.Name = "x2"
err = db.InsertPackage(x1)
err := database.InsertPackage(ctx, x1)
if err != nil {
t.Errorf("Expected no error, got %s", err)
}
err = db.InsertPackage(x2)
err = database.InsertPackage(ctx, x2)
if err != nil {
t.Errorf("Expected no error, got %s", err)
}
result, err := db.GetPkgs("name LIKE 'x%'")
result, err := database.GetPkgs(ctx, "name LIKE 'x%'")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
@@ -142,28 +149,26 @@ func TestGetPkgs(t *testing.T) {
}
func TestGetPkg(t *testing.T) {
_, err := db.Open(":memory:")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
defer db.Close()
ctx := context.Background()
database := prepareDb()
defer database.Close()
x1 := testPkg
x1.Name = "x1"
x2 := testPkg
x2.Name = "x2"
err = db.InsertPackage(x1)
err := database.InsertPackage(ctx, x1)
if err != nil {
t.Errorf("Expected no error, got %s", err)
}
err = db.InsertPackage(x2)
err = database.InsertPackage(ctx, x2)
if err != nil {
t.Errorf("Expected no error, got %s", err)
}
pkg, err := db.GetPkg("name LIKE 'x%' ORDER BY name")
pkg, err := database.GetPkg(ctx, "name LIKE 'x%' ORDER BY name")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
@@ -178,34 +183,32 @@ func TestGetPkg(t *testing.T) {
}
func TestDeletePkgs(t *testing.T) {
_, err := db.Open(":memory:")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
defer db.Close()
ctx := context.Background()
database := prepareDb()
defer database.Close()
x1 := testPkg
x1.Name = "x1"
x2 := testPkg
x2.Name = "x2"
err = db.InsertPackage(x1)
err := database.InsertPackage(ctx, x1)
if err != nil {
t.Errorf("Expected no error, got %s", err)
}
err = db.InsertPackage(x2)
err = database.InsertPackage(ctx, x2)
if err != nil {
t.Errorf("Expected no error, got %s", err)
}
err = db.DeletePkgs("name = 'x1'")
err = database.DeletePkgs(ctx, "name = 'x1'")
if err != nil {
t.Errorf("Expected no error, got %s", err)
}
var dbPkg db.Package
err = db.DB().Get(&dbPkg, "SELECT * FROM pkgs WHERE name LIKE 'x%' ORDER BY name LIMIT 1;")
err = database.GetConn().Get(&dbPkg, "SELECT * FROM pkgs WHERE name LIKE 'x%' ORDER BY name LIMIT 1;")
if err != nil {
t.Errorf("Expected no error, got %s", err)
}
@@ -216,11 +219,9 @@ func TestDeletePkgs(t *testing.T) {
}
func TestJsonArrayContains(t *testing.T) {
_, err := db.Open(":memory:")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
defer db.Close()
ctx := context.Background()
database := prepareDb()
defer database.Close()
x1 := testPkg
x1.Name = "x1"
@@ -228,18 +229,18 @@ func TestJsonArrayContains(t *testing.T) {
x2.Name = "x2"
x2.Provides.Val = append(x2.Provides.Val, "x")
err = db.InsertPackage(x1)
err := database.InsertPackage(ctx, x1)
if err != nil {
t.Errorf("Expected no error, got %s", err)
}
err = db.InsertPackage(x2)
err = database.InsertPackage(ctx, x2)
if err != nil {
t.Errorf("Expected no error, got %s", err)
}
var dbPkg db.Package
err = db.DB().Get(&dbPkg, "SELECT * FROM pkgs WHERE json_array_contains(provides, 'x');")
err = database.GetConn().Get(&dbPkg, "SELECT * FROM pkgs WHERE json_array_contains(provides, 'x');")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}

80
internal/db/json.go Normal file
View File

@@ -0,0 +1,80 @@
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package db
import (
"database/sql"
"database/sql/driver"
"encoding/json"
"errors"
"fmt"
)
// JSON represents a JSON value in the database
type JSON[T any] struct {
Val T
}
// NewJSON creates a new database JSON value
func NewJSON[T any](v T) JSON[T] {
return JSON[T]{Val: v}
}
func (s *JSON[T]) Scan(val any) error {
if val == nil {
return nil
}
switch val := val.(type) {
case string:
err := json.Unmarshal([]byte(val), &s.Val)
if err != nil {
return err
}
case sql.NullString:
if val.Valid {
err := json.Unmarshal([]byte(val.String), &s.Val)
if err != nil {
return err
}
}
default:
return errors.New("sqlite json types must be strings")
}
return nil
}
func (s JSON[T]) Value() (driver.Value, error) {
data, err := json.Marshal(s.Val)
if err != nil {
return nil, err
}
return string(data), nil
}
func (s JSON[T]) MarshalYAML() (any, error) {
return s.Val, nil
}
func (s JSON[T]) String() string {
return fmt.Sprint(s.Val)
}
func (s JSON[T]) GoString() string {
return fmt.Sprintf("%#v", s.Val)
}

52
internal/db/utils.go Normal file
View File

@@ -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/>.
package db
import (
"database/sql/driver"
"encoding/json"
"errors"
"golang.org/x/exp/slices"
"modernc.org/sqlite"
)
func init() {
sqlite.MustRegisterScalarFunction("json_array_contains", 2, jsonArrayContains)
}
// jsonArrayContains is an SQLite function that checks if a JSON array
// in the database contains a given value
func jsonArrayContains(ctx *sqlite.FunctionContext, args []driver.Value) (driver.Value, error) {
value, ok := args[0].(string)
if !ok {
return nil, errors.New("both arguments to json_array_contains must be strings")
}
item, ok := args[1].(string)
if !ok {
return nil, errors.New("both arguments to json_array_contains must be strings")
}
var array []string
err := json.Unmarshal([]byte(value), &array)
if err != nil {
return nil, err
}
return slices.Contains(array, item), nil
}

View File

@@ -1,20 +1,21 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// Пакет dl содержит абстракции для загрузки файлов и каталогов
// из различных источников.
@@ -30,17 +31,20 @@ import (
"fmt"
"hash"
"io"
"log/slog"
"os"
"path/filepath"
"strings"
"github.com/PuerkitoBio/purell"
"github.com/leonelquinteros/gotext"
"github.com/vmihailenco/msgpack/v5"
"golang.org/x/crypto/blake2b"
"golang.org/x/crypto/blake2s"
"golang.org/x/exp/slices"
"plemya-x.ru/alr/internal/dlcache"
"plemya-x.ru/alr/pkg/loggerctx"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/dlcache"
)
// Константа для имени файла манифеста кэша
@@ -141,7 +145,9 @@ type UpdatingDownloader interface {
// Функция Download загружает файл или каталог с использованием указанных параметров
func Download(ctx context.Context, opts Options) (err error) {
log := loggerctx.From(ctx)
cfg := config.GetInstance(ctx)
dc := dlcache.New(cfg)
normalized, err := normalizeURL(opts.URL)
if err != nil {
return err
@@ -156,11 +162,15 @@ func Download(ctx context.Context, opts Options) (err error) {
}
var t Type
cacheDir, ok := dlcache.Get(ctx, opts.URL)
cacheDir, ok := dc.Get(ctx, opts.URL)
if ok {
var updated bool
if d, ok := d.(UpdatingDownloader); ok {
log.Info("Source can be updated, updating if required").Str("source", opts.Name).Str("downloader", d.Name()).Send()
slog.Info(
gotext.Get("Source can be updated, updating if required"),
"source", opts.Name,
"downloader", d.Name(),
)
updated, err = d.Update(Options{
Hash: opts.Hash,
@@ -187,10 +197,18 @@ func Download(ctx context.Context, opts Options) (err error) {
}
if ok && !updated {
log.Info("Source found in cache and linked to destination").Str("source", opts.Name).Stringer("type", t).Send()
slog.Info(
gotext.Get("Source found in cache and linked to destination"),
"source", opts.Name,
"type", t,
)
return nil
} else if ok {
log.Info("Source updated and linked to destination").Str("source", opts.Name).Stringer("type", t).Send()
slog.Info(
gotext.Get("Source updated and linked to destination"),
"source", opts.Name,
"type", t,
)
return nil
}
} else {
@@ -201,9 +219,9 @@ func Download(ctx context.Context, opts Options) (err error) {
}
}
log.Info("Downloading source").Str("source", opts.Name).Str("downloader", d.Name()).Send()
slog.Info(gotext.Get("Downloading source"), "source", opts.Name, "downloader", d.Name())
cacheDir, err = dlcache.New(ctx, opts.URL)
cacheDir, err = dc.New(ctx, opts.URL)
if err != nil {
return err
}
@@ -299,8 +317,6 @@ func linkDir(src, dest string) error {
return nil
}
rel, err := filepath.Rel(src, path)
if err != nil {
return err

View File

@@ -1,20 +1,21 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package dl
@@ -33,7 +34,8 @@ import (
"github.com/mholt/archiver/v4"
"github.com/schollz/progressbar/v3"
"plemya-x.ru/alr/internal/shutils/handlers"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/handlers"
)
// FileDownloader загружает файлы с использованием HTTP
@@ -222,7 +224,7 @@ func extractFile(r io.Reader, format archiver.Format, name string, opts Options)
}
if f.IsDir() {
err = os.Mkdir(path, 0o755)
err = os.MkdirAll(path, 0o755)
if err != nil {
return err
}
@@ -279,4 +281,4 @@ func getFilename(res *http.Response) (name string) {
} else {
return path.Base(res.Request.URL.Path)
}
}
}

View File

@@ -1,20 +1,21 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package dl

View File

@@ -1,3 +1,22 @@
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package dl
import (

View File

@@ -1,48 +1,61 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package dlcache
import (
"context"
"crypto/sha1"
"encoding/hex"
"io"
"os"
"path/filepath"
"plemya-x.ru/alr/internal/config"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
)
// BasePath returns the base path of the download cache
func BasePath(ctx context.Context) string {
return filepath.Join(config.GetPaths(ctx).CacheDir, "dl")
type Config interface {
GetPaths(ctx context.Context) *config.Paths
}
type DownloadCache struct {
cfg Config
}
func New(cfg Config) *DownloadCache {
return &DownloadCache{
cfg,
}
}
func (dc *DownloadCache) BasePath(ctx context.Context) string {
return filepath.Join(
dc.cfg.GetPaths(ctx).CacheDir, "dl",
)
}
// New creates a new directory with the given ID in the cache.
// If a directory with the same ID already exists,
// it will be deleted before creating a new one.
func New(ctx context.Context, id string) (string, error) {
func (dc *DownloadCache) New(ctx context.Context, id string) (string, error) {
h, err := hashID(id)
if err != nil {
return "", err
}
itemPath := filepath.Join(BasePath(ctx), h)
itemPath := filepath.Join(dc.BasePath(ctx), h)
fi, err := os.Stat(itemPath)
if err == nil || (fi != nil && !fi.IsDir()) {
@@ -65,12 +78,12 @@ func New(ctx context.Context, id string) (string, error) {
// returns the directory and true. If it
// does not exist, it returns an empty string
// and false.
func Get(ctx context.Context, id string) (string, bool) {
func (dc *DownloadCache) Get(ctx context.Context, id string) (string, bool) {
h, err := hashID(id)
if err != nil {
return "", false
}
itemPath := filepath.Join(BasePath(ctx), h)
itemPath := filepath.Join(dc.BasePath(ctx), h)
_, err = os.Stat(itemPath)
if err != nil {
@@ -79,15 +92,3 @@ func Get(ctx context.Context, id string) (string, bool) {
return itemPath, true
}
// hashID hashes the input ID with SHA1
// and returns the hex string of the hashed
// ID.
func hashID(id string) (string, error) {
h := sha1.New()
_, err := io.WriteString(h, id)
if err != nil {
return "", err
}
return hex.EncodeToString(h.Sum(nil)), nil
}

View File

@@ -1,20 +1,21 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package dlcache_test
@@ -27,8 +28,8 @@ import (
"path/filepath"
"testing"
"plemya-x.ru/alr/internal/config"
"plemya-x.ru/alr/internal/dlcache"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/dlcache"
)
func init() {
@@ -39,14 +40,49 @@ func init() {
config.GetPaths(context.Background()).RepoDir = dir
}
type TestALRConfig struct {
CacheDir string
}
func (c *TestALRConfig) GetPaths(ctx context.Context) *config.Paths {
return &config.Paths{
CacheDir: c.CacheDir,
}
}
func prepare(t *testing.T) *TestALRConfig {
t.Helper()
dir, err := os.MkdirTemp("/tmp", "alr-dlcache-test.*")
if err != nil {
panic(err)
}
return &TestALRConfig{
CacheDir: dir,
}
}
func cleanup(t *testing.T, cfg *TestALRConfig) {
t.Helper()
os.Remove(cfg.CacheDir)
}
func TestNew(t *testing.T) {
cfg := prepare(t)
defer cleanup(t, cfg)
dc := dlcache.New(cfg)
ctx := context.Background()
const id = "https://example.com"
dir, err := dlcache.New(id)
dir, err := dc.New(ctx, id)
if err != nil {
t.Errorf("Expected no error, got %s", err)
}
exp := filepath.Join(dlcache.BasePath(), sha1sum(id))
exp := filepath.Join(dc.BasePath(ctx), sha1sum(id))
if dir != exp {
t.Errorf("Expected %s, got %s", exp, dir)
}
@@ -60,7 +96,7 @@ func TestNew(t *testing.T) {
t.Errorf("Expected cache item to be a directory")
}
dir2, ok := dlcache.Get(id)
dir2, ok := dc.Get(ctx, id)
if !ok {
t.Errorf("Expected Get() to return valid value")
}

32
internal/dlcache/utils.go Normal file
View File

@@ -0,0 +1,32 @@
// 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 dlcache
import (
"crypto/sha1"
"encoding/hex"
"io"
)
func hashID(id string) (string, error) {
h := sha1.New()
_, err := io.WriteString(h, id)
if err != nil {
return "", err
}
return hex.EncodeToString(h.Sum(nil)), nil
}

93
internal/logger/log.go Normal file
View File

@@ -0,0 +1,93 @@
// 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 logger
import (
"context"
"log/slog"
"os"
"github.com/charmbracelet/lipgloss"
"github.com/charmbracelet/log"
"github.com/leonelquinteros/gotext"
)
type Logger struct {
lOut slog.Handler
lErr slog.Handler
}
func setupOutLogger() *log.Logger {
styles := log.DefaultStyles()
logger := log.New(os.Stdout)
logger.SetStyles(styles)
return logger
}
func setupErrorLogger() *log.Logger {
styles := log.DefaultStyles()
styles.Levels[log.ErrorLevel] = lipgloss.NewStyle().
SetString(gotext.Get("ERROR")).
Padding(0, 1, 0, 1).
Background(lipgloss.Color("204")).
Foreground(lipgloss.Color("0"))
logger := log.New(os.Stderr)
logger.SetStyles(styles)
return logger
}
func New() *Logger {
standardLogger := setupOutLogger()
errLogger := setupErrorLogger()
return &Logger{
lOut: standardLogger,
lErr: errLogger,
}
}
func (l *Logger) Enabled(ctx context.Context, level slog.Level) bool {
if level <= slog.LevelInfo {
return l.lOut.Enabled(ctx, level)
}
return l.lErr.Enabled(ctx, level)
}
func (l *Logger) Handle(ctx context.Context, rec slog.Record) error {
if rec.Level <= slog.LevelInfo {
return l.lOut.Handle(ctx, rec)
}
return l.lErr.Handle(ctx, rec)
}
func (l *Logger) WithAttrs(attrs []slog.Attr) slog.Handler {
sl := *l
sl.lOut = l.lOut.WithAttrs(attrs)
sl.lErr = l.lErr.WithAttrs(attrs)
return &sl
}
func (l *Logger) WithGroup(name string) slog.Handler {
sl := *l
sl.lOut = l.lOut.WithGroup(name)
sl.lErr = l.lErr.WithGroup(name)
return &sl
}
func SetupDefault() {
logger := slog.New(New())
slog.SetDefault(logger)
}

View File

@@ -1,20 +1,21 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package osutils

View File

@@ -1,20 +1,21 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package overrides
@@ -22,11 +23,12 @@ import (
"reflect"
"strings"
"plemya-x.ru/alr/internal/cpu"
"plemya-x.ru/alr/internal/db"
"plemya-x.ru/alr/pkg/distro"
"golang.org/x/exp/slices"
"golang.org/x/text/language"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/cpu"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
)
type Opts struct {

View File

@@ -1,20 +1,21 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package overrides_test
@@ -23,9 +24,10 @@ import (
"reflect"
"testing"
"plemya-x.ru/alr/internal/overrides"
"plemya-x.ru/alr/pkg/distro"
"golang.org/x/text/language"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/overrides"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
)
var info = &distro.OSRelease{

View File

@@ -1,20 +1,21 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package pager

View File

@@ -1,20 +1,21 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package pager

View File

@@ -1,20 +1,21 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package decoder
@@ -25,12 +26,13 @@ import (
"strings"
"github.com/mitchellh/mapstructure"
"plemya-x.ru/alr/internal/overrides"
"plemya-x.ru/alr/pkg/distro"
"golang.org/x/exp/slices"
"mvdan.cc/sh/v3/expand"
"mvdan.cc/sh/v3/interp"
"mvdan.cc/sh/v3/syntax"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/overrides"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
)
var ErrNotPointerToStruct = errors.New("val must be a pointer to a struct")
@@ -221,3 +223,8 @@ func (d *Decoder) getVar(name string) *expand.Variable {
}
return nil
}
func IsTruthy(value string) bool {
value = strings.ToLower(strings.TrimSpace(value))
return value == "true" || value == "yes" || value == "1"
}

View File

@@ -1,20 +1,21 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package decoder_test
@@ -27,10 +28,11 @@ import (
"strings"
"testing"
"plemya-x.ru/alr/internal/shutils/decoder"
"plemya-x.ru/alr/pkg/distro"
"mvdan.cc/sh/v3/interp"
"mvdan.cc/sh/v3/syntax"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/decoder"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
)
type BuildVars struct {
@@ -56,7 +58,7 @@ const testScript = `
release=1
epoch=2
desc="Test package"
homepage='//https://gitea.plemya-x.ru/xpamych/ALR'
homepage='https://gitea.plemya-x.ru/xpamych/ALR'
maintainer='Евгений Храмов <xpamych@yandex.ru>'
architectures=('arm64' 'amd64')
license=('GPL-3.0-or-later')

View File

@@ -1,20 +1,21 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package handlers

View File

@@ -1,20 +1,21 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package handlers_test
@@ -23,11 +24,12 @@ import (
"strings"
"testing"
"plemya-x.ru/alr/internal/shutils/handlers"
"plemya-x.ru/alr/internal/shutils/decoder"
"plemya-x.ru/alr/pkg/distro"
"mvdan.cc/sh/v3/interp"
"mvdan.cc/sh/v3/syntax"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/decoder"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/handlers"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
)
const testScript = `
@@ -89,7 +91,7 @@ func TestExecFuncs(t *testing.T) {
t.Fatalf("Expected test() function to exist")
}
eh := shutils.ExecFuncs{
eh := handlers.ExecFuncs{
"test-cmd": func(hc interp.HandlerContext, name string, args []string) error {
if name != "test-cmd" {
t.Errorf("Expected name to be 'test-cmd', got '%s'", name)

View File

@@ -1,3 +1,22 @@
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package handlers
import (
@@ -10,9 +29,9 @@ import (
"syscall"
"time"
"plemya-x.ru/fakeroot"
"mvdan.cc/sh/v3/expand"
"mvdan.cc/sh/v3/interp"
"plemya-x.ru/fakeroot"
)
// FakerootExecHandler was extracted from github.com/mvdan/sh/interp/handler.go

View File

@@ -1,20 +1,21 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package handlers

View File

@@ -1,20 +1,21 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package handlers_test
@@ -25,9 +26,10 @@ import (
"strings"
"testing"
"plemya-x.ru/alr/internal/shutils/handlers"
"mvdan.cc/sh/v3/interp"
"mvdan.cc/sh/v3/syntax"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/handlers"
)
func TestNopExec(t *testing.T) {

View File

@@ -1,20 +1,21 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package handlers

View File

@@ -1,20 +1,21 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package helpers
@@ -31,8 +32,9 @@ import (
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing/object"
"golang.org/x/exp/slices"
"plemya-x.ru/alr/internal/shutils/handlers"
"mvdan.cc/sh/v3/interp"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/handlers"
)
var (

View File

@@ -0,0 +1,353 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: build.go:69
msgid "Error pulling repositories"
msgstr ""
#: build.go:75
msgid "Unable to detect a supported package manager on the system"
msgstr ""
#: build.go:86
msgid "Error building package"
msgstr ""
#: build.go:92
msgid "Error getting working directory"
msgstr ""
#: build.go:100
msgid "Error moving the package"
msgstr ""
#: fix.go:43
msgid "Removing cache directory"
msgstr ""
#: fix.go:47
msgid "Unable to remove cache directory"
msgstr ""
#: fix.go:51
msgid "Rebuilding cache"
msgstr ""
#: fix.go:55
msgid "Unable to create new cache directory"
msgstr ""
#: fix.go:61
msgid "Error pulling repos"
msgstr ""
#: fix.go:65
msgid "Done"
msgstr ""
#: helper.go:60
msgid "No such helper command"
msgstr ""
#: info.go:42
msgid "Print information about a package"
msgstr ""
#: info.go:47
msgid "Show all information, not just for the current distro"
msgstr ""
#: info.go:59
msgid "Command info expected at least 1 argument, got %d"
msgstr ""
#: info.go:71
msgid "Error finding packages"
msgstr ""
#: info.go:87
msgid "Error parsing os-release file"
msgstr ""
#: info.go:96
msgid "Error resolving overrides"
msgstr ""
#: info.go:105 info.go:111
msgid "Error encoding script variables"
msgstr ""
#: install.go:55
msgid "Command install expected at least 1 argument, got %d"
msgstr ""
#: install.go:88
msgid "Error getting packages"
msgstr ""
#: install.go:97
msgid "Error iterating over packages"
msgstr ""
#: install.go:113
msgid "Command remove expected at least 1 argument, got %d"
msgstr ""
#: install.go:125
msgid "Error removing packages"
msgstr ""
#: internal/cliutils/prompt.go:60
msgid "Would you like to view the build script for %s"
msgstr ""
#: internal/cliutils/prompt.go:71
msgid "Would you still like to continue?"
msgstr ""
#: internal/cliutils/prompt.go:77
msgid "User chose not to continue after reading script"
msgstr ""
#: internal/cliutils/prompt.go:111
msgid "Error prompting for choice of package"
msgstr ""
#: internal/cliutils/prompt.go:135
msgid "Choose which package to %s"
msgstr ""
#: internal/cliutils/prompt.go:156
msgid "Choose which optional package(s) to install"
msgstr ""
#: internal/config/config.go:63
msgid "Error opening config file, using defaults"
msgstr ""
#: internal/config/config.go:76
msgid "Error decoding config file, using defaults"
msgstr ""
#: internal/config/config.go:88
msgid "Unable to detect user config directory"
msgstr ""
#: internal/config/config.go:96
msgid "Unable to create ALR config directory"
msgstr ""
#: internal/config/config.go:105
msgid "Unable to create ALR config file"
msgstr ""
#: internal/config/config.go:111
msgid "Error encoding default configuration"
msgstr ""
#: internal/config/config.go:120
msgid "Unable to detect cache directory"
msgstr ""
#: internal/config/config.go:130
msgid "Unable to create repo cache directory"
msgstr ""
#: internal/config/config.go:136
msgid "Unable to create package cache directory"
msgstr ""
#: internal/config/lang.go:50
msgid "Error parsing system language"
msgstr ""
#: internal/db/db.go:131
msgid "Database version mismatch; resetting"
msgstr ""
#: internal/db/db.go:135
msgid ""
"Database version does not exist. Run alr fix if something isn't working."
msgstr ""
#: internal/db/db_legacy.go:101
msgid "Error opening database"
msgstr ""
#: internal/dl/dl.go:170
msgid "Source can be updated, updating if required"
msgstr ""
#: internal/dl/dl.go:201
msgid "Source found in cache and linked to destination"
msgstr ""
#: internal/dl/dl.go:208
msgid "Source updated and linked to destination"
msgstr ""
#: internal/dl/dl.go:222
msgid "Downloading source"
msgstr ""
#: internal/logger/log.go:44
msgid "ERROR"
msgstr ""
#: list.go:53
msgid "Error initialization database"
msgstr ""
#: list.go:87
msgid "Error listing installed packages"
msgstr ""
#: main.go:88
msgid ""
"Running ALR as root is forbidden as it may cause catastrophic damage to your "
"system"
msgstr ""
#: main.go:122
msgid "Error while running app"
msgstr ""
#: pkg/build/build.go:104
msgid "Failed to prompt user to view build script"
msgstr ""
#: pkg/build/build.go:108
msgid "Building package"
msgstr ""
#: pkg/build/build.go:152
msgid "Downloading sources"
msgstr ""
#: pkg/build/build.go:164
msgid "Building package metadata"
msgstr ""
#: pkg/build/build.go:186
msgid "Compressing package"
msgstr ""
#: pkg/build/build.go:312
msgid ""
"Your system's CPU architecture doesn't match this package. Do you want to "
"build anyway?"
msgstr ""
#: pkg/build/build.go:323
msgid "This package is already installed"
msgstr ""
#: pkg/build/build.go:351
msgid "Installing build dependencies"
msgstr ""
#: pkg/build/build.go:393
msgid "Installing dependencies"
msgstr ""
#: pkg/build/build.go:435
msgid "Executing version()"
msgstr ""
#: pkg/build/build.go:460
msgid "Executing prepare()"
msgstr ""
#: pkg/build/build.go:470
msgid "Executing build()"
msgstr ""
#: pkg/build/build.go:482
msgid "Executing package()"
msgstr ""
#: pkg/build/build.go:557
msgid "AutoProv is not implemented for this package format, so it's skiped"
msgstr ""
#: pkg/build/build.go:568
msgid "AutoReq is not implemented for this package format, so it's skiped"
msgstr ""
#: pkg/build/build.go:653
msgid "Would you like to remove the build dependencies?"
msgstr ""
#: pkg/build/build.go:759
msgid "The checksums array must be the same length as sources"
msgstr ""
#: pkg/build/findDeps.go:35
msgid "Command not found on the system"
msgstr ""
#: pkg/build/findDeps.go:82
msgid "Provided dependency found"
msgstr ""
#: pkg/build/findDeps.go:89
msgid "Required dependency found"
msgstr ""
#: pkg/build/install.go:42
msgid "Error installing native packages"
msgstr ""
#: pkg/build/install.go:79
msgid "Error installing package"
msgstr ""
#: pkg/repos/pull.go:75
msgid "Pulling repository"
msgstr ""
#: pkg/repos/pull.go:99
msgid "Repository up to date"
msgstr ""
#: pkg/repos/pull.go:156
msgid "Git repository does not appear to be a valid ALR repo"
msgstr ""
#: pkg/repos/pull.go:172
msgid ""
"ALR repo's minumum ALR version is greater than the current version. Try "
"updating ALR if something doesn't work."
msgstr ""
#: repo.go:78 repo.go:133
msgid "Error opening config file"
msgstr ""
#: repo.go:84 repo.go:139
msgid "Error encoding config"
msgstr ""
#: repo.go:125
msgid "Repo does not exist"
msgstr ""
#: repo.go:145
msgid "Error removing repo directory"
msgstr ""
#: repo.go:151
msgid "Error removing packages from database"
msgstr ""
#: upgrade.go:78
msgid "Error checking for updates"
msgstr ""

View File

@@ -1,155 +0,0 @@
[[translation]]
id = 1228660974
value = 'Pulling repository'
[[translation]]
id = 2779805870
value = 'Repository up to date'
[[translation]]
id = 1433222829
value = 'Would you like to view the build script for'
[[translation]]
id = 2470847050
value = 'Failed to prompt user to view build script'
[[translation]]
id = 855659503
value = 'Would you still like to continue?'
[[translation]]
id = 1997041569
value = 'User chose not to continue after reading script'
[[translation]]
id = 2347700990
value = 'Building package'
[[translation]]
id = 2105058868
value = 'Downloading sources'
[[translation]]
id = 1884485082
value = 'Downloading source'
[[translation]]
id = 1519177982
value = 'Error building package'
[[translation]]
id = 2125220917
value = 'Choose which package(s) to install'
[[translation]]
id = 812531604
value = 'Error prompting for choice of package'
[[translation]]
id = 1040982801
value = 'Updating version'
[[translation]]
id = 1014897988
value = 'Remove build dependencies?'
[[translation]]
id = 2205430948
value = 'Installing build dependencies'
[[translation]]
id = 2522710805
value = 'Installing dependencies'
[[translation]]
id = 3602138206
value = 'Error installing package'
[[translation]]
id = 2235794125
value = 'Would you like to remove build dependencies?'
[[translation]]
id = 2562049386
value = "Your system's CPU architecture doesn't match this package. Do you want to build anyway?"
[[translation]]
id = 4006393493
value = 'The checksums array must be the same length as sources'
[[translation]]
id = 3759891273
value = 'The package() function is required'
[[translation]]
id = 1057080231
value = 'Executing package()'
[[translation]]
id = 2687735200
value = 'Executing prepare()'
[[translation]]
id = 535572372
value = 'Executing version()'
[[translation]]
id = 436644691
value = 'Executing build()'
[[translation]]
id = 1393316459
value = 'This package is already installed'
[[translation]]
id = 1267660189
value = 'Source can be updated, updating if required'
[[translation]]
id = 21753247
value = 'Source found in cache, linked to destination'
[[translation]]
id = 257354570
value = 'Compressing package'
[[translation]]
id = 2952487371
value = 'Building package metadata'
[[translation]]
id = 3121791194
value = 'Running ALR as root is forbidden as it may cause catastrophic damage to your system'
[[translation]]
id = 1256604213
value = 'Waiting for torrent metadata'
[[translation]]
id = 432261354
value = 'Downloading torrent file'
[[translation]]
id = 1579384326
value = 'name'
[[translation]]
id = 3206337475
value = 'version'
[[translation]]
id = 1810056261
value = 'new'
[[translation]]
id = 1602912115
value = 'source'
[[translation]]
id = 2363381545
value = 'type'
[[translation]]
id = 3419504365
value = 'downloader'

View File

@@ -1,151 +0,0 @@
[[translation]]
id = 1228660974
value = 'Скачивание репозитория'
[[translation]]
id = 2779805870
value = 'Репозиторий уже обновлен'
[[translation]]
id = 1433222829
value = 'Показать скрипт для пакета'
[[translation]]
id = 2470847050
value = 'Не удалось предложить просмотреть скрипт'
[[translation]]
id = 855659503
value = 'Продолжить?'
[[translation]]
id = 1997041569
value = 'Пользователь решил не продолжать после просмотра скрипта'
[[translation]]
id = 2347700990
value = 'Сборка пакета'
[[translation]]
id = 2105058868
value = 'Скачивание файлов'
[[translation]]
id = 1884485082
value = 'Скачивание источника'
[[translation]]
id = 1519177982
value = 'Ошибка при сборке пакета'
[[translation]]
id = 2125220917
value = 'Выберите, какие пакеты установить'
[[translation]]
id = 812531604
value = 'Ошибка при запросе выбора пакета'
[[translation]]
id = 1040982801
value = 'Обновление версии'
[[translation]]
id = 2235794125
value = 'Удалить зависимости сборки?'
[[translation]]
id = 2205430948
value = 'Установка зависимостей сборки'
[[translation]]
id = 2522710805
value = 'Установка зависимостей'
[[translation]]
id = 3602138206
value = 'Ошибка при установке пакета'
[[translation]]
id = 1057080231
value = 'Вызов функции package()'
[[translation]]
id = 2687735200
value = 'Вызов функции prepare()'
[[translation]]
id = 535572372
value = 'Вызов функции version()'
[[translation]]
id = 436644691
value = 'Вызов функции build()'
[[translation]]
id = 2562049386
value = "Архитектура процессора вашей системы не соответствует этому пакету. Продолжать несмотря на это?"
[[translation]]
id = 3759891273
value = 'Функция package() необходима'
[[translation]]
id = 4006393493
value = 'Массив checksums должен быть той же длины, что и sources'
[[translation]]
id = 1393316459
value = 'Этот пакет уже установлен'
[[translation]]
id = 1267660189
value = 'Источник может быть обновлен, если требуется, обновляем'
[[translation]]
id = 21753247
value = 'Источник найден в кэше'
[[translation]]
id = 257354570
value = 'Сжатие пакета'
[[translation]]
id = 2952487371
value = 'Создание метаданных пакета'
[[translation]]
id = 3121791194
value = 'Запуск ALR от имени root запрещен, так как это может привести к катастрофическому повреждению вашей системы'
[[translation]]
id = 1256604213
value = 'Ожидание метаданных торрента'
[[translation]]
id = 432261354
value = 'Скачивание торрент-файла'
[[translation]]
id = 1579384326
value = 'название'
[[translation]]
id = 3206337475
value = 'версия'
[[translation]]
id = 1810056261
value = 'новая'
[[translation]]
id = 1602912115
value = 'источник'
[[translation]]
id = 2363381545
value = 'вид'
[[translation]]
id = 3419504365
value = 'протокол-скачивание'

View File

@@ -0,0 +1,359 @@
#
# Maxim Slipenko <maks1ms@alt-gnome.ru>, 2025.
#
msgid ""
msgstr ""
"Project-Id-Version: unnamed project\n"
"PO-Revision-Date: 2025-01-22 16:50+0300\n"
"Last-Translator: Maxim Slipenko <maks1ms@alt-gnome.ru>\n"
"Language-Team: Russian\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
"X-Generator: Gtranslator 47.1\n"
#: build.go:69
msgid "Error pulling repositories"
msgstr ""
#: build.go:75
msgid "Unable to detect a supported package manager on the system"
msgstr ""
#: build.go:86
msgid "Error building package"
msgstr "Ошибка при сборке пакета"
#: build.go:92
msgid "Error getting working directory"
msgstr ""
#: build.go:100
msgid "Error moving the package"
msgstr ""
#: fix.go:43
msgid "Removing cache directory"
msgstr ""
#: fix.go:47
msgid "Unable to remove cache directory"
msgstr ""
#: fix.go:51
msgid "Rebuilding cache"
msgstr ""
#: fix.go:55
msgid "Unable to create new cache directory"
msgstr ""
#: fix.go:61
msgid "Error pulling repos"
msgstr ""
#: fix.go:65
msgid "Done"
msgstr ""
#: helper.go:60
msgid "No such helper command"
msgstr ""
#: info.go:42
msgid "Print information about a package"
msgstr "Напечатать информацию о пакете"
#: info.go:47
msgid "Show all information, not just for the current distro"
msgstr "Показывать всю информацию, а не только для текущего дистрибутива"
#: info.go:59
msgid "Command info expected at least 1 argument, got %d"
msgstr ""
#: info.go:71
msgid "Error finding packages"
msgstr ""
#: info.go:87
msgid "Error parsing os-release file"
msgstr ""
#: info.go:96
msgid "Error resolving overrides"
msgstr ""
#: info.go:105 info.go:111
msgid "Error encoding script variables"
msgstr ""
#: install.go:55
msgid "Command install expected at least 1 argument, got %d"
msgstr ""
#: install.go:88
msgid "Error getting packages"
msgstr ""
#: install.go:97
msgid "Error iterating over packages"
msgstr ""
#: install.go:113
msgid "Command remove expected at least 1 argument, got %d"
msgstr ""
#: install.go:125
msgid "Error removing packages"
msgstr ""
#: internal/cliutils/prompt.go:60
msgid "Would you like to view the build script for %s"
msgstr "Показать скрипт для пакета %s"
#: internal/cliutils/prompt.go:71
msgid "Would you still like to continue?"
msgstr "Продолжить?"
#: internal/cliutils/prompt.go:77
msgid "User chose not to continue after reading script"
msgstr "Пользователь решил не продолжать после просмотра скрипта"
#: internal/cliutils/prompt.go:111
msgid "Error prompting for choice of package"
msgstr ""
#: internal/cliutils/prompt.go:135
msgid "Choose which package to %s"
msgstr ""
#: internal/cliutils/prompt.go:156
msgid "Choose which optional package(s) to install"
msgstr ""
#: internal/config/config.go:63
msgid "Error opening config file, using defaults"
msgstr ""
#: internal/config/config.go:76
msgid "Error decoding config file, using defaults"
msgstr ""
#: internal/config/config.go:88
msgid "Unable to detect user config directory"
msgstr ""
#: internal/config/config.go:96
msgid "Unable to create ALR config directory"
msgstr ""
#: internal/config/config.go:105
msgid "Unable to create ALR config file"
msgstr ""
#: internal/config/config.go:111
msgid "Error encoding default configuration"
msgstr ""
#: internal/config/config.go:120
msgid "Unable to detect cache directory"
msgstr ""
#: internal/config/config.go:130
msgid "Unable to create repo cache directory"
msgstr ""
#: internal/config/config.go:136
msgid "Unable to create package cache directory"
msgstr ""
#: internal/config/lang.go:50
msgid "Error parsing system language"
msgstr ""
#: internal/db/db.go:131
msgid "Database version mismatch; resetting"
msgstr ""
#: internal/db/db.go:135
msgid ""
"Database version does not exist. Run alr fix if something isn't working."
msgstr ""
#: internal/db/db_legacy.go:101
msgid "Error opening database"
msgstr ""
#: internal/dl/dl.go:170
msgid "Source can be updated, updating if required"
msgstr ""
#: internal/dl/dl.go:201
msgid "Source found in cache and linked to destination"
msgstr ""
#: internal/dl/dl.go:208
msgid "Source updated and linked to destination"
msgstr ""
#: internal/dl/dl.go:222
msgid "Downloading source"
msgstr "Скачивание источника"
#: internal/logger/log.go:44
msgid "ERROR"
msgstr "ОШИБКА"
#: list.go:53
msgid "Error initialization database"
msgstr ""
#: list.go:87
msgid "Error listing installed packages"
msgstr ""
#: main.go:88
msgid ""
"Running ALR as root is forbidden as it may cause catastrophic damage to your "
"system"
msgstr ""
#: main.go:122
msgid "Error while running app"
msgstr ""
#: pkg/build/build.go:104
msgid "Failed to prompt user to view build script"
msgstr "Не удалось предложить просмотреть скрипт"
#: pkg/build/build.go:108
msgid "Building package"
msgstr "Сборка пакета"
#: pkg/build/build.go:152
msgid "Downloading sources"
msgstr "Скачивание файлов"
#: pkg/build/build.go:164
msgid "Building package metadata"
msgstr ""
#: pkg/build/build.go:186
msgid "Compressing package"
msgstr ""
#: pkg/build/build.go:312
msgid ""
"Your system's CPU architecture doesn't match this package. Do you want to "
"build anyway?"
msgstr ""
#: pkg/build/build.go:323
msgid "This package is already installed"
msgstr ""
#: pkg/build/build.go:351
msgid "Installing build dependencies"
msgstr ""
#: pkg/build/build.go:393
msgid "Installing dependencies"
msgstr ""
#: pkg/build/build.go:435
msgid "Executing version()"
msgstr ""
#: pkg/build/build.go:460
msgid "Executing prepare()"
msgstr ""
#: pkg/build/build.go:470
msgid "Executing build()"
msgstr ""
#: pkg/build/build.go:482
msgid "Executing package()"
msgstr ""
#: pkg/build/build.go:557
msgid "AutoProv is not implemented for this package format, so it's skiped"
msgstr ""
#: pkg/build/build.go:568
msgid "AutoReq is not implemented for this package format, so it's skiped"
msgstr ""
#: pkg/build/build.go:653
msgid "Would you like to remove the build dependencies?"
msgstr ""
#: pkg/build/build.go:759
msgid "The checksums array must be the same length as sources"
msgstr ""
#: pkg/build/findDeps.go:35
msgid "Command not found on the system"
msgstr ""
#: pkg/build/findDeps.go:82
msgid "Provided dependency found"
msgstr ""
#: pkg/build/findDeps.go:89
msgid "Required dependency found"
msgstr ""
#: pkg/build/install.go:42
msgid "Error installing native packages"
msgstr ""
#: pkg/build/install.go:79
msgid "Error installing package"
msgstr ""
#: pkg/repos/pull.go:75
msgid "Pulling repository"
msgstr "Скачивание репозитория"
#: pkg/repos/pull.go:99
msgid "Repository up to date"
msgstr "Репозиторий уже обновлен"
#: pkg/repos/pull.go:156
msgid "Git repository does not appear to be a valid ALR repo"
msgstr ""
#: pkg/repos/pull.go:172
msgid ""
"ALR repo's minumum ALR version is greater than the current version. Try "
"updating ALR if something doesn't work."
msgstr ""
#: repo.go:78 repo.go:133
msgid "Error opening config file"
msgstr ""
#: repo.go:84 repo.go:139
msgid "Error encoding config"
msgstr ""
#: repo.go:125
msgid "Repo does not exist"
msgstr ""
#: repo.go:145
msgid "Error removing repo directory"
msgstr ""
#: repo.go:151
msgid "Error removing packages from database"
msgstr ""
#: upgrade.go:78
msgid "Error checking for updates"
msgstr ""

View File

@@ -1,56 +1,52 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package translations
import (
"context"
"embed"
"sync"
"io/fs"
"os"
"path"
"go.elara.ws/logger"
"plemya-x.ru/alr/pkg/loggerctx"
"go.elara.ws/translate"
"golang.org/x/text/language"
"github.com/jeandeaual/go-locale"
"github.com/leonelquinteros/gotext"
)
//go:embed files
var translationFS embed.FS
//go:embed po
var poFS embed.FS
var (
mu sync.Mutex
translator *translate.Translator
)
func Translator(ctx context.Context) *translate.Translator {
mu.Lock()
defer mu.Unlock()
log := loggerctx.From(ctx)
if translator == nil {
t, err := translate.NewFromFS(translationFS)
if err != nil {
log.Fatal("Error creating new translator").Err(err).Send()
}
translator = &t
func Setup() {
userLanguage, err := locale.GetLanguage()
if err != nil {
panic(err)
}
return translator
}
func NewLogger(ctx context.Context, l logger.Logger, lang language.Tag) *translate.TranslatedLogger {
return translate.NewLogger(l, *Translator(ctx), lang)
_, err = fs.Stat(poFS, path.Join("po", userLanguage))
if err != nil {
if os.IsNotExist(err) {
return
}
panic(err)
}
loc := gotext.NewLocaleFSWithPath(userLanguage, &poFS, "po")
loc.SetDomain("default")
gotext.SetLocales([]*gotext.Locale{loc})
}

View File

@@ -1,24 +1,25 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package types
import "plemya-x.ru/alr/pkg/manager"
import "gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager"
type BuildOpts struct {
Script string
@@ -49,6 +50,8 @@ type BuildVars struct {
Checksums []string `sh:"checksums"`
Backup []string `sh:"backup"`
Scripts Scripts `sh:"scripts"`
AutoReq []string `sh:"auto_req"`
AutoProv []string `sh:"auto_prov"`
}
type Scripts struct {

View File

@@ -1,20 +1,21 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package types

View File

@@ -1,20 +1,21 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package types

View File

@@ -0,0 +1,18 @@
This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
ALR - Any Linux Repository
Copyright (C) {{ .Year }} Евгений Храмов
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/>.

15
license-header.tmpl Normal file
View File

@@ -0,0 +1,15 @@
ALR - Any Linux Repository
Copyright (C) {{ .Year }} Евгений Храмов
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/>.

79
list.go
View File

@@ -1,33 +1,37 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"fmt"
"log/slog"
"os"
"github.com/leonelquinteros/gotext"
"github.com/urfave/cli/v2"
"plemya-x.ru/alr/internal/config"
"plemya-x.ru/alr/internal/db"
"plemya-x.ru/alr/pkg/loggerctx"
"plemya-x.ru/alr/pkg/manager"
"plemya-x.ru/alr/pkg/repos"
"golang.org/x/exp/slices"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos"
)
var listCmd = &cli.Command{
@@ -42,11 +46,18 @@ var listCmd = &cli.Command{
},
Action: func(c *cli.Context) error {
ctx := c.Context
log := loggerctx.From(ctx)
err := repos.Pull(ctx, config.Config(ctx).Repos)
cfg := config.New()
db := database.New(cfg)
err := db.Init(ctx)
if err != nil {
log.Fatal("Error pulling repositories").Err(err).Send()
slog.Error(gotext.Get("Error initialization database"), "err", err)
os.Exit(1)
}
rs := repos.New(cfg, db)
err = rs.Pull(ctx, cfg.Repos(ctx))
if err != nil {
slog.Error(gotext.Get("Error pulling repositories"), "err", err)
os.Exit(1)
}
where := "true"
@@ -58,7 +69,8 @@ var listCmd = &cli.Command{
result, err := db.GetPkgs(ctx, where, args...)
if err != nil {
log.Fatal("Error getting packages").Err(err).Send()
slog.Error(gotext.Get("Error getting packages"), "err", err)
os.Exit(1)
}
defer result.Close()
@@ -66,23 +78,25 @@ var listCmd = &cli.Command{
if c.Bool("installed") {
mgr := manager.Detect()
if mgr == nil {
log.Fatal("Unable to detect a supported package manager on the system").Send()
slog.Error(gotext.Get("Unable to detect a supported package manager on the system"))
os.Exit(1)
}
installed, err = mgr.ListInstalled(&manager.Opts{AsRoot: false})
if err != nil {
log.Fatal("Error listing installed packages").Err(err).Send()
slog.Error(gotext.Get("Error listing installed packages"), "err", err)
os.Exit(1)
}
}
for result.Next() {
var pkg db.Package
var pkg database.Package
err := result.StructScan(&pkg)
if err != nil {
return err
}
if slices.Contains(config.Config(ctx).IgnorePkgUpdates, pkg.Name) {
if slices.Contains(cfg.IgnorePkgUpdates(ctx), pkg.Name) {
continue
}
@@ -100,7 +114,8 @@ var listCmd = &cli.Command{
}
if err != nil {
log.Fatal("Error iterating over packages").Err(err).Send()
slog.Error(gotext.Get("Error iterating over packages"), "err", err)
os.Exit(1)
}
return nil

167
main.go
View File

@@ -1,93 +1,44 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"context"
"log/slog"
"os"
"os/signal"
"strings"
"syscall"
"github.com/leonelquinteros/gotext"
"github.com/mattn/go-isatty"
"github.com/urfave/cli/v2"
"go.elara.ws/logger"
"plemya-x.ru/alr/internal/config"
"plemya-x.ru/alr/internal/db"
"plemya-x.ru/alr/internal/translations"
"plemya-x.ru/alr/pkg/loggerctx"
"plemya-x.ru/alr/pkg/manager"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/translations"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/logger"
)
var app = &cli.App{
Name: "alr",
Usage: "Any Linux Repository",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "pm-args",
Aliases: []string{"P"},
Usage: "Arguments to be passed on to the package manager",
},
&cli.BoolFlag{
Name: "interactive",
Aliases: []string{"i"},
Value: isatty.IsTerminal(os.Stdin.Fd()),
Usage: "Enable interactive questions and prompts",
},
},
Commands: []*cli.Command{
installCmd,
removeCmd,
upgradeCmd,
infoCmd,
listCmd,
buildCmd,
addrepoCmd,
removerepoCmd,
refreshCmd,
fixCmd,
genCmd,
helperCmd,
versionCmd,
},
Before: func(c *cli.Context) error {
ctx := c.Context
log := loggerctx.From(ctx)
cmd := c.Args().First()
if cmd != "helper" && !config.Config(ctx).Unsafe.AllowRunAsRoot && os.Geteuid() == 0 {
log.Fatal("Running ALR as root is forbidden as it may cause catastrophic damage to your system").Send()
}
if trimmed := strings.TrimSpace(c.String("pm-args")); trimmed != "" {
args := strings.Split(trimmed, " ")
manager.Args = append(manager.Args, args...)
}
return nil
},
After: func(ctx *cli.Context) error {
return db.Close()
},
EnableBashCompletion: true,
}
var versionCmd = &cli.Command{
Name: "version",
Usage: "Print the current ALR version and exit",
@@ -97,10 +48,68 @@ var versionCmd = &cli.Command{
},
}
func GetApp() *cli.App {
return &cli.App{
Name: "alr",
Usage: "Any Linux Repository",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "pm-args",
Aliases: []string{"P"},
Usage: "Arguments to be passed on to the package manager",
},
&cli.BoolFlag{
Name: "interactive",
Aliases: []string{"i"},
Value: isatty.IsTerminal(os.Stdin.Fd()),
Usage: "Enable interactive questions and prompts",
},
},
Commands: []*cli.Command{
installCmd,
removeCmd,
upgradeCmd,
GetInfoCmd(),
listCmd,
buildCmd,
addrepoCmd,
removerepoCmd,
refreshCmd,
fixCmd,
genCmd,
helperCmd,
versionCmd,
},
Before: func(c *cli.Context) error {
ctx := c.Context
cmd := c.Args().First()
if cmd != "helper" && !config.Config(ctx).Unsafe.AllowRunAsRoot && os.Geteuid() == 0 {
slog.Error(gotext.Get("Running ALR as root is forbidden as it may cause catastrophic damage to your system"))
os.Exit(1)
}
if trimmed := strings.TrimSpace(c.String("pm-args")); trimmed != "" {
args := strings.Split(trimmed, " ")
manager.Args = append(manager.Args, args...)
}
return nil
},
After: func(ctx *cli.Context) error {
return db.Close()
},
EnableBashCompletion: true,
}
}
func main() {
translations.Setup()
logger.SetupDefault()
app := GetApp()
ctx := context.Background()
log := translations.NewLogger(ctx, logger.NewCLI(os.Stderr), config.Language(ctx))
ctx = loggerctx.With(ctx, log)
// Set the root command to the one set in the ALR config
manager.DefaultRootCmd = config.Config(ctx).RootCmd
@@ -110,6 +119,6 @@ func main() {
err := app.RunContext(ctx, os.Args)
if err != nil {
log.Error("Error while running app").Err(err).Send()
slog.Error(gotext.Get("Error while running app"), "err", err)
}
}

83
old-files Normal file
View File

@@ -0,0 +1,83 @@
./.github/FUNDING.yml
./.gitignore
./.goreleaser.yaml
./.woodpecker.yml
./LICENSE
./Makefile
./README.md
./assets/logo.png
./build.go
./docs/README.md
./docs/configuration.md
./docs/packages/README.md
./docs/packages/adding-packages.md
./docs/packages/build-scripts.md
./docs/packages/conventions.md
./docs/usage.md
./fix.go
./gen.go
./go.mod
./go.sum
./helper.go
./info.go
./install.go
./internal/cliutils/prompt.go
./internal/config/config.go
./internal/config/lang.go
./internal/config/paths.go
./internal/config/version.go
./internal/cpu/cpu.go
./internal/db/db.go
./internal/db/db_test.go
./internal/dl/dl.go
./internal/dl/file.go
./internal/dl/git.go
./internal/dl/torrent.go
./internal/dlcache/dlcache.go
./internal/dlcache/dlcache_test.go
./internal/osutils/move.go
./internal/overrides/overrides.go
./internal/overrides/overrides_test.go
./internal/pager/highlighting.go
./internal/pager/pager.go
./internal/shutils/decoder/decoder.go
./internal/shutils/decoder/decoder_test.go
./internal/shutils/handlers/exec.go
./internal/shutils/handlers/exec_test.go
./internal/shutils/handlers/fakeroot.go
./internal/shutils/handlers/nop.go
./internal/shutils/handlers/nop_test.go
./internal/shutils/handlers/restricted.go
./internal/shutils/helpers/helpers.go
./internal/translations/files/lure.en.toml
./internal/translations/files/lure.ru.toml
./internal/translations/translations.go
./internal/types/build.go
./internal/types/config.go
./internal/types/repo.go
./list.go
./main.go
./pkg/build/build.go
./pkg/build/install.go
./pkg/distro/osrelease.go
./pkg/gen/funcs.go
./pkg/gen/pip.go
./pkg/gen/tmpls/pip.tmpl.sh
./pkg/loggerctx/log.go
./pkg/manager/apk.go
./pkg/manager/apt.go
./pkg/manager/dnf.go
./pkg/manager/managers.go
./pkg/manager/pacman.go
./pkg/manager/yum.go
./pkg/manager/zypper.go
./pkg/repos/find.go
./pkg/repos/find_test.go
./pkg/repos/pull.go
./pkg/repos/pull_test.go
./pkg/search/search.go
./repo.go
./scripts/completion/bash
./scripts/completion/zsh
./scripts/install.sh
./upgrade.go

View File

@@ -1,20 +1,21 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package build
@@ -24,6 +25,7 @@ import (
"encoding/hex"
"fmt"
"io"
"log/slog"
"os"
"path/filepath"
"runtime"
@@ -32,35 +34,37 @@ import (
"strings"
"time"
// Импортируем пакеты для поддержки различных форматов пакетов (APK, DEB, RPM и ARCH).
_ "github.com/goreleaser/nfpm/v2/apk"
_ "github.com/goreleaser/nfpm/v2/arch"
_ "github.com/goreleaser/nfpm/v2/deb"
_ "github.com/goreleaser/nfpm/v2/rpm"
"github.com/goreleaser/nfpm/v2"
"github.com/goreleaser/nfpm/v2/files"
"plemya-x.ru/alr/internal/cliutils"
"plemya-x.ru/alr/internal/config"
"plemya-x.ru/alr/internal/cpu"
"plemya-x.ru/alr/internal/db"
"plemya-x.ru/alr/internal/dl"
"plemya-x.ru/alr/internal/shutils/decoder"
"plemya-x.ru/alr/internal/shutils/handlers"
"plemya-x.ru/alr/internal/shutils/helpers"
"plemya-x.ru/alr/internal/types"
"plemya-x.ru/alr/pkg/distro"
"plemya-x.ru/alr/pkg/loggerctx"
"plemya-x.ru/alr/pkg/manager"
"plemya-x.ru/alr/pkg/repos"
"github.com/leonelquinteros/gotext"
"mvdan.cc/sh/v3/expand"
"mvdan.cc/sh/v3/interp"
"mvdan.cc/sh/v3/syntax"
"github.com/goreleaser/nfpm/v2"
"github.com/goreleaser/nfpm/v2/files"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/cpu"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/dl"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/decoder"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/handlers"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/helpers"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/types"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos"
)
// BuildPackage builds the script at the given path. It returns two slices. One contains the paths
// to the built package(s), the other contains the names of the built package(s).
// Функция BuildPackage выполняет сборку скрипта по указанному пути. Возвращает два среза.
// Один содержит пути к собранным пакетам, другой - имена собранных пакетов.
func BuildPackage(ctx context.Context, opts types.BuildOpts) ([]string, []string, error) {
log := loggerctx.From(ctx)
reposInstance := repos.GetInstance(ctx)
info, err := distro.ParseOSRelease(ctx)
if err != nil {
@@ -72,9 +76,8 @@ func BuildPackage(ctx context.Context, opts types.BuildOpts) ([]string, []string
return nil, nil, err
}
// The first pass is just used to get variable values and runs before
// the script is displayed, so it's restricted so as to prevent malicious
// code from executing.
// Первый проход предназначен для получения значений переменных и выполняется
// до отображения скрипта, чтобы предотвратить выполнение вредоносного кода.
vars, err := executeFirstPass(ctx, info, fl, opts.Script)
if err != nil {
return nil, nil, err
@@ -82,8 +85,8 @@ func BuildPackage(ctx context.Context, opts types.BuildOpts) ([]string, []string
dirs := getDirs(ctx, vars, opts.Script)
// If opts.Clean isn't set and we find the package already built,
// just return it rather than rebuilding
// Если флаг opts.Clean не установлен, и пакет уже собран,
// возвращаем его, а не собираем заново.
if !opts.Clean {
builtPkgPath, ok, err := checkForBuiltPackage(opts.Manager, vars, getPkgFormat(opts.Manager), dirs.BaseDir)
if err != nil {
@@ -95,169 +98,170 @@ func BuildPackage(ctx context.Context, opts types.BuildOpts) ([]string, []string
}
}
// Ask the user if they'd like to see the build script
// Спрашиваем у пользователя, хочет ли он увидеть скрипт сборки.
err = cliutils.PromptViewScript(ctx, opts.Script, vars.Name, config.Config(ctx).PagerStyle, opts.Interactive)
if err != nil {
log.Fatal("Failed to prompt user to view build script").Err(err).Send()
slog.Error(gotext.Get("Failed to prompt user to view build script"), "err", err)
os.Exit(1)
}
log.Info("Building package").Str("name", vars.Name).Str("version", vars.Version).Send()
slog.Info(gotext.Get("Building package"), "name", vars.Name, "version", vars.Version)
// The second pass will be used to execute the actual code,
// so it's unrestricted. The script has already been displayed
// to the user by this point, so it should be safe
// Второй проход будет использоваться для выполнения реального кода,
// поэтому он не ограничен. Скрипт уже был показан
// пользователю к этому моменту, так что это должно быть безопасно.
dec, err := executeSecondPass(ctx, info, fl, dirs)
if err != nil {
return nil, nil, err
}
// Get the installed packages on the system
// Получаем список установленных пакетов в системе
installed, err := opts.Manager.ListInstalled(nil)
if err != nil {
return nil, nil, err
}
cont, err := performChecks(ctx, vars, opts.Interactive, installed)
cont, err := performChecks(ctx, vars, opts.Interactive, installed) // Выполняем различные проверки
if err != nil {
return nil, nil, err
} else if !cont {
os.Exit(1)
os.Exit(1) // Если проверки не пройдены, выходим из программы
}
// Prepare the directories for building
// Подготавливаем директории для сборки
err = prepareDirs(dirs)
if err != nil {
return nil, nil, err
}
buildDeps, err := installBuildDeps(ctx, vars, opts, installed)
buildDeps, err := installBuildDeps(ctx, reposInstance, vars, opts) // Устанавливаем зависимости для сборки
if err != nil {
return nil, nil, err
}
err = installOptDeps(ctx, vars, opts, installed)
err = installOptDeps(ctx, reposInstance, vars, opts) // Устанавливаем опциональные зависимости
if err != nil {
return nil, nil, err
}
builtPaths, builtNames, repoDeps, err := buildALRDeps(ctx, opts, vars)
builtPaths, builtNames, repoDeps, err := buildALRDeps(ctx, opts, vars) // Собираем зависимости
if err != nil {
return nil, nil, err
}
log.Info("Downloading sources").Send()
slog.Info(gotext.Get("Downloading sources")) // Записываем в лог загрузку источников
err = getSources(ctx, dirs, vars)
err = getSources(ctx, dirs, vars) // Загружаем исходники
if err != nil {
return nil, nil, err
}
err = executeFunctions(ctx, dec, dirs, vars)
err = executeFunctions(ctx, dec, dirs, vars) // Выполняем специальные функции
if err != nil {
return nil, nil, err
}
log.Info("Building package metadata").Str("name", vars.Name).Send()
slog.Info(gotext.Get("Building package metadata"), "name", vars.Name)
pkgFormat := getPkgFormat(opts.Manager)
pkgFormat := getPkgFormat(opts.Manager) // Получаем формат пакета
pkgInfo, err := buildPkgMetadata(vars, dirs, pkgFormat, append(repoDeps, builtNames...))
pkgInfo, err := buildPkgMetadata(ctx, vars, dirs, pkgFormat, info, append(repoDeps, builtNames...)) // Собираем метаданные пакета
if err != nil {
return nil, nil, err
}
packager, err := nfpm.Get(pkgFormat)
packager, err := nfpm.Get(pkgFormat) // Получаем упаковщик для формата пакета
if err != nil {
return nil, nil, err
}
pkgName := packager.ConventionalFileName(pkgInfo)
pkgPath := filepath.Join(dirs.BaseDir, pkgName)
pkgName := packager.ConventionalFileName(pkgInfo) // Получаем имя файла пакета
pkgPath := filepath.Join(dirs.BaseDir, pkgName) // Определяем путь к пакету
pkgFile, err := os.Create(pkgPath)
pkgFile, err := os.Create(pkgPath) // Создаём файл пакета
if err != nil {
return nil, nil, err
}
log.Info("Compressing package").Str("name", pkgName).Send()
slog.Info(gotext.Get("Compressing package"), "name", pkgName) // Логгируем сжатие пакета
err = packager.Package(pkgInfo, pkgFile)
err = packager.Package(pkgInfo, pkgFile) // Упаковываем пакет
if err != nil {
return nil, nil, err
}
err = removeBuildDeps(ctx, buildDeps, opts)
err = removeBuildDeps(ctx, buildDeps, opts) // Удаляем зависимости для сборки
if err != nil {
return nil, nil, err
}
// Add the path and name of the package we just built to the
// appropriate slices
// Добавляем путь и имя только что собранного пакета в
// соответствующие срезы
pkgPaths := append(builtPaths, pkgPath)
pkgNames := append(builtNames, vars.Name)
// Remove any duplicates from the pkgPaths and pkgNames.
// Duplicates can be introduced if several of the dependencies
// depend on the same packages.
// Удаляем дубликаты из pkgPaths и pkgNames.
// Дубликаты могут появиться, если несколько зависимостей
// зависят от одних и тех же пакетов.
pkgPaths = removeDuplicates(pkgPaths)
pkgNames = removeDuplicates(pkgNames)
return pkgPaths, pkgNames, nil
return pkgPaths, pkgNames, nil // Возвращаем пути и имена пакетов
}
// parseScript parses the build script using the built-in bash implementation
// Функция parseScript анализирует скрипт сборки с использованием встроенной реализации bash
func parseScript(info *distro.OSRelease, script string) (*syntax.File, error) {
fl, err := os.Open(script)
fl, err := os.Open(script) // Открываем файл скрипта
if err != nil {
return nil, err
}
defer fl.Close()
defer fl.Close() // Закрываем файл после выполнения
file, err := syntax.NewParser().Parse(fl, "alr.sh")
file, err := syntax.NewParser().Parse(fl, "alr.sh") // Парсим скрипт с помощью синтаксического анализатора
if err != nil {
return nil, err
}
return file, nil
return file, nil // Возвращаем синтаксическое дерево
}
// executeFirstPass executes the parsed script in a restricted environment
// to extract the build variables without executing any actual code.
// Функция executeFirstPass выполняет парсированный скрипт в ограниченной среде,
// чтобы извлечь переменные сборки без выполнения реального кода.
func executeFirstPass(ctx context.Context, info *distro.OSRelease, fl *syntax.File, script string) (*types.BuildVars, error) {
scriptDir := filepath.Dir(script)
env := createBuildEnvVars(info, types.Directories{ScriptDir: scriptDir})
scriptDir := filepath.Dir(script) // Получаем директорию скрипта
env := createBuildEnvVars(info, types.Directories{ScriptDir: scriptDir}) // Создаём переменные окружения для сборки
runner, err := interp.New(
interp.Env(expand.ListEnviron(env...)),
interp.StdIO(os.Stdin, os.Stdout, os.Stderr),
interp.ExecHandler(helpers.Restricted.ExecHandler(handlers.NopExec)),
interp.ReadDirHandler(handlers.RestrictedReadDir(scriptDir)),
interp.StatHandler(handlers.RestrictedStat(scriptDir)),
interp.OpenHandler(handlers.RestrictedOpen(scriptDir)),
interp.Env(expand.ListEnviron(env...)), // Устанавливаем окружение
interp.StdIO(os.Stdin, os.Stdout, os.Stderr), // Устанавливаем стандартный ввод-вывод
interp.ExecHandler(helpers.Restricted.ExecHandler(handlers.NopExec)), // Ограничиваем выполнение
interp.ReadDirHandler(handlers.RestrictedReadDir(scriptDir)), // Ограничиваем чтение директорий
interp.StatHandler(handlers.RestrictedStat(scriptDir)), // Ограничиваем доступ к статистике файлов
interp.OpenHandler(handlers.RestrictedOpen(scriptDir)), // Ограничиваем открытие файлов
)
if err != nil {
return nil, err
}
err = runner.Run(ctx, fl)
err = runner.Run(ctx, fl) // Запускаем скрипт
if err != nil {
return nil, err
}
dec := decoder.New(info, runner)
dec := decoder.New(info, runner) // Создаём новый декодер
var vars types.BuildVars
err = dec.DecodeVars(&vars)
err = dec.DecodeVars(&vars) // Декодируем переменные
if err != nil {
return nil, err
}
return &vars, nil
return &vars, nil // Возвращаем переменные сборки
}
// getDirs returns the appropriate directories for the script
// Функция getDirs возвращает соответствующие директории для скрипта
func getDirs(ctx context.Context, vars *types.BuildVars, script string) types.Directories {
baseDir := filepath.Join(config.GetPaths(ctx).PkgsDir, vars.Name)
baseDir := filepath.Join(config.GetPaths(ctx).PkgsDir, vars.Name) // Определяем базовую директорию
return types.Directories{
BaseDir: baseDir,
SrcDir: filepath.Join(baseDir, "src"),
@@ -266,47 +270,46 @@ func getDirs(ctx context.Context, vars *types.BuildVars, script string) types.Di
}
}
// executeSecondPass executes the build script for the second time, this time without any restrictions.
// It returns a decoder that can be used to retrieve functions and variables from the script.
// Функция executeSecondPass выполняет скрипт сборки второй раз без каких-либо ограничений. Возвращается декодер,
// который может быть использован для получения функций и переменных из скрипта.
func executeSecondPass(ctx context.Context, info *distro.OSRelease, fl *syntax.File, dirs types.Directories) (*decoder.Decoder, error) {
env := createBuildEnvVars(info, dirs)
env := createBuildEnvVars(info, dirs) // Создаём переменные окружения для сборки
fakeroot := handlers.FakerootExecHandler(2 * time.Second)
fakeroot := handlers.FakerootExecHandler(2 * time.Second) // Настраиваем "fakeroot" для выполнения
runner, err := interp.New(
interp.Env(expand.ListEnviron(env...)),
interp.StdIO(os.Stdin, os.Stdout, os.Stderr),
interp.ExecHandler(helpers.Helpers.ExecHandler(fakeroot)),
interp.Env(expand.ListEnviron(env...)), // Устанавливаем окружение
interp.StdIO(os.Stdin, os.Stdout, os.Stderr), // Устанавливаем стандартный ввод-вывод
interp.ExecHandler(helpers.Helpers.ExecHandler(fakeroot)), // Обрабатываем выполнение через fakeroot
)
if err != nil {
return nil, err
}
err = runner.Run(ctx, fl)
err = runner.Run(ctx, fl) // Запускаем скрипт
if err != nil {
return nil, err
}
return decoder.New(info, runner), nil
return decoder.New(info, runner), nil // Возвращаем новый декодер
}
// prepareDirs prepares the directories for building.
// Функция prepareDirs подготавливает директории для сборки.
func prepareDirs(dirs types.Directories) error {
err := os.RemoveAll(dirs.BaseDir)
err := os.RemoveAll(dirs.BaseDir) // Удаляем базовую директорию, если она существует
if err != nil {
return err
}
err = os.MkdirAll(dirs.SrcDir, 0o755)
err = os.MkdirAll(dirs.SrcDir, 0o755) // Создаем директорию для источников
if err != nil {
return err
}
return os.MkdirAll(dirs.PkgDir, 0o755)
return os.MkdirAll(dirs.PkgDir, 0o755) // Создаем директорию для пакетов
}
// performChecks checks various things on the system to ensure that the package can be installed.
// Функция performChecks проверяет различные аспекты в системе, чтобы убедиться, что пакет может быть установлен.
func performChecks(ctx context.Context, vars *types.BuildVars, interactive bool, installed map[string]string) (bool, error) {
log := loggerctx.From(ctx)
if !cpu.IsCompatibleWith(cpu.Arch(), vars.Architectures) {
cont, err := cliutils.YesNoPrompt(ctx, "Your system's CPU architecture doesn't match this package. Do you want to build anyway?", interactive, true)
if !cpu.IsCompatibleWith(cpu.Arch(), vars.Architectures) { // Проверяем совместимость архитектуры
cont, err := cliutils.YesNoPrompt(ctx, gotext.Get("Your system's CPU architecture doesn't match this package. Do you want to build anyway?"), interactive, true)
if err != nil {
return false, err
}
@@ -316,43 +319,53 @@ func performChecks(ctx context.Context, vars *types.BuildVars, interactive bool,
}
}
if instVer, ok := installed[vars.Name]; ok {
log.Warn("This package is already installed").
Str("name", vars.Name).
Str("version", instVer).
Send()
if instVer, ok := installed[vars.Name]; ok { // Если пакет уже установлен, выводим предупреждение
slog.Warn(gotext.Get("This package is already installed"),
"name", vars.Name,
"version", instVer,
)
}
return true, nil
}
// installBuildDeps installs any build dependencies that aren't already installed and returns
// a slice containing the names of all the packages it installed.
func installBuildDeps(ctx context.Context, vars *types.BuildVars, opts types.BuildOpts, installed map[string]string) ([]string, error) {
log := loggerctx.From(ctx)
type PackageFinder interface {
FindPkgs(ctx context.Context, pkgs []string) (map[string][]db.Package, []string, error)
}
// Функция installBuildDeps устанавливает все зависимости сборки, которые еще не установлены, и возвращает
// срез, содержащий имена всех установленных пакетов.
func installBuildDeps(ctx context.Context, repos PackageFinder, vars *types.BuildVars, opts types.BuildOpts) ([]string, error) {
var buildDeps []string
if len(vars.BuildDepends) > 0 {
found, notFound, err := repos.FindPkgs(ctx, vars.BuildDepends)
deps, err := removeAlreadyInstalled(opts, vars.BuildDepends)
if err != nil {
return nil, err
}
found = removeAlreadyInstalled(found, installed)
found, notFound, err := repos.FindPkgs(ctx, deps) // Находим пакеты-зависимости
if err != nil {
return nil, err
}
log.Info("Installing build dependencies").Send()
slog.Info(gotext.Get("Installing build dependencies")) // Логгируем установку зависимостей
flattened := cliutils.FlattenPkgs(ctx, found, "install", opts.Interactive)
flattened := cliutils.FlattenPkgs(ctx, found, "install", opts.Interactive) // Уплощаем список зависимостей
buildDeps = packageNames(flattened)
InstallPkgs(ctx, flattened, notFound, opts)
InstallPkgs(ctx, flattened, notFound, opts) // Устанавливаем пакеты
}
return buildDeps, nil
}
// installOptDeps asks the user which, if any, optional dependencies they want to install.
// If the user chooses to install any optional dependencies, it performs the installation.
func installOptDeps(ctx context.Context, vars *types.BuildVars, opts types.BuildOpts, installed map[string]string) error {
if len(vars.OptDepends) > 0 {
optDeps, err := cliutils.ChooseOptDepends(ctx, vars.OptDepends, "install", opts.Interactive)
// Функция installOptDeps спрашивает у пользователя, какие, если таковые имеются, опциональные зависимости он хочет установить.
// Если пользователь решает установить какие-либо опциональные зависимости, выполняется их установка.
func installOptDeps(ctx context.Context, repos PackageFinder, vars *types.BuildVars, opts types.BuildOpts) error {
optDeps, err := removeAlreadyInstalled(opts, vars.OptDepends)
if err != nil {
return err
}
if len(optDeps) > 0 {
optDeps, err := cliutils.ChooseOptDepends(ctx, optDeps, "install", opts.Interactive) // Пользователя просят выбрать опциональные зависимости
if err != nil {
return err
}
@@ -361,68 +374,65 @@ func installOptDeps(ctx context.Context, vars *types.BuildVars, opts types.Build
return nil
}
found, notFound, err := repos.FindPkgs(ctx, optDeps)
found, notFound, err := repos.FindPkgs(ctx, optDeps) // Находим опциональные зависимости
if err != nil {
return err
}
found = removeAlreadyInstalled(found, installed)
flattened := cliutils.FlattenPkgs(ctx, found, "install", opts.Interactive)
InstallPkgs(ctx, flattened, notFound, opts)
InstallPkgs(ctx, flattened, notFound, opts) // Устанавливаем выбранные пакеты
}
return nil
}
// buildALRDeps builds all the ALR dependencies of the package. It returns the paths and names
// of the packages it built, as well as all the dependencies it didn't find in the ALR repo so
// they can be installed from the system repos.
// Функция buildALRDeps собирает все ALR зависимости пакета. Возвращает пути и имена
// пакетов, которые она собрала, а также все зависимости, которые не были найдены в ALR репозитории,
// чтобы они могли быть установлены из системных репозиториев.
func buildALRDeps(ctx context.Context, opts types.BuildOpts, vars *types.BuildVars) (builtPaths, builtNames, repoDeps []string, err error) {
log := loggerctx.From(ctx)
if len(vars.Depends) > 0 {
log.Info("Installing dependencies").Send()
slog.Info(gotext.Get("Installing dependencies"))
found, notFound, err := repos.FindPkgs(ctx, vars.Depends)
found, notFound, err := repos.FindPkgs(ctx, vars.Depends) // Поиск зависимостей
if err != nil {
return nil, nil, nil, err
}
repoDeps = notFound
// If there are multiple options for some packages, flatten them all into a single slice
// Если для некоторых пакетов есть несколько опций, упрощаем их все в один срез
pkgs := cliutils.FlattenPkgs(ctx, found, "install", opts.Interactive)
scripts := GetScriptPaths(ctx, pkgs)
for _, script := range scripts {
newOpts := opts
newOpts.Script = script
// Build the dependency
// Собираем зависимости
pkgPaths, pkgNames, err := BuildPackage(ctx, newOpts)
if err != nil {
return nil, nil, nil, err
}
// Append the paths of all the built packages to builtPaths
// Добавляем пути всех собранных пакетов в builtPaths
builtPaths = append(builtPaths, pkgPaths...)
// Append the names of all the built packages to builtNames
// Добавляем пути всех собранных пакетов в builtPaths
builtNames = append(builtNames, pkgNames...)
// Append the name of the current package to builtNames
// Добавляем имя текущего пакета в builtNames
builtNames = append(builtNames, filepath.Base(filepath.Dir(script)))
}
}
// Remove any potential duplicates, which can be introduced if
// several of the dependencies depend on the same packages.
// Удаляем возможные дубликаты, которые могут быть введены, если
// несколько зависимостей зависят от одних и тех же пакетов.
repoDeps = removeDuplicates(repoDeps)
builtPaths = removeDuplicates(builtPaths)
builtNames = removeDuplicates(builtNames)
return builtPaths, builtNames, repoDeps, nil
}
// executeFunctions executes the special ALR functions, such as version(), prepare(), etc.
// Функция executeFunctions выполняет специальные функции ALR, такие как version(), prepare() и т.д.
func executeFunctions(ctx context.Context, dec *decoder.Decoder, dirs types.Directories, vars *types.BuildVars) (err error) {
log := loggerctx.From(ctx)
version, ok := dec.GetFunc("version")
if ok {
log.Info("Executing version()").Send()
slog.Info(gotext.Get("Executing version()"))
buf := &bytes.Buffer{}
@@ -442,12 +452,12 @@ func executeFunctions(ctx context.Context, dec *decoder.Decoder, dirs types.Dire
}
vars.Version = newVer
log.Info("Updating version").Str("new", newVer).Send()
slog.Info("Updating version", "new", newVer)
}
prepare, ok := dec.GetFunc("prepare")
if ok {
log.Info("Executing prepare()").Send()
slog.Info(gotext.Get("Executing prepare()"))
err = prepare(ctx, interp.Dir(dirs.SrcDir))
if err != nil {
@@ -457,7 +467,7 @@ func executeFunctions(ctx context.Context, dec *decoder.Decoder, dirs types.Dire
build, ok := dec.GetFunc("build")
if ok {
log.Info("Executing build()").Send()
slog.Info(gotext.Get("Executing build()"))
err = build(ctx, interp.Dir(dirs.SrcDir))
if err != nil {
@@ -465,48 +475,62 @@ func executeFunctions(ctx context.Context, dec *decoder.Decoder, dirs types.Dire
}
}
packageFn, ok := dec.GetFunc("package")
if ok {
log.Info("Executing package()").Send()
err = packageFn(ctx, interp.Dir(dirs.SrcDir))
if err != nil {
return err
// Выполнение всех функций, начинающихся с package_
for {
packageFn, ok := dec.GetFunc("package")
if ok {
slog.Info(gotext.Get("Executing package()"))
err = packageFn(ctx, interp.Dir(dirs.SrcDir))
if err != nil {
return err
}
}
} else {
log.Fatal("The package() function is required").Send()
/*
// Проверка на наличие дополнительных функций package_*
packageFuncName := "package_"
if packageFunc, ok := dec.GetFunc(packageFuncName); ok {
slog.Info("Executing " + packageFuncName)
err = packageFunc(ctx, interp.Dir(dirs.SrcDir))
if err != nil {
return err
}
} else {
break // Если больше нет функций package_*, выходим из цикла
}
*/
break
}
return nil
}
// buildPkgMetadata builds the metadata for the package that's going to be built.
func buildPkgMetadata(vars *types.BuildVars, dirs types.Directories, pkgFormat string, deps []string) (*nfpm.Info, error) {
pkgInfo := &nfpm.Info{
Name: vars.Name,
Description: vars.Description,
Arch: cpu.Arch(),
Platform: "linux",
Version: vars.Version,
Release: strconv.Itoa(vars.Release),
Homepage: vars.Homepage,
License: strings.Join(vars.Licenses, ", "),
Maintainer: vars.Maintainer,
Overridables: nfpm.Overridables{
Conflicts: vars.Conflicts,
Replaces: vars.Replaces,
Provides: vars.Provides,
Depends: deps,
},
// Функция buildPkgMetadata создает метаданные для пакета, который будет собран.
func buildPkgMetadata(ctx context.Context, vars *types.BuildVars, dirs types.Directories, pkgFormat string, info *distro.OSRelease, deps []string) (*nfpm.Info, error) {
pkgInfo := getBasePkgInfo(vars)
pkgInfo.Description = vars.Description
pkgInfo.Platform = "linux"
pkgInfo.Homepage = vars.Homepage
pkgInfo.License = strings.Join(vars.Licenses, ", ")
pkgInfo.Maintainer = vars.Maintainer
pkgInfo.Overridables = nfpm.Overridables{
Conflicts: vars.Conflicts,
Replaces: vars.Replaces,
Provides: vars.Provides,
Depends: deps,
}
if pkgFormat == "apk" {
// Alpine refuses to install packages that provide themselves, so remove any such provides
// Alpine отказывается устанавливать пакеты, которые предоставляют сами себя, поэтому удаляем такие элементы
pkgInfo.Overridables.Provides = slices.DeleteFunc(pkgInfo.Overridables.Provides, func(s string) bool {
return s == pkgInfo.Name
})
}
if pkgFormat == "rpm" && info.ID == "altlinux" {
pkgInfo.Release = "alt" + pkgInfo.Release
}
if vars.Epoch != 0 {
pkgInfo.Epoch = strconv.FormatUint(uint64(vars.Epoch), 10)
}
@@ -523,11 +547,33 @@ func buildPkgMetadata(vars *types.BuildVars, dirs types.Directories, pkgFormat s
}
pkgInfo.Overridables.Contents = contents
if len(vars.AutoProv) == 1 && decoder.IsTruthy(vars.AutoProv[0]) {
if pkgFormat == "rpm" {
err = rpmFindProvides(ctx, pkgInfo, dirs)
if err != nil {
return nil, err
}
} else {
slog.Info(gotext.Get("AutoProv is not implemented for this package format, so it's skiped"))
}
}
if len(vars.AutoReq) == 1 && decoder.IsTruthy(vars.AutoReq[0]) {
if pkgFormat == "rpm" {
err = rpmFindRequires(ctx, pkgInfo, dirs)
if err != nil {
return nil, err
}
} else {
slog.Info(gotext.Get("AutoReq is not implemented for this package format, so it's skiped"))
}
}
return pkgInfo, nil
}
// buildContents builds the contents section of the package, which contains the files
// that will be placed into the final package.
// Функция buildContents создает секцию содержимого пакета, которая содержит файлы,
// которые будут включены в конечный пакет.
func buildContents(vars *types.BuildVars, dirs types.Directories) ([]*files.Content, error) {
contents := []*files.Content{}
err := filepath.Walk(dirs.PkgDir, func(path string, fi os.FileInfo, err error) error {
@@ -539,7 +585,7 @@ func buildContents(vars *types.BuildVars, dirs types.Directories) ([]*files.Cont
return err
}
// If the directory is empty, skip it
// Если директория пустая, пропускаем её
_, err = f.Readdirnames(1)
if err != io.EOF {
return nil
@@ -556,13 +602,13 @@ func buildContents(vars *types.BuildVars, dirs types.Directories) ([]*files.Cont
return f.Close()
}
// Если файл является символической ссылкой, прорабатываем это
if fi.Mode()&os.ModeSymlink != 0 {
link, err := os.Readlink(path)
if err != nil {
return err
}
// Remove pkgdir from the symlink's path
// Удаляем pkgdir из пути символической ссылки
link = strings.TrimPrefix(link, dirs.PkgDir)
contents = append(contents, &files.Content{
@@ -577,7 +623,7 @@ func buildContents(vars *types.BuildVars, dirs types.Directories) ([]*files.Cont
return nil
}
// Обрабатываем обычные файлы
fileContent := &files.Content{
Source: path,
Destination: trimmed,
@@ -588,7 +634,7 @@ func buildContents(vars *types.BuildVars, dirs types.Directories) ([]*files.Cont
},
}
// If the file is supposed to be backed up, set its type to config|noreplace
// Если файл должен быть сохранен, установите его тип как config|noreplace
if slices.Contains(vars.Backup, trimmed) {
fileContent.Type = "config|noreplace"
}
@@ -600,11 +646,11 @@ func buildContents(vars *types.BuildVars, dirs types.Directories) ([]*files.Cont
return contents, err
}
// removeBuildDeps asks the user if they'd like to remove the build dependencies that were
// installed by installBuildDeps. If so, it uses the package manager to do that.
// Функция removeBuildDeps спрашивает у пользователя, хочет ли он удалить зависимости,
// установленные для сборки. Если да, использует менеджер пакетов для их удаления.
func removeBuildDeps(ctx context.Context, buildDeps []string, opts types.BuildOpts) error {
if len(buildDeps) > 0 {
remove, err := cliutils.YesNoPrompt(ctx, "Would you like to remove the build dependencies?", opts.Interactive, false)
remove, err := cliutils.YesNoPrompt(ctx, gotext.Get("Would you like to remove the build dependencies?"), opts.Interactive, false)
if err != nil {
return err
}
@@ -625,8 +671,8 @@ func removeBuildDeps(ctx context.Context, buildDeps []string, opts types.BuildOp
return nil
}
// checkForBuiltPackage tries to detect a previously-built package and returns its path
// and true if it finds one. If it doesn't find it, it returns "", false, nil.
// Функция checkForBuiltPackage пытается обнаружить ранее собранный пакет и вернуть его путь
// и true, если нашла. Если нет, возвратит "", false, nil.
func checkForBuiltPackage(mgr manager.Manager, vars *types.BuildVars, pkgFormat, baseDir string) (string, bool, error) {
filename, err := pkgFileName(vars, pkgFormat)
if err != nil {
@@ -643,16 +689,20 @@ func checkForBuiltPackage(mgr manager.Manager, vars *types.BuildVars, pkgFormat,
return pkgPath, true, nil
}
// pkgFileName returns the filename of the package if it were to be built.
// This is used to check if the package has already been built.
func pkgFileName(vars *types.BuildVars, pkgFormat string) (string, error) {
pkgInfo := &nfpm.Info{
func getBasePkgInfo(vars *types.BuildVars) *nfpm.Info {
return &nfpm.Info{
Name: vars.Name,
Arch: cpu.Arch(),
Version: vars.Version,
Release: strconv.Itoa(vars.Release),
Epoch: strconv.FormatUint(uint64(vars.Epoch), 10),
}
}
// pkgFileName returns the filename of the package if it were to be built.
// This is used to check if the package has already been built.
func pkgFileName(vars *types.BuildVars, pkgFormat string) (string, error) {
pkgInfo := getBasePkgInfo(vars)
packager, err := nfpm.Get(pkgFormat)
if err != nil {
@@ -662,8 +712,8 @@ func pkgFileName(vars *types.BuildVars, pkgFormat string) (string, error) {
return packager.ConventionalFileName(pkgInfo), nil
}
// getPkgFormat returns the package format of the package manager,
// or ALR_PKG_FORMAT if that's set.
// Функция getPkgFormat возвращает формат пакета из менеджера пакетов,
// или ALR_PKG_FORMAT, если он установлен.
func getPkgFormat(mgr manager.Manager) string {
pkgFormat := mgr.Format()
if format, ok := os.LookupEnv("ALR_PKG_FORMAT"); ok {
@@ -672,8 +722,8 @@ func getPkgFormat(mgr manager.Manager) string {
return pkgFormat
}
// createBuildEnvVars creates the environment variables that will be set in the
// build script when it's executed.
// Функция createBuildEnvVars создает переменные окружения, которые будут установлены
// в скрипте сборки при его выполнении.
func createBuildEnvVars(info *distro.OSRelease, dirs types.Directories) []string {
env := os.Environ()
@@ -703,11 +753,11 @@ func createBuildEnvVars(info *distro.OSRelease, dirs types.Directories) []string
return env
}
// getSources downloads the sources from the script.
// Функция getSources загружает исходники скрипта.
func getSources(ctx context.Context, dirs types.Directories, bv *types.BuildVars) error {
log := loggerctx.From(ctx)
if len(bv.Sources) != len(bv.Checksums) {
log.Fatal("The checksums array must be the same length as sources").Send()
slog.Error(gotext.Get("The checksums array must be the same length as sources"))
os.Exit(1)
}
for i, src := range bv.Sources {
@@ -720,9 +770,9 @@ func getSources(ctx context.Context, dirs types.Directories, bv *types.BuildVars
}
if !strings.EqualFold(bv.Checksums[i], "SKIP") {
// If the checksum contains a colon, use the part before the colon
// as the algorithm and the part after as the actual checksum.
// Otherwise, use the default sha256 with the whole string as the checksum.
// Если контрольная сумма содержит двоеточие, используйте часть до двоеточия
// как алгоритм, а часть после как фактическую контрольную сумму.
// В противном случае используйте sha256 по умолчанию с целой строкой как контрольной суммой.
algo, hashData, ok := strings.Cut(bv.Checksums[i], ":")
if ok {
checksum, err := hex.DecodeString(hashData)
@@ -749,7 +799,7 @@ func getSources(ctx context.Context, dirs types.Directories, bv *types.BuildVars
return nil
}
// setScripts adds any hook scripts to the package metadata.
// Функция setScripts добавляет скрипты-перехватчики к метаданным пакета.
func setScripts(vars *types.BuildVars, info *nfpm.Info, scriptDir string) {
if vars.Scripts.PreInstall != "" {
info.Scripts.PreInstall = filepath.Join(scriptDir, vars.Scripts.PreInstall)
@@ -786,8 +836,8 @@ func setScripts(vars *types.BuildVars, info *nfpm.Info, scriptDir string) {
}
}
// setVersion changes the version variable in the script runner.
// It's used to set the version to the output of the version() function.
// Функция setVersion изменяет переменную версии в скрипте runner.
// Она используется для установки версии на вывод функции version().
func setVersion(ctx context.Context, r *interp.Runner, to string) error {
fl, err := syntax.NewParser().Parse(strings.NewReader("version='"+to+"'"), "")
if err != nil {
@@ -796,24 +846,25 @@ func setVersion(ctx context.Context, r *interp.Runner, to string) error {
return r.Run(ctx, fl)
}
// removeAlreadyInstalled returns a map without any dependencies that are already installed
func removeAlreadyInstalled(found map[string][]db.Package, installed map[string]string) map[string][]db.Package {
filteredPackages := make(map[string][]db.Package)
// Returns not installed dependencies
func removeAlreadyInstalled(opts types.BuildOpts, dependencies []string) ([]string, error) {
filteredPackages := []string{}
for name, pkgList := range found {
filteredPkgList := []db.Package{}
for _, pkg := range pkgList {
if _, isInstalled := installed[pkg.Name]; !isInstalled {
filteredPkgList = append(filteredPkgList, pkg)
}
for _, dep := range dependencies {
installed, err := opts.Manager.IsInstalled(dep)
if err != nil {
return nil, err
}
filteredPackages[name] = filteredPkgList
if installed {
continue
}
filteredPackages = append(filteredPackages, dep)
}
return filteredPackages
return filteredPackages, nil
}
// packageNames returns the names of all the given packages
// Функция packageNames возвращает имена всех предоставленных пакетов.
func packageNames(pkgs []db.Package) []string {
names := make([]string, len(pkgs))
for i, p := range pkgs {
@@ -822,7 +873,7 @@ func packageNames(pkgs []db.Package) []string {
return names
}
// removeDuplicates removes any duplicates from the given slice
// Функция removeDuplicates убирает любые дубликаты из предоставленного среза.
func removeDuplicates(slice []string) []string {
seen := map[string]struct{}{}
result := []string{}

View File

@@ -0,0 +1,225 @@
// 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 (
"context"
"testing"
"github.com/stretchr/testify/assert"
"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/pkg/manager"
)
type TestPackageFinder struct {
FindPkgsFunc func(ctx context.Context, pkgs []string) (map[string][]db.Package, []string, error)
}
func (pf *TestPackageFinder) FindPkgs(ctx context.Context, pkgs []string) (map[string][]db.Package, []string, error) {
if pf.FindPkgsFunc != nil {
return pf.FindPkgsFunc(ctx, pkgs)
}
return map[string][]db.Package{}, []string{}, nil
}
type TestManager struct {
NameFunc func() string
FormatFunc func() string
ExistsFunc func() bool
SetRootCmdFunc func(cmd string)
SyncFunc func(opts *manager.Opts) error
InstallFunc func(opts *manager.Opts, pkgs ...string) error
RemoveFunc func(opts *manager.Opts, pkgs ...string) error
UpgradeFunc func(opts *manager.Opts, pkgs ...string) error
InstallLocalFunc func(opts *manager.Opts, files ...string) error
UpgradeAllFunc func(opts *manager.Opts) error
ListInstalledFunc func(opts *manager.Opts) (map[string]string, error)
IsInstalledFunc func(pkg string) (bool, error)
}
func (m *TestManager) Name() string {
if m.NameFunc != nil {
return m.NameFunc()
}
return "TestManager"
}
func (m *TestManager) Format() string {
if m.FormatFunc != nil {
return m.FormatFunc()
}
return "testpkg"
}
func (m *TestManager) Exists() bool {
if m.ExistsFunc != nil {
return m.ExistsFunc()
}
return true
}
func (m *TestManager) SetRootCmd(cmd string) {
if m.SetRootCmdFunc != nil {
m.SetRootCmdFunc(cmd)
}
}
func (m *TestManager) Sync(opts *manager.Opts) error {
if m.SyncFunc != nil {
return m.SyncFunc(opts)
}
return nil
}
func (m *TestManager) Install(opts *manager.Opts, pkgs ...string) error {
if m.InstallFunc != nil {
return m.InstallFunc(opts, pkgs...)
}
return nil
}
func (m *TestManager) Remove(opts *manager.Opts, pkgs ...string) error {
if m.RemoveFunc != nil {
return m.RemoveFunc(opts, pkgs...)
}
return nil
}
func (m *TestManager) Upgrade(opts *manager.Opts, pkgs ...string) error {
if m.UpgradeFunc != nil {
return m.UpgradeFunc(opts, pkgs...)
}
return nil
}
func (m *TestManager) InstallLocal(opts *manager.Opts, files ...string) error {
if m.InstallLocalFunc != nil {
return m.InstallLocalFunc(opts, files...)
}
return nil
}
func (m *TestManager) UpgradeAll(opts *manager.Opts) error {
if m.UpgradeAllFunc != nil {
return m.UpgradeAllFunc(opts)
}
return nil
}
func (m *TestManager) ListInstalled(opts *manager.Opts) (map[string]string, error) {
if m.ListInstalledFunc != nil {
return m.ListInstalledFunc(opts)
}
return map[string]string{}, nil
}
func (m *TestManager) IsInstalled(pkg string) (bool, error) {
if m.IsInstalledFunc != nil {
return m.IsInstalledFunc(pkg)
}
return true, nil
}
func TestInstallBuildDeps(t *testing.T) {
type testEnv struct {
pf PackageFinder
vars *types.BuildVars
opts types.BuildOpts
// Contains pkgs captured by FindPkgsFunc
capturedPkgs []string
}
type testCase struct {
Name string
Prepare func() *testEnv
Expected func(t *testing.T, e *testEnv, res []string, err error)
}
for _, tc := range []testCase{
{
Name: "install only needed deps",
Prepare: func() *testEnv {
pf := TestPackageFinder{}
vars := types.BuildVars{}
m := TestManager{}
opts := types.BuildOpts{
Manager: &m,
Interactive: false,
}
env := &testEnv{
pf: &pf,
vars: &vars,
opts: opts,
capturedPkgs: []string{},
}
pf.FindPkgsFunc = func(ctx context.Context, pkgs []string) (map[string][]db.Package, []string, error) {
env.capturedPkgs = append(env.capturedPkgs, pkgs...)
result := make(map[string][]db.Package)
result["bar"] = []db.Package{{
Name: "bar-pkg",
}}
result["buz"] = []db.Package{{
Name: "buz-pkg",
}}
return result, []string{}, nil
}
vars.BuildDepends = []string{
"foo",
"bar",
"buz",
}
m.IsInstalledFunc = func(pkg string) (bool, error) {
if pkg == "foo" {
return true, nil
} else {
return false, nil
}
}
return env
},
Expected: func(t *testing.T, e *testEnv, res []string, err error) {
assert.NoError(t, err)
assert.Len(t, res, 2)
assert.ElementsMatch(t, res, []string{"bar-pkg", "buz-pkg"})
assert.ElementsMatch(t, e.capturedPkgs, []string{"bar", "buz"})
},
},
} {
t.Run(tc.Name, func(tt *testing.T) {
ctx := context.Background()
env := tc.Prepare()
result, err := installBuildDeps(
ctx,
env.pf,
env.vars,
env.opts,
)
tc.Expected(tt, env, result, err)
})
}
}

92
pkg/build/findDeps.go Normal file
View File

@@ -0,0 +1,92 @@
// 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 (
"bytes"
"context"
"log/slog"
"os/exec"
"path"
"strings"
"github.com/goreleaser/nfpm/v2"
"github.com/leonelquinteros/gotext"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/types"
)
func rpmFindDependencies(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories, command string, updateFunc func(string)) error {
if _, err := exec.LookPath(command); err != nil {
slog.Info(gotext.Get("Command not found on the system"), "command", command)
return nil
}
var paths []string
for _, content := range pkgInfo.Contents {
if content.Type != "dir" {
paths = append(paths,
path.Join(dirs.PkgDir, content.Destination),
)
}
}
if len(paths) == 0 {
return nil
}
cmd := exec.Command(command)
cmd.Stdin = bytes.NewBufferString(strings.Join(paths, "\n"))
cmd.Env = append(cmd.Env,
"RPM_BUILD_ROOT="+dirs.PkgDir,
"RPM_FINDPROV_METHOD=",
"RPM_FINDREQ_METHOD=",
"RPM_DATADIR=",
"RPM_SUBPACKAGE_NAME=",
)
var out bytes.Buffer
var stderr bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
slog.Error(stderr.String())
return err
}
dependencies := strings.Split(strings.TrimSpace(out.String()), "\n")
for _, dep := range dependencies {
if dep != "" {
updateFunc(dep)
}
}
return nil
}
func rpmFindProvides(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories) error {
return rpmFindDependencies(ctx, pkgInfo, dirs, "/usr/lib/rpm/find-provides", func(dep string) {
slog.Info(gotext.Get("Provided dependency found"), "dep", dep)
pkgInfo.Overridables.Provides = append(pkgInfo.Overridables.Provides, dep)
})
}
func rpmFindRequires(ctx context.Context, pkgInfo *nfpm.Info, dirs types.Directories) error {
return rpmFindDependencies(ctx, pkgInfo, dirs, "/usr/lib/rpm/find-requires", func(dep string) {
slog.Info(gotext.Get("Required dependency found"), "dep", dep)
pkgInfo.Overridables.Depends = append(pkgInfo.Overridables.Depends, dep)
})
}

View File

@@ -1,72 +1,84 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package build
import (
"context"
"log/slog"
"os"
"path/filepath"
"plemya-x.ru/alr/internal/config"
"plemya-x.ru/alr/internal/db"
"plemya-x.ru/alr/internal/types"
"plemya-x.ru/alr/pkg/loggerctx"
"github.com/leonelquinteros/gotext"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/types"
)
// InstallPkgs installs native packages via the package manager,
// then builds and installs the ALR packages
// InstallPkgs устанавливает нативные пакеты с использованием менеджера пакетов,
// затем строит и устанавливает пакеты ALR
func InstallPkgs(ctx context.Context, alrPkgs []db.Package, nativePkgs []string, opts types.BuildOpts) {
log := loggerctx.From(ctx)
if len(nativePkgs) > 0 {
err := opts.Manager.Install(nil, nativePkgs...)
// Если есть нативные пакеты, выполняем их установку
if err != nil {
log.Fatal("Error installing native packages").Err(err).Send()
slog.Error(gotext.Get("Error installing native packages"), "err", err)
os.Exit(1)
// Логируем и завершаем выполнение при ошибке
}
}
InstallScripts(ctx, GetScriptPaths(ctx, alrPkgs), opts)
// Устанавливаем скрипты сборки через функцию InstallScripts
}
// GetScriptPaths returns a slice of script paths corresponding to the
// given packages
// GetScriptPaths возвращает срез путей к скриптам, соответствующий
// данным пакетам
func GetScriptPaths(ctx context.Context, pkgs []db.Package) []string {
var scripts []string
for _, pkg := range pkgs {
// Для каждого пакета создаем путь к скрипту сборки
scriptPath := filepath.Join(config.GetPaths(ctx).RepoDir, pkg.Repository, pkg.Name, "alr.sh")
scripts = append(scripts, scriptPath)
}
return scripts
}
// InstallScripts builds and installs the given alr build scripts
// InstallScripts строит и устанавливает переданные alr скрипты сборки
func InstallScripts(ctx context.Context, scripts []string, opts types.BuildOpts) {
log := loggerctx.From(ctx)
for _, script := range scripts {
opts.Script = script
opts.Script = script // Устанавливаем текущий скрипт в опции
builtPkgs, _, err := BuildPackage(ctx, opts)
// Выполняем сборку пакета
if err != nil {
log.Fatal("Error building package").Err(err).Send()
slog.Error(gotext.Get("Error building package"), "err", err)
os.Exit(1)
// Логируем и завершаем выполнение при ошибке сборки
}
err = opts.Manager.InstallLocal(nil, builtPkgs...)
// Устанавливаем локально собранные пакеты
if err != nil {
log.Fatal("Error installing package").Err(err).Send()
slog.Error(gotext.Get("Error installing package"), "err", err)
os.Exit(1)
// Логируем и завершаем выполнение при ошибке установки
}
}
}

View File

@@ -1,20 +1,21 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package distro
@@ -23,10 +24,11 @@ import (
"os"
"strings"
"plemya-x.ru/alr/internal/shutils/handlers"
"mvdan.cc/sh/v3/expand"
"mvdan.cc/sh/v3/interp"
"mvdan.cc/sh/v3/syntax"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/handlers"
)
// OSRelease contains information from an os-release file

View File

@@ -1,3 +1,22 @@
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package gen
import (
@@ -5,8 +24,15 @@ import (
"text/template"
)
// Определяем переменную funcs типа template.FuncMap, которая будет использоваться для
// предоставления пользовательских функций в шаблонах
var funcs = template.FuncMap{
// Функция "tolower" использует strings.ToLower
// для преобразования строки в нижний регистр
"tolower": strings.ToLower,
// Функция "firstchar" — это лямбда-функция, которая берет строку
// и возвращает её первый символ
"firstchar": func(s string) string {
return s[:1]
},

View File

@@ -1,29 +1,54 @@
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package gen
import (
_ "embed"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"text/template"
_ "embed" // Пакет для встраивания содержимого файлов в бинарники Go, использовав откладку //go:embed
"encoding/json" // Пакет для работы с JSON: декодирование и кодирование
"errors" // Пакет для создания и обработки ошибок
"fmt" // Пакет для форматированного ввода и вывода
"io" // Пакет для интерфейсов ввода и вывода
"net/http" // Пакет для HTTP-клиентов и серверов
"text/template" // Пакет для обработки текстовых шаблонов
)
// Используем директиву //go:embed для встраивания содержимого файла шаблона в строку pipTmpl
// Встраивание файла tmpls/pip.tmpl.sh
//
//go:embed tmpls/pip.tmpl.sh
var pipTmpl string
// PipOptions содержит параметры, которые будут переданы в шаблон
type PipOptions struct {
Name string
Version string
Description string
Name string // Имя пакета
Version string // Версия пакета
Description string // Описание пакета
}
// pypiAPIResponse представляет структуру ответа от API PyPI
type pypiAPIResponse struct {
Info pypiInfo `json:"info"`
URLs []pypiURL `json:"urls"`
Info pypiInfo `json:"info"` // Информация о пакете
URLs []pypiURL `json:"urls"` // Список URL-адресов для загрузки пакета
}
// Метод SourceURL ищет и возвращает URL исходного distribution для пакета, если он существует
func (res pypiAPIResponse) SourceURL() (pypiURL, error) {
for _, url := range res.URLs {
if url.PackageType == "sdist" {
@@ -33,6 +58,7 @@ func (res pypiAPIResponse) SourceURL() (pypiURL, error) {
return pypiURL{}, errors.New("package doesn't have a source distribution")
}
// pypiInfo содержит основную информацию о пакете, такую как имя, версия и пр.
type pypiInfo struct {
Name string `json:"name"`
Version string `json:"version"`
@@ -41,13 +67,16 @@ type pypiInfo struct {
License string `json:"license"`
}
// pypiURL представляет информацию об одном из доступных для загрузки URL
type pypiURL struct {
Digests map[string]string `json:"digests"`
Filename string `json:"filename"`
PackageType string `json:"packagetype"`
Digests map[string]string `json:"digests"` // Контрольные суммы для файлов
Filename string `json:"filename"` // Имя файла
PackageType string `json:"packagetype"` // Тип пакета (например sdist)
}
// Функция Pip загружает информацию о пакете из PyPI и использует шаблон для вывода информации
func Pip(w io.Writer, opts PipOptions) error {
// Создаем новый шаблон с добавлением функций из FuncMap
tmpl, err := template.New("pip").
Funcs(funcs).
Parse(pipTmpl)
@@ -55,30 +84,35 @@ func Pip(w io.Writer, opts PipOptions) error {
return err
}
// Формируем URL для запроса к PyPI на основании имени и версии пакета
url := fmt.Sprintf(
"https://pypi.org/pypi/%s/%s/json",
opts.Name,
opts.Version,
)
// Выполняем HTTP GET запрос к PyPI
res, err := http.Get(url)
if err != nil {
return err
}
defer res.Body.Close()
defer res.Body.Close() // Закрываем тело ответа после завершения работы
if res.StatusCode != 200 {
return fmt.Errorf("pypi: %s", res.Status)
}
// Раскодируем ответ JSON от PyPI в структуру pypiAPIResponse
var resp pypiAPIResponse
err = json.NewDecoder(res.Body).Decode(&resp)
if err != nil {
return err
}
// Если в opts указано описание, используем его вместо описания из PyPI
if opts.Description != "" {
resp.Info.Summary = opts.Description
}
// Выполняем шаблон с использованием данных из resp и записываем результат в w
return tmpl.Execute(w, resp)
}

View File

@@ -1,3 +1,22 @@
# This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
# It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
#
# ALR - Any Linux Repository
# Copyright (C) 2025 Евгений Храмов
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
name='{{.Info.Name | tolower}}'
version='{{.Info.Version}}'
release='1'

View File

@@ -1,29 +0,0 @@
package loggerctx
import (
"context"
"go.elara.ws/logger"
)
// loggerCtxKey is used as the context key for loggers
type loggerCtxKey struct{}
// With returns a copy of ctx containing log
func With(ctx context.Context, log logger.Logger) context.Context {
return context.WithValue(ctx, loggerCtxKey{}, log)
}
// From attempts to get a logger from ctx. If ctx doesn't
// contain a logger, it returns a nop logger.
func From(ctx context.Context) logger.Logger {
if val := ctx.Value(loggerCtxKey{}); val != nil {
if log, ok := val.(logger.Logger); ok && log != nil {
return log
} else {
return logger.NewNop()
}
} else {
return logger.NewNop()
}
}

View File

@@ -1,20 +1,21 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package manager
@@ -148,6 +149,21 @@ func (a *APK) ListInstalled(opts *Opts) (map[string]string, error) {
return out, nil
}
func (a *APK) IsInstalled(pkg string) (bool, error) {
cmd := exec.Command("apk", "info", "--installed", pkg)
output, err := cmd.CombinedOutput()
if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
// Exit code 1 means the package is not installed
if exitErr.ExitCode() == 1 {
return false, nil
}
}
return false, fmt.Errorf("apk: isinstalled: %w, output: %s", err, output)
}
return true, nil
}
func (a *APK) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
var cmd *exec.Cmd
if opts.AsRoot {

View File

@@ -1,20 +1,21 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package manager
@@ -134,6 +135,21 @@ func (a *APT) ListInstalled(opts *Opts) (map[string]string, error) {
return out, nil
}
func (a *APT) IsInstalled(pkg string) (bool, error) {
cmd := exec.Command("dpkg-query", "-l", pkg)
output, err := cmd.CombinedOutput()
if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
// Exit code 1 means the package is not installed
if exitErr.ExitCode() == 1 {
return false, nil
}
}
return false, fmt.Errorf("apt: isinstalled: %w, output: %s", err, output)
}
return true, nil
}
func (a *APT) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
var cmd *exec.Cmd
if opts.AsRoot {

124
pkg/manager/apt_rpm.go Normal file
View File

@@ -0,0 +1,124 @@
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package manager
import (
"fmt"
"os/exec"
"strings"
)
// APTRpm represents the APT-RPM package manager
type APTRpm struct {
CommonRPM
rootCmd string
}
func (*APTRpm) Exists() bool {
cmd := exec.Command("apt-config", "dump")
output, err := cmd.Output()
if err != nil {
return false
}
return strings.Contains(string(output), "RPM")
}
func (*APTRpm) Name() string {
return "apt-rpm"
}
func (*APTRpm) Format() string {
return "rpm"
}
func (a *APTRpm) SetRootCmd(s string) {
a.rootCmd = s
}
func (a *APTRpm) Sync(opts *Opts) error {
opts = ensureOpts(opts)
cmd := a.getCmd(opts, "apt-get", "update")
setCmdEnv(cmd)
err := cmd.Run()
if err != nil {
return fmt.Errorf("apt-get: sync: %w", err)
}
return nil
}
func (a *APTRpm) Install(opts *Opts, pkgs ...string) error {
opts = ensureOpts(opts)
cmd := a.getCmd(opts, "apt-get", "install")
cmd.Args = append(cmd.Args, pkgs...)
setCmdEnv(cmd)
err := cmd.Run()
if err != nil {
return fmt.Errorf("apt-get: install: %w", err)
}
return nil
}
func (a *APTRpm) InstallLocal(opts *Opts, pkgs ...string) error {
opts = ensureOpts(opts)
return a.Install(opts, pkgs...)
}
func (a *APTRpm) Remove(opts *Opts, pkgs ...string) error {
opts = ensureOpts(opts)
cmd := a.getCmd(opts, "apt-get", "remove")
cmd.Args = append(cmd.Args, pkgs...)
setCmdEnv(cmd)
err := cmd.Run()
if err != nil {
return fmt.Errorf("apt-get: remove: %w", err)
}
return nil
}
func (a *APTRpm) Upgrade(opts *Opts, pkgs ...string) error {
opts = ensureOpts(opts)
return a.Install(opts, pkgs...)
}
func (a *APTRpm) UpgradeAll(opts *Opts) error {
opts = ensureOpts(opts)
cmd := a.getCmd(opts, "apt-get", "dist-upgrade")
setCmdEnv(cmd)
err := cmd.Run()
if err != nil {
return fmt.Errorf("apt-get: upgradeall: %w", err)
}
return nil
}
func (a *APTRpm) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
var cmd *exec.Cmd
if opts.AsRoot {
cmd = exec.Command(getRootCmd(a.rootCmd), mgrCmd)
cmd.Args = append(cmd.Args, opts.Args...)
cmd.Args = append(cmd.Args, args...)
} else {
cmd = exec.Command(mgrCmd, args...)
}
if opts.NoConfirm {
cmd.Args = append(cmd.Args, "-y")
}
return cmd
}

72
pkg/manager/common_rpm.go Normal file
View File

@@ -0,0 +1,72 @@
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package manager
import (
"bufio"
"fmt"
"os/exec"
"strings"
)
type CommonRPM struct{}
func (c *CommonRPM) ListInstalled(opts *Opts) (map[string]string, error) {
out := map[string]string{}
cmd := exec.Command("rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")
stdout, err := cmd.StdoutPipe()
if err != nil {
return nil, err
}
err = cmd.Start()
if err != nil {
return nil, err
}
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
name, version, ok := strings.Cut(scanner.Text(), "\u200b")
if !ok {
continue
}
version = strings.TrimPrefix(version, "0:")
out[name] = version
}
err = scanner.Err()
if err != nil {
return nil, err
}
return out, nil
}
func (a *CommonRPM) IsInstalled(pkg string) (bool, error) {
cmd := exec.Command("rpm", "-q", "--whatprovides", pkg)
output, err := cmd.CombinedOutput()
if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
if exitErr.ExitCode() == 1 {
return false, nil
}
}
return false, fmt.Errorf("rpm: isinstalled: %w, output: %s", err, output)
}
return true, nil
}

View File

@@ -1,67 +1,72 @@
/*
* ALR - Any Linux Repository
* ALR - Любой Linux Репозиторий
* Copyright (C) 2024 Евгений Храмов
*
* 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 является свободным: вы можете распространять его и/или изменять
* на условиях GNU General Public License, опубликованной Free Software Foundation,
* либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.
*
* 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.
* Это программное обеспечение распространяется в надежде, что оно будет полезным,
* но БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ; без подразумеваемой гарантии
* КОММЕРЧЕСКОЙ ПРИГОДНОСТИ или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННОЙ ЦЕЛИ.
* Подробности см. в GNU General Public License.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* Вы должны были получить копию GNU General Public License
* вместе с этой программой. Если нет, см. <http://www.gnu.org/licenses/>.
*/
package manager
import (
"bufio"
"fmt"
"os/exec"
"strings"
)
// DNF represents the DNF package manager
// DNF представляет менеджер пакетов DNF
type DNF struct {
rootCmd string
CommonRPM
rootCmd string // rootCmd хранит команду, используемую для выполнения команд с правами root
}
// Exists проверяет, доступен ли DNF в системе, возвращает true если да
func (*DNF) Exists() bool {
_, err := exec.LookPath("dnf")
return err == nil
}
// Name возвращает имя менеджера пакетов, в данном случае "dnf"
func (*DNF) Name() string {
return "dnf"
}
// Format возвращает формат пакетов "rpm", используемый DNF
func (*DNF) Format() string {
return "rpm"
}
// SetRootCmd устанавливает команду, используемую для выполнения операций с правами root
func (d *DNF) SetRootCmd(s string) {
d.rootCmd = s
}
// Sync выполняет upgrade всех установленных пакетов, обновляя их до более новых версий
func (d *DNF) Sync(opts *Opts) error {
opts = ensureOpts(opts)
opts = ensureOpts(opts) // Гарантирует, что opts не равен nil и содержит допустимые значения
cmd := d.getCmd(opts, "dnf", "upgrade")
setCmdEnv(cmd)
err := cmd.Run()
setCmdEnv(cmd) // Устанавливает переменные окружения для команды
err := cmd.Run() // Выполняет команду
if err != nil {
return fmt.Errorf("dnf: sync: %w", err)
}
return nil
}
// Install устанавливает указанные пакеты с помощью DNF
func (d *DNF) Install(opts *Opts, pkgs ...string) error {
opts = ensureOpts(opts)
cmd := d.getCmd(opts, "dnf", "install", "--allowerasing")
cmd.Args = append(cmd.Args, pkgs...)
cmd.Args = append(cmd.Args, pkgs...) // Добавляем названия пакетов к команде
setCmdEnv(cmd)
err := cmd.Run()
if err != nil {
@@ -70,11 +75,13 @@ func (d *DNF) Install(opts *Opts, pkgs ...string) error {
return nil
}
// InstallLocal расширяет метод Install для установки пакетов, расположенных локально
func (d *DNF) InstallLocal(opts *Opts, pkgs ...string) error {
opts = ensureOpts(opts)
return d.Install(opts, pkgs...)
}
// Remove удаляет указанные пакеты с помощью DNF
func (d *DNF) Remove(opts *Opts, pkgs ...string) error {
opts = ensureOpts(opts)
cmd := d.getCmd(opts, "dnf", "remove")
@@ -87,6 +94,7 @@ func (d *DNF) Remove(opts *Opts, pkgs ...string) error {
return nil
}
// Upgrade обновляет указанные пакеты до более новых версий
func (d *DNF) Upgrade(opts *Opts, pkgs ...string) error {
opts = ensureOpts(opts)
cmd := d.getCmd(opts, "dnf", "upgrade")
@@ -99,6 +107,7 @@ func (d *DNF) Upgrade(opts *Opts, pkgs ...string) error {
return nil
}
// UpgradeAll обновляет все установленные пакеты
func (d *DNF) UpgradeAll(opts *Opts) error {
opts = ensureOpts(opts)
cmd := d.getCmd(opts, "dnf", "upgrade")
@@ -110,38 +119,7 @@ func (d *DNF) UpgradeAll(opts *Opts) error {
return nil
}
func (d *DNF) ListInstalled(opts *Opts) (map[string]string, error) {
out := map[string]string{}
cmd := exec.Command("rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")
stdout, err := cmd.StdoutPipe()
if err != nil {
return nil, err
}
err = cmd.Start()
if err != nil {
return nil, err
}
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
name, version, ok := strings.Cut(scanner.Text(), "\u200b")
if !ok {
continue
}
version = strings.TrimPrefix(version, "0:")
out[name] = version
}
err = scanner.Err()
if err != nil {
return nil, err
}
return out, nil
}
// getCmd создает и возвращает команду exec.Cmd для менеджера пакетов DNF
func (d *DNF) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
var cmd *exec.Cmd
if opts.AsRoot {
@@ -153,7 +131,7 @@ func (d *DNF) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
}
if opts.NoConfirm {
cmd.Args = append(cmd.Args, "-y")
cmd.Args = append(cmd.Args, "-y") // Добавляет параметр автоматического подтверждения (-y)
}
return cmd

View File

@@ -1,20 +1,21 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package manager
@@ -46,6 +47,7 @@ var managers = []Manager{
&YUM{},
&APK{},
&Zypper{},
&APTRpm{},
}
// Register registers a new package manager
@@ -78,6 +80,8 @@ type Manager interface {
UpgradeAll(*Opts) error
// ListInstalled returns all installed packages mapped to their versions
ListInstalled(*Opts) (map[string]string, error)
//
IsInstalled(string) (bool, error)
}
// Detect returns the package manager detected on the system

View File

@@ -1,20 +1,21 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package manager
@@ -141,6 +142,21 @@ func (p *Pacman) ListInstalled(opts *Opts) (map[string]string, error) {
return out, nil
}
func (p *Pacman) IsInstalled(pkg string) (bool, error) {
cmd := exec.Command("pacman", "-Q", pkg)
output, err := cmd.CombinedOutput()
if err != nil {
// Pacman returns exit code 1 if the package is not found
if exitErr, ok := err.(*exec.ExitError); ok {
if exitErr.ExitCode() == 1 {
return false, nil
}
}
return false, fmt.Errorf("pacman: isinstalled: %w, output: %s", err, output)
}
return true, nil
}
func (p *Pacman) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
var cmd *exec.Cmd
if opts.AsRoot {

View File

@@ -1,32 +1,33 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package manager
import (
"bufio"
"fmt"
"os/exec"
"strings"
)
// YUM represents the YUM package manager
type YUM struct {
CommonRPM
rootCmd string
}
@@ -110,38 +111,6 @@ func (y *YUM) UpgradeAll(opts *Opts) error {
return nil
}
func (y *YUM) ListInstalled(opts *Opts) (map[string]string, error) {
out := map[string]string{}
cmd := exec.Command("rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")
stdout, err := cmd.StdoutPipe()
if err != nil {
return nil, err
}
err = cmd.Start()
if err != nil {
return nil, err
}
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
name, version, ok := strings.Cut(scanner.Text(), "\u200b")
if !ok {
continue
}
version = strings.TrimPrefix(version, "0:")
out[name] = version
}
err = scanner.Err()
if err != nil {
return nil, err
}
return out, nil
}
func (y *YUM) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
var cmd *exec.Cmd
if opts.AsRoot {

View File

@@ -1,32 +1,32 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package manager
import (
"bufio"
"fmt"
"os/exec"
"strings"
)
// Zypper represents the Zypper package manager
type Zypper struct {
CommonRPM
rootCmd string
}
@@ -110,38 +110,6 @@ func (z *Zypper) UpgradeAll(opts *Opts) error {
return nil
}
func (z *Zypper) ListInstalled(opts *Opts) (map[string]string, error) {
out := map[string]string{}
cmd := exec.Command("rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")
stdout, err := cmd.StdoutPipe()
if err != nil {
return nil, err
}
err = cmd.Start()
if err != nil {
return nil, err
}
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
name, version, ok := strings.Cut(scanner.Text(), "\u200b")
if !ok {
continue
}
version = strings.TrimPrefix(version, "0:")
out[name] = version
}
err = scanner.Err()
if err != nil {
return nil, err
}
return out, nil
}
func (z *Zypper) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
var cmd *exec.Cmd
if opts.AsRoot {

View File

@@ -1,33 +1,31 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package repos
import (
"context"
"plemya-x.ru/alr/internal/db"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
)
// FindPkgs looks for packages matching the inputs inside the database.
// It returns a map that maps the package name input to any packages found for it.
// It also returns a slice that contains the names of all packages that were not found.
func FindPkgs(ctx context.Context, pkgs []string) (map[string][]db.Package, []string, error) {
func (rs *Repos) FindPkgs(ctx context.Context, pkgs []string) (map[string][]db.Package, []string, error) {
found := map[string][]db.Package{}
notFound := []string(nil)
@@ -36,7 +34,7 @@ func FindPkgs(ctx context.Context, pkgs []string) (map[string][]db.Package, []st
continue
}
result, err := db.GetPkgs(ctx, "json_array_contains(provides, ?)", pkgName)
result, err := rs.db.GetPkgs(ctx, "json_array_contains(provides, ?)", pkgName)
if err != nil {
return nil, nil, err
}
@@ -55,7 +53,7 @@ func FindPkgs(ctx context.Context, pkgs []string) (map[string][]db.Package, []st
result.Close()
if added == 0 {
result, err := db.GetPkgs(ctx, "name LIKE ?", pkgName)
result, err := rs.db.GetPkgs(ctx, "name LIKE ?", pkgName)
if err != nil {
return nil, nil, err
}

View File

@@ -1,47 +1,44 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package repos_test
import (
"context"
"reflect"
"strings"
"testing"
"plemya-x.ru/alr/internal/db"
"plemya-x.ru/alr/internal/types"
"plemya-x.ru/alr/pkg/repos"
"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/pkg/repos"
)
func TestFindPkgs(t *testing.T) {
_, err := db.Open(":memory:")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
defer db.Close()
e := prepare(t)
defer cleanup(t, e)
setCfgDirs(t)
defer removeCacheDir(t)
rs := repos.New(
e.Cfg,
e.Db,
)
ctx := context.Background()
err = repos.Pull(ctx, []types.Repo{
err := rs.Pull(e.Ctx, []types.Repo{
{
Name: "default",
URL: "https://gitea.plemya-x.ru/xpamych/xpamych-alr-repo.git",
@@ -51,7 +48,10 @@ func TestFindPkgs(t *testing.T) {
t.Fatalf("Expected no error, got %s", err)
}
found, notFound, err := repos.FindPkgs([]string{"itd", "nonexistentpackage1", "nonexistentpackage2"})
found, notFound, err := rs.FindPkgs(
e.Ctx,
[]string{"alr", "nonexistentpackage1", "nonexistentpackage2"},
)
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
@@ -64,33 +64,32 @@ func TestFindPkgs(t *testing.T) {
t.Errorf("Expected 1 package found, got %d", len(found))
}
itdPkgs, ok := found["itd"]
alrPkgs, ok := found["alr"]
if !ok {
t.Fatalf("Expected 'itd' packages to be found")
t.Fatalf("Expected 'alr' packages to be found")
}
if len(itdPkgs) < 2 {
t.Errorf("Expected two 'itd' packages to be found")
if len(alrPkgs) < 2 {
t.Errorf("Expected two 'alr' packages to be found")
}
for i, pkg := range itdPkgs {
if !strings.HasPrefix(pkg.Name, "itd") {
t.Errorf("Expected package name of all found packages to start with 'itd', got %s on element %d", pkg.Name, i)
for i, pkg := range alrPkgs {
if !strings.HasPrefix(pkg.Name, "alr") {
t.Errorf("Expected package name of all found packages to start with 'alr', got %s on element %d", pkg.Name, i)
}
}
}
func TestFindPkgsEmpty(t *testing.T) {
_, err := db.Open(":memory:")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
defer db.Close()
e := prepare(t)
defer cleanup(t, e)
setCfgDirs(t)
defer removeCacheDir(t)
rs := repos.New(
e.Cfg,
e.Db,
)
err = db.InsertPackage(db.Package{
err := e.Db.InsertPackage(e.Ctx, db.Package{
Name: "test1",
Repository: "default",
Version: "0.0.1",
@@ -105,7 +104,7 @@ func TestFindPkgsEmpty(t *testing.T) {
t.Fatalf("Expected no error, got %s", err)
}
err = db.InsertPackage(db.Package{
err = e.Db.InsertPackage(e.Ctx, db.Package{
Name: "test2",
Repository: "default",
Version: "0.0.1",
@@ -120,7 +119,7 @@ func TestFindPkgsEmpty(t *testing.T) {
t.Fatalf("Expected no error, got %s", err)
}
found, notFound, err := repos.FindPkgs([]string{"test", ""})
found, notFound, err := rs.FindPkgs(e.Ctx, []string{"test", ""})
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}

View File

@@ -1,61 +1,69 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package repos
import (
"context"
"errors"
"io"
"log/slog"
"net/url"
"os"
"path/filepath"
"reflect"
"strings"
"github.com/go-git/go-billy/v5"
"github.com/go-git/go-billy/v5/osfs"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/format/diff"
"github.com/leonelquinteros/gotext"
"github.com/pelletier/go-toml/v2"
"go.elara.ws/vercmp"
"plemya-x.ru/alr/internal/config"
"plemya-x.ru/alr/internal/db"
"plemya-x.ru/alr/internal/shutils/decoder"
"plemya-x.ru/alr/internal/shutils/handlers"
"plemya-x.ru/alr/internal/types"
"plemya-x.ru/alr/pkg/distro"
"plemya-x.ru/alr/pkg/loggerctx"
"mvdan.cc/sh/v3/expand"
"mvdan.cc/sh/v3/interp"
"mvdan.cc/sh/v3/syntax"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/handlers"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/types"
)
type actionType uint8
const (
actionDelete actionType = iota
actionUpdate
)
type action struct {
Type actionType
File string
}
// Pull pulls the provided repositories. If a repo doesn't exist, it will be cloned
// and its packages will be written to the DB. If it does exist, it will be pulled.
// In this case, only changed packages will be processed if possible.
// If repos is set to nil, the repos in the ALR config will be used.
func Pull(ctx context.Context, repos []types.Repo) error {
log := loggerctx.From(ctx)
func (rs *Repos) Pull(ctx context.Context, repos []types.Repo) error {
if repos == nil {
repos = config.Config(ctx).Repos
repos = rs.cfg.Repos(ctx)
}
for _, repo := range repos {
@@ -64,7 +72,7 @@ func Pull(ctx context.Context, repos []types.Repo) error {
return err
}
log.Info("Pulling repository").Str("name", repo.Name).Send()
slog.Info(gotext.Get("Pulling repository"), "name", repo.Name)
repoDir := filepath.Join(config.GetPaths(ctx).RepoDir, repo.Name)
var repoFS billy.Filesystem
@@ -88,14 +96,14 @@ func Pull(ctx context.Context, repos []types.Repo) error {
err = w.PullContext(ctx, &git.PullOptions{Progress: os.Stderr})
if errors.Is(err, git.NoErrAlreadyUpToDate) {
log.Info("Repository up to date").Str("name", repo.Name).Send()
slog.Info(gotext.Get("Repository up to date"), "name", repo.Name)
} else if err != nil {
return err
}
repoFS = w.Filesystem
// Make sure the DB is created even if the repo is up to date
if !errors.Is(err, git.NoErrAlreadyUpToDate) || db.IsEmpty(ctx) {
if !errors.Is(err, git.NoErrAlreadyUpToDate) || rs.db.IsEmpty(ctx) {
new, err := r.Head()
if err != nil {
return err
@@ -104,13 +112,13 @@ func Pull(ctx context.Context, repos []types.Repo) error {
// If the DB was not present at startup, that means it's
// empty. In this case, we need to update the DB fully
// rather than just incrementally.
if db.IsEmpty(ctx) {
err = processRepoFull(ctx, repo, repoDir)
if rs.db.IsEmpty(ctx) {
err = rs.processRepoFull(ctx, repo, repoDir)
if err != nil {
return err
}
} else {
err = processRepoChanges(ctx, repo, r, w, old, new)
err = rs.processRepoChanges(ctx, repo, r, w, old, new)
if err != nil {
return err
}
@@ -135,7 +143,7 @@ func Pull(ctx context.Context, repos []types.Repo) error {
return err
}
err = processRepoFull(ctx, repo, repoDir)
err = rs.processRepoFull(ctx, repo, repoDir)
if err != nil {
return err
}
@@ -145,7 +153,7 @@ func Pull(ctx context.Context, repos []types.Repo) error {
fl, err := repoFS.Open("alr-repo.toml")
if err != nil {
log.Warn("Git repository does not appear to be a valid ALR repo").Str("repo", repo.Name).Send()
slog.Warn(gotext.Get("Git repository does not appear to be a valid ALR repo"), "repo", repo.Name)
continue
}
@@ -161,7 +169,7 @@ func Pull(ctx context.Context, repos []types.Repo) error {
// to compare it to the repo version, so only compare versions with the "v".
if strings.HasPrefix(config.Version, "v") {
if vercmp.Compare(config.Version, repoCfg.Repo.MinVersion) == -1 {
log.Warn("ALR repo's minumum ALR version is greater than the current version. Try updating ALR if something doesn't work.").Str("repo", repo.Name).Send()
slog.Warn(gotext.Get("ALR repo's minumum ALR version is greater than the current version. Try updating ALR if something doesn't work."), "repo", repo.Name)
}
}
}
@@ -169,19 +177,7 @@ func Pull(ctx context.Context, repos []types.Repo) error {
return nil
}
type actionType uint8
const (
actionDelete actionType = iota
actionUpdate
)
type action struct {
Type actionType
File string
}
func processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository, w *git.Worktree, old, new *plumbing.Reference) error {
func (rs *Repos) processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository, w *git.Worktree, old, new *plumbing.Reference) error {
oldCommit, err := r.CommitObject(old.Hash())
if err != nil {
return err
@@ -275,7 +271,7 @@ func processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository,
return err
}
err = db.DeletePkgs(ctx, "name = ? AND repository = ?", pkg.Name, repo.Name)
err = rs.db.DeletePkgs(ctx, "name = ? AND repository = ?", pkg.Name, repo.Name)
if err != nil {
return err
}
@@ -310,7 +306,7 @@ func processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository,
resolveOverrides(runner, &pkg)
err = db.InsertPackage(ctx, pkg)
err = rs.db.InsertPackage(ctx, pkg)
if err != nil {
return err
}
@@ -320,23 +316,7 @@ func processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository,
return nil
}
// isValid makes sure the path of the file being updated is valid.
// It checks to make sure the file is not within a nested directory
// and that it is called alr.sh.
func isValid(from, to diff.File) bool {
var path string
if from != nil {
path = from.Path()
}
if to != nil {
path = to.Path()
}
match, _ := filepath.Match("*/*.sh", path)
return match
}
func processRepoFull(ctx context.Context, repo types.Repo, repoDir string) error {
func (rs *Repos) processRepoFull(ctx context.Context, repo types.Repo, repoDir string) error {
glob := filepath.Join(repoDir, "/*/alr.sh")
matches, err := filepath.Glob(glob)
if err != nil {
@@ -380,7 +360,7 @@ func processRepoFull(ctx context.Context, repo types.Repo, repoDir string) error
resolveOverrides(runner, &pkg)
err = db.InsertPackage(ctx, pkg)
err = rs.db.InsertPackage(ctx, pkg)
if err != nil {
return err
}
@@ -388,54 +368,3 @@ func processRepoFull(ctx context.Context, repo types.Repo, repoDir string) error
return nil
}
func parseScript(ctx context.Context, parser *syntax.Parser, runner *interp.Runner, r io.ReadCloser, pkg *db.Package) error {
defer r.Close()
fl, err := parser.Parse(r, "alr.sh")
if err != nil {
return err
}
runner.Reset()
err = runner.Run(ctx, fl)
if err != nil {
return err
}
d := decoder.New(&distro.OSRelease{}, runner)
d.Overrides = false
d.LikeDistros = false
return d.DecodeVars(pkg)
}
var overridable = map[string]string{
"deps": "Depends",
"build_deps": "BuildDepends",
"desc": "Description",
"homepage": "Homepage",
"maintainer": "Maintainer",
}
func resolveOverrides(runner *interp.Runner, pkg *db.Package) {
pkgVal := reflect.ValueOf(pkg).Elem()
for name, val := range runner.Vars {
for prefix, field := range overridable {
if strings.HasPrefix(name, prefix) {
override := strings.TrimPrefix(name, prefix)
override = strings.TrimPrefix(override, "_")
field := pkgVal.FieldByName(field)
varVal := field.FieldByName("Val")
varType := varVal.Type()
switch varType.Elem().String() {
case "[]string":
varVal.SetMapIndex(reflect.ValueOf(override), reflect.ValueOf(val.List))
case "string":
varVal.SetMapIndex(reflect.ValueOf(override), reflect.ValueOf(val.Str))
}
break
}
}
}
}

View File

@@ -1,20 +1,21 @@
/*
* alr - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package repos_test
@@ -24,71 +25,106 @@ import (
"path/filepath"
"testing"
"plemya-x.ru/alr/internal/config"
"plemya-x.ru/alr/internal/db"
"plemya-x.ru/alr/internal/types"
"plemya-x.ru/alr/pkg/repos"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/types"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos"
)
func setCfgDirs(t *testing.T) {
t.Helper()
paths := config.GetPaths()
var err error
paths.CacheDir, err = os.MkdirTemp("/tmp", "alr-pull-test.*")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
paths.RepoDir = filepath.Join(paths.CacheDir, "repo")
paths.PkgsDir = filepath.Join(paths.CacheDir, "pkgs")
err = os.MkdirAll(paths.RepoDir, 0o755)
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
err = os.MkdirAll(paths.PkgsDir, 0o755)
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
paths.DBPath = filepath.Join(paths.CacheDir, "db")
type TestEnv struct {
Ctx context.Context
Cfg *TestALRConfig
Db *db.Database
}
func removeCacheDir(t *testing.T) {
t.Helper()
type TestALRConfig struct {
CacheDir string
RepoDir string
PkgsDir string
}
err := os.RemoveAll(config.GetPaths().CacheDir)
if err != nil {
t.Fatalf("Expected no error, got %s", err)
func (c *TestALRConfig) GetPaths(ctx context.Context) *config.Paths {
return &config.Paths{
DBPath: ":memory:",
CacheDir: c.CacheDir,
RepoDir: c.RepoDir,
PkgsDir: c.PkgsDir,
}
}
func TestPull(t *testing.T) {
_, err := db.Open(":memory:")
func (c *TestALRConfig) Repos(ctx context.Context) []types.Repo {
return []types.Repo{}
}
func prepare(t *testing.T) *TestEnv {
t.Helper()
cacheDir, err := os.MkdirTemp("/tmp", "alr-pull-test.*")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
defer db.Close()
setCfgDirs(t)
defer removeCacheDir(t)
repoDir := filepath.Join(cacheDir, "repo")
err = os.MkdirAll(repoDir, 0o755)
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
pkgsDir := filepath.Join(cacheDir, "pkgs")
err = os.MkdirAll(pkgsDir, 0o755)
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
cfg := &TestALRConfig{
CacheDir: cacheDir,
RepoDir: repoDir,
PkgsDir: pkgsDir,
}
ctx := context.Background()
err = repos.Pull(ctx, []types.Repo{
db := database.New(cfg)
db.Init(ctx)
return &TestEnv{
Cfg: cfg,
Db: db,
Ctx: ctx,
}
}
func cleanup(t *testing.T, e *TestEnv) {
t.Helper()
err := os.RemoveAll(e.Cfg.CacheDir)
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
e.Db.Close()
}
func TestPull(t *testing.T) {
e := prepare(t)
defer cleanup(t, e)
rs := repos.New(
e.Cfg,
e.Db,
)
err := rs.Pull(e.Ctx, []types.Repo{
{
Name: "default",
URL: "https://gitea.plemya-x.ru/xpamych/ALR.git",
URL: "https://gitea.plemya-x.ru/Plemya-x/xpamych-alr-repo.git",
},
})
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
result, err := db.GetPkgs("name LIKE 'itd%'")
result, err := e.Db.GetPkgs(e.Ctx, "true")
if err != nil {
t.Fatalf("Expected no error, got %s", err)
}
@@ -103,7 +139,7 @@ func TestPull(t *testing.T) {
pkgAmt++
}
if pkgAmt < 2 {
t.Errorf("Expected 2 packages to match, got %d", pkgAmt)
if pkgAmt == 0 {
t.Errorf("Expected at least 1 matching package, but got %d", pkgAmt)
}
}

45
pkg/repos/repos.go Normal file
View File

@@ -0,0 +1,45 @@
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package repos
import (
"context"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/types"
)
type Config interface {
GetPaths(ctx context.Context) *config.Paths
Repos(ctx context.Context) []types.Repo
}
type Repos struct {
cfg Config
db *database.Database
}
func New(
cfg Config,
db *database.Database,
) *Repos {
return &Repos{
cfg,
db,
}
}

70
pkg/repos/repos_legacy.go Normal file
View 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/>.
package repos
import (
"context"
"sync"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/types"
)
// Pull pulls the provided repositories. If a repo doesn't exist, it will be cloned
// and its packages will be written to the DB. If it does exist, it will be pulled.
// In this case, only changed packages will be processed if possible.
// If repos is set to nil, the repos in the ALR config will be used.
//
// Deprecated: use struct method
func Pull(ctx context.Context, repos []types.Repo) error {
return GetInstance(ctx).Pull(ctx, repos)
}
// FindPkgs looks for packages matching the inputs inside the database.
// It returns a map that maps the package name input to any packages found for it.
// It also returns a slice that contains the names of all packages that were not found.
//
// Deprecated: use struct method
func FindPkgs(ctx context.Context, pkgs []string) (map[string][]db.Package, []string, error) {
return GetInstance(ctx).FindPkgs(ctx, pkgs)
}
// =======================
// FOR LEGACY ONLY
// =======================
var (
reposInstance *Repos
alrConfigOnce sync.Once
)
// Deprecated: For legacy only
func GetInstance(ctx context.Context) *Repos {
alrConfigOnce.Do(func() {
cfg := config.GetInstance(ctx)
db := database.GetInstance(ctx)
reposInstance = New(
cfg,
db,
)
})
return reposInstance
}

100
pkg/repos/utils.go Normal file
View File

@@ -0,0 +1,100 @@
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package repos
import (
"context"
"io"
"path/filepath"
"reflect"
"strings"
"github.com/go-git/go-git/v5/plumbing/format/diff"
"mvdan.cc/sh/v3/interp"
"mvdan.cc/sh/v3/syntax"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/shutils/decoder"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
)
// isValid makes sure the path of the file being updated is valid.
// It checks to make sure the file is not within a nested directory
// and that it is called alr.sh.
func isValid(from, to diff.File) bool {
var path string
if from != nil {
path = from.Path()
}
if to != nil {
path = to.Path()
}
match, _ := filepath.Match("*/*.sh", path)
return match
}
func parseScript(ctx context.Context, parser *syntax.Parser, runner *interp.Runner, r io.ReadCloser, pkg *db.Package) error {
defer r.Close()
fl, err := parser.Parse(r, "alr.sh")
if err != nil {
return err
}
runner.Reset()
err = runner.Run(ctx, fl)
if err != nil {
return err
}
d := decoder.New(&distro.OSRelease{}, runner)
d.Overrides = false
d.LikeDistros = false
return d.DecodeVars(pkg)
}
var overridable = map[string]string{
"deps": "Depends",
"build_deps": "BuildDepends",
"desc": "Description",
"homepage": "Homepage",
"maintainer": "Maintainer",
}
func resolveOverrides(runner *interp.Runner, pkg *db.Package) {
pkgVal := reflect.ValueOf(pkg).Elem()
for name, val := range runner.Vars {
for prefix, field := range overridable {
if strings.HasPrefix(name, prefix) {
override := strings.TrimPrefix(name, prefix)
override = strings.TrimPrefix(override, "_")
field := pkgVal.FieldByName(field)
varVal := field.FieldByName("Val")
varType := varVal.Type()
switch varType.Elem().String() {
case "[]string":
varVal.SetMapIndex(reflect.ValueOf(override), reflect.ValueOf(val.List))
case "string":
varVal.SetMapIndex(reflect.ValueOf(override), reflect.ValueOf(val.Str))
}
break
}
}
}
}

View File

@@ -1,3 +1,22 @@
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package search
import (
@@ -10,8 +29,8 @@ import (
"strconv"
"strings"
"plemya-x.ru/alr/internal/config"
"plemya-x.ru/alr/internal/db"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
)
// Filter represents search filters.

80
repo.go
View File

@@ -1,35 +1,38 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"log/slog"
"os"
"path/filepath"
"github.com/leonelquinteros/gotext"
"github.com/pelletier/go-toml/v2"
"github.com/urfave/cli/v2"
"plemya-x.ru/alr/internal/config"
"plemya-x.ru/alr/internal/db"
"plemya-x.ru/alr/internal/types"
"plemya-x.ru/alr/pkg/loggerctx"
"plemya-x.ru/alr/pkg/repos"
"golang.org/x/exp/slices"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/types"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos"
)
var addrepoCmd = &cli.Command{
@@ -52,7 +55,6 @@ var addrepoCmd = &cli.Command{
},
Action: func(c *cli.Context) error {
ctx := c.Context
log := loggerctx.From(ctx)
name := c.String("name")
repoURL := c.String("url")
@@ -61,7 +63,8 @@ var addrepoCmd = &cli.Command{
for _, repo := range cfg.Repos {
if repo.URL == repoURL {
log.Fatal("Repo already exists").Str("name", repo.Name).Send()
slog.Error("Repo already exists", "name", repo.Name)
os.Exit(1)
}
}
@@ -72,17 +75,20 @@ var addrepoCmd = &cli.Command{
cfgFl, err := os.Create(config.GetPaths(ctx).ConfigPath)
if err != nil {
log.Fatal("Error opening config file").Err(err).Send()
slog.Error(gotext.Get("Error opening config file"), "err", err)
os.Exit(1)
}
err = toml.NewEncoder(cfgFl).Encode(cfg)
if err != nil {
log.Fatal("Error encoding config").Err(err).Send()
slog.Error(gotext.Get("Error encoding config"), "err", err)
os.Exit(1)
}
err = repos.Pull(ctx, cfg.Repos)
if err != nil {
log.Fatal("Error pulling repos").Err(err).Send()
slog.Error(gotext.Get("Error pulling repos"), "err", err)
os.Exit(1)
}
return nil
@@ -103,7 +109,6 @@ var removerepoCmd = &cli.Command{
},
Action: func(c *cli.Context) error {
ctx := c.Context
log := loggerctx.From(ctx)
name := c.String("name")
cfg := config.Config(ctx)
@@ -117,29 +122,34 @@ var removerepoCmd = &cli.Command{
}
}
if !found {
log.Fatal("Repo does not exist").Str("name", name).Send()
slog.Error(gotext.Get("Repo does not exist"), "name", name)
os.Exit(1)
}
cfg.Repos = slices.Delete(cfg.Repos, index, index+1)
cfgFl, err := os.Create(config.GetPaths(ctx).ConfigPath)
if err != nil {
log.Fatal("Error opening config file").Err(err).Send()
slog.Error(gotext.Get("Error opening config file"), "err", err)
os.Exit(1)
}
err = toml.NewEncoder(cfgFl).Encode(&cfg)
if err != nil {
log.Fatal("Error encoding config").Err(err).Send()
slog.Error(gotext.Get("Error encoding config"), "err", err)
os.Exit(1)
}
err = os.RemoveAll(filepath.Join(config.GetPaths(ctx).RepoDir, name))
if err != nil {
log.Fatal("Error removing repo directory").Err(err).Send()
slog.Error(gotext.Get("Error removing repo directory"), "err", err)
os.Exit(1)
}
err = db.DeletePkgs(ctx, "repository = ?", name)
if err != nil {
log.Fatal("Error removing packages from database").Err(err).Send()
slog.Error(gotext.Get("Error removing packages from database"), "err", err)
os.Exit(1)
}
return nil
@@ -152,10 +162,10 @@ var refreshCmd = &cli.Command{
Aliases: []string{"ref"},
Action: func(c *cli.Context) error {
ctx := c.Context
log := loggerctx.From(ctx)
err := repos.Pull(ctx, config.Config(ctx).Repos)
if err != nil {
log.Fatal("Error pulling repos").Err(err).Send()
slog.Error(gotext.Get("Error pulling repos"), "err", err)
os.Exit(1)
}
return nil
},

View File

@@ -1,3 +1,22 @@
# This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
# It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
#
# ALR - Any Linux Repository
# Copyright (C) 2025 Евгений Храмов
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
info() {
echo $'\x1b[32m[ИНФО]\x1b[0m' $@
}

View File

@@ -1,39 +1,44 @@
/*
* ALR - Any Linux Repository
* Copyright (C) 2024 Евгений Храмов
*
* 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/>.
*/
// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
// It has been modified as part of "ALR - Any Linux Repository" by Евгений Храмов.
//
// ALR - Any Linux Repository
// Copyright (C) 2025 Евгений Храмов
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"context"
"fmt"
"log/slog"
"os"
"github.com/leonelquinteros/gotext"
"github.com/urfave/cli/v2"
"plemya-x.ru/alr/internal/config"
"plemya-x.ru/alr/internal/db"
"plemya-x.ru/alr/internal/types"
"plemya-x.ru/alr/pkg/build"
"plemya-x.ru/alr/pkg/distro"
"plemya-x.ru/alr/pkg/loggerctx"
"plemya-x.ru/alr/pkg/manager"
"plemya-x.ru/alr/pkg/repos"
"go.elara.ws/logger/log"
"go.elara.ws/vercmp"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/types"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/build"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos"
)
var upgradeCmd = &cli.Command{
@@ -49,26 +54,29 @@ var upgradeCmd = &cli.Command{
},
Action: func(c *cli.Context) error {
ctx := c.Context
log := loggerctx.From(ctx)
info, err := distro.ParseOSRelease(ctx)
if err != nil {
log.Fatal("Error parsing os-release file").Err(err).Send()
slog.Error(gotext.Get("Error parsing os-release file"), "err", err)
os.Exit(1)
}
mgr := manager.Detect()
if mgr == nil {
log.Fatal("Unable to detect a supported package manager on the system").Send()
slog.Error(gotext.Get("Unable to detect a supported package manager on the system"))
os.Exit(1)
}
err = repos.Pull(ctx, config.Config(ctx).Repos)
if err != nil {
log.Fatal("Error pulling repos").Err(err).Send()
slog.Error(gotext.Get("Error pulling repos"), "err", err)
os.Exit(1)
}
updates, err := checkForUpdates(ctx, mgr, info)
if err != nil {
log.Fatal("Error checking for updates").Err(err).Send()
slog.Error(gotext.Get("Error checking for updates"), "err", err)
os.Exit(1)
}
if len(updates) > 0 {