Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
8dea5e1e7f | |||
86a982478e | |||
8bc82cb95c | |||
9783ce37de | |||
b852688ab0 |
@@ -76,6 +76,7 @@ var configKeys = []string{
|
||||
"autoPull",
|
||||
"logLevel",
|
||||
"ignorePkgUpdates",
|
||||
"updateSystemOnUpgrade",
|
||||
}
|
||||
|
||||
func SetConfig() *cli.Command {
|
||||
@@ -137,6 +138,12 @@ func SetConfig() *cli.Command {
|
||||
}
|
||||
}
|
||||
deps.Cfg.System.SetIgnorePkgUpdates(updates)
|
||||
case "updateSystemOnUpgrade":
|
||||
boolValue, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return cliutils.FormatCliExit(gotext.Get("invalid boolean value for %s: %s", key, value), err)
|
||||
}
|
||||
deps.Cfg.System.SetUpdateSystemOnUpgrade(boolValue)
|
||||
case "repo", "repos":
|
||||
return cliutils.FormatCliExit(gotext.Get("use 'repo add/remove' commands to manage repositories"), nil)
|
||||
default:
|
||||
@@ -206,6 +213,8 @@ func GetConfig() *cli.Command {
|
||||
} else {
|
||||
fmt.Println(strings.Join(updates, ", "))
|
||||
}
|
||||
case "updateSystemOnUpgrade":
|
||||
fmt.Println(deps.Cfg.UpdateSystemOnUpgrade())
|
||||
case "repo", "repos":
|
||||
repos := deps.Cfg.Repos()
|
||||
if len(repos) == 0 {
|
||||
|
@@ -32,6 +32,7 @@ import (
|
||||
"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils"
|
||||
"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
|
||||
"gitea.plemya-x.ru/Plemya-x/ALR/internal/manager"
|
||||
"gitea.plemya-x.ru/Plemya-x/ALR/internal/stats"
|
||||
"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"
|
||||
@@ -319,9 +320,9 @@ func (b *Builder) BuildPackage(
|
||||
}
|
||||
|
||||
var builtDeps []*BuiltDep
|
||||
var remainingVars []*alrsh.Package
|
||||
|
||||
if !input.opts.Clean {
|
||||
var remainingVars []*alrsh.Package
|
||||
for _, vars := range varsOfPackages {
|
||||
builtPkgPath, ok, err := b.cacheExecutor.CheckForBuiltPackage(ctx, input, vars)
|
||||
if err != nil {
|
||||
@@ -330,6 +331,7 @@ func (b *Builder) BuildPackage(
|
||||
if ok {
|
||||
builtDeps = append(builtDeps, &BuiltDep{
|
||||
Path: builtPkgPath,
|
||||
Name: vars.Name,
|
||||
})
|
||||
} else {
|
||||
remainingVars = append(remainingVars, vars)
|
||||
@@ -337,8 +339,12 @@ func (b *Builder) BuildPackage(
|
||||
}
|
||||
|
||||
if len(remainingVars) == 0 {
|
||||
slog.Info(gotext.Get("Using cached package"), "name", basePkg)
|
||||
return builtDeps, nil
|
||||
}
|
||||
|
||||
// Обновляем varsOfPackages только теми пакетами, которые нужно собрать
|
||||
varsOfPackages = remainingVars
|
||||
}
|
||||
|
||||
slog.Debug("ViewScript")
|
||||
@@ -401,9 +407,19 @@ func (b *Builder) BuildPackage(
|
||||
|
||||
// We filter so as not to re-build what has already been built at the `installBuildDeps` stage.
|
||||
var filteredDepends []string
|
||||
|
||||
// Создаем набор подпакетов текущего мультипакета для исключения циклических зависимостей
|
||||
currentPackageNames := make(map[string]struct{})
|
||||
for _, pkg := range input.packages {
|
||||
currentPackageNames[pkg] = struct{}{}
|
||||
}
|
||||
|
||||
for _, d := range depends {
|
||||
if _, found := depNames[d]; !found {
|
||||
filteredDepends = append(filteredDepends, d)
|
||||
// Исключаем зависимости, которые являются подпакетами текущего мультипакета
|
||||
if _, isCurrentPackage := currentPackageNames[d]; !isCurrentPackage {
|
||||
filteredDepends = append(filteredDepends, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -528,6 +544,13 @@ func (b *Builder) InstallALRPackages(
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Отслеживание установки ALR пакетов
|
||||
for _, dep := range res {
|
||||
if stats.ShouldTrackPackage(dep.Name) {
|
||||
stats.TrackInstallation(ctx, dep.Name, "upgrade")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -552,11 +575,13 @@ func (b *Builder) BuildALRDeps(
|
||||
repoDeps = notFound
|
||||
|
||||
// Если для некоторых пакетов есть несколько опций, упрощаем их все в один срез
|
||||
pkgs := cliutils.FlattenPkgs(
|
||||
// Для зависимостей указываем isDependency = true
|
||||
pkgs := cliutils.FlattenPkgsWithContext(
|
||||
ctx,
|
||||
found,
|
||||
"install",
|
||||
input.BuildOpts().Interactive,
|
||||
true,
|
||||
)
|
||||
type item struct {
|
||||
pkg *alrsh.Package
|
||||
@@ -691,6 +716,13 @@ func (i *Builder) InstallPkgs(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Отслеживание установки локальных пакетов
|
||||
for _, dep := range builtDeps {
|
||||
if stats.ShouldTrackPackage(dep.Name) {
|
||||
stats.TrackInstallation(ctx, dep.Name, "install")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(repoDeps) > 0 {
|
||||
@@ -700,6 +732,13 @@ func (i *Builder) InstallPkgs(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Отслеживание установки пакетов из репозитория
|
||||
for _, pkg := range repoDeps {
|
||||
if stats.ShouldTrackPackage(pkg) {
|
||||
stats.TrackInstallation(ctx, pkg, "install")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return builtDeps, nil
|
||||
|
@@ -103,22 +103,62 @@ func ShowScript(path, name, style string) error {
|
||||
// FlattenPkgs attempts to flatten the a map of slices of packages into a single slice
|
||||
// of packages by prompting the user if multiple packages match.
|
||||
func FlattenPkgs(ctx context.Context, found map[string][]alrsh.Package, verb string, interactive bool) []alrsh.Package {
|
||||
return FlattenPkgsWithContext(ctx, found, verb, interactive, false)
|
||||
}
|
||||
|
||||
// FlattenPkgsWithContext расширенная версия FlattenPkgs с контекстом обработки зависимостей
|
||||
func FlattenPkgsWithContext(ctx context.Context, found map[string][]alrsh.Package, verb string, interactive bool, isDependency bool) []alrsh.Package {
|
||||
var outPkgs []alrsh.Package
|
||||
for _, pkgs := range found {
|
||||
if len(pkgs) > 1 && interactive {
|
||||
choice, err := PkgPrompt(ctx, pkgs, verb, interactive)
|
||||
if err != nil {
|
||||
slog.Error(gotext.Get("Error prompting for choice of package"))
|
||||
os.Exit(1)
|
||||
if len(pkgs) > 1 {
|
||||
// Проверяем, являются ли пакеты подпакетами одного мультипакета
|
||||
if isMultiPackage(pkgs) && verb == "install" {
|
||||
// Для мультипакетов при установке ВСЕГДА берем все подпакеты без выбора
|
||||
// Это правильное поведение как для прямой установки, так и для зависимостей
|
||||
outPkgs = append(outPkgs, pkgs...)
|
||||
} else if interactive {
|
||||
// Для разных пакетов с одинаковым именем - показываем меню выбора
|
||||
choice, err := PkgPrompt(ctx, pkgs, verb, interactive)
|
||||
if err != nil {
|
||||
slog.Error(gotext.Get("Error prompting for choice of package"))
|
||||
os.Exit(1)
|
||||
}
|
||||
outPkgs = append(outPkgs, choice)
|
||||
} else {
|
||||
// Если не интерактивный режим - берем первый
|
||||
outPkgs = append(outPkgs, pkgs[0])
|
||||
}
|
||||
outPkgs = append(outPkgs, choice)
|
||||
} else if len(pkgs) == 1 || !interactive {
|
||||
} else {
|
||||
// Если только один пакет - берем его
|
||||
outPkgs = append(outPkgs, pkgs[0])
|
||||
}
|
||||
}
|
||||
return outPkgs
|
||||
}
|
||||
|
||||
// isMultiPackage проверяет, являются ли пакеты подпакетами одного мультипакета
|
||||
func isMultiPackage(pkgs []alrsh.Package) bool {
|
||||
if len(pkgs) <= 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Проверяем, что у всех пакетов одинаковый BasePkgName и Repository
|
||||
firstBasePkg := pkgs[0].BasePkgName
|
||||
firstRepo := pkgs[0].Repository
|
||||
|
||||
if firstBasePkg == "" {
|
||||
return false // Не мультипакет
|
||||
}
|
||||
|
||||
for _, pkg := range pkgs[1:] {
|
||||
if pkg.BasePkgName != firstBasePkg || pkg.Repository != firstRepo {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// PkgPrompt asks the user to choose between multiple packages.
|
||||
func PkgPrompt(ctx context.Context, options []alrsh.Package, verb string, interactive bool) (alrsh.Package, error) {
|
||||
if !interactive {
|
||||
|
@@ -22,11 +22,13 @@ package config
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/goccy/go-yaml"
|
||||
"github.com/knadh/koanf/providers/confmap"
|
||||
"github.com/knadh/koanf/v2"
|
||||
ktoml "github.com/knadh/koanf/parsers/toml/v2"
|
||||
|
||||
"gitea.plemya-x.ru/Plemya-x/ALR/internal/constants"
|
||||
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
|
||||
@@ -56,6 +58,7 @@ func defaultConfigKoanf() *koanf.Koanf {
|
||||
"ignorePkgUpdates": []string{},
|
||||
"logLevel": "info",
|
||||
"autoPull": true,
|
||||
"updateSystemOnUpgrade": false,
|
||||
"repos": []types.Repo{
|
||||
{
|
||||
Name: "alr-default",
|
||||
@@ -114,6 +117,11 @@ func (c *ALRConfig) Load() error {
|
||||
}
|
||||
}
|
||||
|
||||
// Выполняем миграцию конфигурации при необходимости
|
||||
if err := c.migrateConfig(); err != nil {
|
||||
return fmt.Errorf("failed to migrate config: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -125,6 +133,126 @@ func (c *ALRConfig) ToYAML() (string, error) {
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
func (c *ALRConfig) migrateConfig() error {
|
||||
// Проверяем, существует ли конфигурационный файл
|
||||
if _, err := os.Stat(constants.SystemConfigPath); os.IsNotExist(err) {
|
||||
// Если файла нет, создаем полный конфигурационный файл с дефолтными значениями
|
||||
if err := c.createDefaultConfig(); err != nil {
|
||||
// Если не удается создать конфиг, это не критично - продолжаем работу
|
||||
// но выводим предупреждение
|
||||
fmt.Fprintf(os.Stderr, "Предупреждение: не удалось создать конфигурационный файл %s: %v\n", constants.SystemConfigPath, err)
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
// Если файл существует, проверяем, есть ли в нем новая опция
|
||||
if !c.System.k.Exists("updateSystemOnUpgrade") {
|
||||
// Если опции нет, добавляем ее со значением по умолчанию
|
||||
c.System.SetUpdateSystemOnUpgrade(false)
|
||||
// Сохраняем обновленную конфигурацию
|
||||
if err := c.System.Save(); err != nil {
|
||||
// Если не удается сохранить - это не критично, продолжаем работу
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ALRConfig) createDefaultConfig() error {
|
||||
// Проверяем, запущен ли процесс от root
|
||||
if os.Getuid() != 0 {
|
||||
// Если не root, пытаемся запустить создание конфига с повышением привилегий
|
||||
return c.createDefaultConfigWithPrivileges()
|
||||
}
|
||||
|
||||
// Если уже root, создаем конфиг напрямую
|
||||
return c.doCreateDefaultConfig()
|
||||
}
|
||||
|
||||
func (c *ALRConfig) createDefaultConfigWithPrivileges() error {
|
||||
// Если useRootCmd отключен, просто пытаемся создать без повышения привилегий
|
||||
if !c.cfg.UseRootCmd {
|
||||
return c.doCreateDefaultConfig()
|
||||
}
|
||||
|
||||
// Определяем команду для повышения привилегий
|
||||
rootCmd := c.cfg.RootCmd
|
||||
if rootCmd == "" {
|
||||
rootCmd = "sudo" // fallback
|
||||
}
|
||||
|
||||
// Создаем временный файл с дефолтной конфигурацией
|
||||
tmpFile, err := os.CreateTemp("", "alr-config-*.toml")
|
||||
if err != nil {
|
||||
return fmt.Errorf("не удалось создать временный файл: %w", err)
|
||||
}
|
||||
defer os.Remove(tmpFile.Name())
|
||||
defer tmpFile.Close()
|
||||
|
||||
// Генерируем дефолтную конфигурацию во временный файл
|
||||
defaults := defaultConfigKoanf()
|
||||
tempSystemConfig := &SystemConfig{k: defaults}
|
||||
|
||||
bytes, err := tempSystemConfig.k.Marshal(ktoml.Parser())
|
||||
if err != nil {
|
||||
return fmt.Errorf("не удалось сериализовать конфигурацию: %w", err)
|
||||
}
|
||||
|
||||
if _, err := tmpFile.Write(bytes); err != nil {
|
||||
return fmt.Errorf("не удалось записать во временный файл: %w", err)
|
||||
}
|
||||
tmpFile.Close()
|
||||
|
||||
// Используем команду повышения привилегий для создания директории и копирования файла
|
||||
|
||||
// Создаем директорию с правами
|
||||
configDir := filepath.Dir(constants.SystemConfigPath)
|
||||
mkdirCmd := exec.Command(rootCmd, "mkdir", "-p", configDir)
|
||||
if err := mkdirCmd.Run(); err != nil {
|
||||
return fmt.Errorf("не удалось создать директорию %s: %w", configDir, err)
|
||||
}
|
||||
|
||||
// Копируем файл в нужное место
|
||||
cpCmd := exec.Command(rootCmd, "cp", tmpFile.Name(), constants.SystemConfigPath)
|
||||
if err := cpCmd.Run(); err != nil {
|
||||
return fmt.Errorf("не удалось скопировать конфигурацию в %s: %w", constants.SystemConfigPath, err)
|
||||
}
|
||||
|
||||
// Устанавливаем правильные права доступа
|
||||
chmodCmd := exec.Command(rootCmd, "chmod", "644", constants.SystemConfigPath)
|
||||
if err := chmodCmd.Run(); err != nil {
|
||||
// Не критично, продолжаем
|
||||
fmt.Fprintf(os.Stderr, "Предупреждение: не удалось установить права доступа для %s: %v\n", constants.SystemConfigPath, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ALRConfig) doCreateDefaultConfig() error {
|
||||
// Проверяем, существует ли директория для конфига
|
||||
configDir := filepath.Dir(constants.SystemConfigPath)
|
||||
if _, err := os.Stat(configDir); os.IsNotExist(err) {
|
||||
// Пытаемся создать директорию
|
||||
if err := os.MkdirAll(configDir, 0755); err != nil {
|
||||
return fmt.Errorf("не удалось создать директорию %s: %w", configDir, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Загружаем дефолтную конфигурацию
|
||||
defaults := defaultConfigKoanf()
|
||||
|
||||
// Копируем все дефолтные значения в системную конфигурацию
|
||||
c.System.k = defaults
|
||||
|
||||
// Сохраняем конфигурацию в файл
|
||||
if err := c.System.Save(); err != nil {
|
||||
return fmt.Errorf("не удалось сохранить конфигурацию в %s: %w", constants.SystemConfigPath, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ALRConfig) RootCmd() string { return c.cfg.RootCmd }
|
||||
func (c *ALRConfig) PagerStyle() string { return c.cfg.PagerStyle }
|
||||
func (c *ALRConfig) AutoPull() bool { return c.cfg.AutoPull }
|
||||
@@ -133,4 +261,5 @@ func (c *ALRConfig) SetRepos(repos []types.Repo) { c.System.SetRepos(repos) }
|
||||
func (c *ALRConfig) IgnorePkgUpdates() []string { return c.cfg.IgnorePkgUpdates }
|
||||
func (c *ALRConfig) LogLevel() string { return c.cfg.LogLevel }
|
||||
func (c *ALRConfig) UseRootCmd() bool { return c.cfg.UseRootCmd }
|
||||
func (c *ALRConfig) UpdateSystemOnUpgrade() bool { return c.cfg.UpdateSystemOnUpgrade }
|
||||
func (c *ALRConfig) GetPaths() *Paths { return c.paths }
|
||||
|
@@ -142,3 +142,10 @@ func (c *SystemConfig) SetRepos(v []types.Repo) {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *SystemConfig) SetUpdateSystemOnUpgrade(v bool) {
|
||||
err := c.k.Set("updateSystemOnUpgrade", v)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
@@ -60,6 +60,13 @@ func (rs *Repos) FindPkgs(ctx context.Context, pkgs []string) (map[string][]alrs
|
||||
return nil, nil, fmt.Errorf("FindPkgs: get by provides: %w", err)
|
||||
}
|
||||
|
||||
if len(result) == 0 {
|
||||
result, err = rs.db.GetPkgs(ctx, "basepkg_name = ?", pkgName)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("FindPkgs: get by basepkg_name: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(result) == 0 {
|
||||
result, err = rs.db.GetPkgs(ctx, "name LIKE ?", pkgName)
|
||||
}
|
||||
|
106
internal/stats/tracker.go
Normal file
106
internal/stats/tracker.go
Normal file
@@ -0,0 +1,106 @@
|
||||
// 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 stats
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type InstallationData struct {
|
||||
PackageName string `json:"packageName"`
|
||||
Version string `json:"version,omitempty"`
|
||||
InstallType string `json:"installType"` // "install" or "upgrade"
|
||||
UserAgent string `json:"userAgent"`
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
}
|
||||
|
||||
var (
|
||||
apiEndpoints = []string{
|
||||
"https://alr.plemya-x.ru/api/packages/track-install",
|
||||
"http://localhost:3001/api/packages/track-install",
|
||||
}
|
||||
userAgent = "ALR-CLI/1.0"
|
||||
)
|
||||
|
||||
func generateFingerprint(packageName string) string {
|
||||
hostname, _ := os.Hostname()
|
||||
data := fmt.Sprintf("%s_%s_%s", hostname, packageName, time.Now().Format("2006-01-02"))
|
||||
hash := sha256.Sum256([]byte(data))
|
||||
return hex.EncodeToString(hash[:])
|
||||
}
|
||||
|
||||
// TrackInstallation отправляет статистику установки пакета
|
||||
func TrackInstallation(ctx context.Context, packageName string, installType string) {
|
||||
// Запускаем в отдельной горутине, чтобы не блокировать основной процесс
|
||||
go func() {
|
||||
data := InstallationData{
|
||||
PackageName: packageName,
|
||||
InstallType: installType,
|
||||
UserAgent: userAgent,
|
||||
Fingerprint: generateFingerprint(packageName),
|
||||
}
|
||||
|
||||
jsonData, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return // Тихо игнорируем ошибки - статистика не критична
|
||||
}
|
||||
|
||||
// Пробуем отправить запрос к разным endpoint-ам
|
||||
for _, endpoint := range apiEndpoints {
|
||||
if sendRequest(endpoint, jsonData) {
|
||||
return // Если хотя бы один запрос прошёл успешно, выходим
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func sendRequest(endpoint string, data []byte) bool {
|
||||
client := &http.Client{
|
||||
Timeout: 5 * time.Second,
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", endpoint, bytes.NewBuffer(data))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("User-Agent", userAgent)
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return resp.StatusCode >= 200 && resp.StatusCode < 300
|
||||
}
|
||||
|
||||
// ShouldTrackPackage проверяет, нужно ли отслеживать установку этого пакета
|
||||
func ShouldTrackPackage(packageName string) bool {
|
||||
// Отслеживаем только alr-bin
|
||||
return strings.Contains(packageName, "alr-bin")
|
||||
}
|
@@ -21,13 +21,14 @@ package types
|
||||
|
||||
// Config represents the ALR configuration file
|
||||
type Config struct {
|
||||
RootCmd string `json:"rootCmd" koanf:"rootCmd"`
|
||||
UseRootCmd bool `json:"useRootCmd" koanf:"useRootCmd"`
|
||||
PagerStyle string `json:"pagerStyle" koanf:"pagerStyle"`
|
||||
IgnorePkgUpdates []string `json:"ignorePkgUpdates" koanf:"ignorePkgUpdates"`
|
||||
Repos []Repo `json:"repo" koanf:"repo"`
|
||||
AutoPull bool `json:"autoPull" koanf:"autoPull"`
|
||||
LogLevel string `json:"logLevel" koanf:"logLevel"`
|
||||
RootCmd string `json:"rootCmd" koanf:"rootCmd"`
|
||||
UseRootCmd bool `json:"useRootCmd" koanf:"useRootCmd"`
|
||||
PagerStyle string `json:"pagerStyle" koanf:"pagerStyle"`
|
||||
IgnorePkgUpdates []string `json:"ignorePkgUpdates" koanf:"ignorePkgUpdates"`
|
||||
Repos []Repo `json:"repo" koanf:"repo"`
|
||||
AutoPull bool `json:"autoPull" koanf:"autoPull"`
|
||||
LogLevel string `json:"logLevel" koanf:"logLevel"`
|
||||
UpdateSystemOnUpgrade bool `json:"updateSystemOnUpgrade" koanf:"updateSystemOnUpgrade"`
|
||||
}
|
||||
|
||||
// Repo represents a ALR repo within a configuration file
|
||||
|
@@ -56,6 +56,31 @@ installPkg() {
|
||||
esac
|
||||
}
|
||||
|
||||
trackInstallation() {
|
||||
# Отправить статистику установки (не критично если не получится)
|
||||
if command -v curl &>/dev/null; then
|
||||
# Генерируем уникальный отпечаток на основе hostname и даты
|
||||
fingerprint=$(echo "$(hostname)_$(date +%Y-%m-%d)" | sha256sum 2>/dev/null | cut -d' ' -f1 || echo "$(hostname)_$(date +%Y-%m-%d)")
|
||||
|
||||
# Пробуем разные домены/порты для отправки статистики
|
||||
for api_url in "https://alr.plemya-x.ru/api/packages/track-install" "http://localhost:3001/api/packages/track-install"; do
|
||||
curl -s -m 5 -X POST "$api_url" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "User-Agent: ALR-InstallScript/1.0" \
|
||||
-d "{
|
||||
\"packageName\": \"alr-bin\",
|
||||
\"installType\": \"script\",
|
||||
\"userAgent\": \"ALR-InstallScript/1.0\",
|
||||
\"fingerprint\": \"$fingerprint\"
|
||||
}" >/dev/null 2>&1
|
||||
# Если один запрос удался, не пробуем остальные
|
||||
if [ $? -eq 0 ]; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
if ! command -v curl &>/dev/null; then
|
||||
error "Этот скрипт требует команду curl. Пожалуйста, установите её и запустите снова."
|
||||
fi
|
||||
@@ -142,16 +167,15 @@ if [ -z "$noPkgMgr" ]; then
|
||||
info "Получен список файлов релиза"
|
||||
|
||||
if [ "$pkgMgr" == "pacman" ]; then
|
||||
latestFile=$(echo "$fileList" | grep -E "alr-bin-.*\.pkg\.tar\.zst" | sort -V | tail -n 1)
|
||||
latestFile=$(echo "$fileList" | grep -E "alr-bin.*\.pkg\.tar\.zst" | sort -V | tail -n 1)
|
||||
elif [ "$pkgMgr" == "apt" ]; then
|
||||
latestFile=$(echo "$fileList" | grep -E "alr-bin-.*\.${debArch}\.deb" | sort -V | tail -n 1)
|
||||
latestFile=$(echo "$fileList" | grep -E "alr-bin.*\.${debArch}\.deb" | sort -V | tail -n 1)
|
||||
elif [[ "$pkgMgr" == "dnf" || "$pkgMgr" == "yum" || "$pkgMgr" == "zypper" ]]; then
|
||||
latestFile=$(echo "$fileList" | grep -E "alr-bin-.*\.${rpmArch}\.rpm" | grep -v 'alt[0-9]*' | sort -V | tail -n 1)
|
||||
latestFile=$(echo "$fileList" | grep -E "alr-bin.*\.${rpmArch}\.rpm" | grep -v 'alt[0-9]*' | sort -V | tail -n 1)
|
||||
elif [ "$pkgMgr" == "apt-get" ]; then
|
||||
# ALT Linux использует RPM с особой маркировкой
|
||||
latestFile=$(echo "$fileList" | grep -E "alr-bin-.*-alt[0-9]+\.${rpmArch}\.rpm" | sort -V | tail -n 1)
|
||||
latestFile=$(echo "$fileList" | grep -E "alr-bin.*-alt[0-9]+\.${rpmArch}\.rpm" | sort -V | tail -n 1)
|
||||
elif [ "$pkgMgr" == "apk" ]; then
|
||||
latestFile=$(echo "$fileList" | grep -E "alr-bin-.*\.apk" | sort -V | tail -n 1)
|
||||
latestFile=$(echo "$fileList" | grep -E "alr-bin.*\.apk" | sort -V | tail -n 1)
|
||||
else
|
||||
error "Не поддерживаемый менеджер пакетов для автоматической установки"
|
||||
fi
|
||||
@@ -187,6 +211,9 @@ if [ -z "$noPkgMgr" ]; then
|
||||
info "Установка пакета ALR"
|
||||
installPkg "$pkgMgr" "$fname"
|
||||
|
||||
# Отправляем статистику установки
|
||||
trackInstallation
|
||||
|
||||
info "Очистка"
|
||||
rm -f "$fname"
|
||||
trap - EXIT
|
||||
|
13
upgrade.go
13
upgrade.go
@@ -84,6 +84,19 @@ func UpgradeCmd() *cli.Command {
|
||||
}
|
||||
defer deps.Defer()
|
||||
|
||||
// Обновляем систему, если это включено в конфигурации
|
||||
if deps.Cfg.UpdateSystemOnUpgrade() {
|
||||
slog.Info(gotext.Get("Updating system packages..."))
|
||||
err = deps.Manager.UpgradeAll(&manager.Opts{
|
||||
NoConfirm: !c.Bool("interactive"),
|
||||
Args: manager.Args,
|
||||
})
|
||||
if err != nil {
|
||||
return cliutils.FormatCliExit(gotext.Get("Error updating system packages"), err)
|
||||
}
|
||||
slog.Info(gotext.Get("System packages updated successfully"))
|
||||
}
|
||||
|
||||
builder, err := build.NewMainBuilder(
|
||||
deps.Cfg,
|
||||
deps.Manager,
|
||||
|
Reference in New Issue
Block a user