Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
8bc82cb95c | |||
9783ce37de | |||
b852688ab0 |
@@ -76,6 +76,7 @@ var configKeys = []string{
|
|||||||
"autoPull",
|
"autoPull",
|
||||||
"logLevel",
|
"logLevel",
|
||||||
"ignorePkgUpdates",
|
"ignorePkgUpdates",
|
||||||
|
"updateSystemOnUpgrade",
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetConfig() *cli.Command {
|
func SetConfig() *cli.Command {
|
||||||
@@ -137,6 +138,12 @@ func SetConfig() *cli.Command {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
deps.Cfg.System.SetIgnorePkgUpdates(updates)
|
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":
|
case "repo", "repos":
|
||||||
return cliutils.FormatCliExit(gotext.Get("use 'repo add/remove' commands to manage repositories"), nil)
|
return cliutils.FormatCliExit(gotext.Get("use 'repo add/remove' commands to manage repositories"), nil)
|
||||||
default:
|
default:
|
||||||
@@ -206,6 +213,8 @@ func GetConfig() *cli.Command {
|
|||||||
} else {
|
} else {
|
||||||
fmt.Println(strings.Join(updates, ", "))
|
fmt.Println(strings.Join(updates, ", "))
|
||||||
}
|
}
|
||||||
|
case "updateSystemOnUpgrade":
|
||||||
|
fmt.Println(deps.Cfg.UpdateSystemOnUpgrade())
|
||||||
case "repo", "repos":
|
case "repo", "repos":
|
||||||
repos := deps.Cfg.Repos()
|
repos := deps.Cfg.Repos()
|
||||||
if len(repos) == 0 {
|
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/cliutils"
|
||||||
"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/manager"
|
"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/alrsh"
|
||||||
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
|
"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"
|
||||||
@@ -401,9 +402,19 @@ func (b *Builder) BuildPackage(
|
|||||||
|
|
||||||
// We filter so as not to re-build what has already been built at the `installBuildDeps` stage.
|
// We filter so as not to re-build what has already been built at the `installBuildDeps` stage.
|
||||||
var filteredDepends []string
|
var filteredDepends []string
|
||||||
|
|
||||||
|
// Создаем набор подпакетов текущего мультипакета для исключения циклических зависимостей
|
||||||
|
currentPackageNames := make(map[string]struct{})
|
||||||
|
for _, pkg := range input.packages {
|
||||||
|
currentPackageNames[pkg] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
for _, d := range depends {
|
for _, d := range depends {
|
||||||
if _, found := depNames[d]; !found {
|
if _, found := depNames[d]; !found {
|
||||||
filteredDepends = append(filteredDepends, d)
|
// Исключаем зависимости, которые являются подпакетами текущего мультипакета
|
||||||
|
if _, isCurrentPackage := currentPackageNames[d]; !isCurrentPackage {
|
||||||
|
filteredDepends = append(filteredDepends, d)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -528,6 +539,13 @@ func (b *Builder) InstallALRPackages(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Отслеживание установки ALR пакетов
|
||||||
|
for _, dep := range res {
|
||||||
|
if stats.ShouldTrackPackage(dep.Name) {
|
||||||
|
stats.TrackInstallation(ctx, dep.Name, "upgrade")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -552,11 +570,13 @@ func (b *Builder) BuildALRDeps(
|
|||||||
repoDeps = notFound
|
repoDeps = notFound
|
||||||
|
|
||||||
// Если для некоторых пакетов есть несколько опций, упрощаем их все в один срез
|
// Если для некоторых пакетов есть несколько опций, упрощаем их все в один срез
|
||||||
pkgs := cliutils.FlattenPkgs(
|
// Для зависимостей указываем isDependency = true
|
||||||
|
pkgs := cliutils.FlattenPkgsWithContext(
|
||||||
ctx,
|
ctx,
|
||||||
found,
|
found,
|
||||||
"install",
|
"install",
|
||||||
input.BuildOpts().Interactive,
|
input.BuildOpts().Interactive,
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
type item struct {
|
type item struct {
|
||||||
pkg *alrsh.Package
|
pkg *alrsh.Package
|
||||||
@@ -691,6 +711,13 @@ func (i *Builder) InstallPkgs(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Отслеживание установки локальных пакетов
|
||||||
|
for _, dep := range builtDeps {
|
||||||
|
if stats.ShouldTrackPackage(dep.Name) {
|
||||||
|
stats.TrackInstallation(ctx, dep.Name, "install")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(repoDeps) > 0 {
|
if len(repoDeps) > 0 {
|
||||||
@@ -700,6 +727,13 @@ func (i *Builder) InstallPkgs(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Отслеживание установки пакетов из репозитория
|
||||||
|
for _, pkg := range repoDeps {
|
||||||
|
if stats.ShouldTrackPackage(pkg) {
|
||||||
|
stats.TrackInstallation(ctx, pkg, "install")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return builtDeps, nil
|
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
|
// 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.
|
// 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 {
|
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
|
var outPkgs []alrsh.Package
|
||||||
for _, pkgs := range found {
|
for _, pkgs := range found {
|
||||||
if len(pkgs) > 1 && interactive {
|
if len(pkgs) > 1 {
|
||||||
choice, err := PkgPrompt(ctx, pkgs, verb, interactive)
|
// Проверяем, являются ли пакеты подпакетами одного мультипакета
|
||||||
if err != nil {
|
if isMultiPackage(pkgs) && verb == "install" {
|
||||||
slog.Error(gotext.Get("Error prompting for choice of package"))
|
// Для мультипакетов при установке ВСЕГДА берем все подпакеты без выбора
|
||||||
os.Exit(1)
|
// Это правильное поведение как для прямой установки, так и для зависимостей
|
||||||
|
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 {
|
||||||
} else if len(pkgs) == 1 || !interactive {
|
// Если только один пакет - берем его
|
||||||
outPkgs = append(outPkgs, pkgs[0])
|
outPkgs = append(outPkgs, pkgs[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return outPkgs
|
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.
|
// PkgPrompt asks the user to choose between multiple packages.
|
||||||
func PkgPrompt(ctx context.Context, options []alrsh.Package, verb string, interactive bool) (alrsh.Package, error) {
|
func PkgPrompt(ctx context.Context, options []alrsh.Package, verb string, interactive bool) (alrsh.Package, error) {
|
||||||
if !interactive {
|
if !interactive {
|
||||||
|
@@ -56,6 +56,7 @@ func defaultConfigKoanf() *koanf.Koanf {
|
|||||||
"ignorePkgUpdates": []string{},
|
"ignorePkgUpdates": []string{},
|
||||||
"logLevel": "info",
|
"logLevel": "info",
|
||||||
"autoPull": true,
|
"autoPull": true,
|
||||||
|
"updateSystemOnUpgrade": false,
|
||||||
"repos": []types.Repo{
|
"repos": []types.Repo{
|
||||||
{
|
{
|
||||||
Name: "alr-default",
|
Name: "alr-default",
|
||||||
@@ -114,6 +115,11 @@ func (c *ALRConfig) Load() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Выполняем миграцию конфигурации при необходимости
|
||||||
|
if err := c.migrateConfig(); err != nil {
|
||||||
|
return fmt.Errorf("failed to migrate config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,6 +131,45 @@ func (c *ALRConfig) ToYAML() (string, error) {
|
|||||||
return string(data), nil
|
return string(data), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ALRConfig) migrateConfig() error {
|
||||||
|
// Проверяем, существует ли конфигурационный файл
|
||||||
|
if _, err := os.Stat(constants.SystemConfigPath); os.IsNotExist(err) {
|
||||||
|
// Если файла нет, но конфигурация уже загружена (из defaults или env),
|
||||||
|
// создаем файл с настройкой по умолчанию
|
||||||
|
needsCreation := false
|
||||||
|
|
||||||
|
// Проверяем, установлена ли переменная окружения ALR_UPDATESYSTEMONUPGRADE
|
||||||
|
if os.Getenv("ALR_UPDATESYSTEMONUPGRADE") == "" {
|
||||||
|
// Если переменная не установлена, проверяем наличие пакетов ALR
|
||||||
|
// чтобы определить, нужно ли включить эту опцию для обновления
|
||||||
|
needsCreation = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if needsCreation {
|
||||||
|
// Устанавливаем значение false по умолчанию для новой опции
|
||||||
|
c.System.SetUpdateSystemOnUpgrade(false)
|
||||||
|
// Сохраняем конфигурацию
|
||||||
|
if err := c.System.Save(); err != nil {
|
||||||
|
// Если не удается сохранить - это не критично, продолжаем работу
|
||||||
|
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) RootCmd() string { return c.cfg.RootCmd }
|
func (c *ALRConfig) RootCmd() string { return c.cfg.RootCmd }
|
||||||
func (c *ALRConfig) PagerStyle() string { return c.cfg.PagerStyle }
|
func (c *ALRConfig) PagerStyle() string { return c.cfg.PagerStyle }
|
||||||
func (c *ALRConfig) AutoPull() bool { return c.cfg.AutoPull }
|
func (c *ALRConfig) AutoPull() bool { return c.cfg.AutoPull }
|
||||||
@@ -133,4 +178,5 @@ func (c *ALRConfig) SetRepos(repos []types.Repo) { c.System.SetRepos(repos) }
|
|||||||
func (c *ALRConfig) IgnorePkgUpdates() []string { return c.cfg.IgnorePkgUpdates }
|
func (c *ALRConfig) IgnorePkgUpdates() []string { return c.cfg.IgnorePkgUpdates }
|
||||||
func (c *ALRConfig) LogLevel() string { return c.cfg.LogLevel }
|
func (c *ALRConfig) LogLevel() string { return c.cfg.LogLevel }
|
||||||
func (c *ALRConfig) UseRootCmd() bool { return c.cfg.UseRootCmd }
|
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 }
|
func (c *ALRConfig) GetPaths() *Paths { return c.paths }
|
||||||
|
@@ -142,3 +142,10 @@ func (c *SystemConfig) SetRepos(v []types.Repo) {
|
|||||||
panic(err)
|
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)
|
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 {
|
if len(result) == 0 {
|
||||||
result, err = rs.db.GetPkgs(ctx, "name LIKE ?", pkgName)
|
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
|
// Config represents the ALR configuration file
|
||||||
type Config struct {
|
type Config struct {
|
||||||
RootCmd string `json:"rootCmd" koanf:"rootCmd"`
|
RootCmd string `json:"rootCmd" koanf:"rootCmd"`
|
||||||
UseRootCmd bool `json:"useRootCmd" koanf:"useRootCmd"`
|
UseRootCmd bool `json:"useRootCmd" koanf:"useRootCmd"`
|
||||||
PagerStyle string `json:"pagerStyle" koanf:"pagerStyle"`
|
PagerStyle string `json:"pagerStyle" koanf:"pagerStyle"`
|
||||||
IgnorePkgUpdates []string `json:"ignorePkgUpdates" koanf:"ignorePkgUpdates"`
|
IgnorePkgUpdates []string `json:"ignorePkgUpdates" koanf:"ignorePkgUpdates"`
|
||||||
Repos []Repo `json:"repo" koanf:"repo"`
|
Repos []Repo `json:"repo" koanf:"repo"`
|
||||||
AutoPull bool `json:"autoPull" koanf:"autoPull"`
|
AutoPull bool `json:"autoPull" koanf:"autoPull"`
|
||||||
LogLevel string `json:"logLevel" koanf:"logLevel"`
|
LogLevel string `json:"logLevel" koanf:"logLevel"`
|
||||||
|
UpdateSystemOnUpgrade bool `json:"updateSystemOnUpgrade" koanf:"updateSystemOnUpgrade"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Repo represents a ALR repo within a configuration file
|
// Repo represents a ALR repo within a configuration file
|
||||||
|
@@ -56,6 +56,31 @@ installPkg() {
|
|||||||
esac
|
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
|
if ! command -v curl &>/dev/null; then
|
||||||
error "Этот скрипт требует команду curl. Пожалуйста, установите её и запустите снова."
|
error "Этот скрипт требует команду curl. Пожалуйста, установите её и запустите снова."
|
||||||
fi
|
fi
|
||||||
@@ -142,16 +167,15 @@ if [ -z "$noPkgMgr" ]; then
|
|||||||
info "Получен список файлов релиза"
|
info "Получен список файлов релиза"
|
||||||
|
|
||||||
if [ "$pkgMgr" == "pacman" ]; then
|
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
|
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
|
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
|
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
|
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
|
else
|
||||||
error "Не поддерживаемый менеджер пакетов для автоматической установки"
|
error "Не поддерживаемый менеджер пакетов для автоматической установки"
|
||||||
fi
|
fi
|
||||||
@@ -187,6 +211,9 @@ if [ -z "$noPkgMgr" ]; then
|
|||||||
info "Установка пакета ALR"
|
info "Установка пакета ALR"
|
||||||
installPkg "$pkgMgr" "$fname"
|
installPkg "$pkgMgr" "$fname"
|
||||||
|
|
||||||
|
# Отправляем статистику установки
|
||||||
|
trackInstallation
|
||||||
|
|
||||||
info "Очистка"
|
info "Очистка"
|
||||||
rm -f "$fname"
|
rm -f "$fname"
|
||||||
trap - EXIT
|
trap - EXIT
|
||||||
|
13
upgrade.go
13
upgrade.go
@@ -84,6 +84,19 @@ func UpgradeCmd() *cli.Command {
|
|||||||
}
|
}
|
||||||
defer deps.Defer()
|
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(
|
builder, err := build.NewMainBuilder(
|
||||||
deps.Cfg,
|
deps.Cfg,
|
||||||
deps.Manager,
|
deps.Manager,
|
||||||
|
Reference in New Issue
Block a user