7 Commits

Author SHA1 Message Date
d7e910c06c Оптимизация сборки зависимостей и исправление кеширования
All checks were successful
Create Release / changelog (push) Successful in 2m26s
- Добавлено полное разрешение дерева зависимостей перед сборкой
- Общие зависимости теперь собираются только один раз
- Исправлена работа кеша для подпакетов
- Исправлена обработка системных зависимостей
2025-12-08 21:58:41 +00:00
6529094fa7 Добавлен go-generate hook в pre-commit и лицензионные заголовки в автогенерируемые файлы
Some checks failed
Pre-commit / pre-commit (push) Failing after 3m21s
Изменения:
- Добавлен hook go-generate перед update-license в .pre-commit-config.yaml
- Добавлены лицензионные заголовки в pkg/alrsh/package_gen.go
2025-12-07 11:19:42 +03:00
c2d48c1a13 Исправлена проблема дублирования обновлений пакетов с подпакетами
Some checks failed
Pre-commit / pre-commit (push) Failing after 8m9s
Create Release / changelog (push) Successful in 3m30s
Изменения:
- Заменён вызов InstallALRPackages на InstallPkgs в upgrade.go
- Переименована функция mapUptatesInfoToPackages в mapUpdatesToPackageNames
- Добавлена дедупликация подпакетов по полному имени (package+repo)
- Теперь возвращаются строки с именами пакетов вместо объектов Package
2025-12-06 13:08:13 +03:00
72cdfcaa4b Замена vercmp и оптимизация сборки зависимостей с полной русификацией интерфейса
All checks were successful
Pre-commit / pre-commit (push) Successful in 6m13s
Create Release / changelog (push) Successful in 2m58s
- Заменен vercmp с go.elara.ws/vercmp на gitea.plemya-x.ru/xpamych/vercmp v0.0.1
- Добавлена функция FilterPackagesByVersion для проверки версий установленных
  пакетов перед пересборкой зависимостей (учитывает version-release и epoch)
- Исправлена инициализация переводов в плагинах: добавлены вызовы translations.Setup()
  во всех plugin subcommands (_internal-safe-script-executor, _internal-installer,
  _internal-repos)
- Добавлен GetSubcommandHelpTemplate для корректного отображения справки команд
  с подкомандами на русском языке
- Добавлены кастомные help команды для config, repo, helper и mirror
- Добавлены русские переводы для всех пользовательских сообщений:
  * Сообщения о создании пакетов (Creating package file, Packaging with nfpm и др.)
  * Сообщения команды fix (Clearing cache, Fixing permissions и др.)
  * Сообщения обновления (Updating system packages, System packages updated)
  * Сообщения о версиях пакетов (Package is installed with older/newer version)
  * Заголовки справки (NAME, USAGE, COMMANDS, OPTIONS)
  * Справочные сообщения (Shows a list of commands or help for one command)
- Оптимизирован assets/logo.png (уменьшен с 37KB до 17KB)
2025-11-29 19:32:13 +03:00
c9c8397856 Исправлена проблема, когда при первом запуске ALR требовалось вручную
All checks were successful
Pre-commit / pre-commit (push) Successful in 6m30s
Create Release / changelog (push) Successful in 3m31s
выполнять 'sudo alr fix' для создания необходимых директорий. Теперь
  директории /var/cache/alr и /tmp/alr создаются автоматически при первом
  использовании с правильными правами доступа.
2025-11-23 15:16:22 +03:00
107075e8ef Исправлен dlcache_prod
All checks were successful
Pre-commit / pre-commit (push) Successful in 5m46s
2025-10-12 19:11:15 +03:00
41e3d8119f Добавлены files-find: systemd, systemd-user, license
All checks were successful
Pre-commit / pre-commit (push) Successful in 5m33s
Create Release / changelog (push) Successful in 3m12s
2025-09-25 22:10:47 +03:00
34 changed files with 1456 additions and 453 deletions

5
.gitignore vendored
View File

@@ -11,4 +11,7 @@
e2e-tests/alr e2e-tests/alr
CLAUDE.md CLAUDE.md
commit_msg.txt commit_msg.txt
/scripts/.claude/settings.local.json
/ALR
.claude/settings.local.json

View File

@@ -29,6 +29,12 @@ repos:
language: system language: system
pass_filenames: false pass_filenames: false
- id: go-generate
name: Run go generate
entry: bash -c 'go generate ./...'
language: system
pass_filenames: false
- id: update-license - id: update-license
name: Update license name: Update license
entry: make update-license entry: make update-license

View File

@@ -12,7 +12,7 @@
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="37" y="15" fill="#010101" fill-opacity=".3">ru translate</text> <text x="37" y="15" fill="#010101" fill-opacity=".3">ru translate</text>
<text x="37" y="14">ru translate</text> <text x="37" y="14">ru translate</text>
<text x="100" y="15" fill="#010101" fill-opacity=".3">100.00%</text> <text x="100" y="15" fill="#010101" fill-opacity=".3">97.00%</text>
<text x="100" y="14">100.00%</text> <text x="100" y="14">97.00%</text>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 942 B

After

Width:  |  Height:  |  Size: 940 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -200,7 +200,7 @@ func BuildCmd() *cli.Command {
// Проверяем, существует ли файл перед перемещением // Проверяем, существует ли файл перед перемещением
if _, err := os.Stat(pkg.Path); os.IsNotExist(err) { if _, err := os.Stat(pkg.Path); os.IsNotExist(err) {
slog.Info("Package file already moved or removed, skipping", "path", pkg.Path) slog.Info(gotext.Get("Package file already moved or removed, skipping"), "path", pkg.Path)
continue continue
} }

View File

@@ -38,6 +38,24 @@ func ConfigCmd() *cli.Command {
ShowCmd(), ShowCmd(),
SetConfig(), SetConfig(),
GetConfig(), GetConfig(),
ConfigHelpCmd(),
},
}
}
func ConfigHelpCmd() *cli.Command {
return &cli.Command{
Name: "help",
Aliases: []string{"h"},
Usage: gotext.Get("Shows a list of commands or help for one command"),
ArgsUsage: "[command]",
Action: func(cCtx *cli.Context) error {
args := cCtx.Args()
if args.Present() {
return cli.ShowCommandHelp(cCtx, args.First())
}
cli.ShowSubcommandHelp(cCtx)
return nil
}, },
} }
} }

2
go.mod
View File

@@ -4,6 +4,7 @@ go 1.24.4
require ( require (
gitea.plemya-x.ru/Plemya-x/fakeroot v0.0.2-0.20250408104831-427aaa7713c3 gitea.plemya-x.ru/Plemya-x/fakeroot v0.0.2-0.20250408104831-427aaa7713c3
gitea.plemya-x.ru/xpamych/vercmp v0.0.1
github.com/AlecAivazis/survey/v2 v2.3.7 github.com/AlecAivazis/survey/v2 v2.3.7
github.com/PuerkitoBio/purell v1.2.0 github.com/PuerkitoBio/purell v1.2.0
github.com/alecthomas/chroma/v2 v2.9.1 github.com/alecthomas/chroma/v2 v2.9.1
@@ -36,7 +37,6 @@ require (
github.com/vmihailenco/msgpack/v5 v5.3.5 github.com/vmihailenco/msgpack/v5 v5.3.5
go.alt-gnome.ru/capytest v0.0.3-0.20250706082755-f20413e052f9 go.alt-gnome.ru/capytest v0.0.3-0.20250706082755-f20413e052f9
go.alt-gnome.ru/capytest/providers/podman v0.0.3-0.20250706082755-f20413e052f9 go.alt-gnome.ru/capytest/providers/podman v0.0.3-0.20250706082755-f20413e052f9
go.elara.ws/vercmp v0.0.0-20230622214216-0b2b067575c4
golang.org/x/crypto v0.36.0 golang.org/x/crypto v0.36.0
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
golang.org/x/sys v0.33.0 golang.org/x/sys v0.33.0

6
go.sum
View File

@@ -21,6 +21,8 @@ gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGq
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
gitea.plemya-x.ru/Plemya-x/fakeroot v0.0.2-0.20250408104831-427aaa7713c3 h1:56BjRJJ2Sv50DfSvNUydUMJwwFuiBMWC1uYtH2GYjk8= gitea.plemya-x.ru/Plemya-x/fakeroot v0.0.2-0.20250408104831-427aaa7713c3 h1:56BjRJJ2Sv50DfSvNUydUMJwwFuiBMWC1uYtH2GYjk8=
gitea.plemya-x.ru/Plemya-x/fakeroot v0.0.2-0.20250408104831-427aaa7713c3/go.mod h1:iKQM6uttMJgE5CFrPw6SQqAV7TKtlJNICRAie/dTciw= gitea.plemya-x.ru/Plemya-x/fakeroot v0.0.2-0.20250408104831-427aaa7713c3/go.mod h1:iKQM6uttMJgE5CFrPw6SQqAV7TKtlJNICRAie/dTciw=
gitea.plemya-x.ru/xpamych/vercmp v0.0.1 h1:tFQzsPfnQQDQ3jrqW0UwUSbK+HwJuq0sA0GfnvIkatw=
gitea.plemya-x.ru/xpamych/vercmp v0.0.1/go.mod h1:z9qQ4QJDou1AULVKPIW5blu/jT+O3O5HpTV8aujWSIM=
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
github.com/AlekSi/pointer v1.2.0 h1:glcy/gc4h8HnG2Z3ZECSzZ1IX1x2JxRVuDzaJwQE0+w= github.com/AlekSi/pointer v1.2.0 h1:glcy/gc4h8HnG2Z3ZECSzZ1IX1x2JxRVuDzaJwQE0+w=
@@ -432,14 +434,10 @@ github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsr
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
gitlab.com/digitalxero/go-conventional-commit v1.0.7 h1:8/dO6WWG+98PMhlZowt/YjuiKhqhGlOCwlIV8SqqGh8= gitlab.com/digitalxero/go-conventional-commit v1.0.7 h1:8/dO6WWG+98PMhlZowt/YjuiKhqhGlOCwlIV8SqqGh8=
gitlab.com/digitalxero/go-conventional-commit v1.0.7/go.mod h1:05Xc2BFsSyC5tKhK0y+P3bs0AwUtNuTp+mTpbCU/DZ0= gitlab.com/digitalxero/go-conventional-commit v1.0.7/go.mod h1:05Xc2BFsSyC5tKhK0y+P3bs0AwUtNuTp+mTpbCU/DZ0=
go.alt-gnome.ru/capytest v0.0.2 h1:clmvIqmYS86hhA1rsvivSSPpfOFkJTpbn38EQP7I3E8=
go.alt-gnome.ru/capytest v0.0.2/go.mod h1:lvxPx3H6h+LPnStBFblgoT2wkjv0wbug3S14troykEg=
go.alt-gnome.ru/capytest v0.0.3-0.20250706082755-f20413e052f9 h1:NST+V5LV/eLgs0p6PsuvfHiZ4UrIWqftCdifO8zgg0g= go.alt-gnome.ru/capytest v0.0.3-0.20250706082755-f20413e052f9 h1:NST+V5LV/eLgs0p6PsuvfHiZ4UrIWqftCdifO8zgg0g=
go.alt-gnome.ru/capytest v0.0.3-0.20250706082755-f20413e052f9/go.mod h1:qiM8LARP+JBZr5mrDoVylOoqjrN0MAzvZ21NR9qMc0Y= go.alt-gnome.ru/capytest v0.0.3-0.20250706082755-f20413e052f9/go.mod h1:qiM8LARP+JBZr5mrDoVylOoqjrN0MAzvZ21NR9qMc0Y=
go.alt-gnome.ru/capytest/providers/podman v0.0.3-0.20250706082755-f20413e052f9 h1:VZclgdJxARvhZ6PIWWW2hQ6Ge4XeE36pzUr/U/y62bE= go.alt-gnome.ru/capytest/providers/podman v0.0.3-0.20250706082755-f20413e052f9 h1:VZclgdJxARvhZ6PIWWW2hQ6Ge4XeE36pzUr/U/y62bE=
go.alt-gnome.ru/capytest/providers/podman v0.0.3-0.20250706082755-f20413e052f9/go.mod h1:Wpq1Ny3eMzADJpMJArA2TZGZbsviUBmawtEPcxnoerg= go.alt-gnome.ru/capytest/providers/podman v0.0.3-0.20250706082755-f20413e052f9/go.mod h1:Wpq1Ny3eMzADJpMJArA2TZGZbsviUBmawtEPcxnoerg=
go.elara.ws/vercmp v0.0.0-20230622214216-0b2b067575c4 h1:Ep54XceQlKhcCHl9awG+wWP4kz4kIP3c3Lzw/Gc/zwY=
go.elara.ws/vercmp v0.0.0-20230622214216-0b2b067575c4/go.mod h1:/7PNW7nFnDR5W7UXZVc04gdVLR/wBNgkm33KgIz0OBk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=

View File

@@ -49,11 +49,26 @@ func HelperCmd() *cli.Command {
}, },
} }
helperHelpCmd := &cli.Command{
Name: "help",
Aliases: []string{"h"},
Usage: gotext.Get("Shows a list of commands or help for one command"),
ArgsUsage: "[command]",
Action: func(cCtx *cli.Context) error {
args := cCtx.Args()
if args.Present() {
return cli.ShowCommandHelp(cCtx, args.First())
}
cli.ShowSubcommandHelp(cCtx)
return nil
},
}
return &cli.Command{ return &cli.Command{
Name: "helper", Name: "helper",
Usage: gotext.Get("Run a ALR helper command"), Usage: gotext.Get("Run a ALR helper command"),
ArgsUsage: `<helper_name|"list">`, ArgsUsage: `<helper_name|"list">`,
Subcommands: []*cli.Command{helperListCmd}, Subcommands: []*cli.Command{helperListCmd, helperHelpCmd},
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "dest-dir", Name: "dest-dir",
@@ -100,7 +115,6 @@ func HelperCmd() *cli.Command {
return helper(hc, c.Args().First(), c.Args().Slice()[1:]) return helper(hc, c.Args().First(), c.Args().Slice()[1:])
}, },
CustomHelpTemplate: cli.CommandHelpTemplate,
BashComplete: func(ctx *cli.Context) { BashComplete: func(ctx *cli.Context) {
for name := range helpers.Helpers { for name := range helpers.Helpers {
fmt.Println(name) fmt.Println(name)

View File

@@ -32,6 +32,7 @@ import (
"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" "gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/logger" "gitea.plemya-x.ru/Plemya-x/ALR/internal/logger"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/manager" "gitea.plemya-x.ru/Plemya-x/ALR/internal/manager"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/translations"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils" "gitea.plemya-x.ru/Plemya-x/ALR/internal/utils"
) )
@@ -42,6 +43,7 @@ func InternalBuildCmd() *cli.Command {
Hidden: true, Hidden: true,
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
logger.SetupForGoPlugin() logger.SetupForGoPlugin()
translations.Setup()
slog.Debug("start _internal-safe-script-executor", "uid", syscall.Getuid(), "gid", syscall.Getgid()) slog.Debug("start _internal-safe-script-executor", "uid", syscall.Getuid(), "gid", syscall.Getgid())
@@ -81,7 +83,7 @@ func InternalReposCmd() *cli.Command {
Hidden: true, Hidden: true,
Action: utils.RootNeededAction(func(ctx *cli.Context) error { Action: utils.RootNeededAction(func(ctx *cli.Context) error {
logger.SetupForGoPlugin() logger.SetupForGoPlugin()
translations.Setup()
deps, err := appbuilder. deps, err := appbuilder.
New(ctx.Context). New(ctx.Context).
@@ -115,6 +117,7 @@ func InternalInstallCmd() *cli.Command {
Hidden: true, Hidden: true,
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
logger.SetupForGoPlugin() logger.SetupForGoPlugin()
translations.Setup()
// Запуск от текущего пользователя, повышение прав будет через sudo при необходимости // Запуск от текущего пользователя, повышение прав будет через sudo при необходимости

View File

@@ -565,73 +565,173 @@ func (b *Builder) BuildALRDeps(
}, },
depends []string, depends []string,
) (buildDeps []*BuiltDep, repoDeps []string, err error) { ) (buildDeps []*BuiltDep, repoDeps []string, err error) {
if len(depends) > 0 { if len(depends) == 0 {
slog.Info(gotext.Get("Installing dependencies")) return nil, nil, nil
}
found, notFound, err := b.repos.FindPkgs(ctx, depends) // Поиск зависимостей
if err != nil { slog.Info(gotext.Get("Installing dependencies"))
return nil, nil, fmt.Errorf("failed FindPkgs: %w", err)
} // Шаг 1: Рекурсивно разрешаем ВСЕ зависимости
repoDeps = notFound depTree, systemDeps, err := b.ResolveDependencyTree(ctx, input, depends)
if err != nil {
// Если для некоторых пакетов есть несколько опций, упрощаем их все в один срез return nil, nil, fmt.Errorf("failed to resolve dependency tree: %w", err)
// Для зависимостей указываем isDependency = true }
pkgs := cliutils.FlattenPkgsWithContext(
ctx, // Системные зависимости возвращаем как repoDeps
found, repoDeps = systemDeps
"install",
input.BuildOpts().Interactive, // Шаг 2: Собираем список всех пакетов из дерева для топологической сортировки
true, allFound := make(map[string][]alrsh.Package)
) for baseName, node := range depTree {
type item struct { allFound[baseName] = []alrsh.Package{*node.Package}
pkg *alrsh.Package }
packages []string
} // Шаг 3: Топологическая сортировка (от корней к листьям)
pkgsMap := make(map[string]*item) sortedPkgs, err := TopologicalSort(depTree, allFound)
for _, pkg := range pkgs { if err != nil {
name := pkg.BasePkgName return nil, nil, fmt.Errorf("failed to sort dependencies: %w", err)
if name == "" { }
name = pkg.Name
} // Шаг 4: Собираем пакеты в правильном порядке, проверяя кеш
if pkgsMap[name] == nil { for _, basePkgName := range sortedPkgs {
pkgsMap[name] = &item{ node := depTree[basePkgName]
pkg: &pkg, if node == nil {
} continue
} }
pkgsMap[name].packages = append(
pkgsMap[name].packages, pkg := node.Package
pkg.Name,
) // Находим ВСЕ подпакеты с этим BasePkgName
} allSubpkgs, err := b.findAllSubpackages(ctx, basePkgName, pkg.Repository)
if err != nil {
for basePkgName := range pkgsMap { return nil, nil, fmt.Errorf("failed to find subpackages for %s: %w", basePkgName, err)
pkg := pkgsMap[basePkgName].pkg }
res, err := b.BuildPackageFromDb(
ctx, // Проверяем кеш для ВСЕХ подпакетов
&BuildPackageFromDbArgs{ scriptInfo := b.scriptResolver.ResolveScript(ctx, pkg)
Package: pkg, buildInput := &BuildInput{
Packages: pkgsMap[basePkgName].packages, script: scriptInfo.Script,
BuildArgs: BuildArgs{ repository: scriptInfo.Repository,
Opts: input.BuildOpts(), packages: allSubpkgs,
Info: input.OSRelease(), pkgFormat: input.PkgFormat(),
PkgFormat_: input.PkgFormat(), opts: input.BuildOpts(),
}, info: input.OSRelease(),
}, }
)
if err != nil { cachedDeps, allInCache, err := b.checkCacheForAllSubpackages(ctx, buildInput, basePkgName, allSubpkgs)
return nil, nil, fmt.Errorf("failed build package from db: %w", err) if err != nil {
} return nil, nil, err
}
buildDeps = append(buildDeps, res...)
} if allInCache {
// Все подпакеты в кеше, используем их
buildDeps = append(buildDeps, cachedDeps...)
continue
}
// Собираем пакет (без рекурсивной сборки зависимостей, так как они уже собраны)
res, err := b.BuildPackageFromDb(
ctx,
&BuildPackageFromDbArgs{
Package: pkg,
Packages: allSubpkgs,
BuildArgs: BuildArgs{
Opts: input.BuildOpts(),
Info: input.OSRelease(),
PkgFormat_: input.PkgFormat(),
},
},
)
if err != nil {
return nil, nil, fmt.Errorf("failed build package from db: %w", err)
}
buildDeps = append(buildDeps, res...)
} }
repoDeps = removeDuplicates(repoDeps)
buildDeps = removeDuplicates(buildDeps) buildDeps = removeDuplicates(buildDeps)
return buildDeps, repoDeps, nil return buildDeps, repoDeps, nil
} }
// findAllSubpackages находит все подпакеты для базового пакета
func (b *Builder) findAllSubpackages(ctx context.Context, basePkgName, repository string) ([]string, error) {
// Запрашиваем все пакеты с этим basepkg_name
pkgs, _, err := b.repos.FindPkgs(ctx, []string{basePkgName})
if err != nil {
return nil, err
}
var subpkgs []string
seen := make(map[string]bool)
for _, pkgList := range pkgs {
for _, pkg := range pkgList {
// Проверяем, что это пакет из нужного репозитория
if pkg.Repository == repository {
pkgBase := pkg.BasePkgName
if pkgBase == "" {
pkgBase = pkg.Name
}
// Добавляем только если это пакет с нужным BasePkgName
if pkgBase == basePkgName && !seen[pkg.Name] {
subpkgs = append(subpkgs, pkg.Name)
seen[pkg.Name] = true
}
}
}
}
return subpkgs, nil
}
// checkCacheForAllSubpackages проверяет кеш для всех подпакетов
func (b *Builder) checkCacheForAllSubpackages(
ctx context.Context,
buildInput *BuildInput,
basePkgName string,
subpkgs []string,
) ([]*BuiltDep, bool, error) {
var cachedDeps []*BuiltDep
allInCache := true
// Получаем информацию обо всех подпакетах
pkgsInfo, _, err := b.repos.FindPkgs(ctx, subpkgs)
if err != nil {
return nil, false, fmt.Errorf("failed to find subpackages info: %w", err)
}
for _, pkgName := range subpkgs {
var pkgForCheck *alrsh.Package
// Находим Package для подпакета
if pkgList, ok := pkgsInfo[pkgName]; ok && len(pkgList) > 0 {
pkgForCheck = &pkgList[0]
}
if pkgForCheck != nil {
pkgPath, found, err := b.cacheExecutor.CheckForBuiltPackage(ctx, buildInput, pkgForCheck)
if err != nil {
return nil, false, fmt.Errorf("failed to check cache: %w", err)
}
if found {
slog.Info(gotext.Get("Using cached package"), "name", pkgName, "path", pkgPath)
cachedDeps = append(cachedDeps, &BuiltDep{
Name: pkgName,
Path: pkgPath,
})
} else {
allInCache = false
break
}
}
}
return cachedDeps, allInCache && len(cachedDeps) > 0, nil
}
func (i *Builder) installBuildDeps( func (i *Builder) installBuildDeps(
ctx context.Context, ctx context.Context,
input interface { input interface {

View File

@@ -40,7 +40,12 @@ func (c *Cache) CheckForBuiltPackage(
return "", false, err return "", false, err
} }
pkgPath := filepath.Join(getBaseDir(c.cfg, vars.Name), filename) // Для подпакетов используем BasePkgName, чтобы искать в правильной директории
baseName := vars.BasePkgName
if baseName == "" {
baseName = vars.Name
}
pkgPath := filepath.Join(getBaseDir(c.cfg, baseName), filename)
_, err = os.Stat(pkgPath) _, err = os.Stat(pkgPath)
if err != nil { if err != nil {

View File

@@ -0,0 +1,193 @@
// ALR - Any Linux Repository
// Copyright (C) 2025 The ALR Authors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package build
import (
"context"
"fmt"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh"
)
// DependencyNode представляет узел в дереве зависимостей
type DependencyNode struct {
Package *alrsh.Package
BasePkgName string
Dependencies []string // Имена зависимостей
}
// ResolveDependencyTree рекурсивно разрешает все зависимости и возвращает
// плоский список всех уникальных пакетов, необходимых для сборки
// и список системных зависимостей (не найденных в ALR-репозиториях)
func (b *Builder) ResolveDependencyTree(
ctx context.Context,
input interface {
OsInfoProvider
PkgFormatProvider
},
initialPkgs []string,
) (map[string]*DependencyNode, []string, error) {
resolved := make(map[string]*DependencyNode)
visited := make(map[string]bool)
systemDeps := make(map[string]bool) // Для дедупликации системных зависимостей
var resolve func(pkgNames []string) error
resolve = func(pkgNames []string) error {
if len(pkgNames) == 0 {
return nil
}
// Находим пакеты
found, notFound, err := b.repos.FindPkgs(ctx, pkgNames)
if err != nil {
return fmt.Errorf("failed to find packages: %w", err)
}
// Собираем системные зависимости (не найденные в ALR)
for _, pkgName := range notFound {
systemDeps[pkgName] = true
}
// Обрабатываем найденные пакеты
for pkgName, pkgList := range found {
if visited[pkgName] {
continue
}
visited[pkgName] = true
// Берем первый пакет из списка (или можно добавить выбор пользователя)
if len(pkgList) == 0 {
continue
}
pkg := pkgList[0]
// Определяем базовое имя пакета
baseName := pkg.BasePkgName
if baseName == "" {
baseName = pkg.Name
}
// Если уже обработали этот базовый пакет, пропускаем
if resolved[baseName] != nil {
continue
}
// Получаем зависимости для этого дистрибутива
// Пакет из БД уже содержит разрешенные значения для текущего дистрибутива
deps := pkg.Depends.Resolved()
buildDeps := pkg.BuildDepends.Resolved()
// Объединяем зависимости
allDeps := append([]string{}, deps...)
allDeps = append(allDeps, buildDeps...)
// Добавляем узел в resolved
resolved[baseName] = &DependencyNode{
Package: &pkg,
BasePkgName: baseName,
Dependencies: allDeps,
}
// Рекурсивно разрешаем зависимости
if len(allDeps) > 0 {
if err := resolve(allDeps); err != nil {
return err
}
}
}
return nil
}
// Начинаем разрешение с начальных пакетов
if err := resolve(initialPkgs); err != nil {
return nil, nil, err
}
// Преобразуем map в слайс для системных зависимостей
var systemDepsList []string
for dep := range systemDeps {
systemDepsList = append(systemDepsList, dep)
}
return resolved, systemDepsList, nil
}
// TopologicalSort выполняет топологическую сортировку пакетов по зависимостям
// Возвращает список базовых имен пакетов в порядке сборки (от корней к листьям)
func TopologicalSort(nodes map[string]*DependencyNode, allPkgs map[string][]alrsh.Package) ([]string, error) {
// Список для результата
var result []string
// Множество посещенных узлов
visited := make(map[string]bool)
// Множество узлов в текущем пути (для обнаружения циклов)
inStack := make(map[string]bool)
var visit func(basePkgName string) error
visit = func(basePkgName string) error {
if visited[basePkgName] {
return nil
}
if inStack[basePkgName] {
return fmt.Errorf("circular dependency detected: %s", basePkgName)
}
node := nodes[basePkgName]
if node == nil {
// Это системный пакет, игнорируем
return nil
}
inStack[basePkgName] = true
// Посещаем все зависимости
for _, dep := range node.Dependencies {
// Находим базовое имя для зависимости
depBaseName := dep
// Проверяем, есть ли этот пакет в allPkgs
if pkgs, ok := allPkgs[dep]; ok && len(pkgs) > 0 {
if pkgs[0].BasePkgName != "" {
depBaseName = pkgs[0].BasePkgName
}
}
if err := visit(depBaseName); err != nil {
return err
}
}
inStack[basePkgName] = false
visited[basePkgName] = true
result = append(result, basePkgName)
return nil
}
// Посещаем все узлы
for basePkgName := range nodes {
if err := visit(basePkgName); err != nil {
return nil, err
}
}
return result, nil
}

View File

@@ -42,6 +42,15 @@ func getDirs(
cfg Config, cfg Config,
scriptPath string, scriptPath string,
basePkg string, basePkg string,
) (types.Directories, error) {
return getDirsForPackage(cfg, scriptPath, basePkg, "")
}
func getDirsForPackage(
cfg Config,
scriptPath string,
basePkg string,
packageName string,
) (types.Directories, error) { ) (types.Directories, error) {
pkgsDir := cfg.GetPaths().PkgsDir pkgsDir := cfg.GetPaths().PkgsDir
@@ -50,10 +59,18 @@ func getDirs(
return types.Directories{}, err return types.Directories{}, err
} }
baseDir := filepath.Join(pkgsDir, basePkg) baseDir := filepath.Join(pkgsDir, basePkg)
// Для подпакетов используем отдельную директорию pkg_<имя_подпакета>
// Для обычных пакетов используем просто pkg
pkgDirName := "pkg"
if packageName != "" {
pkgDirName = "pkg_" + packageName
}
return types.Directories{ return types.Directories{
BaseDir: getBaseDir(cfg, basePkg), BaseDir: getBaseDir(cfg, basePkg),
SrcDir: getSrcDir(cfg, basePkg), SrcDir: getSrcDir(cfg, basePkg),
PkgDir: filepath.Join(baseDir, "pkg"), PkgDir: filepath.Join(baseDir, pkgDirName),
ScriptDir: getScriptDir(scriptPath), ScriptDir: getScriptDir(scriptPath),
}, nil }, nil
} }

View File

@@ -18,8 +18,16 @@ package build
import ( import (
"context" "context"
"fmt"
"log/slog"
"github.com/leonelquinteros/gotext"
"gitea.plemya-x.ru/xpamych/vercmp"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/manager" "gitea.plemya-x.ru/Plemya-x/ALR/internal/manager"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/overrides"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
) )
func NewInstaller(mgr manager.Manager) *Installer { func NewInstaller(mgr manager.Manager) *Installer {
@@ -58,3 +66,44 @@ func (i *Installer) RemoveAlreadyInstalled(ctx context.Context, pkgs []string) (
return filteredPackages, nil return filteredPackages, nil
} }
func (i *Installer) FilterPackagesByVersion(ctx context.Context, packages []alrsh.Package, osRelease *distro.OSRelease) ([]alrsh.Package, error) {
installedPkgs, err := i.mgr.ListInstalled(nil)
if err != nil {
return nil, fmt.Errorf("failed to list installed packages: %w", err)
}
var filteredPackages []alrsh.Package
for _, pkg := range packages {
alrPkgName := fmt.Sprintf("%s+%s", pkg.Name, pkg.Repository)
installedVer, isInstalled := installedPkgs[alrPkgName]
if !isInstalled {
filteredPackages = append(filteredPackages, pkg)
continue
}
repoVer := pkg.Version
releaseStr := overrides.ReleasePlatformSpecific(pkg.Release, osRelease)
if pkg.Release != 0 && pkg.Epoch == 0 {
repoVer = fmt.Sprintf("%s-%s", pkg.Version, releaseStr)
} else if pkg.Release != 0 && pkg.Epoch != 0 {
repoVer = fmt.Sprintf("%d:%s-%s", pkg.Epoch, pkg.Version, releaseStr)
}
cmp := vercmp.Compare(repoVer, installedVer)
if cmp > 0 {
slog.Info(gotext.Get("Package %s is installed with older version %s, will rebuild with version %s", alrPkgName, installedVer, repoVer))
filteredPackages = append(filteredPackages, pkg)
} else if cmp == 0 {
slog.Info(gotext.Get("Package %s is already installed with version %s, skipping build", alrPkgName, installedVer))
} else {
slog.Info(gotext.Get("Package %s is installed with newer version %s (repo has %s), skipping build", alrPkgName, installedVer, repoVer))
}
}
return filteredPackages, nil
}

View File

@@ -21,6 +21,7 @@ import (
"gitea.plemya-x.ru/Plemya-x/ALR/internal/manager" "gitea.plemya-x.ru/Plemya-x/ALR/internal/manager"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh" "gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/types" "gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
) )
@@ -34,6 +35,7 @@ type InstallerExecutor interface {
Install(ctx context.Context, pkgs []string, opts *manager.Opts) error Install(ctx context.Context, pkgs []string, opts *manager.Opts) error
Remove(ctx context.Context, pkgs []string, opts *manager.Opts) error Remove(ctx context.Context, pkgs []string, opts *manager.Opts) error
RemoveAlreadyInstalled(ctx context.Context, pkgs []string) ([]string, error) RemoveAlreadyInstalled(ctx context.Context, pkgs []string) ([]string, error)
FilterPackagesByVersion(ctx context.Context, packages []alrsh.Package, osRelease *distro.OSRelease) ([]alrsh.Package, error)
} }
type ScriptExecutor interface { type ScriptExecutor interface {

View File

@@ -24,6 +24,7 @@ import (
"context" "context"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/manager" "gitea.plemya-x.ru/Plemya-x/ALR/internal/manager"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh" "gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/types" "gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
"github.com/hashicorp/go-plugin" "github.com/hashicorp/go-plugin"
) )
@@ -205,6 +206,38 @@ func (s *InstallerExecutorRPCServer) RemoveAlreadyInstalled(args *InstallerExecu
return nil return nil
} }
type InstallerExecutorFilterPackagesByVersionArgs struct {
Packages []alrsh.Package
OsRelease *distro.OSRelease
}
type InstallerExecutorFilterPackagesByVersionResp struct {
Result0 []alrsh.Package
}
func (s *InstallerExecutorRPC) FilterPackagesByVersion(ctx context.Context, packages []alrsh.Package, osRelease *distro.OSRelease) ([]alrsh.Package, error) {
var resp *InstallerExecutorFilterPackagesByVersionResp
err := s.client.Call("Plugin.FilterPackagesByVersion", &InstallerExecutorFilterPackagesByVersionArgs{
Packages: packages,
OsRelease: osRelease,
}, &resp)
if err != nil {
return nil, err
}
return resp.Result0, nil
}
func (s *InstallerExecutorRPCServer) FilterPackagesByVersion(args *InstallerExecutorFilterPackagesByVersionArgs, resp *InstallerExecutorFilterPackagesByVersionResp) error {
result0, err := s.Impl.FilterPackagesByVersion(context.Background(), args.Packages, args.OsRelease)
if err != nil {
return err
}
*resp = InstallerExecutorFilterPackagesByVersionResp{
Result0: result0,
}
return nil
}
type ScriptExecutorReadScriptArgs struct { type ScriptExecutorReadScriptArgs struct {
ScriptPath string ScriptPath string
} }

View File

@@ -130,12 +130,34 @@ func (e *LocalScriptExecutor) ExecuteSecondPass(
packageName = vars.Name packageName = vars.Name
} }
// Для каждого подпакета создаём отдельную директорию
pkgDirs, err := getDirsForPackage(e.cfg, sf.Path(), basePkg, packageName)
if err != nil {
return nil, err
}
// Создаём директорию для подпакета
if err := os.MkdirAll(pkgDirs.PkgDir, 0o755); err != nil {
return nil, err
}
// Обновляем переменную окружения $pkgdir для текущего подпакета
setPkgdirCmd := fmt.Sprintf("pkgdir='%s'", pkgDirs.PkgDir)
setPkgdirScript, err := syntax.NewParser().Parse(strings.NewReader(setPkgdirCmd), "")
if err != nil {
return nil, err
}
err = runner.Run(ctx, setPkgdirScript)
if err != nil {
return nil, err
}
pkgFormat := input.pkgFormat pkgFormat := input.pkgFormat
funcOut, err := e.ExecutePackageFunctions( funcOut, err := e.ExecutePackageFunctions(
ctx, ctx,
dec, dec,
dirs, pkgDirs,
packageName, packageName,
) )
if err != nil { if err != nil {
@@ -148,7 +170,7 @@ func (e *LocalScriptExecutor) ExecuteSecondPass(
ctx, ctx,
input, input,
vars, vars,
dirs, pkgDirs,
append( append(
repoDeps, repoDeps,
GetBuiltName(builtDeps)..., GetBuiltName(builtDeps)...,
@@ -165,32 +187,32 @@ func (e *LocalScriptExecutor) ExecuteSecondPass(
} }
pkgName := packager.ConventionalFileName(pkgInfo) // Получаем имя файла пакета pkgName := packager.ConventionalFileName(pkgInfo) // Получаем имя файла пакета
pkgPath := filepath.Join(dirs.BaseDir, pkgName) // Определяем путь к пакету pkgPath := filepath.Join(pkgDirs.BaseDir, pkgName) // Определяем путь к пакету
slog.Info("Creating package file", "path", pkgPath, "name", pkgName) slog.Info(gotext.Get("Creating package file"), "path", pkgPath, "name", pkgName)
pkgFile, err := os.Create(pkgPath) pkgFile, err := os.Create(pkgPath)
if err != nil { if err != nil {
slog.Error("Failed to create package file", "path", pkgPath, "error", err) slog.Error(gotext.Get("Failed to create package file"), "path", pkgPath, "error", err)
return nil, err return nil, err
} }
defer pkgFile.Close() defer pkgFile.Close()
slog.Info("Packaging with nfpm", "format", pkgFormat) slog.Info(gotext.Get("Packaging with nfpm"), "format", pkgFormat)
err = packager.Package(pkgInfo, pkgFile) err = packager.Package(pkgInfo, pkgFile)
if err != nil { if err != nil {
slog.Error("Failed to create package", "path", pkgPath, "error", err) slog.Error(gotext.Get("Failed to create package"), "path", pkgPath, "error", err)
return nil, err return nil, err
} }
slog.Info("Package created successfully", "path", pkgPath) slog.Info(gotext.Get("Package created successfully"), "path", pkgPath)
// Проверяем, что файл действительно существует // Проверяем, что файл действительно существует
if _, err := os.Stat(pkgPath); err != nil { if _, err := os.Stat(pkgPath); err != nil {
slog.Error("Package file not found after creation", "path", pkgPath, "error", err) slog.Error(gotext.Get("Package file not found after creation"), "path", pkgPath, "error", err)
return nil, err return nil, err
} }
slog.Info("Package file verified to exist", "path", pkgPath) slog.Info(gotext.Get("Package file verified to exist"), "path", pkgPath)
builtDeps = append(builtDeps, &BuiltDep{ builtDeps = append(builtDeps, &BuiltDep{
Name: vars.Name, Name: vars.Name,

View File

@@ -42,7 +42,7 @@ type AppDeps struct {
func (d *AppDeps) Defer() { func (d *AppDeps) Defer() {
if d.DB != nil { if d.DB != nil {
if err := d.DB.Close(); err != nil { if err := d.DB.Close(); err != nil {
slog.Warn("failed to close db", "err", err) slog.Warn(gotext.Get("failed to close db"), "err", err)
} }
} }
} }

View File

@@ -100,3 +100,28 @@ func GetCommandHelpTemplate() string {
gotext.Get("OPTIONS"), gotext.Get("OPTIONS"),
) )
} }
func GetSubcommandHelpTemplate() string {
return fmt.Sprintf(`%s:
{{template "helpNameTemplate" .}}
%s:
{{.HelpName}} %s [%s] {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[%s...]{{end}}
{{if .Description}}
%s:
{{template "descriptionTemplate" .}}{{end}}
{{- if len .Authors}}
%s{{template "authorsTemplate" .}}{{end}}{{if .VisibleCommands}}
%s:{{template "visibleCommandCategoryTemplate" .}}{{end}}{{if .VisibleFlagCategories}}
%s:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}}
%s:{{template "visibleFlagTemplate" .}}{{end}}{{if .Copyright}}
%s:
{{template "copyrightTemplate" .}}{{end}}
`, gotext.Get("NAME"), gotext.Get("USAGE"), gotext.Get("command"), gotext.Get("command options"), gotext.Get("arguments"), gotext.Get("DESCRIPTION"), gotext.Get("AUTHOR"), gotext.Get("COMMANDS"), gotext.Get("OPTIONS"), gotext.Get("OPTIONS"), gotext.Get("COPYRIGHT"))
}

View File

@@ -31,6 +31,7 @@ import (
"xorm.io/xorm" "xorm.io/xorm"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" "gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/fsutils"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh" "gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh"
) )
@@ -57,19 +58,21 @@ func New(config Config) *Database {
func (d *Database) Connect() error { func (d *Database) Connect() error {
dsn := d.config.GetPaths().DBPath dsn := d.config.GetPaths().DBPath
// Проверяем директорию для БД // Проверяем директорию для БД
dbDir := filepath.Dir(dsn) dbDir := filepath.Dir(dsn)
if _, err := os.Stat(dbDir); err != nil { if _, err := os.Stat(dbDir); err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
// Директория не существует - не пытаемся создать // Директория не существует - создаем автоматически
// Пользователь должен использовать alr fix для создания системных каталогов slog.Info(gotext.Get("Cache directory does not exist, creating it"))
return fmt.Errorf("cache directory does not exist, please run 'sudo alr fix' to create it") if err := fsutils.EnsureTempDirWithRootOwner(dbDir, 0o2775); err != nil {
return fmt.Errorf("failed to create cache directory: %w", err)
}
} else { } else {
return fmt.Errorf("failed to check database directory: %w", err) return fmt.Errorf("failed to check database directory: %w", err)
} }
} }
engine, err := xorm.NewEngine("sqlite", dsn) engine, err := xorm.NewEngine("sqlite", dsn)
// engine.SetLogLevel(log.LOG_DEBUG) // engine.SetLogLevel(log.LOG_DEBUG)
// engine.ShowSQL(true) // engine.ShowSQL(true)

100
internal/fsutils/dirs.go Normal file
View File

@@ -0,0 +1,100 @@
// ALR - Any Linux Repository
// Copyright (C) 2025 The ALR Authors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package fsutils
import (
"fmt"
"os"
"os/exec"
"strings"
)
// EnsureTempDirWithRootOwner создает каталог в /tmp/alr или /var/cache/alr с правами для привилегированной группы
// Все каталоги в /tmp/alr и /var/cache/alr принадлежат root:привилегированная_группа с правами 2775
// Для других каталогов использует стандартные права
func EnsureTempDirWithRootOwner(path string, mode os.FileMode) error {
needsElevation := strings.HasPrefix(path, "/tmp/alr") || strings.HasPrefix(path, "/var/cache/alr")
if needsElevation {
// В CI или если мы уже root, не нужно использовать sudo
isRoot := os.Geteuid() == 0
isCI := os.Getenv("CI") == "true"
// В CI создаем директории с обычными правами
if isCI {
err := os.MkdirAll(path, mode)
if err != nil {
return err
}
// В CI не используем группу wheel и не меняем права
// Устанавливаем базовые права 777 для временных каталогов
chmodCmd := exec.Command("chmod", "777", path)
chmodCmd.Run() // Игнорируем ошибки
return nil
}
// Для обычной работы устанавливаем права и привилегированную группу
permissions := "2775"
group := GetPrivilegedGroup()
var mkdirCmd, chmodCmd, chownCmd *exec.Cmd
if isRoot {
// Выполняем команды напрямую без sudo
mkdirCmd = exec.Command("mkdir", "-p", path)
chmodCmd = exec.Command("chmod", permissions, path)
chownCmd = exec.Command("chown", "root:"+group, path)
} else {
// Используем sudo для всех операций с привилегированными каталогами
mkdirCmd = exec.Command("sudo", "mkdir", "-p", path)
chmodCmd = exec.Command("sudo", "chmod", permissions, path)
chownCmd = exec.Command("sudo", "chown", "root:"+group, path)
}
// Создаем директорию через sudo если нужно
err := mkdirCmd.Run()
if err != nil {
// Игнорируем ошибку если директория уже существует
if !isRoot {
// Проверяем существует ли директория
if _, statErr := os.Stat(path); statErr != nil {
return fmt.Errorf("не удалось создать директорию %s: %w", path, err)
}
}
}
// Устанавливаем права с setgid битом для наследования группы
err = chmodCmd.Run()
if err != nil {
if !isRoot {
return fmt.Errorf("не удалось установить права на %s: %w", path, err)
}
}
// Устанавливаем владельца root:группа
err = chownCmd.Run()
if err != nil {
if !isRoot {
return fmt.Errorf("не удалось установить владельца на %s: %w", path, err)
}
}
return nil
}
// Для остальных каталогов обычное создание
return os.MkdirAll(path, mode)
}

View File

@@ -14,7 +14,7 @@
// You should have received a copy of the 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/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
package utils package fsutils
import ( import (
"context" "context"

View File

@@ -36,7 +36,7 @@ import (
"github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing"
"github.com/leonelquinteros/gotext" "github.com/leonelquinteros/gotext"
"github.com/pelletier/go-toml/v2" "github.com/pelletier/go-toml/v2"
"go.elara.ws/vercmp" "gitea.plemya-x.ru/xpamych/vercmp"
"mvdan.cc/sh/v3/expand" "mvdan.cc/sh/v3/expand"
"mvdan.cc/sh/v3/interp" "mvdan.cc/sh/v3/interp"
"mvdan.cc/sh/v3/syntax" "mvdan.cc/sh/v3/syntax"
@@ -420,13 +420,13 @@ func (rs *Repos) processRepoChanges(ctx context.Context, repo types.Repo, r *git
case actionDelete: case actionDelete:
scriptFl, err := oldCommit.File(action.File) scriptFl, err := oldCommit.File(action.File)
if err != nil { if err != nil {
slog.Warn("Failed to get deleted file from old commit", "file", action.File, "error", err) slog.Warn(gotext.Get("Failed to get deleted file from old commit"), "file", action.File, "error", err)
continue continue
} }
r, err := scriptFl.Reader() r, err := scriptFl.Reader()
if err != nil { if err != nil {
slog.Warn("Failed to read deleted file", "file", action.File, "error", err) slog.Warn(gotext.Get("Failed to read deleted file"), "file", action.File, "error", err)
continue continue
} }
@@ -445,13 +445,13 @@ func (rs *Repos) processRepoChanges(ctx context.Context, repo types.Repo, r *git
case actionUpdate: case actionUpdate:
scriptFl, err := newCommit.File(action.File) scriptFl, err := newCommit.File(action.File)
if err != nil { if err != nil {
slog.Warn("Failed to get updated file from new commit", "file", action.File, "error", err) slog.Warn(gotext.Get("Failed to get updated file from new commit"), "file", action.File, "error", err)
continue continue
} }
r, err := scriptFl.Reader() r, err := scriptFl.Reader()
if err != nil { if err != nil {
slog.Warn("Failed to read updated file", "file", action.File, "error", err) slog.Warn(gotext.Get("Failed to read updated file"), "file", action.File, "error", err)
continue continue
} }
@@ -505,7 +505,7 @@ func (rs *Repos) processRepoFull(ctx context.Context, repo types.Repo, repoDir s
} }
if len(matches) == 0 { if len(matches) == 0 {
slog.Warn("No alr.sh files found in repository", "repo", repo.Name) slog.Warn(gotext.Get("No alr.sh files found in repository"), "repo", repo.Name)
return nil return nil
} }

View File

@@ -402,3 +402,108 @@ func filesFindConfigCmd(hc interp.HandlerContext, cmd string, args []string) err
return outputFiles(hc, configFiles) return outputFiles(hc, configFiles)
} }
func filesFindSystemdCmd(hc interp.HandlerContext, cmd string, args []string) error {
namePattern := "*"
if len(args) > 0 {
namePattern = args[0]
}
systemdPath := "./usr/lib/systemd/system/"
realPath := path.Join(hc.Dir, systemdPath)
if err := validateDir(realPath, "files-find-systemd"); err != nil {
return err
}
var systemdFiles []string
err := filepath.Walk(realPath, func(p string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && matchNamePattern(info.Name(), namePattern) {
relPath, relErr := makeRelativePath(hc.Dir, p)
if relErr != nil {
return relErr
}
systemdFiles = append(systemdFiles, relPath)
}
return nil
})
if err != nil {
return fmt.Errorf("files-find-systemd: %w", err)
}
return outputFiles(hc, systemdFiles)
}
func filesFindSystemdUserCmd(hc interp.HandlerContext, cmd string, args []string) error {
namePattern := "*"
if len(args) > 0 {
namePattern = args[0]
}
systemdUserPath := "./usr/lib/systemd/user/"
realPath := path.Join(hc.Dir, systemdUserPath)
if err := validateDir(realPath, "files-find-systemd-user"); err != nil {
return err
}
var systemdUserFiles []string
err := filepath.Walk(realPath, func(p string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && matchNamePattern(info.Name(), namePattern) {
relPath, relErr := makeRelativePath(hc.Dir, p)
if relErr != nil {
return relErr
}
systemdUserFiles = append(systemdUserFiles, relPath)
}
return nil
})
if err != nil {
return fmt.Errorf("files-find-systemd-user: %w", err)
}
return outputFiles(hc, systemdUserFiles)
}
func filesFindLicenseCmd(hc interp.HandlerContext, cmd string, args []string) error {
namePattern := "*"
if len(args) > 0 {
namePattern = args[0]
}
licensePath := "./usr/share/licenses/"
realPath := path.Join(hc.Dir, licensePath)
if err := validateDir(realPath, "files-find-license"); err != nil {
return err
}
var licenseFiles []string
err := filepath.Walk(realPath, func(p string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && matchNamePattern(info.Name(), namePattern) {
relPath, relErr := makeRelativePath(hc.Dir, p)
if relErr != nil {
return relErr
}
licenseFiles = append(licenseFiles, relPath)
}
return nil
})
if err != nil {
return fmt.Errorf("files-find-license: %w", err)
}
return outputFiles(hc, licenseFiles)
}

View File

@@ -56,30 +56,36 @@ var Helpers = handlers.ExecFuncs{
"install-library": installLibraryCmd, "install-library": installLibraryCmd,
"git-version": gitVersionCmd, "git-version": gitVersionCmd,
"files-find": filesFindCmd, "files-find": filesFindCmd,
"files-find-lang": filesFindLangCmd, "files-find-lang": filesFindLangCmd,
"files-find-doc": filesFindDocCmd, "files-find-doc": filesFindDocCmd,
"files-find-bin": filesFindBinCmd, "files-find-bin": filesFindBinCmd,
"files-find-lib": filesFindLibCmd, "files-find-lib": filesFindLibCmd,
"files-find-include": filesFindIncludeCmd, "files-find-include": filesFindIncludeCmd,
"files-find-share": filesFindShareCmd, "files-find-share": filesFindShareCmd,
"files-find-man": filesFindManCmd, "files-find-man": filesFindManCmd,
"files-find-config": filesFindConfigCmd, "files-find-config": filesFindConfigCmd,
"files-find-systemd": filesFindSystemdCmd,
"files-find-systemd-user": filesFindSystemdUserCmd,
"files-find-license": filesFindLicenseCmd,
} }
// Restricted contains restricted read-only helper commands // Restricted contains restricted read-only helper commands
// that don't modify any state // that don't modify any state
var Restricted = handlers.ExecFuncs{ var Restricted = handlers.ExecFuncs{
"git-version": gitVersionCmd, "git-version": gitVersionCmd,
"files-find": filesFindCmd, "files-find": filesFindCmd,
"files-find-lang": filesFindLangCmd, "files-find-lang": filesFindLangCmd,
"files-find-doc": filesFindDocCmd, "files-find-doc": filesFindDocCmd,
"files-find-bin": filesFindBinCmd, "files-find-bin": filesFindBinCmd,
"files-find-lib": filesFindLibCmd, "files-find-lib": filesFindLibCmd,
"files-find-include": filesFindIncludeCmd, "files-find-include": filesFindIncludeCmd,
"files-find-share": filesFindShareCmd, "files-find-share": filesFindShareCmd,
"files-find-man": filesFindManCmd, "files-find-man": filesFindManCmd,
"files-find-config": filesFindConfigCmd, "files-find-config": filesFindConfigCmd,
"files-find-systemd": filesFindSystemdCmd,
"files-find-systemd-user": filesFindSystemdUserCmd,
"files-find-license": filesFindLicenseCmd,
} }
func installHelperCmd(prefix string, perms os.FileMode) handlers.ExecFunc { func installHelperCmd(prefix string, perms os.FileMode) handlers.ExecFunc {

View File

@@ -34,27 +34,31 @@ msgstr ""
msgid "Error getting working directory" msgid "Error getting working directory"
msgstr "" msgstr ""
#: build.go:117 #: build.go:111
msgid "Cannot get absolute script path" msgid "Cannot get absolute script path"
msgstr "" msgstr ""
#: build.go:143 #: build.go:137
msgid "Package not found" msgid "Package not found"
msgstr "" msgstr ""
#: build.go:156 #: build.go:150
msgid "Nothing to build" msgid "Nothing to build"
msgstr "" msgstr ""
#: build.go:213 #: build.go:195
msgid "Error building package" msgid "Error building package"
msgstr "" msgstr ""
#: build.go:220 #: build.go:203
msgid "Package file already moved or removed, skipping"
msgstr ""
#: build.go:209
msgid "Error moving the package" msgid "Error moving the package"
msgstr "" msgstr ""
#: build.go:224 #: build.go:213
msgid "Done" msgid "Done"
msgstr "" msgstr ""
@@ -62,71 +66,123 @@ msgstr ""
msgid "Manage config" msgid "Manage config"
msgstr "" msgstr ""
#: config.go:48 #: config.go:50
msgid "Shows a list of commands or help for one command"
msgstr ""
#: config.go:66
msgid "Show config" msgid "Show config"
msgstr "" msgstr ""
#: config.go:84 #: config.go:103
msgid "Set config value" msgid "Set config value"
msgstr "" msgstr ""
#: config.go:85 #: config.go:104
msgid "<key> <value>" msgid "<key> <value>"
msgstr "" msgstr ""
#: config.go:118 config.go:126 #: config.go:137 config.go:145 config.go:162
msgid "invalid boolean value for %s: %s" msgid "invalid boolean value for %s: %s"
msgstr "" msgstr ""
#: config.go:141 #: config.go:166
msgid "use 'repo add/remove' commands to manage repositories" msgid "use 'repo add/remove' commands to manage repositories"
msgstr "" msgstr ""
#: config.go:143 config.go:221 #: config.go:168 config.go:248
msgid "unknown config key: %s" msgid "unknown config key: %s"
msgstr "" msgstr ""
#: config.go:147 #: config.go:172
msgid "failed to save config" msgid "failed to save config"
msgstr "" msgstr ""
#: config.go:150 #: config.go:175
msgid "Successfully set %s = %s" msgid "Successfully set %s = %s"
msgstr "" msgstr ""
#: config.go:159 #: config.go:184
msgid "Get config value" msgid "Get config value"
msgstr "" msgstr ""
#: config.go:160 #: config.go:185
msgid "<key>" msgid "<key>"
msgstr "" msgstr ""
#: fix.go:39 #: fix.go:55
msgid "Attempt to fix problems with ALR" msgid "Attempt to fix problems with ALR"
msgstr "" msgstr ""
#: fix.go:60 #: fix.go:75
msgid "Clearing cache directory" msgid "Clearing cache and temporary directories"
msgstr ""
#: fix.go:64
msgid "Unable to open cache directory"
msgstr ""
#: fix.go:70
msgid "Unable to read cache directory contents"
msgstr "" msgstr ""
#: fix.go:82 #: fix.go:82
msgid "Cache directory does not exist, will create it"
msgstr ""
#: fix.go:84
msgid "Unable to open cache directory"
msgstr ""
#: fix.go:91
msgid "Unable to read cache directory contents"
msgstr ""
#: fix.go:106
msgid "Unable to remove cache item (%s) as current user, trying with sudo"
msgstr ""
#: fix.go:111
msgid "Unable to remove cache item (%s)" msgid "Unable to remove cache item (%s)"
msgstr "" msgstr ""
#: fix.go:86 #: fix.go:119
msgid "Clearing temporary directory"
msgstr ""
#: fix.go:126
msgid "Unable to remove temporary directory as current user, trying with sudo"
msgstr ""
#: fix.go:129
msgid "Unable to remove temporary directory"
msgstr ""
#: fix.go:137
msgid "Unable to create temporary directory"
msgstr ""
#: fix.go:144
msgid "Unable to create download directory"
msgstr ""
#: fix.go:151
msgid "Unable to create packages directory"
msgstr ""
#: fix.go:156
msgid "Fixing permissions on temporary files"
msgstr ""
#: fix.go:164
msgid "Unable to fix file ownership"
msgstr ""
#: fix.go:169
msgid "Unable to fix file permissions"
msgstr ""
#: fix.go:174
msgid "Rebuilding cache" msgid "Rebuilding cache"
msgstr "" msgstr ""
#: fix.go:90 #: fix.go:177
msgid "Creating cache directory"
msgstr ""
#: fix.go:180
msgid "Unable to create new cache directory" msgid "Unable to create new cache directory"
msgstr "" msgstr ""
@@ -138,55 +194,67 @@ msgstr ""
msgid "Generate a ALR script for a pip module" msgid "Generate a ALR script for a pip module"
msgstr "" msgstr ""
#: gen.go:66
msgid "Generate a ALR script for an AUR package"
msgstr ""
#: gen.go:72
msgid "Name of the AUR package"
msgstr ""
#: gen.go:77
msgid "Version of the package (optional, uses latest if not specified)"
msgstr ""
#: helper.go:42 #: helper.go:42
msgid "List all the available helper commands" msgid "List all the available helper commands"
msgstr "" msgstr ""
#: helper.go:54 #: helper.go:69
msgid "Run a ALR helper command" msgid "Run a ALR helper command"
msgstr "" msgstr ""
#: helper.go:61 #: helper.go:76
msgid "The directory that the install commands will install to" msgid "The directory that the install commands will install to"
msgstr "" msgstr ""
#: helper.go:74 helper.go:75 #: helper.go:89 helper.go:90
msgid "No such helper command" msgid "No such helper command"
msgstr "" msgstr ""
#: helper.go:85 #: helper.go:100
msgid "Error parsing os-release file" msgid "Error parsing os-release file"
msgstr "" msgstr ""
#: info.go:42 #: info.go:41
msgid "Print information about a package" msgid "Print information about a package"
msgstr "" msgstr ""
#: info.go:47 #: info.go:46
msgid "Show all information, not just for the current distro" msgid "Show all information, not just for the current distro"
msgstr "" msgstr ""
#: info.go:68 #: info.go:64
msgid "Error getting packages" msgid "Error getting packages"
msgstr "" msgstr ""
#: info.go:83 #: info.go:77
msgid "Command info expected at least 1 argument, got %d" msgid "Command info expected at least 1 argument, got %d"
msgstr "" msgstr ""
#: info.go:104 #: info.go:98
msgid "Error finding packages" msgid "Error finding packages"
msgstr "" msgstr ""
#: info.go:118 #: info.go:112
msgid "Can't detect system language" msgid "Can't detect system language"
msgstr "" msgstr ""
#: info.go:134 #: info.go:128
msgid "Error resolving overrides" msgid "Error resolving overrides"
msgstr "" msgstr ""
#: info.go:143 #: info.go:137
msgid "Error encoding script variables" msgid "Error encoding script variables"
msgstr "" msgstr ""
@@ -198,43 +266,47 @@ msgstr ""
msgid "Command install expected at least 1 argument, got %d" msgid "Command install expected at least 1 argument, got %d"
msgstr "" msgstr ""
#: install.go:113 #: install.go:107
msgid "Error when installing the package" msgid "Error when installing the package"
msgstr "" msgstr ""
#: install.go:151 #: install.go:142
msgid "Remove an installed package" msgid "Remove an installed package"
msgstr "" msgstr ""
#: install.go:170 #: install.go:161
msgid "Error listing installed packages" msgid "Error listing installed packages"
msgstr "" msgstr ""
#: install.go:199 #: install.go:190
msgid "Command remove expected at least 1 argument, got %d" msgid "Command remove expected at least 1 argument, got %d"
msgstr "" msgstr ""
#: install.go:214 #: install.go:205
msgid "Error removing packages" msgid "Error removing packages"
msgstr "" msgstr ""
#: internal/build/build.go:351 #: internal/build/build.go:342 internal/build/build.go:653
msgid "Using cached package"
msgstr ""
#: internal/build/build.go:357
msgid "Building package" msgid "Building package"
msgstr "" msgstr ""
#: internal/build/build.go:380 #: internal/build/build.go:386
msgid "The checksums array must be the same length as sources" msgid "The checksums array must be the same length as sources"
msgstr "" msgstr ""
#: internal/build/build.go:422 #: internal/build/build.go:438
msgid "Downloading sources" msgid "Downloading sources"
msgstr "" msgstr ""
#: internal/build/build.go:468 #: internal/build/build.go:484
msgid "Would you like to remove the build dependencies?" msgid "Would you like to remove the build dependencies?"
msgstr "" msgstr ""
#: internal/build/build.go:546 #: internal/build/build.go:569
msgid "Installing dependencies" msgid "Installing dependencies"
msgstr "" msgstr ""
@@ -272,22 +344,68 @@ msgstr ""
msgid "Applying FireJail integration" msgid "Applying FireJail integration"
msgstr "" msgstr ""
#: internal/build/script_executor.go:145 #: internal/build/installer.go:99
msgid ""
"Package %s is installed with older version %s, will rebuild with version %s"
msgstr ""
#: internal/build/installer.go:102
msgid "Package %s is already installed with version %s, skipping build"
msgstr ""
#: internal/build/installer.go:104
msgid ""
"Package %s is installed with newer version %s (repo has %s), skipping build"
msgstr ""
#: internal/build/script_executor.go:167
msgid "Building package metadata" msgid "Building package metadata"
msgstr "" msgstr ""
#: internal/build/script_executor.go:285 #: internal/build/script_executor.go:192
msgid "Creating package file"
msgstr ""
#: internal/build/script_executor.go:196
msgid "Failed to create package file"
msgstr ""
#: internal/build/script_executor.go:201
msgid "Packaging with nfpm"
msgstr ""
#: internal/build/script_executor.go:204
msgid "Failed to create package"
msgstr ""
#: internal/build/script_executor.go:208
msgid "Package created successfully"
msgstr ""
#: internal/build/script_executor.go:212
msgid "Package file not found after creation"
msgstr ""
#: internal/build/script_executor.go:215
msgid "Package file verified to exist"
msgstr ""
#: internal/build/script_executor.go:322
msgid "Executing prepare()" msgid "Executing prepare()"
msgstr "" msgstr ""
#: internal/build/script_executor.go:294 #: internal/build/script_executor.go:331
msgid "Executing build()" msgid "Executing build()"
msgstr "" msgstr ""
#: internal/build/script_executor.go:323 internal/build/script_executor.go:343 #: internal/build/script_executor.go:360 internal/build/script_executor.go:380
msgid "Executing %s()" msgid "Executing %s()"
msgstr "" msgstr ""
#: internal/cliutils/app_builder/builder.go:45
msgid "failed to close db"
msgstr ""
#: internal/cliutils/app_builder/builder.go:75 #: internal/cliutils/app_builder/builder.go:75
msgid "Error loading config" msgid "Error loading config"
msgstr "" msgstr ""
@@ -320,23 +438,25 @@ msgstr ""
msgid "User chose not to continue after reading script" msgid "User chose not to continue after reading script"
msgstr "" msgstr ""
#: internal/cliutils/prompt.go:111 #: internal/cliutils/prompt.go:123
msgid "Error prompting for choice of package" msgid "Error prompting for choice of package"
msgstr "" msgstr ""
#: internal/cliutils/prompt.go:135 #: internal/cliutils/prompt.go:175
msgid "Choose which package to %s" msgid "Choose which package to %s"
msgstr "" msgstr ""
#: internal/cliutils/prompt.go:156 #: internal/cliutils/prompt.go:196
msgid "Choose which optional package(s) to install" msgid "Choose which optional package(s) to install"
msgstr "" msgstr ""
#: internal/cliutils/template.go:74 internal/cliutils/template.go:93 #: internal/cliutils/template.go:74 internal/cliutils/template.go:93
#: internal/cliutils/template.go:126
msgid "NAME" msgid "NAME"
msgstr "" msgstr ""
#: internal/cliutils/template.go:74 internal/cliutils/template.go:94 #: internal/cliutils/template.go:74 internal/cliutils/template.go:94
#: internal/cliutils/template.go:126
msgid "USAGE" msgid "USAGE"
msgstr "" msgstr ""
@@ -344,15 +464,17 @@ msgstr ""
msgid "global options" msgid "global options"
msgstr "" msgstr ""
#: internal/cliutils/template.go:74 #: internal/cliutils/template.go:74 internal/cliutils/template.go:126
msgid "command" msgid "command"
msgstr "" msgstr ""
#: internal/cliutils/template.go:74 internal/cliutils/template.go:95 #: internal/cliutils/template.go:74 internal/cliutils/template.go:95
#: internal/cliutils/template.go:126
msgid "command options" msgid "command options"
msgstr "" msgstr ""
#: internal/cliutils/template.go:74 internal/cliutils/template.go:96 #: internal/cliutils/template.go:74 internal/cliutils/template.go:96
#: internal/cliutils/template.go:126
msgid "arguments" msgid "arguments"
msgstr "" msgstr ""
@@ -361,14 +483,15 @@ msgid "VERSION"
msgstr "" msgstr ""
#: internal/cliutils/template.go:74 internal/cliutils/template.go:98 #: internal/cliutils/template.go:74 internal/cliutils/template.go:98
#: internal/cliutils/template.go:126
msgid "DESCRIPTION" msgid "DESCRIPTION"
msgstr "" msgstr ""
#: internal/cliutils/template.go:74 #: internal/cliutils/template.go:74 internal/cliutils/template.go:126
msgid "AUTHOR" msgid "AUTHOR"
msgstr "" msgstr ""
#: internal/cliutils/template.go:74 #: internal/cliutils/template.go:74 internal/cliutils/template.go:126
msgid "COMMANDS" msgid "COMMANDS"
msgstr "" msgstr ""
@@ -376,7 +499,7 @@ msgstr ""
msgid "GLOBAL OPTIONS" msgid "GLOBAL OPTIONS"
msgstr "" msgstr ""
#: internal/cliutils/template.go:74 #: internal/cliutils/template.go:74 internal/cliutils/template.go:126
msgid "COPYRIGHT" msgid "COPYRIGHT"
msgstr "" msgstr ""
@@ -385,6 +508,7 @@ msgid "CATEGORY"
msgstr "" msgstr ""
#: internal/cliutils/template.go:99 internal/cliutils/template.go:100 #: internal/cliutils/template.go:99 internal/cliutils/template.go:100
#: internal/cliutils/template.go:126
msgid "OPTIONS" msgid "OPTIONS"
msgstr "" msgstr ""
@@ -394,11 +518,15 @@ msgid ""
"instead!" "instead!"
msgstr "" msgstr ""
#: internal/db/db.go:76 #: internal/db/db.go:67
msgid "Cache directory does not exist, creating it"
msgstr ""
#: internal/db/db.go:95
msgid "Database version mismatch; resetting" msgid "Database version mismatch; resetting"
msgstr "" msgstr ""
#: internal/db/db.go:82 #: internal/db/db.go:101
msgid "" msgid ""
"Database version does not exist. Run alr fix if something isn't working." "Database version does not exist. Run alr fix if something isn't working."
msgstr "" msgstr ""
@@ -433,43 +561,55 @@ msgid ""
"updating ALR if something doesn't work." "updating ALR if something doesn't work."
msgstr "" msgstr ""
#: internal/utils/cmd.go:97 #: internal/repos/pull.go:423
msgid "Error on dropping capabilities" msgid "Failed to get deleted file from old commit"
msgstr "" msgstr ""
#: internal/utils/cmd.go:164 #: internal/repos/pull.go:429
msgid "You need to be a %s member to perform this action" msgid "Failed to read deleted file"
msgstr "" msgstr ""
#: internal/utils/cmd.go:200 #: internal/repos/pull.go:448
msgid "Failed to get updated file from new commit"
msgstr ""
#: internal/repos/pull.go:454
msgid "Failed to read updated file"
msgstr ""
#: internal/repos/pull.go:508
msgid "No alr.sh files found in repository"
msgstr ""
#: internal/utils/cmd.go:54
msgid "You need to be root to perform this action" msgid "You need to be root to perform this action"
msgstr "" msgstr ""
#: list.go:45 #: list.go:44
msgid "List ALR repo packages" msgid "List ALR repo packages"
msgstr "" msgstr ""
#: list.go:59 #: list.go:58
msgid "Format output using a Go template" msgid "Format output using a Go template"
msgstr "" msgstr ""
#: list.go:91 #: list.go:87
msgid "Error getting packages for upgrade" msgid "Error getting packages for upgrade"
msgstr "" msgstr ""
#: list.go:94 #: list.go:90
msgid "No packages for upgrade" msgid "No packages for upgrade"
msgstr "" msgstr ""
#: list.go:104 list.go:201 #: list.go:100 list.go:197
msgid "Error parsing format template" msgid "Error parsing format template"
msgstr "" msgstr ""
#: list.go:110 list.go:205 #: list.go:106 list.go:201
msgid "Error executing template" msgid "Error executing template"
msgstr "" msgstr ""
#: list.go:164 #: list.go:160
msgid "Failed to parse release" msgid "Failed to parse release"
msgstr "" msgstr ""
@@ -477,19 +617,19 @@ msgstr ""
msgid "Print the current ALR version and exit" msgid "Print the current ALR version and exit"
msgstr "" msgstr ""
#: main.go:61 #: main.go:77
msgid "Arguments to be passed on to the package manager" msgid "Arguments to be passed on to the package manager"
msgstr "" msgstr ""
#: main.go:67 #: main.go:83
msgid "Enable interactive questions and prompts" msgid "Enable interactive questions and prompts"
msgstr "" msgstr ""
#: main.go:148 #: main.go:165
msgid "Show help" msgid "Show help"
msgstr "" msgstr ""
#: main.go:152 #: main.go:169
msgid "Error while running app" msgid "Error while running app"
msgstr "" msgstr ""
@@ -525,124 +665,124 @@ msgstr ""
msgid "Manage repos" msgid "Manage repos"
msgstr "" msgstr ""
#: repo.go:56 repo.go:625 #: repo.go:74 repo.go:658
msgid "Remove an existing repository" msgid "Remove an existing repository"
msgstr "" msgstr ""
#: repo.go:58 repo.go:521 #: repo.go:76 repo.go:554
msgid "<name>" msgid "<name>"
msgstr "" msgstr ""
#: repo.go:103 repo.go:465 repo.go:568 #: repo.go:121 repo.go:498 repo.go:601
msgid "Repo \"%s\" does not exist" msgid "Repo \"%s\" does not exist"
msgstr "" msgstr ""
#: repo.go:110 #: repo.go:128
msgid "Error removing repo directory" msgid "Error removing repo directory"
msgstr "" msgstr ""
#: repo.go:114 repo.go:195 repo.go:253 repo.go:316 repo.go:389 repo.go:504 #: repo.go:132 repo.go:210 repo.go:268 repo.go:331 repo.go:422 repo.go:537
#: repo.go:576 #: repo.go:609
msgid "Error saving config" msgid "Error saving config"
msgstr "" msgstr ""
#: repo.go:133 #: repo.go:148
msgid "Error removing packages from database" msgid "Error removing packages from database"
msgstr "" msgstr ""
#: repo.go:144 repo.go:595 #: repo.go:159 repo.go:628
msgid "Add a new repository" msgid "Add a new repository"
msgstr "" msgstr ""
#: repo.go:145 repo.go:270 repo.go:345 repo.go:402 #: repo.go:160 repo.go:285 repo.go:378 repo.go:435
msgid "<name> <url>" msgid "<name> <url>"
msgstr "" msgstr ""
#: repo.go:170 #: repo.go:185
msgid "Repo \"%s\" already exists" msgid "Repo \"%s\" already exists"
msgstr "" msgstr ""
#: repo.go:206 #: repo.go:221
msgid "Set the reference of the repository" msgid "Set the reference of the repository"
msgstr "" msgstr ""
#: repo.go:207 #: repo.go:222
msgid "<name> <ref>" msgid "<name> <ref>"
msgstr "" msgstr ""
#: repo.go:269 #: repo.go:284
msgid "Set the main url of the repository" msgid "Set the main url of the repository"
msgstr "" msgstr ""
#: repo.go:332 #: repo.go:347
msgid "Manage mirrors of repos" msgid "Manage mirrors of repos"
msgstr "" msgstr ""
#: repo.go:344 #: repo.go:377
msgid "Add a mirror URL to repository" msgid "Add a mirror URL to repository"
msgstr "" msgstr ""
#: repo.go:401 #: repo.go:434
msgid "Remove mirror from the repository" msgid "Remove mirror from the repository"
msgstr "" msgstr ""
#: repo.go:420 #: repo.go:453
msgid "Ignore if mirror does not exist" msgid "Ignore if mirror does not exist"
msgstr "" msgstr ""
#: repo.go:425 #: repo.go:458
msgid "Match partial URL (e.g., github.com instead of full URL)" msgid "Match partial URL (e.g., github.com instead of full URL)"
msgstr "" msgstr ""
#: repo.go:490 #: repo.go:523
msgid "No mirrors containing \"%s\" found in repo \"%s\"" msgid "No mirrors containing \"%s\" found in repo \"%s\""
msgstr "" msgstr ""
#: repo.go:492 #: repo.go:525
msgid "URL \"%s\" does not exist in repo \"%s\"" msgid "URL \"%s\" does not exist in repo \"%s\""
msgstr "" msgstr ""
#: repo.go:508 repo.go:580 #: repo.go:541 repo.go:613
msgid "Removed %d mirrors from repo \"%s\"\n" msgid "Removed %d mirrors from repo \"%s\"\n"
msgstr "" msgstr ""
#: repo.go:520 #: repo.go:553
msgid "Remove all mirrors from the repository" msgid "Remove all mirrors from the repository"
msgstr "" msgstr ""
#: repo.go:602 #: repo.go:635
msgid "Name of the new repo" msgid "Name of the new repo"
msgstr "" msgstr ""
#: repo.go:608 #: repo.go:641
msgid "URL of the new repo" msgid "URL of the new repo"
msgstr "" msgstr ""
#: repo.go:632 #: repo.go:665
msgid "Name of the repo to be deleted" msgid "Name of the repo to be deleted"
msgstr "" msgstr ""
#: search.go:40 #: search.go:39
msgid "Search packages" msgid "Search packages"
msgstr "" msgstr ""
#: search.go:51 #: search.go:50
msgid "Search by name" msgid "Search by name"
msgstr "" msgstr ""
#: search.go:56 #: search.go:55
msgid "Search by description" msgid "Search by description"
msgstr "" msgstr ""
#: search.go:61 #: search.go:60
msgid "Search by repository" msgid "Search by repository"
msgstr "" msgstr ""
#: search.go:66 #: search.go:65
msgid "Search by provides" msgid "Search by provides"
msgstr "" msgstr ""
#: search.go:130 #: search.go:126
msgid "Error while executing search" msgid "Error while executing search"
msgstr "" msgstr ""
@@ -650,10 +790,22 @@ msgstr ""
msgid "Upgrade all installed packages" msgid "Upgrade all installed packages"
msgstr "" msgstr ""
#: upgrade.go:106 upgrade.go:123 #: upgrade.go:89
msgid "Updating system packages..."
msgstr ""
#: upgrade.go:95
msgid "Error updating system packages"
msgstr ""
#: upgrade.go:97
msgid "System packages updated successfully"
msgstr ""
#: upgrade.go:113 upgrade.go:130
msgid "Error checking for updates" msgid "Error checking for updates"
msgstr "" msgstr ""
#: upgrade.go:126 #: upgrade.go:133
msgid "There is nothing to do." msgid "There is nothing to do."
msgstr "" msgstr ""

View File

@@ -41,27 +41,31 @@ msgstr "Создайте пакет с нуля, даже если уже име
msgid "Error getting working directory" msgid "Error getting working directory"
msgstr "Ошибка при получении рабочего каталога" msgstr "Ошибка при получении рабочего каталога"
#: build.go:117 #: build.go:111
msgid "Cannot get absolute script path" msgid "Cannot get absolute script path"
msgstr "Невозможно получить абсолютный путь к скрипту" msgstr "Невозможно получить абсолютный путь к скрипту"
#: build.go:143 #: build.go:137
msgid "Package not found" msgid "Package not found"
msgstr "Пакет не найден" msgstr "Пакет не найден"
#: build.go:156 #: build.go:150
msgid "Nothing to build" msgid "Nothing to build"
msgstr "Нечего собирать" msgstr "Нечего собирать"
#: build.go:213 #: build.go:195
msgid "Error building package" msgid "Error building package"
msgstr "Ошибка при сборке пакета" msgstr "Ошибка при сборке пакета"
#: build.go:220 #: build.go:203
msgid "Package file already moved or removed, skipping"
msgstr "Файл пакета уже перемещён или удалён, пропускаем"
#: build.go:209
msgid "Error moving the package" msgid "Error moving the package"
msgstr "Ошибка при перемещении пакета" msgstr "Ошибка при перемещении пакета"
#: build.go:224 #: build.go:213
msgid "Done" msgid "Done"
msgstr "Сделано" msgstr "Сделано"
@@ -69,71 +73,132 @@ msgstr "Сделано"
msgid "Manage config" msgid "Manage config"
msgstr "Управление конфигурацией" msgstr "Управление конфигурацией"
#: config.go:48 #: config.go:50
msgid "Shows a list of commands or help for one command"
msgstr "Показывает список команд или справку по одной команде"
#: config.go:66
msgid "Show config" msgid "Show config"
msgstr "Показать конфигурацию" msgstr "Показать конфигурацию"
#: config.go:84 #: config.go:103
msgid "Set config value" msgid "Set config value"
msgstr "Установить значение в конфигурации" msgstr "Установить значение в конфигурации"
#: config.go:85 #: config.go:104
msgid "<key> <value>" msgid "<key> <value>"
msgstr "<ключ> <значение>" msgstr "<ключ> <значение>"
#: config.go:118 config.go:126 #: config.go:137 config.go:145 config.go:162
msgid "invalid boolean value for %s: %s" msgid "invalid boolean value for %s: %s"
msgstr "неверное булево значение для %s: %s" msgstr "неверное булево значение для %s: %s"
#: config.go:141 #: config.go:166
msgid "use 'repo add/remove' commands to manage repositories" msgid "use 'repo add/remove' commands to manage repositories"
msgstr "используйте команды 'repo add/remove' для управления репозиториями" msgstr "используйте команды 'repo add/remove' для управления репозиториями"
#: config.go:143 config.go:221 #: config.go:168 config.go:248
msgid "unknown config key: %s" msgid "unknown config key: %s"
msgstr "неизвестный ключ конфигурации: %s" msgstr "неизвестный ключ конфигурации: %s"
#: config.go:147 #: config.go:172
msgid "failed to save config" msgid "failed to save config"
msgstr "не удалось сохранить конфигурацию" msgstr "не удалось сохранить конфигурацию"
#: config.go:150 #: config.go:175
msgid "Successfully set %s = %s" msgid "Successfully set %s = %s"
msgstr "Успешно установлено %s = %s" msgstr "Успешно установлено %s = %s"
#: config.go:159 #: config.go:184
msgid "Get config value" msgid "Get config value"
msgstr "Получить значение из конфигурации" msgstr "Получить значение из конфигурации"
#: config.go:160 #: config.go:185
msgid "<key>" msgid "<key>"
msgstr "<ключ>" msgstr "<ключ>"
#: fix.go:39 #: fix.go:55
msgid "Attempt to fix problems with ALR" msgid "Attempt to fix problems with ALR"
msgstr "Попытка устранить проблемы с ALR" msgstr "Попытка устранить проблемы с ALR"
#: fix.go:60 #: fix.go:75
msgid "Clearing cache directory" msgid "Clearing cache and temporary directories"
msgstr "Очистка каталога кэша" msgstr "Очистка кэша и временных директорий"
#: fix.go:64 #: fix.go:82
msgid "Cache directory does not exist, will create it"
msgstr ""
#: fix.go:84
msgid "Unable to open cache directory" msgid "Unable to open cache directory"
msgstr "Невозможно открыть каталог кэша" msgstr "Невозможно открыть каталог кэша"
#: fix.go:70 #: fix.go:91
msgid "Unable to read cache directory contents" msgid "Unable to read cache directory contents"
msgstr "Невозможно прочитать содержимое каталога кэша" msgstr "Невозможно прочитать содержимое каталога кэша"
#: fix.go:82 #: fix.go:106
#, fuzzy
msgid "Unable to remove cache item (%s) as current user, trying with sudo"
msgstr ""
"Невозможно удалить временную директорию от текущего пользователя, попытка "
"через sudo"
#: fix.go:111
msgid "Unable to remove cache item (%s)" msgid "Unable to remove cache item (%s)"
msgstr "Невозможно удалить элемент кэша (%s)" msgstr "Невозможно удалить элемент кэша (%s)"
#: fix.go:86 #: fix.go:119
msgid "Clearing temporary directory"
msgstr "Очистка временной директории"
#: fix.go:126
msgid "Unable to remove temporary directory as current user, trying with sudo"
msgstr ""
"Невозможно удалить временную директорию от текущего пользователя, попытка "
"через sudo"
#: fix.go:129
#, fuzzy
msgid "Unable to remove temporary directory"
msgstr "Невозможно открыть каталог кэша"
#: fix.go:137
#, fuzzy
msgid "Unable to create temporary directory"
msgstr "Не удалось создать каталог конфигурации ALR"
#: fix.go:144
#, fuzzy
msgid "Unable to create download directory"
msgstr "Не удалось создать каталог конфигурации ALR"
#: fix.go:151
#, fuzzy
msgid "Unable to create packages directory"
msgstr "Не удалось создать каталог кэша пакетов"
#: fix.go:156
msgid "Fixing permissions on temporary files"
msgstr "Исправление прав доступа к временным файлам"
#: fix.go:164
msgid "Unable to fix file ownership"
msgstr ""
#: fix.go:169
msgid "Unable to fix file permissions"
msgstr ""
#: fix.go:174
msgid "Rebuilding cache" msgid "Rebuilding cache"
msgstr "Восстановление кэша" msgstr "Восстановление кэша"
#: fix.go:90 #: fix.go:177
msgid "Creating cache directory"
msgstr "Создание директории кэша"
#: fix.go:180
msgid "Unable to create new cache directory" msgid "Unable to create new cache directory"
msgstr "Не удалось создать новый каталог кэша" msgstr "Не удалось создать новый каталог кэша"
@@ -145,55 +210,69 @@ msgstr "Генерация скрипта ALR из шаблона"
msgid "Generate a ALR script for a pip module" msgid "Generate a ALR script for a pip module"
msgstr "Генерация скрипта ALR для модуля pip" msgstr "Генерация скрипта ALR для модуля pip"
#: gen.go:66
#, fuzzy
msgid "Generate a ALR script for an AUR package"
msgstr "Генерация скрипта ALR из шаблона"
#: gen.go:72
#, fuzzy
msgid "Name of the AUR package"
msgstr "Название нового репозитория"
#: gen.go:77
msgid "Version of the package (optional, uses latest if not specified)"
msgstr ""
#: helper.go:42 #: helper.go:42
msgid "List all the available helper commands" msgid "List all the available helper commands"
msgstr "Список всех доступных вспомогательных команды" msgstr "Список всех доступных вспомогательных команды"
#: helper.go:54 #: helper.go:69
msgid "Run a ALR helper command" msgid "Run a ALR helper command"
msgstr "Запустить вспомогательную команду ALR" msgstr "Запустить вспомогательную команду ALR"
#: helper.go:61 #: helper.go:76
msgid "The directory that the install commands will install to" msgid "The directory that the install commands will install to"
msgstr "Каталог, в который будут устанавливать команды установки" msgstr "Каталог, в который будут устанавливать команды установки"
#: helper.go:74 helper.go:75 #: helper.go:89 helper.go:90
msgid "No such helper command" msgid "No such helper command"
msgstr "Такой вспомогательной команды нет" msgstr "Такой вспомогательной команды нет"
#: helper.go:85 #: helper.go:100
msgid "Error parsing os-release file" msgid "Error parsing os-release file"
msgstr "Ошибка при разборе файла выпуска операционной системы" msgstr "Ошибка при разборе файла выпуска операционной системы"
#: info.go:42 #: info.go:41
msgid "Print information about a package" msgid "Print information about a package"
msgstr "Отобразить информацию о пакете" msgstr "Отобразить информацию о пакете"
#: info.go:47 #: info.go:46
msgid "Show all information, not just for the current distro" msgid "Show all information, not just for the current distro"
msgstr "Показывать всю информацию, не только для текущего дистрибутива" msgstr "Показывать всю информацию, не только для текущего дистрибутива"
#: info.go:68 #: info.go:64
msgid "Error getting packages" msgid "Error getting packages"
msgstr "Ошибка при получении пакетов" msgstr "Ошибка при получении пакетов"
#: info.go:83 #: info.go:77
msgid "Command info expected at least 1 argument, got %d" msgid "Command info expected at least 1 argument, got %d"
msgstr "Для команды info ожидался хотя бы 1 аргумент, получено %d" msgstr "Для команды info ожидался хотя бы 1 аргумент, получено %d"
#: info.go:104 #: info.go:98
msgid "Error finding packages" msgid "Error finding packages"
msgstr "Ошибка при поиске пакетов" msgstr "Ошибка при поиске пакетов"
#: info.go:118 #: info.go:112
msgid "Can't detect system language" msgid "Can't detect system language"
msgstr "Ошибка при определении языка системы" msgstr "Ошибка при определении языка системы"
#: info.go:134 #: info.go:128
msgid "Error resolving overrides" msgid "Error resolving overrides"
msgstr "Ошибка устранения переорпеделений" msgstr "Ошибка устранения переорпеделений"
#: info.go:143 #: info.go:137
msgid "Error encoding script variables" msgid "Error encoding script variables"
msgstr "Ошибка кодирования переменных скрита" msgstr "Ошибка кодирования переменных скрита"
@@ -205,43 +284,47 @@ msgstr "Установить новый пакет"
msgid "Command install expected at least 1 argument, got %d" msgid "Command install expected at least 1 argument, got %d"
msgstr "Для команды install ожидался хотя бы 1 аргумент, получено %d" msgstr "Для команды install ожидался хотя бы 1 аргумент, получено %d"
#: install.go:113 #: install.go:107
msgid "Error when installing the package" msgid "Error when installing the package"
msgstr "Ошибка при установке пакета" msgstr "Ошибка при установке пакета"
#: install.go:151 #: install.go:142
msgid "Remove an installed package" msgid "Remove an installed package"
msgstr "Удалить установленный пакет" msgstr "Удалить установленный пакет"
#: install.go:170 #: install.go:161
msgid "Error listing installed packages" msgid "Error listing installed packages"
msgstr "Ошибка при составлении списка установленных пакетов" msgstr "Ошибка при составлении списка установленных пакетов"
#: install.go:199 #: install.go:190
msgid "Command remove expected at least 1 argument, got %d" msgid "Command remove expected at least 1 argument, got %d"
msgstr "Для команды remove ожидался хотя бы 1 аргумент, получено %d" msgstr "Для команды remove ожидался хотя бы 1 аргумент, получено %d"
#: install.go:214 #: install.go:205
msgid "Error removing packages" msgid "Error removing packages"
msgstr "Ошибка при удалении пакетов" msgstr "Ошибка при удалении пакетов"
#: internal/build/build.go:351 #: internal/build/build.go:342 internal/build/build.go:653
msgid "Using cached package"
msgstr "Используется кешированный пакет"
#: internal/build/build.go:357
msgid "Building package" msgid "Building package"
msgstr "Сборка пакета" msgstr "Сборка пакета"
#: internal/build/build.go:380 #: internal/build/build.go:386
msgid "The checksums array must be the same length as sources" msgid "The checksums array must be the same length as sources"
msgstr "Массив контрольных сумм должен быть той же длины, что и источники" msgstr "Массив контрольных сумм должен быть той же длины, что и источники"
#: internal/build/build.go:422 #: internal/build/build.go:438
msgid "Downloading sources" msgid "Downloading sources"
msgstr "Скачивание источников" msgstr "Скачивание источников"
#: internal/build/build.go:468 #: internal/build/build.go:484
msgid "Would you like to remove the build dependencies?" msgid "Would you like to remove the build dependencies?"
msgstr "Хотели бы вы удалить зависимости сборки?" msgstr "Хотели бы вы удалить зависимости сборки?"
#: internal/build/build.go:546 #: internal/build/build.go:569
msgid "Installing dependencies" msgid "Installing dependencies"
msgstr "Установка зависимостей" msgstr "Установка зависимостей"
@@ -283,22 +366,71 @@ msgstr ""
msgid "Applying FireJail integration" msgid "Applying FireJail integration"
msgstr "Применение интеграции FireJail" msgstr "Применение интеграции FireJail"
#: internal/build/script_executor.go:145 #: internal/build/installer.go:99
msgid ""
"Package %s is installed with older version %s, will rebuild with version %s"
msgstr ""
"Пакет %s установлен с устаревшей версией %s, будет пересобран с версией %s"
#: internal/build/installer.go:102
msgid "Package %s is already installed with version %s, skipping build"
msgstr "Пакет %s уже установлен с версией %s, пропуск сборки"
#: internal/build/installer.go:104
msgid ""
"Package %s is installed with newer version %s (repo has %s), skipping build"
msgstr ""
"Пакет %s установлен с более новой версией %s (в репозитории %s), пропуск "
"сборки"
#: internal/build/script_executor.go:167
msgid "Building package metadata" msgid "Building package metadata"
msgstr "Сборка метаданных пакета" msgstr "Сборка метаданных пакета"
#: internal/build/script_executor.go:285 #: internal/build/script_executor.go:192
msgid "Creating package file"
msgstr "Создание файла пакета"
#: internal/build/script_executor.go:196
msgid "Failed to create package file"
msgstr "Не удалось создать файл пакета"
#: internal/build/script_executor.go:201
msgid "Packaging with nfpm"
msgstr "Упаковка с помощью nfpm"
#: internal/build/script_executor.go:204
msgid "Failed to create package"
msgstr "Не удалось создать пакет"
#: internal/build/script_executor.go:208
msgid "Package created successfully"
msgstr "Пакет успешно создан"
#: internal/build/script_executor.go:212
msgid "Package file not found after creation"
msgstr "Файл пакета не найден после создания"
#: internal/build/script_executor.go:215
msgid "Package file verified to exist"
msgstr "Наличие файла пакета подтверждено"
#: internal/build/script_executor.go:322
msgid "Executing prepare()" msgid "Executing prepare()"
msgstr "Выполнение prepare()" msgstr "Выполнение prepare()"
#: internal/build/script_executor.go:294 #: internal/build/script_executor.go:331
msgid "Executing build()" msgid "Executing build()"
msgstr "Выполнение build()" msgstr "Выполнение build()"
#: internal/build/script_executor.go:323 internal/build/script_executor.go:343 #: internal/build/script_executor.go:360 internal/build/script_executor.go:380
msgid "Executing %s()" msgid "Executing %s()"
msgstr "Выполнение %s()" msgstr "Выполнение %s()"
#: internal/cliutils/app_builder/builder.go:45
msgid "failed to close db"
msgstr "не удалось закрыть БД"
#: internal/cliutils/app_builder/builder.go:75 #: internal/cliutils/app_builder/builder.go:75
msgid "Error loading config" msgid "Error loading config"
msgstr "Ошибка при загрузке" msgstr "Ошибка при загрузке"
@@ -331,23 +463,25 @@ msgstr "Продолжить?"
msgid "User chose not to continue after reading script" msgid "User chose not to continue after reading script"
msgstr "Пользователь решил не продолжать после просмотра скрипта" msgstr "Пользователь решил не продолжать после просмотра скрипта"
#: internal/cliutils/prompt.go:111 #: internal/cliutils/prompt.go:123
msgid "Error prompting for choice of package" msgid "Error prompting for choice of package"
msgstr "Ошибка при запросе выбора пакета" msgstr "Ошибка при запросе выбора пакета"
#: internal/cliutils/prompt.go:135 #: internal/cliutils/prompt.go:175
msgid "Choose which package to %s" msgid "Choose which package to %s"
msgstr "Выберите, какой пакет использовать для %s" msgstr "Выберите, какой пакет использовать для %s"
#: internal/cliutils/prompt.go:156 #: internal/cliutils/prompt.go:196
msgid "Choose which optional package(s) to install" msgid "Choose which optional package(s) to install"
msgstr "Выберите, какой дополнительный пакет(ы) следует установить" msgstr "Выберите дополнительные пакеты для установки"
#: internal/cliutils/template.go:74 internal/cliutils/template.go:93 #: internal/cliutils/template.go:74 internal/cliutils/template.go:93
#: internal/cliutils/template.go:126
msgid "NAME" msgid "NAME"
msgstr "НАЗВАНИЕ" msgstr "НАЗВАНИЕ"
#: internal/cliutils/template.go:74 internal/cliutils/template.go:94 #: internal/cliutils/template.go:74 internal/cliutils/template.go:94
#: internal/cliutils/template.go:126
msgid "USAGE" msgid "USAGE"
msgstr "ИСПОЛЬЗОВАНИЕ" msgstr "ИСПОЛЬЗОВАНИЕ"
@@ -355,15 +489,17 @@ msgstr "ИСПОЛЬЗОВАНИЕ"
msgid "global options" msgid "global options"
msgstr "глобальные опции" msgstr "глобальные опции"
#: internal/cliutils/template.go:74 #: internal/cliutils/template.go:74 internal/cliutils/template.go:126
msgid "command" msgid "command"
msgstr "команда" msgstr "команда"
#: internal/cliutils/template.go:74 internal/cliutils/template.go:95 #: internal/cliutils/template.go:74 internal/cliutils/template.go:95
#: internal/cliutils/template.go:126
msgid "command options" msgid "command options"
msgstr "опции команды" msgstr "опции команды"
#: internal/cliutils/template.go:74 internal/cliutils/template.go:96 #: internal/cliutils/template.go:74 internal/cliutils/template.go:96
#: internal/cliutils/template.go:126
msgid "arguments" msgid "arguments"
msgstr "аргументы" msgstr "аргументы"
@@ -372,14 +508,15 @@ msgid "VERSION"
msgstr "ВЕРСИЯ" msgstr "ВЕРСИЯ"
#: internal/cliutils/template.go:74 internal/cliutils/template.go:98 #: internal/cliutils/template.go:74 internal/cliutils/template.go:98
#: internal/cliutils/template.go:126
msgid "DESCRIPTION" msgid "DESCRIPTION"
msgstr "ОПИСАНИЕ" msgstr "ОПИСАНИЕ"
#: internal/cliutils/template.go:74 #: internal/cliutils/template.go:74 internal/cliutils/template.go:126
msgid "AUTHOR" msgid "AUTHOR"
msgstr "АВТОР" msgstr "АВТОР"
#: internal/cliutils/template.go:74 #: internal/cliutils/template.go:74 internal/cliutils/template.go:126
msgid "COMMANDS" msgid "COMMANDS"
msgstr "КОМАНДЫ" msgstr "КОМАНДЫ"
@@ -387,7 +524,7 @@ msgstr "КОМАНДЫ"
msgid "GLOBAL OPTIONS" msgid "GLOBAL OPTIONS"
msgstr "ГЛОБАЛЬНЫЕ ОПЦИИ" msgstr "ГЛОБАЛЬНЫЕ ОПЦИИ"
#: internal/cliutils/template.go:74 #: internal/cliutils/template.go:74 internal/cliutils/template.go:126
msgid "COPYRIGHT" msgid "COPYRIGHT"
msgstr "АВТОРСКОЕ ПРАВО" msgstr "АВТОРСКОЕ ПРАВО"
@@ -396,6 +533,7 @@ msgid "CATEGORY"
msgstr "КАТЕГОРИЯ" msgstr "КАТЕГОРИЯ"
#: internal/cliutils/template.go:99 internal/cliutils/template.go:100 #: internal/cliutils/template.go:99 internal/cliutils/template.go:100
#: internal/cliutils/template.go:126
msgid "OPTIONS" msgid "OPTIONS"
msgstr "ПАРАМЕТРЫ" msgstr "ПАРАМЕТРЫ"
@@ -407,11 +545,15 @@ msgstr ""
"Эта команда устарела и будет удалена в будущем, используйте вместо нее " "Эта команда устарела и будет удалена в будущем, используйте вместо нее "
"\"%s\"!" "\"%s\"!"
#: internal/db/db.go:76 #: internal/db/db.go:67
msgid "Cache directory does not exist, creating it"
msgstr ""
#: internal/db/db.go:95
msgid "Database version mismatch; resetting" msgid "Database version mismatch; resetting"
msgstr "Несоответствие версий базы данных; сброс настроек" msgstr "Несоответствие версий базы данных; сброс настроек"
#: internal/db/db.go:82 #: internal/db/db.go:101
msgid "" msgid ""
"Database version does not exist. Run alr fix if something isn't working." "Database version does not exist. Run alr fix if something isn't working."
msgstr "" msgstr ""
@@ -449,43 +591,55 @@ msgstr ""
"Минимальная версия ALR для ALR-репозитория выше текущей версии. Попробуйте " "Минимальная версия ALR для ALR-репозитория выше текущей версии. Попробуйте "
"обновить ALR, если что-то не работает." "обновить ALR, если что-то не работает."
#: internal/utils/cmd.go:97 #: internal/repos/pull.go:423
msgid "Error on dropping capabilities" msgid "Failed to get deleted file from old commit"
msgstr "Ошибка при понижении привилегий" msgstr "Не удалось получить удалённый файл из старого коммита"
#: internal/utils/cmd.go:164 #: internal/repos/pull.go:429
msgid "You need to be a %s member to perform this action" msgid "Failed to read deleted file"
msgstr "Вы должны быть членом %s чтобы выполнить это" msgstr "Не удалось прочитать удалённый файл"
#: internal/utils/cmd.go:200 #: internal/repos/pull.go:448
msgid "Failed to get updated file from new commit"
msgstr "Не удалось получить обновлённый файл из нового коммита"
#: internal/repos/pull.go:454
msgid "Failed to read updated file"
msgstr "Не удалось прочитать обновлённый файл"
#: internal/repos/pull.go:508
msgid "No alr.sh files found in repository"
msgstr "Файлы alr.sh не найдены в репозитории"
#: internal/utils/cmd.go:54
msgid "You need to be root to perform this action" msgid "You need to be root to perform this action"
msgstr "Вы должны быть root чтобы выполнить это" msgstr "Вы должны быть root чтобы выполнить это"
#: list.go:45 #: list.go:44
msgid "List ALR repo packages" msgid "List ALR repo packages"
msgstr "Список пакетов репозитория ALR" msgstr "Список пакетов репозитория ALR"
#: list.go:59 #: list.go:58
msgid "Format output using a Go template" msgid "Format output using a Go template"
msgstr "Формат выходных данных с использованием шаблона Go" msgstr "Формат выходных данных с использованием шаблона Go"
#: list.go:91 #: list.go:87
msgid "Error getting packages for upgrade" msgid "Error getting packages for upgrade"
msgstr "Ошибка при получении пакетов для обновления" msgstr "Ошибка при получении пакетов для обновления"
#: list.go:94 #: list.go:90
msgid "No packages for upgrade" msgid "No packages for upgrade"
msgstr "Нет пакетов к обновлению" msgstr "Нет пакетов к обновлению"
#: list.go:104 list.go:201 #: list.go:100 list.go:197
msgid "Error parsing format template" msgid "Error parsing format template"
msgstr "Ошибка при разборе шаблона" msgstr "Ошибка при разборе шаблона"
#: list.go:110 list.go:205 #: list.go:106 list.go:201
msgid "Error executing template" msgid "Error executing template"
msgstr "Ошибка при выполнении шаблона" msgstr "Ошибка при выполнении шаблона"
#: list.go:164 #: list.go:160
msgid "Failed to parse release" msgid "Failed to parse release"
msgstr "Не удалось разобрать релиз" msgstr "Не удалось разобрать релиз"
@@ -493,19 +647,19 @@ msgstr "Не удалось разобрать релиз"
msgid "Print the current ALR version and exit" msgid "Print the current ALR version and exit"
msgstr "Показать текущую версию ALR и выйти" msgstr "Показать текущую версию ALR и выйти"
#: main.go:61 #: main.go:77
msgid "Arguments to be passed on to the package manager" msgid "Arguments to be passed on to the package manager"
msgstr "Аргументы, которые будут переданы менеджеру пакетов" msgstr "Аргументы, которые будут переданы менеджеру пакетов"
#: main.go:67 #: main.go:83
msgid "Enable interactive questions and prompts" msgid "Enable interactive questions and prompts"
msgstr "Включение интерактивных вопросов и запросов" msgstr "Включение интерактивных вопросов и запросов"
#: main.go:148 #: main.go:165
msgid "Show help" msgid "Show help"
msgstr "Показать справку" msgstr "Показать справку"
#: main.go:152 #: main.go:169
msgid "Error while running app" msgid "Error while running app"
msgstr "Ошибка при запуске приложения" msgstr "Ошибка при запуске приложения"
@@ -541,124 +695,124 @@ msgstr "Скачать все изменённые репозитории"
msgid "Manage repos" msgid "Manage repos"
msgstr "Управление репозиториями" msgstr "Управление репозиториями"
#: repo.go:56 repo.go:625 #: repo.go:74 repo.go:658
msgid "Remove an existing repository" msgid "Remove an existing repository"
msgstr "Удалить существующий репозиторий" msgstr "Удалить существующий репозиторий"
#: repo.go:58 repo.go:521 #: repo.go:76 repo.go:554
msgid "<name>" msgid "<name>"
msgstr "<имя>" msgstr "<имя>"
#: repo.go:103 repo.go:465 repo.go:568 #: repo.go:121 repo.go:498 repo.go:601
msgid "Repo \"%s\" does not exist" msgid "Repo \"%s\" does not exist"
msgstr "Репозитория \"%s\" не существует" msgstr "Репозиторий \"%s\" не существует"
#: repo.go:110 #: repo.go:128
msgid "Error removing repo directory" msgid "Error removing repo directory"
msgstr "Ошибка при удалении каталога репозитория" msgstr "Ошибка при удалении каталога репозитория"
#: repo.go:114 repo.go:195 repo.go:253 repo.go:316 repo.go:389 repo.go:504 #: repo.go:132 repo.go:210 repo.go:268 repo.go:331 repo.go:422 repo.go:537
#: repo.go:576 #: repo.go:609
msgid "Error saving config" msgid "Error saving config"
msgstr "Ошибка при сохранении конфигурации" msgstr "Ошибка при сохранении конфигурации"
#: repo.go:133 #: repo.go:148
msgid "Error removing packages from database" msgid "Error removing packages from database"
msgstr "Ошибка при удалении пакетов из базы данных" msgstr "Ошибка при удалении пакетов из базы данных"
#: repo.go:144 repo.go:595 #: repo.go:159 repo.go:628
msgid "Add a new repository" msgid "Add a new repository"
msgstr "Добавить новый репозиторий" msgstr "Добавить новый репозиторий"
#: repo.go:145 repo.go:270 repo.go:345 repo.go:402 #: repo.go:160 repo.go:285 repo.go:378 repo.go:435
msgid "<name> <url>" msgid "<name> <url>"
msgstr "<имя> <url>" msgstr "<имя> <url>"
#: repo.go:170 #: repo.go:185
msgid "Repo \"%s\" already exists" msgid "Repo \"%s\" already exists"
msgstr "Репозиторий \"%s\" уже существует" msgstr "Репозиторий \"%s\" уже существует"
#: repo.go:206 #: repo.go:221
msgid "Set the reference of the repository" msgid "Set the reference of the repository"
msgstr "Установить ссылку на версию репозитория" msgstr "Установить ссылку на версию репозитория"
#: repo.go:207 #: repo.go:222
msgid "<name> <ref>" msgid "<name> <ref>"
msgstr "<имя> <ссылкааерсию>" msgstr "<имя> <ссылкааерсию>"
#: repo.go:269 #: repo.go:284
msgid "Set the main url of the repository" msgid "Set the main url of the repository"
msgstr "Установить главный URL репозитория" msgstr "Установить главный URL репозитория"
#: repo.go:332 #: repo.go:347
msgid "Manage mirrors of repos" msgid "Manage mirrors of repos"
msgstr "Управление зеркалами репозитория" msgstr "Управление зеркалами репозитория"
#: repo.go:344 #: repo.go:377
msgid "Add a mirror URL to repository" msgid "Add a mirror URL to repository"
msgstr "Добавить зеркало репозитория" msgstr "Добавить зеркало репозитория"
#: repo.go:401 #: repo.go:434
msgid "Remove mirror from the repository" msgid "Remove mirror from the repository"
msgstr "Удалить зеркало из репозитория" msgstr "Удалить зеркало из репозитория"
#: repo.go:420 #: repo.go:453
msgid "Ignore if mirror does not exist" msgid "Ignore if mirror does not exist"
msgstr "Игнорировать, если зеркала не существует" msgstr "Игнорировать, если зеркала не существует"
#: repo.go:425 #: repo.go:458
msgid "Match partial URL (e.g., github.com instead of full URL)" msgid "Match partial URL (e.g., github.com instead of full URL)"
msgstr "Соответствует частичному URL (например, github.com вместо полного URL)" msgstr "Соответствует частичному URL (например, github.com вместо полного URL)"
#: repo.go:490 #: repo.go:523
msgid "No mirrors containing \"%s\" found in repo \"%s\"" msgid "No mirrors containing \"%s\" found in repo \"%s\""
msgstr "В репозитории \"%s\" не найдено зеркал, содержащих \"%s\"" msgstr "В репозитории \"%s\" не найдено зеркал, содержащих \"%s\""
#: repo.go:492 #: repo.go:525
msgid "URL \"%s\" does not exist in repo \"%s\"" msgid "URL \"%s\" does not exist in repo \"%s\""
msgstr "URL \"%s\" не существует в репозитории \"%s\"" msgstr "URL \"%s\" не существует в репозитории \"%s\""
#: repo.go:508 repo.go:580 #: repo.go:541 repo.go:613
msgid "Removed %d mirrors from repo \"%s\"\n" msgid "Removed %d mirrors from repo \"%s\"\n"
msgstr "Удалены зеркала %d из репозитория \"%s\"\n" msgstr "Удалено %d зеркал из репозитория \"%s\"\n"
#: repo.go:520 #: repo.go:553
msgid "Remove all mirrors from the repository" msgid "Remove all mirrors from the repository"
msgstr "Удалить все зеркала из репозитория" msgstr "Удалить все зеркала из репозитория"
#: repo.go:602 #: repo.go:635
msgid "Name of the new repo" msgid "Name of the new repo"
msgstr "Название нового репозитория" msgstr "Название нового репозитория"
#: repo.go:608 #: repo.go:641
msgid "URL of the new repo" msgid "URL of the new repo"
msgstr "URL-адрес нового репозитория" msgstr "URL-адрес нового репозитория"
#: repo.go:632 #: repo.go:665
msgid "Name of the repo to be deleted" msgid "Name of the repo to be deleted"
msgstr "Название репозитория удалён" msgstr "Название репозитория для удаления"
#: search.go:40 #: search.go:39
msgid "Search packages" msgid "Search packages"
msgstr "Поиск пакетов" msgstr "Поиск пакетов"
#: search.go:51 #: search.go:50
msgid "Search by name" msgid "Search by name"
msgstr "Искать по имени" msgstr "Искать по имени"
#: search.go:56 #: search.go:55
msgid "Search by description" msgid "Search by description"
msgstr "Искать по описанию" msgstr "Искать по описанию"
#: search.go:61 #: search.go:60
msgid "Search by repository" msgid "Search by repository"
msgstr "Искать по репозиторию" msgstr "Искать по репозиторию"
#: search.go:66 #: search.go:65
msgid "Search by provides" msgid "Search by provides"
msgstr "Иcкать по provides" msgstr "Искать по provides"
#: search.go:130 #: search.go:126
msgid "Error while executing search" msgid "Error while executing search"
msgstr "Ошибка при выполнении поиска" msgstr "Ошибка при выполнении поиска"
@@ -666,13 +820,35 @@ msgstr "Ошибка при выполнении поиска"
msgid "Upgrade all installed packages" msgid "Upgrade all installed packages"
msgstr "Обновить все установленные пакеты" msgstr "Обновить все установленные пакеты"
#: upgrade.go:106 upgrade.go:123 #: upgrade.go:89
msgid "Updating system packages..."
msgstr "Обновление системных пакетов..."
#: upgrade.go:95
#, fuzzy
msgid "Error updating system packages"
msgstr "Обновление системных пакетов..."
#: upgrade.go:97
msgid "System packages updated successfully"
msgstr "Системные пакеты успешно обновлены"
#: upgrade.go:113 upgrade.go:130
msgid "Error checking for updates" msgid "Error checking for updates"
msgstr "Ошибка при проверке обновлений" msgstr "Ошибка при проверке обновлений"
#: upgrade.go:126 #: upgrade.go:133
msgid "There is nothing to do." msgid "There is nothing to do."
msgstr "Здесь нечего делать." msgstr "Действия не требуются."
#~ msgid "Clearing cache directory"
#~ msgstr "Очистка каталога кэша"
#~ msgid "Error on dropping capabilities"
#~ msgstr "Ошибка при понижении привилегий"
#~ msgid "You need to be a %s member to perform this action"
#~ msgstr "Вы должны быть членом %s чтобы выполнить это"
#, fuzzy #, fuzzy
#~ msgid "Failed to clear contents of cache directory" #~ msgid "Failed to clear contents of cache directory"
@@ -692,13 +868,6 @@ msgstr "Здесь нечего делать."
#~ msgid "Error mounting" #~ msgid "Error mounting"
#~ msgstr "Ошибка при кодировании конфигурации" #~ msgstr "Ошибка при кодировании конфигурации"
#, fuzzy
#~ msgid "Unable to create config directory"
#~ msgstr "Не удалось создать каталог конфигурации ALR"
#~ msgid "Unable to create package cache directory"
#~ msgstr "Не удалось создать каталог кэша пакетов"
#~ msgid "" #~ msgid ""
#~ "Running ALR as root is forbidden as it may cause catastrophic damage to " #~ "Running ALR as root is forbidden as it may cause catastrophic damage to "
#~ "your system" #~ "your system"

View File

@@ -19,10 +19,9 @@ package utils
import ( import (
"fmt" "fmt"
"os" "os"
"os/exec"
"os/user" "os/user"
"strings"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/fsutils"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
@@ -31,79 +30,15 @@ func NoNewPrivs() error {
} }
// EnsureTempDirWithRootOwner создает каталог в /tmp/alr или /var/cache/alr с правами для привилегированной группы // EnsureTempDirWithRootOwner создает каталог в /tmp/alr или /var/cache/alr с правами для привилегированной группы
// Все каталоги в /tmp/alr и /var/cache/alr принадлежат root:привилегированная_группа с правами 2775 // Обёртка для обратной совместимости, делегирует вызов в fsutils
// Для других каталогов использует стандартные права
func EnsureTempDirWithRootOwner(path string, mode os.FileMode) error { func EnsureTempDirWithRootOwner(path string, mode os.FileMode) error {
needsElevation := strings.HasPrefix(path, "/tmp/alr") || strings.HasPrefix(path, "/var/cache/alr") return fsutils.EnsureTempDirWithRootOwner(path, mode)
}
if needsElevation { // GetPrivilegedGroup возвращает привилегированную группу для текущей системы
// В CI или если мы уже root, не нужно использовать sudo // Обёртка для обратной совместимости, делегирует вызов в fsutils
isRoot := os.Geteuid() == 0 func GetPrivilegedGroup() string {
isCI := os.Getenv("CI") == "true" return fsutils.GetPrivilegedGroup()
// В CI создаем директории с обычными правами
if isCI {
err := os.MkdirAll(path, mode)
if err != nil {
return err
}
// В CI не используем группу wheel и не меняем права
// Устанавливаем базовые права 777 для временных каталогов
chmodCmd := exec.Command("chmod", "777", path)
chmodCmd.Run() // Игнорируем ошибки
return nil
}
// Для обычной работы устанавливаем права и привилегированную группу
permissions := "2775"
group := GetPrivilegedGroup()
var mkdirCmd, chmodCmd, chownCmd *exec.Cmd
if isRoot {
// Выполняем команды напрямую без sudo
mkdirCmd = exec.Command("mkdir", "-p", path)
chmodCmd = exec.Command("chmod", permissions, path)
chownCmd = exec.Command("chown", "root:"+group, path)
} else {
// Используем sudo для всех операций с привилегированными каталогами
mkdirCmd = exec.Command("sudo", "mkdir", "-p", path)
chmodCmd = exec.Command("sudo", "chmod", permissions, path)
chownCmd = exec.Command("sudo", "chown", "root:"+group, path)
}
// Создаем директорию через sudo если нужно
err := mkdirCmd.Run()
if err != nil {
// Игнорируем ошибку если директория уже существует
if !isRoot {
// Проверяем существует ли директория
if _, statErr := os.Stat(path); statErr != nil {
return fmt.Errorf("не удалось создать директорию %s: %w", path, err)
}
}
}
// Устанавливаем права с setgid битом для наследования группы
err = chmodCmd.Run()
if err != nil {
if !isRoot {
return fmt.Errorf("не удалось установить права на %s: %w", path, err)
}
}
// Устанавливаем владельца root:группа
err = chownCmd.Run()
if err != nil {
if !isRoot {
return fmt.Errorf("не удалось установить владельца на %s: %w", path, err)
}
}
return nil
}
// Для остальных каталогов обычное создание
return os.MkdirAll(path, mode)
} }
// IsUserInGroup проверяет, состоит ли пользователь в указанной группе // IsUserInGroup проверяет, состоит ли пользователь в указанной группе
@@ -149,7 +84,7 @@ func CheckUserPrivileges() error {
return fmt.Errorf("не удалось получить информацию о текущем пользователе: %w", err) return fmt.Errorf("не удалось получить информацию о текущем пользователе: %w", err)
} }
privilegedGroup := GetPrivilegedGroup() privilegedGroup := fsutils.GetPrivilegedGroup()
// Проверяем членство в привилегированной группе // Проверяем членство в привилегированной группе
if !IsUserInGroup(currentUser.Username, privilegedGroup) { if !IsUserInGroup(currentUser.Username, privilegedGroup) {

18
main.go
View File

@@ -50,6 +50,22 @@ func VersionCmd() *cli.Command {
} }
} }
func HelpCmd() *cli.Command {
return &cli.Command{
Name: "help",
Aliases: []string{"h"},
Usage: gotext.Get("Shows a list of commands or help for one command"),
ArgsUsage: "[command]",
Action: func(cCtx *cli.Context) error {
args := cCtx.Args()
if args.Present() {
return cli.ShowCommandHelp(cCtx, args.First())
}
return cli.ShowAppHelp(cCtx)
},
}
}
func GetApp() *cli.App { func GetApp() *cli.App {
return &cli.App{ return &cli.App{
Name: "alr", Name: "alr",
@@ -88,6 +104,7 @@ func GetApp() *cli.App {
InternalBuildCmd(), InternalBuildCmd(),
InternalInstallCmd(), InternalInstallCmd(),
InternalReposCmd(), InternalReposCmd(),
HelpCmd(),
}, },
Before: func(c *cli.Context) error { Before: func(c *cli.Context) error {
if trimmed := strings.TrimSpace(c.String("pm-args")); trimmed != "" { if trimmed := strings.TrimSpace(c.String("pm-args")); trimmed != "" {
@@ -144,6 +161,7 @@ func main() {
// Make the application more internationalized // Make the application more internationalized
cli.AppHelpTemplate = cliutils.GetAppCliTemplate() cli.AppHelpTemplate = cliutils.GetAppCliTemplate()
cli.CommandHelpTemplate = cliutils.GetCommandHelpTemplate() cli.CommandHelpTemplate = cliutils.GetCommandHelpTemplate()
cli.SubcommandHelpTemplate = cliutils.GetSubcommandHelpTemplate()
cli.HelpFlag.(*cli.BoolFlag).Usage = gotext.Get("Show help") cli.HelpFlag.(*cli.BoolFlag).Usage = gotext.Get("Show help")
err = app.RunContext(ctx, os.Args) err = app.RunContext(ctx, os.Args)

View File

@@ -1,19 +1,3 @@
// ALR - Any Linux Repository
// Copyright (C) 2025 The ALR Authors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// DO NOT EDIT MANUALLY. This file is generated. // DO NOT EDIT MANUALLY. This file is generated.
package alrsh package alrsh

View File

@@ -27,9 +27,9 @@ import (
// createDir создает директорию с правильными правами для production // createDir создает директорию с правильными правами для production
func createDir(itemPath string, mode os.FileMode) error { func createDir(itemPath string, mode os.FileMode) error {
// Используем специальную функцию для создания каталогов с setgid битом только для /tmp/alr // Используем специальную функцию для создания каталогов с setgid битом только для /tmp/alr/ и /var/cache/alr/
// В остальных случаях используем обычное создание директории // Проверяем с слешем в конце, чтобы исключить тестовые директории вроде /tmp/alr-test-XXX
if strings.HasPrefix(itemPath, "/tmp/alr") { if strings.HasPrefix(itemPath, "/tmp/alr/") || strings.HasPrefix(itemPath, "/var/cache/alr/") {
return utils.EnsureTempDirWithRootOwner(itemPath, mode) return utils.EnsureTempDirWithRootOwner(itemPath, mode)
} else { } else {
return os.MkdirAll(itemPath, mode) return os.MkdirAll(itemPath, mode)

36
repo.go
View File

@@ -46,6 +46,24 @@ func RepoCmd() *cli.Command {
SetRepoRefCmd(), SetRepoRefCmd(),
RepoMirrorCmd(), RepoMirrorCmd(),
SetUrlCmd(), SetUrlCmd(),
RepoHelpCmd(),
},
}
}
func RepoHelpCmd() *cli.Command {
return &cli.Command{
Name: "help",
Aliases: []string{"h"},
Usage: gotext.Get("Shows a list of commands or help for one command"),
ArgsUsage: "[command]",
Action: func(cCtx *cli.Context) error {
args := cCtx.Args()
if args.Present() {
return cli.ShowCommandHelp(cCtx, args.First())
}
cli.ShowSubcommandHelp(cCtx)
return nil
}, },
} }
} }
@@ -331,6 +349,24 @@ func RepoMirrorCmd() *cli.Command {
AddMirror(), AddMirror(),
RemoveMirror(), RemoveMirror(),
ClearMirrors(), ClearMirrors(),
MirrorHelpCmd(),
},
}
}
func MirrorHelpCmd() *cli.Command {
return &cli.Command{
Name: "help",
Aliases: []string{"h"},
Usage: gotext.Get("Shows a list of commands or help for one command"),
ArgsUsage: "[command]",
Action: func(cCtx *cli.Context) error {
args := cCtx.Args()
if args.Present() {
return cli.ShowCommandHelp(cCtx, args.First())
}
cli.ShowSubcommandHelp(cCtx)
return nil
}, },
} }
} }

View File

@@ -26,7 +26,7 @@ import (
"github.com/leonelquinteros/gotext" "github.com/leonelquinteros/gotext"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"go.elara.ws/vercmp" "gitea.plemya-x.ru/xpamych/vercmp"
"golang.org/x/exp/maps" "golang.org/x/exp/maps"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/build" "gitea.plemya-x.ru/Plemya-x/ALR/internal/build"
@@ -114,7 +114,7 @@ func UpgradeCmd() *cli.Command {
} }
if len(updates) > 0 { if len(updates) > 0 {
err = builder.InstallALRPackages( _, err = builder.InstallPkgs(
ctx, ctx,
&build.BuildArgs{ &build.BuildArgs{
Opts: &types.BuildOpts{ Opts: &types.BuildOpts{
@@ -124,7 +124,7 @@ func UpgradeCmd() *cli.Command {
Info: deps.Info, Info: deps.Info,
PkgFormat_: build.GetPkgFormat(deps.Manager), PkgFormat_: build.GetPkgFormat(deps.Manager),
}, },
mapUptatesInfoToPackages(updates), mapUpdatesToPackageNames(updates),
) )
if err != nil { if err != nil {
return cliutils.FormatCliExit(gotext.Get("Error checking for updates"), err) return cliutils.FormatCliExit(gotext.Get("Error checking for updates"), err)
@@ -138,12 +138,19 @@ func UpgradeCmd() *cli.Command {
} }
} }
func mapUptatesInfoToPackages(updates []UpdateInfo) []alrsh.Package { func mapUpdatesToPackageNames(updates []UpdateInfo) []string {
var pkgs []alrsh.Package seen := make(map[string]bool)
var pkgNames []string
for _, info := range updates { for _, info := range updates {
pkgs = append(pkgs, *info.Package) fullName := fmt.Sprintf("%s+%s", info.Package.Name, info.Package.Repository)
if !seen[fullName] {
seen[fullName] = true
pkgNames = append(pkgNames, fullName)
}
} }
return pkgs
return pkgNames
} }
type UpdateInfo struct { type UpdateInfo struct {