6 Commits

Author SHA1 Message Date
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
cf804ec66b Исправлена проблема с перемещением готового пакета из временной дирректории сборки (в случае зависимости)
All checks were successful
Pre-commit / pre-commit (push) Successful in 5m12s
Create Release / changelog (push) Successful in 3m6s
2025-09-21 17:50:31 +03:00
6773d51caf Добавление функций обработки files
All checks were successful
Pre-commit / pre-commit (push) Successful in 5m8s
Create Release / changelog (push) Successful in 3m5s
2025-09-21 16:42:04 +03:00
4a616f2137 Исправление функционала создания дирректорий для работы ALR
All checks were successful
Pre-commit / pre-commit (push) Successful in 5m23s
Create Release / changelog (push) Successful in 3m3s
2025-09-21 16:21:23 +03:00
9efebbc02a Исправление функционала создания дирректорий для работы ALR
All checks were successful
Pre-commit / pre-commit (push) Successful in 5m11s
Create Release / changelog (push) Successful in 3m11s
2025-09-21 15:31:51 +03:00
13 changed files with 584 additions and 61 deletions

4
.gitignore vendored
View File

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

View File

@@ -197,6 +197,13 @@ func BuildCmd() *cli.Command {
for _, pkg := range res { for _, pkg := range res {
name := filepath.Base(pkg.Path) name := filepath.Base(pkg.Path)
// Проверяем, существует ли файл перед перемещением
if _, err := os.Stat(pkg.Path); os.IsNotExist(err) {
slog.Info("Package file already moved or removed, skipping", "path", pkg.Path)
continue
}
err = osutils.Move(pkg.Path, filepath.Join(wd, name)) err = osutils.Move(pkg.Path, filepath.Join(wd, name))
if err != nil { if err != nil {
return cliutils.FormatCliExit(gotext.Get("Error moving the package"), err) return cliutils.FormatCliExit(gotext.Get("Error moving the package"), err)

View File

@@ -45,17 +45,17 @@ func TestE2EIssue130Install(t *testing.T) {
) )
runMatrixSuite( runMatrixSuite(
t, t,
"alr install {package}+alr-{repo}", "alr install {package}+{repo}",
COMMON_SYSTEMS, COMMON_SYSTEMS,
func(t *testing.T, r capytest.Runner) { func(t *testing.T, r capytest.Runner) {
t.Parallel() t.Parallel()
defaultPrepare(t, r) defaultPrepare(t, r)
r.Command("sudo", "alr", "in", fmt.Sprintf("foo-pkg+alr-%s", REPO_NAME_FOR_E2E_TESTS)). r.Command("sudo", "alr", "in", fmt.Sprintf("foo-pkg+%s", REPO_NAME_FOR_E2E_TESTS)).
ExpectSuccess(). ExpectSuccess().
Run(t) Run(t)
r.Command("sudo", "alr", "in", fmt.Sprintf("bar-pkg+alr-%s", "NOT_REPO_NAME_FOR_E2E_TESTS")). r.Command("sudo", "alr", "in", fmt.Sprintf("bar-pkg+%s", "NOT_REPO_NAME_FOR_E2E_TESTS")).
ExpectFailure(). ExpectFailure().
Run(t) Run(t)
}, },

34
fix.go
View File

@@ -131,22 +131,22 @@ func FixCmd() *cli.Command {
} }
} }
// Создаем базовый каталог /tmp/alr с владельцем root:wheel и правами 775 // Создаем базовый каталог /tmp/alr с владельцем root:wheel и правами 2775
err = utils.EnsureTempDirWithRootOwner(tmpDir, 0o775) err = utils.EnsureTempDirWithRootOwner(tmpDir, 0o2775)
if err != nil { if err != nil {
slog.Warn(gotext.Get("Unable to create temporary directory"), "error", err) slog.Warn(gotext.Get("Unable to create temporary directory"), "error", err)
} }
// Создаем каталог dl с правами для группы wheel // Создаем каталог dl с правами для группы wheel
dlDir := filepath.Join(tmpDir, "dl") dlDir := filepath.Join(tmpDir, "dl")
err = utils.EnsureTempDirWithRootOwner(dlDir, 0o775) err = utils.EnsureTempDirWithRootOwner(dlDir, 0o2775)
if err != nil { if err != nil {
slog.Warn(gotext.Get("Unable to create download directory"), "error", err) slog.Warn(gotext.Get("Unable to create download directory"), "error", err)
} }
// Создаем каталог pkgs с правами для группы wheel // Создаем каталог pkgs с правами для группы wheel
pkgsDir := filepath.Join(tmpDir, "pkgs") pkgsDir := filepath.Join(tmpDir, "pkgs")
err = utils.EnsureTempDirWithRootOwner(pkgsDir, 0o775) err = utils.EnsureTempDirWithRootOwner(pkgsDir, 0o2775)
if err != nil { if err != nil {
slog.Warn(gotext.Get("Unable to create packages directory"), "error", err) slog.Warn(gotext.Get("Unable to create packages directory"), "error", err)
} }
@@ -158,7 +158,8 @@ func FixCmd() *cli.Command {
// Проверяем, есть ли файлы в директории // Проверяем, есть ли файлы в директории
entries, err := os.ReadDir(tmpDir) entries, err := os.ReadDir(tmpDir)
if err == nil && len(entries) > 0 { if err == nil && len(entries) > 0 {
fixCmd := execWithPrivileges("chown", "-R", "root:wheel", tmpDir) group := utils.GetPrivilegedGroup()
fixCmd := execWithPrivileges("chown", "-R", "root:"+group, tmpDir)
if fixErr := fixCmd.Run(); fixErr != nil { if fixErr := fixCmd.Run(); fixErr != nil {
slog.Warn(gotext.Get("Unable to fix file ownership"), "error", fixErr) slog.Warn(gotext.Get("Unable to fix file ownership"), "error", fixErr)
} }
@@ -172,26 +173,11 @@ func FixCmd() *cli.Command {
slog.Info(gotext.Get("Rebuilding cache")) slog.Info(gotext.Get("Rebuilding cache"))
// Пробуем создать директорию кэша // Создаем директорию кэша с правильными правами
err = os.MkdirAll(paths.CacheDir, 0o775) slog.Info(gotext.Get("Creating cache directory"))
err = utils.EnsureTempDirWithRootOwner(paths.CacheDir, 0o2775)
if err != nil { if err != nil {
// Если не получилось, пробуем через sudo с правильными правами для группы wheel return cliutils.FormatCliExit(gotext.Get("Unable to create new cache directory"), err)
slog.Info(gotext.Get("Creating cache directory with sudo"))
sudoCmd := execWithPrivileges("mkdir", "-p", paths.CacheDir)
if sudoErr := sudoCmd.Run(); sudoErr != nil {
return cliutils.FormatCliExit(gotext.Get("Unable to create new cache directory"), err)
}
// Устанавливаем права 775 и группу wheel
chmodCmd := execWithPrivileges("chmod", "775", paths.CacheDir)
if chmodErr := chmodCmd.Run(); chmodErr != nil {
return cliutils.FormatCliExit(gotext.Get("Unable to set cache directory permissions"), chmodErr)
}
chgrpCmd := execWithPrivileges("chgrp", "wheel", paths.CacheDir)
if chgrpErr := chgrpCmd.Run(); chgrpErr != nil {
return cliutils.FormatCliExit(gotext.Get("Unable to set cache directory group"), chgrpErr)
}
} }
deps, err = appbuilder. deps, err = appbuilder.

View File

@@ -167,15 +167,30 @@ func (e *LocalScriptExecutor) ExecuteSecondPass(
pkgName := packager.ConventionalFileName(pkgInfo) // Получаем имя файла пакета pkgName := packager.ConventionalFileName(pkgInfo) // Получаем имя файла пакета
pkgPath := filepath.Join(dirs.BaseDir, pkgName) // Определяем путь к пакету pkgPath := filepath.Join(dirs.BaseDir, pkgName) // Определяем путь к пакету
slog.Info("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)
return nil, err
}
defer pkgFile.Close()
slog.Info("Packaging with nfpm", "format", pkgFormat)
err = packager.Package(pkgInfo, pkgFile)
if err != nil {
slog.Error("Failed to create package", "path", pkgPath, "error", err)
return nil, err return nil, err
} }
err = packager.Package(pkgInfo, pkgFile) slog.Info("Package created successfully", "path", pkgPath)
if err != nil {
// Проверяем, что файл действительно существует
if _, err := os.Stat(pkgPath); err != nil {
slog.Error("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)
builtDeps = append(builtDeps, &BuiltDep{ builtDeps = append(builtDeps, &BuiltDep{
Name: vars.Name, Name: vars.Name,

View File

@@ -49,20 +49,29 @@ import (
// Функция prepareDirs подготавливает директории для сборки. // Функция prepareDirs подготавливает директории для сборки.
func prepareDirs(dirs types.Directories) error { func prepareDirs(dirs types.Directories) error {
// Пробуем удалить базовую директорию, если она существует // Удаляем только директории источников и упаковки, не трогаем файлы пакетов в BaseDir
err := os.RemoveAll(dirs.BaseDir) err := os.RemoveAll(dirs.SrcDir)
if err != nil { if err != nil {
// Если не можем удалить (например, принадлежит root), логируем и продолжаем slog.Debug("Failed to remove src directory", "path", dirs.SrcDir, "error", err)
// Новые директории будут созданы или перезаписаны
slog.Debug("Failed to remove base directory", "path", dirs.BaseDir, "error", err)
} }
err = os.RemoveAll(dirs.PkgDir)
if err != nil {
slog.Debug("Failed to remove pkg directory", "path", dirs.PkgDir, "error", err)
}
// Создаем базовую директорию для пакета с setgid битом
err = utils.EnsureTempDirWithRootOwner(dirs.BaseDir, 0o2775)
if err != nil {
return err
}
// Создаем директории с правильным владельцем для /tmp/alr с setgid битом // Создаем директории с правильным владельцем для /tmp/alr с setgid битом
err = utils.EnsureTempDirWithRootOwner(dirs.SrcDir, 0o2775) err = utils.EnsureTempDirWithRootOwner(dirs.SrcDir, 0o2775)
if err != nil { if err != nil {
return err return err
} }
// Создаем директорию для пакетов с setgid битом // Создаем директорию для пакетов с setgid битом
return utils.EnsureTempDirWithRootOwner(dirs.PkgDir, 0o2775) return utils.EnsureTempDirWithRootOwner(dirs.PkgDir, 0o2775)
} }
@@ -169,7 +178,7 @@ func normalizeContents(contents []*files.Content) {
} }
} }
var RegexpALRPackageName = regexp.MustCompile(`^(?P<package>[^+]+)\+alr-(?P<repo>.+)$`) var RegexpALRPackageName = regexp.MustCompile(`^(?P<package>[^+]+)\+(?P<repo>.+)$`)
func getBasePkgInfo(vars *alrsh.Package, input interface { func getBasePkgInfo(vars *alrsh.Package, input interface {
RepositoryProvider RepositoryProvider
@@ -177,12 +186,8 @@ func getBasePkgInfo(vars *alrsh.Package, input interface {
}, },
) *nfpm.Info { ) *nfpm.Info {
repo := input.Repository() repo := input.Repository()
// Избегаем дублирования "alr-" префикса
if strings.HasPrefix(repo, "alr-") {
repo = repo[4:] // убираем "alr-" префикс
}
return &nfpm.Info{ return &nfpm.Info{
Name: fmt.Sprintf("%s+alr-%s", vars.Name, repo), Name: fmt.Sprintf("%s+%s", vars.Name, repo),
Arch: cpu.Arch(), Arch: cpu.Arch(),
Version: vars.Version, Version: vars.Version,
Release: overrides.ReleasePlatformSpecific(vars.Release, input.OSRelease()), Release: overrides.ReleasePlatformSpecific(vars.Release, input.OSRelease()),

View File

@@ -0,0 +1,158 @@
// 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 (
"testing"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
)
type mockInput struct {
repo string
osInfo *distro.OSRelease
}
func (m *mockInput) Repository() string {
return m.repo
}
func (m *mockInput) OSRelease() *distro.OSRelease {
return m.osInfo
}
func TestGetBasePkgInfo(t *testing.T) {
tests := []struct {
name string
packageName string
repoName string
expectedName string
}{
{
name: "обычный репозиторий",
packageName: "test-package",
repoName: "default",
expectedName: "test-package+default",
},
{
name: "репозиторий с alr- префиксом",
packageName: "test-package",
repoName: "alr-default",
expectedName: "test-package+alr-default",
},
{
name: "репозиторий с двойным alr- префиксом",
packageName: "test-package",
repoName: "alr-alr-repo",
expectedName: "test-package+alr-alr-repo",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
pkg := &alrsh.Package{
Name: tt.packageName,
Version: "1.0.0",
Release: 1,
}
input := &mockInput{
repo: tt.repoName,
osInfo: &distro.OSRelease{
ID: "test",
},
}
info := getBasePkgInfo(pkg, input)
if info.Name != tt.expectedName {
t.Errorf("getBasePkgInfo() имя пакета = %v, ожидается %v", info.Name, tt.expectedName)
}
})
}
}
func TestRegexpALRPackageName(t *testing.T) {
tests := []struct {
name string
packageName string
expectedPkg string
expectedRepo string
shouldMatch bool
}{
{
name: "новый формат - обычный репозиторий",
packageName: "test-package+default",
expectedPkg: "test-package",
expectedRepo: "default",
shouldMatch: true,
},
{
name: "новый формат - alr-default репозиторий",
packageName: "test-package+alr-default",
expectedPkg: "test-package",
expectedRepo: "alr-default",
shouldMatch: true,
},
{
name: "новый формат - двойной alr- префикс",
packageName: "test-package+alr-alr-repo",
expectedPkg: "test-package",
expectedRepo: "alr-alr-repo",
shouldMatch: true,
},
{
name: "некорректный формат - без плюса",
packageName: "test-package",
shouldMatch: false,
},
{
name: "некорректный формат - пустое имя пакета",
packageName: "+repo",
shouldMatch: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
matches := RegexpALRPackageName.FindStringSubmatch(tt.packageName)
if tt.shouldMatch {
if matches == nil {
t.Errorf("RegexpALRPackageName должен найти совпадение для %q", tt.packageName)
return
}
packageName := matches[RegexpALRPackageName.SubexpIndex("package")]
repoName := matches[RegexpALRPackageName.SubexpIndex("repo")]
if packageName != tt.expectedPkg {
t.Errorf("RegexpALRPackageName извлеченное имя пакета = %v, ожидается %v", packageName, tt.expectedPkg)
}
if repoName != tt.expectedRepo {
t.Errorf("RegexpALRPackageName извлеченное имя репозитория = %v, ожидается %v", repoName, tt.expectedRepo)
}
} else {
if matches != nil {
t.Errorf("RegexpALRPackageName не должен найти совпадение для %q", tt.packageName)
}
}
})
}
}

View File

@@ -62,11 +62,9 @@ func (d *Database) Connect() error {
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) {
// Директория не существует - пытаемся создать // Директория не существует - не пытаемся создать
if mkErr := os.MkdirAll(dbDir, 0775); mkErr != nil { // Пользователь должен использовать alr fix для создания системных каталогов
// Не смогли создать - вернём ошибку, пользователь должен использовать alr fix return fmt.Errorf("cache directory does not exist, please run 'sudo alr fix' to create it")
return fmt.Errorf("cache directory does not exist, please run 'alr fix' to create it: %w", mkErr)
}
} else { } else {
return fmt.Errorf("failed to check database directory: %w", err) return fmt.Errorf("failed to check database directory: %w", err)
} }

View File

@@ -47,9 +47,9 @@ func (rs *Repos) FindPkgs(ctx context.Context, pkgs []string) (map[string][]alrs
name := parts[1] name := parts[1]
result, err = rs.db.GetPkgs(ctx, "name = ? AND repository = ?", name, repo) result, err = rs.db.GetPkgs(ctx, "name = ? AND repository = ?", name, repo)
case strings.Contains(pkgName, "+alr-"): case strings.Contains(pkgName, "+"):
// pkg+alr-repo // pkg+repo
parts := strings.SplitN(pkgName, "+alr-", 2) parts := strings.SplitN(pkgName, "+", 2)
name := parts[0] name := parts[0]
repo := parts[1] repo := parts[1]
result, err = rs.db.GetPkgs(ctx, "name = ? AND repository = ?", name, repo) result, err = rs.db.GetPkgs(ctx, "name = ? AND repository = ?", name, repo)

View File

@@ -177,3 +177,333 @@ func filesFindCmd(hc interp.HandlerContext, cmd string, args []string) error {
return outputFiles(hc, foundFiles) return outputFiles(hc, foundFiles)
} }
func filesFindBinCmd(hc interp.HandlerContext, cmd string, args []string) error {
namePattern := "*"
if len(args) > 0 {
namePattern = args[0]
}
binPath := "./usr/bin/"
realPath := path.Join(hc.Dir, binPath)
if err := validateDir(realPath, "files-find-bin"); err != nil {
return err
}
var binFiles []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
}
binFiles = append(binFiles, relPath)
}
return nil
})
if err != nil {
return fmt.Errorf("files-find-bin: %w", err)
}
return outputFiles(hc, binFiles)
}
func filesFindLibCmd(hc interp.HandlerContext, cmd string, args []string) error {
namePattern := "*"
if len(args) > 0 {
namePattern = args[0]
}
libPaths := []string{"./usr/lib/", "./usr/lib64/"}
var libFiles []string
for _, libPath := range libPaths {
realPath := path.Join(hc.Dir, libPath)
if _, err := os.Stat(realPath); os.IsNotExist(err) {
continue
}
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
}
libFiles = append(libFiles, relPath)
}
return nil
})
if err != nil {
return fmt.Errorf("files-find-lib: %w", err)
}
}
return outputFiles(hc, libFiles)
}
func filesFindIncludeCmd(hc interp.HandlerContext, cmd string, args []string) error {
namePattern := "*"
if len(args) > 0 {
namePattern = args[0]
}
includePath := "./usr/include/"
realPath := path.Join(hc.Dir, includePath)
if err := validateDir(realPath, "files-find-include"); err != nil {
return err
}
var includeFiles []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
}
includeFiles = append(includeFiles, relPath)
}
return nil
})
if err != nil {
return fmt.Errorf("files-find-include: %w", err)
}
return outputFiles(hc, includeFiles)
}
func filesFindShareCmd(hc interp.HandlerContext, cmd string, args []string) error {
namePattern := "*"
sharePath := "./usr/share/"
if len(args) > 0 {
if len(args) == 1 {
sharePath = "./usr/share/" + args[0] + "/"
} else {
sharePath = "./usr/share/" + args[0] + "/"
namePattern = args[1]
}
}
realPath := path.Join(hc.Dir, sharePath)
if err := validateDir(realPath, "files-find-share"); err != nil {
return err
}
var shareFiles []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
}
shareFiles = append(shareFiles, relPath)
}
return nil
})
if err != nil {
return fmt.Errorf("files-find-share: %w", err)
}
return outputFiles(hc, shareFiles)
}
func filesFindManCmd(hc interp.HandlerContext, cmd string, args []string) error {
namePattern := "*"
manSection := "*"
if len(args) > 0 {
if len(args) == 1 {
manSection = args[0]
} else {
manSection = args[0]
namePattern = args[1]
}
}
manPath := "./usr/share/man/man" + manSection + "/"
realPath := path.Join(hc.Dir, manPath)
if err := validateDir(realPath, "files-find-man"); err != nil {
return err
}
var manFiles []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
}
manFiles = append(manFiles, relPath)
}
return nil
})
if err != nil {
return fmt.Errorf("files-find-man: %w", err)
}
return outputFiles(hc, manFiles)
}
func filesFindConfigCmd(hc interp.HandlerContext, cmd string, args []string) error {
namePattern := "*"
if len(args) > 0 {
namePattern = args[0]
}
configPath := "./etc/"
realPath := path.Join(hc.Dir, configPath)
if err := validateDir(realPath, "files-find-config"); err != nil {
return err
}
var configFiles []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
}
configFiles = append(configFiles, relPath)
}
return nil
})
if err != nil {
return fmt.Errorf("files-find-config: %w", err)
}
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,18 +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-lib": filesFindLibCmd,
"files-find-include": filesFindIncludeCmd,
"files-find-share": filesFindShareCmd,
"files-find-man": filesFindManCmd,
"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-lib": filesFindLibCmd,
"files-find-include": filesFindIncludeCmd,
"files-find-share": filesFindShareCmd,
"files-find-man": filesFindManCmd,
"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

@@ -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)

View File

@@ -21,6 +21,7 @@ import (
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder" appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils"
) )
func RefreshCmd() *cli.Command { func RefreshCmd() *cli.Command {
@@ -29,6 +30,9 @@ func RefreshCmd() *cli.Command {
Usage: gotext.Get("Pull all repositories that have changed"), Usage: gotext.Get("Pull all repositories that have changed"),
Aliases: []string{"ref"}, Aliases: []string{"ref"},
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
if err := utils.CheckUserPrivileges(); err != nil {
return err
}
ctx := c.Context ctx := c.Context