diff --git a/gen.go b/gen.go
index ac00a8a..ccbde5d 100644
--- a/gen.go
+++ b/gen.go
@@ -61,6 +61,29 @@ func GenCmd() *cli.Command {
})
},
},
+ {
+ Name: "aur",
+ Usage: gotext.Get("Generate a ALR script for an AUR package"),
+ Flags: []cli.Flag{
+ &cli.StringFlag{
+ Name: "name",
+ Aliases: []string{"n"},
+ Required: true,
+ Usage: gotext.Get("Name of the AUR package"),
+ },
+ &cli.StringFlag{
+ Name: "version",
+ Aliases: []string{"v"},
+ Usage: gotext.Get("Version of the package (optional, uses latest if not specified)"),
+ },
+ },
+ Action: func(c *cli.Context) error {
+ return gen.AUR(os.Stdout, gen.AUROptions{
+ Name: c.String("name"),
+ Version: c.String("version"),
+ })
+ },
+ },
},
}
}
diff --git a/internal/gen/aur.go b/internal/gen/aur.go
new file mode 100644
index 0000000..99c1236
--- /dev/null
+++ b/internal/gen/aur.go
@@ -0,0 +1,668 @@
+// This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
+// It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors.
+//
+// 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 .
+
+package gen
+
+import (
+ _ "embed"
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+ "regexp"
+ "strings"
+ "text/template"
+)
+
+// Встраиваем шаблон для AUR пакетов
+//
+//go:embed tmpls/aur.tmpl.sh
+var aurTmpl string
+
+// AUROptions содержит параметры для генерации шаблона AUR
+type AUROptions struct {
+ Name string // Имя пакета в AUR
+ Version string // Версия пакета (опционально, если не указана - берется последняя)
+ CreateDir bool // Создавать ли директорию для пакета и дополнительные файлы
+}
+
+// aurAPIResponse представляет структуру ответа от API AUR
+type aurAPIResponse struct {
+ Version int `json:"version"` // Версия API
+ Type string `json:"type"` // Тип ответа
+ ResultCount int `json:"resultcount"` // Количество результатов
+ Results []aurResult `json:"results"` // Массив результатов
+ Error string `json:"error"` // Сообщение об ошибке (если есть)
+}
+
+// aurResult содержит информацию о пакете из AUR
+type aurResult struct {
+ ID int `json:"ID"`
+ Name string `json:"Name"`
+ PackageBaseID int `json:"PackageBaseID"`
+ PackageBase string `json:"PackageBase"`
+ Version string `json:"Version"`
+ Description string `json:"Description"`
+ URL string `json:"URL"`
+ NumVotes int `json:"NumVotes"`
+ Popularity float64 `json:"Popularity"`
+ OutOfDate *int `json:"OutOfDate"`
+ Maintainer string `json:"Maintainer"`
+ FirstSubmitted int `json:"FirstSubmitted"`
+ LastModified int `json:"LastModified"`
+ URLPath string `json:"URLPath"`
+ License []string `json:"License"`
+ Keywords []string `json:"Keywords"`
+ Depends []string `json:"Depends"`
+ MakeDepends []string `json:"MakeDepends"`
+ OptDepends []string `json:"OptDepends"`
+ CheckDepends []string `json:"CheckDepends"`
+ Conflicts []string `json:"Conflicts"`
+ Provides []string `json:"Provides"`
+ Replaces []string `json:"Replaces"`
+ // Дополнительные поля для данных из PKGBUILD
+ Sources []string `json:"-"`
+ Checksums []string `json:"-"`
+ BuildFunc string `json:"-"`
+ PackageFunc string `json:"-"`
+ PrepareFunc string `json:"-"`
+ PackageType string `json:"-"` // python, go, rust, cpp, nodejs, bin, git
+ HasDesktop bool `json:"-"` // Есть ли desktop файлы
+ HasSystemd bool `json:"-"` // Есть ли systemd сервисы
+ HasVersion bool `json:"-"` // Есть ли функция version()
+ HasScripts []string `json:"-"` // Дополнительные скрипты (postinstall, postremove, etc)
+ HasPatches bool `json:"-"` // Есть ли патчи
+ Architectures []string `json:"-"` // Поддерживаемые архитектуры
+
+ // Автоматически определяемые файлы для install-* команд
+ BinaryFiles []string `json:"-"` // Исполняемые файлы для install-binary
+ LicenseFiles []string `json:"-"` // Лицензионные файлы для install-license
+ ManualFiles []string `json:"-"` // Man страницы для install-manual
+ DesktopFiles []string `json:"-"` // Desktop файлы для install-desktop
+ ServiceFiles []string `json:"-"` // Systemd сервисы для install-systemd
+ CompletionFiles map[string]string `json:"-"` // Файлы автодополнения по типу (bash, zsh, fish)
+}
+
+// Вспомогательные методы для шаблона
+func (r aurResult) LicenseString() string {
+ if len(r.License) == 0 {
+ return "custom:Unknown"
+ }
+ // Форматируем лицензии для alr.sh
+ licenses := make([]string, len(r.License))
+ for i, l := range r.License {
+ licenses[i] = fmt.Sprintf("'%s'", l)
+ }
+ return strings.Join(licenses, " ")
+}
+
+func (r aurResult) DependsString() string {
+ if len(r.Depends) == 0 {
+ return ""
+ }
+ deps := make([]string, len(r.Depends))
+ for i, d := range r.Depends {
+ // Убираем версионные ограничения для простоты
+ dep := strings.Split(d, ">=")[0]
+ dep = strings.Split(dep, "<=")[0]
+ dep = strings.Split(dep, "=")[0]
+ dep = strings.Split(dep, ">")[0]
+ dep = strings.Split(dep, "<")[0]
+ deps[i] = fmt.Sprintf("'%s'", dep)
+ }
+ return strings.Join(deps, " ")
+}
+
+func (r aurResult) MakeDependsString() string {
+ if len(r.MakeDepends) == 0 {
+ return ""
+ }
+ deps := make([]string, len(r.MakeDepends))
+ for i, d := range r.MakeDepends {
+ // Убираем версионные ограничения для простоты
+ dep := strings.Split(d, ">=")[0]
+ dep = strings.Split(dep, "<=")[0]
+ dep = strings.Split(dep, "=")[0]
+ dep = strings.Split(dep, ">")[0]
+ dep = strings.Split(dep, "<")[0]
+ deps[i] = fmt.Sprintf("'%s'", dep)
+ }
+ return strings.Join(deps, " ")
+}
+
+func (r aurResult) GitURL() string {
+ // Формируем URL для клонирования из AUR
+ return fmt.Sprintf("https://aur.archlinux.org/%s.git", r.PackageBase)
+}
+
+func (r aurResult) ArchitecturesString() string {
+ if len(r.Architectures) == 0 {
+ return "'all'"
+ }
+ archs := make([]string, len(r.Architectures))
+ for i, arch := range r.Architectures {
+ archs[i] = fmt.Sprintf("'%s'", arch)
+ }
+ return strings.Join(archs, " ")
+}
+
+func (r aurResult) OptDependsString() string {
+ if len(r.OptDepends) == 0 {
+ return ""
+ }
+ optDeps := make([]string, 0, len(r.OptDepends))
+ for _, dep := range r.OptDepends {
+ // Форматируем опциональные зависимости для alr.sh
+ parts := strings.SplitN(dep, ": ", 2)
+ if len(parts) == 2 {
+ optDeps = append(optDeps, fmt.Sprintf("'%s: %s'", parts[0], parts[1]))
+ } else {
+ optDeps = append(optDeps, fmt.Sprintf("'%s'", dep))
+ }
+ }
+ return strings.Join(optDeps, "\n\t")
+}
+
+func (r aurResult) ScriptsString() string {
+ if len(r.HasScripts) == 0 {
+ return ""
+ }
+ scripts := make([]string, len(r.HasScripts))
+ for i, script := range r.HasScripts {
+ scripts[i] = fmt.Sprintf("['%s']='%s.sh'", script, script)
+ }
+ return strings.Join(scripts, "\n\t")
+}
+
+// GenerateInstallCommands генерирует команды install-* для шаблона
+func (r aurResult) GenerateInstallCommands() string {
+ var commands []string
+
+ // install-binary команды
+ for _, binary := range r.BinaryFiles {
+ if binary == "./"+r.Name {
+ commands = append(commands, fmt.Sprintf("\tinstall-binary %s", binary))
+ } else {
+ commands = append(commands, fmt.Sprintf("\tinstall-binary %s %s", binary, r.Name))
+ }
+ }
+
+ // install-license команды
+ for _, license := range r.LicenseFiles {
+ if license == "LICENSE" || license == "./LICENSE" {
+ commands = append(commands, fmt.Sprintf("\tinstall-license %s %s/LICENSE", license, r.Name))
+ } else {
+ commands = append(commands, fmt.Sprintf("\tinstall-license %s %s/LICENSE", license, r.Name))
+ }
+ }
+
+ // install-manual команды
+ for _, manual := range r.ManualFiles {
+ commands = append(commands, fmt.Sprintf("\tinstall-manual %s", manual))
+ }
+
+ // install-desktop команды
+ for _, desktop := range r.DesktopFiles {
+ commands = append(commands, fmt.Sprintf("\tinstall-desktop %s", desktop))
+ }
+
+ // install-systemd команды
+ for _, service := range r.ServiceFiles {
+ if strings.Contains(service, "user") {
+ commands = append(commands, fmt.Sprintf("\tinstall-systemd-user %s", service))
+ } else {
+ commands = append(commands, fmt.Sprintf("\tinstall-systemd %s", service))
+ }
+ }
+
+ // install-completion команды
+ for shell, file := range r.CompletionFiles {
+ switch shell {
+ case "bash":
+ commands = append(commands, fmt.Sprintf("\tinstall-completion bash %s < %s", r.Name, file))
+ case "zsh":
+ commands = append(commands, fmt.Sprintf("\tinstall-completion zsh %s < %s", r.Name, file))
+ case "fish":
+ commands = append(commands, fmt.Sprintf("\t%s completion fish | install-completion fish %s", r.Name, r.Name))
+ }
+ }
+
+ if len(commands) == 0 {
+ return "\t# TODO: Добавьте команды установки файлов"
+ }
+
+ return strings.Join(commands, "\n")
+}
+
+// fetchPKGBUILD загружает PKGBUILD файл для пакета
+func fetchPKGBUILD(packageBase string) (string, error) {
+ // URL для raw PKGBUILD
+ pkgbuildURL := fmt.Sprintf("https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?h=%s", packageBase)
+
+ res, err := http.Get(pkgbuildURL)
+ if err != nil {
+ return "", fmt.Errorf("failed to fetch PKGBUILD: %w", err)
+ }
+ defer res.Body.Close()
+
+ if res.StatusCode != 200 {
+ return "", fmt.Errorf("failed to fetch PKGBUILD: status %s", res.Status)
+ }
+
+ data, err := io.ReadAll(res.Body)
+ if err != nil {
+ return "", fmt.Errorf("failed to read PKGBUILD: %w", err)
+ }
+
+ return string(data), nil
+}
+
+// parseSources извлекает источники из PKGBUILD
+func parseSources(pkgbuild string) []string {
+ var sources []string
+
+ // Регулярное выражение для поиска массива source
+ // Поддерживает как однострочные, так и многострочные определения
+ sourceRegex := regexp.MustCompile(`(?ms)source=\((.*?)\)`)
+ matches := sourceRegex.FindStringSubmatch(pkgbuild)
+
+ if len(matches) > 1 {
+ // Извлекаем содержимое массива source
+ sourceContent := matches[1]
+
+ // Разбираем элементы массива
+ // Учитываем кавычки и переносы строк
+ elemRegex := regexp.MustCompile(`['"]([^'"]+)['"]`)
+ elements := elemRegex.FindAllStringSubmatch(sourceContent, -1)
+
+ for _, elem := range elements {
+ if len(elem) > 1 {
+ source := elem[1]
+ // Заменяем переменные версии
+ source = strings.ReplaceAll(source, "$pkgver", "${version}")
+ source = strings.ReplaceAll(source, "${pkgver}", "${version}")
+ source = strings.ReplaceAll(source, "$pkgname", "${name}")
+ source = strings.ReplaceAll(source, "${pkgname}", "${name}")
+ // Обрабатываем другие переменные (упрощенно)
+ source = strings.ReplaceAll(source, "$_commit", "${_commit}")
+ source = strings.ReplaceAll(source, "${_commit}", "${_commit}")
+ sources = append(sources, source)
+ }
+ }
+ }
+
+ // Если источники не найдены в source=(), проверяем source_x86_64 и другие архитектуры
+ if len(sources) == 0 {
+ archSourceRegex := regexp.MustCompile(`(?ms)source_(?:x86_64|aarch64)=\((.*?)\)`)
+ matches = archSourceRegex.FindStringSubmatch(pkgbuild)
+ if len(matches) > 1 {
+ sourceContent := matches[1]
+ elemRegex := regexp.MustCompile(`['"]([^'"]+)['"]`)
+ elements := elemRegex.FindAllStringSubmatch(sourceContent, -1)
+
+ for _, elem := range elements {
+ if len(elem) > 1 {
+ source := elem[1]
+ source = strings.ReplaceAll(source, "$pkgver", "${version}")
+ source = strings.ReplaceAll(source, "${pkgver}", "${version}")
+ source = strings.ReplaceAll(source, "$pkgname", "${name}")
+ source = strings.ReplaceAll(source, "${pkgname}", "${name}")
+ sources = append(sources, source)
+ }
+ }
+ }
+ }
+
+ return sources
+}
+
+// parseChecksums извлекает контрольные суммы из PKGBUILD
+func parseChecksums(pkgbuild string) []string {
+ var checksums []string
+
+ // Пробуем разные типы контрольных сумм
+ for _, hashType := range []string{"sha256sums", "sha512sums", "sha1sums", "md5sums", "b2sums"} {
+ regex := regexp.MustCompile(fmt.Sprintf(`(?ms)%s=\((.*?)\)`, hashType))
+ matches := regex.FindStringSubmatch(pkgbuild)
+
+ if len(matches) > 1 {
+ content := matches[1]
+ elemRegex := regexp.MustCompile(`['"]([^'"]+)['"]`)
+ elements := elemRegex.FindAllStringSubmatch(content, -1)
+
+ for _, elem := range elements {
+ if len(elem) > 1 {
+ checksums = append(checksums, elem[1])
+ }
+ }
+
+ if len(checksums) > 0 {
+ break // Используем первый найденный тип хешей
+ }
+ }
+ }
+
+ return checksums
+}
+
+// parseFunctions извлекает функции build(), package() и prepare() из PKGBUILD
+func parseFunctions(pkgbuild string) (buildFunc, packageFunc, prepareFunc string) {
+ // Извлекаем функцию build()
+ buildRegex := regexp.MustCompile(`(?ms)^build\(\)\s*\{(.*?)^\}`)
+ if matches := buildRegex.FindStringSubmatch(pkgbuild); len(matches) > 1 {
+ buildFunc = strings.TrimSpace(matches[1])
+ }
+
+ // Извлекаем функцию package()
+ packageRegex := regexp.MustCompile(`(?ms)^package\(\)\s*\{(.*?)^\}`)
+ if matches := packageRegex.FindStringSubmatch(pkgbuild); len(matches) > 1 {
+ packageFunc = strings.TrimSpace(matches[1])
+ }
+
+ // Извлекаем функцию prepare()
+ prepareRegex := regexp.MustCompile(`(?ms)^prepare\(\)\s*\{(.*?)^\}`)
+ if matches := prepareRegex.FindStringSubmatch(pkgbuild); len(matches) > 1 {
+ prepareFunc = strings.TrimSpace(matches[1])
+ }
+
+ return buildFunc, packageFunc, prepareFunc
+}
+
+// detectInstallableFiles анализирует PKGBUILD и определяет файлы для install-* команд
+func detectInstallableFiles(pkg *aurResult, pkgbuild string) {
+ // Инициализируем карту для файлов автодополнения
+ pkg.CompletionFiles = make(map[string]string)
+
+ // Для простоты, добавляем стандартные файлы для типа пакета
+ switch pkg.PackageType {
+ case "go":
+ pkg.BinaryFiles = append(pkg.BinaryFiles, "./"+pkg.Name)
+ case "rust":
+ pkg.BinaryFiles = append(pkg.BinaryFiles, "./target/release/"+pkg.Name)
+ case "cpp", "meson":
+ pkg.BinaryFiles = append(pkg.BinaryFiles, "./"+pkg.Name) // обычно в корне после сборки
+ case "bin":
+ pkg.BinaryFiles = append(pkg.BinaryFiles, "./"+pkg.Name)
+ default:
+ if pkg.PackageType != "python" && pkg.PackageType != "nodejs" {
+ pkg.BinaryFiles = append(pkg.BinaryFiles, "./"+pkg.Name)
+ }
+ }
+
+ // Ищем лицензионные файлы для install-license с более точными паттернами
+ licenseRegex := regexp.MustCompile(`(?i)\b(LICENSE|COPYING|COPYRIGHT|LICENCE)(?:\.[a-zA-Z0-9]+)?\b`)
+ licenseMatches := licenseRegex.FindAllString(pkgbuild, -1)
+ for _, match := range licenseMatches {
+ // Фильтруем только реальные файлы лицензий
+ if strings.Contains(strings.ToLower(match), "license") ||
+ strings.Contains(strings.ToLower(match), "copying") ||
+ strings.Contains(strings.ToLower(match), "copyright") {
+ if !contains(pkg.LicenseFiles, "./"+match) {
+ pkg.LicenseFiles = append(pkg.LicenseFiles, "./"+match)
+ }
+ }
+ }
+
+ // Если не найдены лицензионные файлы, добавляем стандартные
+ if len(pkg.LicenseFiles) == 0 {
+ pkg.LicenseFiles = append(pkg.LicenseFiles, "LICENSE")
+ }
+
+ // Ищем man страницы для install-manual с более точными паттернами
+ manRegex := regexp.MustCompile(`\b\w+\.(?:1|2|3|4|5|6|7|8)(?:\.gz)?\b`)
+ manMatches := manRegex.FindAllString(pkgbuild, -1)
+ for _, match := range manMatches {
+ // Проверяем, что это не переменная или часть кода
+ if !strings.Contains(match, "$") && !strings.Contains(match, "{") {
+ if !contains(pkg.ManualFiles, "./"+match) {
+ pkg.ManualFiles = append(pkg.ManualFiles, "./"+match)
+ }
+ }
+ }
+
+ // Ищем desktop файлы для install-desktop
+ desktopRegex := regexp.MustCompile(`[^/\s]*\.desktop`)
+ desktopMatches := desktopRegex.FindAllString(pkgbuild, -1)
+ for _, match := range desktopMatches {
+ if !contains(pkg.DesktopFiles, "./"+match) {
+ pkg.DesktopFiles = append(pkg.DesktopFiles, "./"+match)
+ }
+ }
+
+ // Ищем systemd сервисы для install-systemd
+ serviceRegex := regexp.MustCompile(`[^/\s]*\.service`)
+ serviceMatches := serviceRegex.FindAllString(pkgbuild, -1)
+ for _, match := range serviceMatches {
+ if !contains(pkg.ServiceFiles, "./"+match) {
+ pkg.ServiceFiles = append(pkg.ServiceFiles, "./"+match)
+ }
+ }
+
+ // Ищем файлы автодополнения
+ completionPatterns := map[string]string{
+ "bash": `completions?/.*\.bash|bash-completion`,
+ "zsh": `completions?/.*\.zsh|zsh.*completion`,
+ "fish": `completions?/.*\.fish|fish.*completion`,
+ }
+
+ for shell, pattern := range completionPatterns {
+ regex := regexp.MustCompile(fmt.Sprintf(`(?i)%s`, pattern))
+ matches := regex.FindAllString(pkgbuild, -1)
+ if len(matches) > 0 {
+ pkg.CompletionFiles[shell] = matches[0]
+ }
+ }
+}
+
+// contains проверяет, содержит ли слайс строк указанную строку
+func contains(slice []string, item string) bool {
+ for _, s := range slice {
+ if s == item {
+ return true
+ }
+ }
+ return false
+}
+
+// detectPackageType определяет тип пакета на основе имени, зависимостей и источников
+func detectPackageType(pkg *aurResult, pkgbuild string) {
+ name := strings.ToLower(pkg.Name)
+
+ // Определяем тип на основе имени пакета
+ switch {
+ case strings.HasPrefix(name, "python") || strings.HasPrefix(name, "python3-"):
+ pkg.PackageType = "python"
+ case strings.Contains(name, "nodejs") || strings.Contains(name, "node-"):
+ pkg.PackageType = "nodejs"
+ case strings.HasSuffix(name, "-bin"):
+ pkg.PackageType = "bin"
+ case strings.HasSuffix(name, "-git"):
+ pkg.PackageType = "git"
+ pkg.HasVersion = true // Git пакеты обычно имеют функцию version()
+ case strings.Contains(name, "rust") || hasRustSources(pkg.Sources):
+ pkg.PackageType = "rust"
+ case strings.Contains(name, "go-") || hasGoSources(pkg.Sources):
+ pkg.PackageType = "go"
+ case strings.Contains(name, "-rust") || strings.Contains(name, "paru") || strings.Contains(name, "cargo-"):
+ pkg.PackageType = "rust"
+ default:
+ // Определяем по зависимостям сборки
+ for _, dep := range pkg.MakeDepends {
+ depLower := strings.ToLower(dep)
+ switch {
+ case strings.Contains(depLower, "meson") || strings.Contains(depLower, "ninja"):
+ pkg.PackageType = "meson"
+ case strings.Contains(depLower, "cmake") || strings.Contains(depLower, "gcc") || strings.Contains(depLower, "clang"):
+ pkg.PackageType = "cpp"
+ case strings.Contains(depLower, "python"):
+ pkg.PackageType = "python"
+ case strings.Contains(depLower, "go"):
+ pkg.PackageType = "go"
+ case strings.Contains(depLower, "rust") || strings.Contains(depLower, "cargo"):
+ pkg.PackageType = "rust"
+ case strings.Contains(depLower, "npm") || strings.Contains(depLower, "nodejs"):
+ pkg.PackageType = "nodejs"
+ }
+ if pkg.PackageType != "" {
+ break
+ }
+ }
+ }
+
+ // Определяем архитектуры на основе типа пакета
+ if pkg.PackageType == "bin" {
+ pkg.Architectures = []string{"amd64"} // Бинарные пакеты обычно специфичны для архитектуры
+ } else {
+ pkg.Architectures = []string{"all"} // Исходный код собирается для любой архитектуры
+ }
+
+ // Определяем наличие desktop файлов
+ pkg.HasDesktop = strings.Contains(pkgbuild, ".desktop") ||
+ strings.Contains(pkgbuild, "install-desktop") ||
+ strings.Contains(pkgbuild, "xdg-desktop")
+
+ // Определяем наличие systemd сервисов
+ pkg.HasSystemd = strings.Contains(pkgbuild, ".service") ||
+ strings.Contains(pkgbuild, "systemctl") ||
+ strings.Contains(pkgbuild, "install-systemd")
+
+ // Определяем наличие функции version() для -git пакетов
+ pkg.HasVersion = strings.Contains(pkgbuild, "pkgver()") ||
+ (strings.HasSuffix(name, "-git") && strings.Contains(pkgbuild, "git describe"))
+
+ // Определяем наличие патчей
+ pkg.HasPatches = strings.Contains(pkgbuild, "patch ") ||
+ strings.Contains(pkgbuild, ".patch") ||
+ strings.Contains(pkgbuild, ".diff")
+
+ // Определяем дополнительные скрипты
+ if strings.Contains(pkgbuild, "post_install") {
+ pkg.HasScripts = append(pkg.HasScripts, "postinstall")
+ }
+ if strings.Contains(pkgbuild, "pre_remove") || strings.Contains(pkgbuild, "post_remove") {
+ pkg.HasScripts = append(pkg.HasScripts, "postremove")
+ }
+}
+
+// hasRustSources проверяет, содержат ли источники Rust проекты
+func hasRustSources(sources []string) bool {
+ for _, src := range sources {
+ if strings.Contains(src, "crates.io") || strings.Contains(src, "Cargo.toml") {
+ return true
+ }
+ }
+ return false
+}
+
+// hasGoSources проверяет, содержат ли источники Go проекты
+func hasGoSources(sources []string) bool {
+ for _, src := range sources {
+ if strings.Contains(src, "github.com") && strings.Contains(src, "/go") {
+ return true
+ }
+ }
+ return false
+}
+
+// AUR генерирует шаблон alr.sh на основе пакета из AUR
+func AUR(w io.Writer, opts AUROptions) error {
+ // Создаем шаблон с функциями
+ tmpl, err := template.New("aur").
+ Funcs(funcs).
+ Parse(aurTmpl)
+ if err != nil {
+ return err
+ }
+
+ // Формируем URL запроса к AUR API
+ apiURL := "https://aur.archlinux.org/rpc/v5/info"
+ params := url.Values{}
+ params.Add("arg[]", opts.Name)
+ fullURL := fmt.Sprintf("%s?%s", apiURL, params.Encode())
+
+ // Выполняем запрос к AUR API
+ res, err := http.Get(fullURL)
+ if err != nil {
+ return fmt.Errorf("failed to fetch AUR package info: %w", err)
+ }
+ defer res.Body.Close()
+
+ if res.StatusCode != 200 {
+ return fmt.Errorf("AUR API returned status: %s", res.Status)
+ }
+
+ // Декодируем ответ
+ var resp aurAPIResponse
+ err = json.NewDecoder(res.Body).Decode(&resp)
+ if err != nil {
+ return fmt.Errorf("failed to decode AUR response: %w", err)
+ }
+
+ // Проверяем наличие ошибки в ответе
+ if resp.Error != "" {
+ return fmt.Errorf("AUR API error: %s", resp.Error)
+ }
+
+ // Проверяем, что пакет найден
+ if resp.ResultCount == 0 {
+ return fmt.Errorf("package '%s' not found in AUR", opts.Name)
+ }
+
+ // Берем первый результат
+ pkg := resp.Results[0]
+
+ // Если указана версия, проверяем соответствие
+ if opts.Version != "" && pkg.Version != opts.Version {
+ // Предупреждаем, но продолжаем с актуальной версией из AUR
+ fmt.Fprintf(w, "# WARNING: Requested version %s, but AUR has %s\n", opts.Version, pkg.Version)
+ }
+
+ // Загружаем PKGBUILD для получения источников
+ pkgbuild, err := fetchPKGBUILD(pkg.PackageBase)
+ if err != nil {
+ // Если не удалось загрузить PKGBUILD, используем fallback на AUR репозиторий
+ fmt.Fprintf(w, "# WARNING: Could not fetch PKGBUILD: %v\n", err)
+ fmt.Fprintf(w, "# Using AUR repository as source\n")
+ pkg.Sources = []string{fmt.Sprintf("%s::git+%s", pkg.Name, pkg.GitURL())}
+ pkg.Checksums = []string{"SKIP"}
+ } else {
+ // Извлекаем источники из PKGBUILD
+ pkg.Sources = parseSources(pkgbuild)
+ pkg.Checksums = parseChecksums(pkgbuild)
+ pkg.BuildFunc, pkg.PackageFunc, pkg.PrepareFunc = parseFunctions(pkgbuild)
+
+ // Определяем тип пакета
+ detectPackageType(&pkg, pkgbuild)
+
+ // Определяем файлы для install-* команд
+ detectInstallableFiles(&pkg, pkgbuild)
+
+ // Если источники не найдены, используем fallback
+ if len(pkg.Sources) == 0 {
+ fmt.Fprintf(w, "# WARNING: No sources found in PKGBUILD\n")
+ fmt.Fprintf(w, "# Using AUR repository as source\n")
+ pkg.Sources = []string{fmt.Sprintf("%s::git+%s", pkg.Name, pkg.GitURL())}
+ pkg.Checksums = []string{"SKIP"}
+ }
+ }
+
+ // Выполняем шаблон
+ return tmpl.Execute(w, pkg)
+}
\ No newline at end of file
diff --git a/internal/gen/tmpls/aur.tmpl.sh b/internal/gen/tmpls/aur.tmpl.sh
new file mode 100644
index 0000000..97dfb38
--- /dev/null
+++ b/internal/gen/tmpls/aur.tmpl.sh
@@ -0,0 +1,133 @@
+# This file was originally part of the project "LURE - Linux User REpository", created by Elara Musayelyan.
+# It has been modified as part of "ALR - Any Linux Repository" by the ALR Authors.
+#
+# 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 .
+
+# Generated from AUR package: {{.Name}}
+# Package type: {{.PackageType}}
+# AUR votes: {{.NumVotes}} | Popularity: {{printf "%.2f" .Popularity}}
+# Original maintainer: {{.Maintainer}}
+# Adapted for ALR by automation
+
+name='{{.Name}}'
+version='{{.Version}}'
+release='1'
+desc='{{.Description}}'
+{{if ne .Description ""}}desc_ru='{{.Description}}'{{end}}
+homepage='{{.URL}}'
+maintainer="Евгений Храмов (imported from AUR)"
+{{if ne .Description ""}}maintainer_ru="Евгений Храмов (импортирован из AUR)"{{end}}
+architectures=({{.ArchitecturesString}})
+license=({{.LicenseString}})
+{{if .Provides}}provides=({{range .Provides}}'{{.}}' {{end}}){{end}}
+{{if .Conflicts}}conflicts=({{range .Conflicts}}'{{.}}' {{end}}){{end}}
+{{if .Replaces}}replaces=({{range .Replaces}}'{{.}}' {{end}}){{end}}
+
+# Базовые зависимости
+{{if .DependsString}}deps=({{.DependsString}}){{else}}deps=(){{end}}
+{{if .MakeDependsString}}build_deps=({{.MakeDependsString}}){{else}}build_deps=(){{end}}
+
+# Зависимости для конкретных дистрибутивов (адаптируйте под нужды пакета)
+{{if .DependsString}}deps_arch=({{.DependsString}})
+deps_debian=({{.DependsString}})
+deps_altlinux=({{.DependsString}})
+deps_alpine=({{.DependsString}}){{end}}
+
+{{if and .MakeDependsString (ne .PackageType "bin")}}# Зависимости сборки для конкретных дистрибутивов
+build_deps_arch=({{.MakeDependsString}})
+build_deps_debian=({{.MakeDependsString}})
+build_deps_altlinux=({{.MakeDependsString}})
+build_deps_alpine=({{.MakeDependsString}}){{end}}
+
+{{if .OptDependsString}}# Опциональные зависимости
+opt_deps=(
+ {{.OptDependsString}}
+){{end}}
+
+# Источники из PKGBUILD
+sources=({{range .Sources}}"{{.}}" {{end}})
+checksums=({{range .Checksums}}'{{.}}' {{end}})
+
+{{if .HasVersion}}# Функция версии для Git-пакетов
+version() {
+ cd "$srcdir/{{.Name}}"
+ git-version
+}
+{{end}}
+
+{{if .ScriptsString}}# Дополнительные скрипты
+scripts=(
+ {{.ScriptsString}}
+){{end}}
+
+{{if or .PrepareFunc .HasPatches}}prepare() {
+ cd "$srcdir"{{if .PrepareFunc}}
+ # Из PKGBUILD:
+ {{.PrepareFunc}}{{else}}
+ # Применение патчей и подготовка исходников
+ # Раскомментируйте и адаптируйте при необходимости:
+ # patch -p1 < "${scriptdir}/fix.patch"{{end}}
+}{{else}}# prepare() {
+# cd "$srcdir"
+# # Применение патчей и подготовка исходников при необходимости
+# # patch -p1 < "${scriptdir}/fix.patch"
+# }{{end}}
+
+{{if ne .PackageType "bin"}}build() {
+ cd "$srcdir"{{if .BuildFunc}}
+ # Из PKGBUILD:
+ {{.BuildFunc}}{{else}}
+
+ # TODO: Адаптируйте команды сборки под конкретный проект ({{.PackageType}})
+ {{if eq .PackageType "meson"}}# Для Meson проектов:
+ meson setup build --prefix=/usr --buildtype=release
+ ninja -C build -j $(nproc){{else if eq .PackageType "cpp"}}# Для C/C++ проектов:
+ mkdir -p build && cd build
+ cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
+ make -j$(nproc){{else if eq .PackageType "go"}}# Для Go проектов:
+ go build -buildmode=pie -trimpath -ldflags "-s -w" -o {{.Name}}{{else if eq .PackageType "python"}}# Для Python проектов:
+ python -m build --wheel --no-isolation{{else if eq .PackageType "nodejs"}}# Для Node.js проектов:
+ npm ci --production
+ npm run build{{else if eq .PackageType "rust"}}# Для Rust проектов:
+ cargo build --release --locked{{else if eq .PackageType "git"}}# Для Git проектов (обычно исходный код):
+ make -j$(nproc){{else}}# Стандартная сборка:
+ make -j$(nproc){{end}}{{end}}
+}{{else}}# Бинарный пакет - сборка не требуется{{end}}
+
+package() {
+ cd "$srcdir"{{if .PackageFunc}}
+ # Из PKGBUILD (адаптировано для ALR):
+ {{.PackageFunc}}
+
+ # Автоматически сгенерированные команды установки:
+{{.GenerateInstallCommands}}{{else}}
+
+ # TODO: Адаптируйте установку файлов под конкретный проект {{.Name}}
+ {{if eq .PackageType "meson"}}# Для Meson проектов:
+ meson install -C build --destdir="$pkgdir"{{else if eq .PackageType "cpp"}}# Для C/C++ проектов:
+ cd build
+ make DESTDIR="$pkgdir" install{{else if eq .PackageType "go"}}# Для Go проектов:
+ # Исполняемый файл уже собран в корне{{else if eq .PackageType "python"}}# Для Python проектов:
+ pip install --root="$pkgdir/" . --no-deps --disable-pip-version-check{{else if eq .PackageType "nodejs"}}# Для Node.js проектов:
+ npm install -g --prefix="$pkgdir/usr" .{{else if eq .PackageType "rust"}}# Для Rust проектов:
+ # Исполняемый файл в target/release/{{else if eq .PackageType "bin"}}# Бинарный пакет:
+ # Файлы уже распакованы{{else}}# Стандартная установка:
+ make DESTDIR="$pkgdir" install{{end}}
+
+ # Автоматически сгенерированные команды установки:
+{{.GenerateInstallCommands}}{{end}}
+}
\ No newline at end of file
diff --git a/pkg/alrsh/package_gen.go b/pkg/alrsh/package_gen.go
index 834deb8..0fca143 100644
--- a/pkg/alrsh/package_gen.go
+++ b/pkg/alrsh/package_gen.go
@@ -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 .
-
// DO NOT EDIT MANUALLY. This file is generated.
package alrsh