Compare commits

...

2 Commits

Author SHA1 Message Date
Maxim Slipenko
cfb3764412 lint: run make fmt 2024-12-28 12:02:20 +03:00
Maxim Slipenko
df87bfcc63 lint: add golangci-lint 2024-12-28 12:02:05 +03:00
33 changed files with 251 additions and 194 deletions

26
.golangci.yml Normal file

@ -0,0 +1,26 @@
run:
timeout: 5m
linters-settings:
goimports:
local-prefixes: "plemya-x.ru/alr"
gofmt:
simplify: true
gofumpt:
extra-rules: true
linters:
enable:
- gofmt
- gofumpt
- goimports
- gocritic
- govet
- staticcheck
- unused
- errcheck
- typecheck
# - forbidigo
issues:
fix: true

@ -11,6 +11,8 @@ ZSH_COMPLETION := $(COMPLETIONS_DIR)/zsh
INSTALLED_BASH_COMPLETION := $(DESTDIR)$(PREFIX)/share/bash-completion/completions/$(NAME) INSTALLED_BASH_COMPLETION := $(DESTDIR)$(PREFIX)/share/bash-completion/completions/$(NAME)
INSTALLED_ZSH_COMPLETION := $(DESTDIR)$(PREFIX)/share/zsh/site-functions/_$(NAME) INSTALLED_ZSH_COMPLETION := $(DESTDIR)$(PREFIX)/share/zsh/site-functions/_$(NAME)
GOLANGCI_LINT := go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.2
.PHONY: build install clean clear uninstall check-no-root .PHONY: build install clean clear uninstall check-no-root
build: check-no-root $(BIN) build: check-no-root $(BIN)
@ -25,6 +27,10 @@ check-no-root:
exit 1; \ exit 1; \
fi fi
# TODO: remove --tests=false
fmt:
$(GOLANGCI_LINT) run --fix --tests=false
install: \ install: \
$(INSTALED_BIN) \ $(INSTALED_BIN) \
$(INSTALLED_BASH_COMPLETION) \ $(INSTALLED_BASH_COMPLETION) \

@ -23,6 +23,7 @@ import (
"path/filepath" "path/filepath"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"plemya-x.ru/alr/internal/config" "plemya-x.ru/alr/internal/config"
"plemya-x.ru/alr/internal/osutils" "plemya-x.ru/alr/internal/osutils"
"plemya-x.ru/alr/internal/types" "plemya-x.ru/alr/internal/types"

1
fix.go

@ -22,6 +22,7 @@ import (
"os" "os"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"plemya-x.ru/alr/internal/config" "plemya-x.ru/alr/internal/config"
"plemya-x.ru/alr/internal/db" "plemya-x.ru/alr/internal/db"
"plemya-x.ru/alr/pkg/loggerctx" "plemya-x.ru/alr/pkg/loggerctx"

1
gen.go

@ -4,6 +4,7 @@ import (
"os" "os"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"plemya-x.ru/alr/pkg/gen" "plemya-x.ru/alr/pkg/gen"
) )

@ -6,12 +6,13 @@ import (
"strings" "strings"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"mvdan.cc/sh/v3/expand"
"mvdan.cc/sh/v3/interp"
"plemya-x.ru/alr/internal/cpu" "plemya-x.ru/alr/internal/cpu"
"plemya-x.ru/alr/internal/shutils/helpers" "plemya-x.ru/alr/internal/shutils/helpers"
"plemya-x.ru/alr/pkg/distro" "plemya-x.ru/alr/pkg/distro"
"plemya-x.ru/alr/pkg/loggerctx" "plemya-x.ru/alr/pkg/loggerctx"
"mvdan.cc/sh/v3/expand"
"mvdan.cc/sh/v3/interp"
) )
var helperCmd = &cli.Command{ var helperCmd = &cli.Command{

@ -23,13 +23,14 @@ import (
"os" "os"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"gopkg.in/yaml.v3"
"plemya-x.ru/alr/internal/cliutils" "plemya-x.ru/alr/internal/cliutils"
"plemya-x.ru/alr/internal/config" "plemya-x.ru/alr/internal/config"
"plemya-x.ru/alr/internal/overrides" "plemya-x.ru/alr/internal/overrides"
"plemya-x.ru/alr/pkg/distro" "plemya-x.ru/alr/pkg/distro"
"plemya-x.ru/alr/pkg/loggerctx" "plemya-x.ru/alr/pkg/loggerctx"
"plemya-x.ru/alr/pkg/repos" "plemya-x.ru/alr/pkg/repos"
"gopkg.in/yaml.v3"
) )
var infoCmd = &cli.Command{ var infoCmd = &cli.Command{

@ -22,6 +22,7 @@ import (
"fmt" "fmt"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"plemya-x.ru/alr/internal/cliutils" "plemya-x.ru/alr/internal/cliutils"
"plemya-x.ru/alr/internal/config" "plemya-x.ru/alr/internal/config"
"plemya-x.ru/alr/internal/db" "plemya-x.ru/alr/internal/db"

@ -24,6 +24,7 @@ import (
"strings" "strings"
"github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2"
"plemya-x.ru/alr/internal/config" "plemya-x.ru/alr/internal/config"
"plemya-x.ru/alr/internal/db" "plemya-x.ru/alr/internal/db"
"plemya-x.ru/alr/internal/pager" "plemya-x.ru/alr/internal/pager"

@ -24,6 +24,7 @@ import (
"sync" "sync"
"github.com/pelletier/go-toml/v2" "github.com/pelletier/go-toml/v2"
"plemya-x.ru/alr/internal/types" "plemya-x.ru/alr/internal/types"
"plemya-x.ru/alr/pkg/loggerctx" "plemya-x.ru/alr/pkg/loggerctx"
) )

@ -24,8 +24,9 @@ import (
"strings" "strings"
"sync" "sync"
"plemya-x.ru/alr/pkg/loggerctx"
"golang.org/x/text/language" "golang.org/x/text/language"
"plemya-x.ru/alr/pkg/loggerctx"
) )
var ( var (

@ -25,6 +25,7 @@ import (
"sync" "sync"
"github.com/pelletier/go-toml/v2" "github.com/pelletier/go-toml/v2"
"plemya-x.ru/alr/pkg/loggerctx" "plemya-x.ru/alr/pkg/loggerctx"
) )

@ -28,10 +28,11 @@ import (
"sync" "sync"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"plemya-x.ru/alr/internal/config"
"plemya-x.ru/alr/pkg/loggerctx"
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
"modernc.org/sqlite" "modernc.org/sqlite"
"plemya-x.ru/alr/internal/config"
"plemya-x.ru/alr/pkg/loggerctx"
) )
// CurrentVersion is the current version of the database. // CurrentVersion is the current version of the database.

@ -14,7 +14,7 @@
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
// Пакет dl содержит абстракции для загрузки файлов и каталогов // Пакет dl содержит абстракции для загрузки файлов и каталогов
// из различных источников. // из различных источников.
@ -39,6 +39,7 @@ import (
"golang.org/x/crypto/blake2b" "golang.org/x/crypto/blake2b"
"golang.org/x/crypto/blake2s" "golang.org/x/crypto/blake2s"
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
"plemya-x.ru/alr/internal/dlcache" "plemya-x.ru/alr/internal/dlcache"
"plemya-x.ru/alr/pkg/loggerctx" "plemya-x.ru/alr/pkg/loggerctx"
) )
@ -299,8 +300,6 @@ func linkDir(src, dest string) error {
return nil return nil
} }
rel, err := filepath.Rel(src, path) rel, err := filepath.Rel(src, path)
if err != nil { if err != nil {
return err return err

@ -33,6 +33,7 @@ import (
"github.com/mholt/archiver/v4" "github.com/mholt/archiver/v4"
"github.com/schollz/progressbar/v3" "github.com/schollz/progressbar/v3"
"plemya-x.ru/alr/internal/shutils/handlers" "plemya-x.ru/alr/internal/shutils/handlers"
) )

@ -22,11 +22,12 @@ import (
"reflect" "reflect"
"strings" "strings"
"golang.org/x/exp/slices"
"golang.org/x/text/language"
"plemya-x.ru/alr/internal/cpu" "plemya-x.ru/alr/internal/cpu"
"plemya-x.ru/alr/internal/db" "plemya-x.ru/alr/internal/db"
"plemya-x.ru/alr/pkg/distro" "plemya-x.ru/alr/pkg/distro"
"golang.org/x/exp/slices"
"golang.org/x/text/language"
) )
type Opts struct { type Opts struct {

@ -25,12 +25,13 @@ import (
"strings" "strings"
"github.com/mitchellh/mapstructure" "github.com/mitchellh/mapstructure"
"plemya-x.ru/alr/internal/overrides"
"plemya-x.ru/alr/pkg/distro"
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
"mvdan.cc/sh/v3/expand" "mvdan.cc/sh/v3/expand"
"mvdan.cc/sh/v3/interp" "mvdan.cc/sh/v3/interp"
"mvdan.cc/sh/v3/syntax" "mvdan.cc/sh/v3/syntax"
"plemya-x.ru/alr/internal/overrides"
"plemya-x.ru/alr/pkg/distro"
) )
var ErrNotPointerToStruct = errors.New("val must be a pointer to a struct") var ErrNotPointerToStruct = errors.New("val must be a pointer to a struct")

@ -10,9 +10,9 @@ import (
"syscall" "syscall"
"time" "time"
"plemya-x.ru/fakeroot"
"mvdan.cc/sh/v3/expand" "mvdan.cc/sh/v3/expand"
"mvdan.cc/sh/v3/interp" "mvdan.cc/sh/v3/interp"
"plemya-x.ru/fakeroot"
) )
// FakerootExecHandler was extracted from github.com/mvdan/sh/interp/handler.go // FakerootExecHandler was extracted from github.com/mvdan/sh/interp/handler.go

@ -31,8 +31,9 @@ import (
"github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing/object" "github.com/go-git/go-git/v5/plumbing/object"
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
"plemya-x.ru/alr/internal/shutils/handlers"
"mvdan.cc/sh/v3/interp" "mvdan.cc/sh/v3/interp"
"plemya-x.ru/alr/internal/shutils/handlers"
) )
var ( var (

@ -24,9 +24,10 @@ import (
"sync" "sync"
"go.elara.ws/logger" "go.elara.ws/logger"
"plemya-x.ru/alr/pkg/loggerctx"
"go.elara.ws/translate" "go.elara.ws/translate"
"golang.org/x/text/language" "golang.org/x/text/language"
"plemya-x.ru/alr/pkg/loggerctx"
) )
//go:embed files //go:embed files

@ -22,12 +22,13 @@ import (
"fmt" "fmt"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"golang.org/x/exp/slices"
"plemya-x.ru/alr/internal/config" "plemya-x.ru/alr/internal/config"
"plemya-x.ru/alr/internal/db" "plemya-x.ru/alr/internal/db"
"plemya-x.ru/alr/pkg/loggerctx" "plemya-x.ru/alr/pkg/loggerctx"
"plemya-x.ru/alr/pkg/manager" "plemya-x.ru/alr/pkg/manager"
"plemya-x.ru/alr/pkg/repos" "plemya-x.ru/alr/pkg/repos"
"golang.org/x/exp/slices"
) )
var listCmd = &cli.Command{ var listCmd = &cli.Command{

@ -28,6 +28,7 @@ import (
"github.com/mattn/go-isatty" "github.com/mattn/go-isatty"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"go.elara.ws/logger" "go.elara.ws/logger"
"plemya-x.ru/alr/internal/config" "plemya-x.ru/alr/internal/config"
"plemya-x.ru/alr/internal/db" "plemya-x.ru/alr/internal/db"
"plemya-x.ru/alr/internal/translations" "plemya-x.ru/alr/internal/translations"

@ -52,6 +52,7 @@ import (
"github.com/goreleaser/nfpm/v2" "github.com/goreleaser/nfpm/v2"
"github.com/goreleaser/nfpm/v2/files" "github.com/goreleaser/nfpm/v2/files"
"plemya-x.ru/alr/internal/cliutils" "plemya-x.ru/alr/internal/cliutils"
"plemya-x.ru/alr/internal/config" "plemya-x.ru/alr/internal/config"
"plemya-x.ru/alr/internal/cpu" "plemya-x.ru/alr/internal/cpu"

@ -8,6 +8,7 @@ import (
"strings" "strings"
"github.com/goreleaser/nfpm/v2" "github.com/goreleaser/nfpm/v2"
"plemya-x.ru/alr/internal/types" "plemya-x.ru/alr/internal/types"
"plemya-x.ru/alr/pkg/loggerctx" "plemya-x.ru/alr/pkg/loggerctx"
) )

@ -46,15 +46,15 @@ func InstallPkgs(ctx context.Context, alrPkgs []db.Package, nativePkgs []string,
if len(nativePkgs) > 0 { if len(nativePkgs) > 0 {
err := opts.Manager.Install(nil, nativePkgs...) err := opts.Manager.Install(nil, nativePkgs...)
// Если есть нативные пакеты, выполняем их установку // Если есть нативные пакеты, выполняем их установку
if err != nil { if err != nil {
log.Fatal("Error installing native packages").Err(err).Send() log.Fatal("Error installing native packages").Err(err).Send()
// Логируем и завершаем выполнение при ошибке // Логируем и завершаем выполнение при ошибке
} }
} }
InstallScripts(ctx, GetScriptPaths(ctx, alrPkgs), opts) InstallScripts(ctx, GetScriptPaths(ctx, alrPkgs), opts)
// Устанавливаем скрипты сборки через функцию InstallScripts // Устанавливаем скрипты сборки через функцию InstallScripts
} }
// GetScriptPaths возвращает срез путей к скриптам, соответствующий // GetScriptPaths возвращает срез путей к скриптам, соответствующий
@ -62,7 +62,7 @@ func InstallPkgs(ctx context.Context, alrPkgs []db.Package, nativePkgs []string,
func GetScriptPaths(ctx context.Context, pkgs []db.Package) []string { func GetScriptPaths(ctx context.Context, pkgs []db.Package) []string {
var scripts []string var scripts []string
for _, pkg := range pkgs { for _, pkg := range pkgs {
// Для каждого пакета создаем путь к скрипту сборки // Для каждого пакета создаем путь к скрипту сборки
scriptPath := filepath.Join(config.GetPaths(ctx).RepoDir, pkg.Repository, pkg.Name, "alr.sh") scriptPath := filepath.Join(config.GetPaths(ctx).RepoDir, pkg.Repository, pkg.Name, "alr.sh")
scripts = append(scripts, scriptPath) scripts = append(scripts, scriptPath)
} }
@ -75,17 +75,17 @@ func InstallScripts(ctx context.Context, scripts []string, opts types.BuildOpts)
for _, script := range scripts { for _, script := range scripts {
opts.Script = script // Устанавливаем текущий скрипт в опции opts.Script = script // Устанавливаем текущий скрипт в опции
builtPkgs, _, err := BuildPackage(ctx, opts) builtPkgs, _, err := BuildPackage(ctx, opts)
// Выполняем сборку пакета // Выполняем сборку пакета
if err != nil { if err != nil {
log.Fatal("Error building package").Err(err).Send() log.Fatal("Error building package").Err(err).Send()
// Логируем и завершаем выполнение при ошибке сборки // Логируем и завершаем выполнение при ошибке сборки
} }
err = opts.Manager.InstallLocal(nil, builtPkgs...) err = opts.Manager.InstallLocal(nil, builtPkgs...)
// Устанавливаем локально собранные пакеты // Устанавливаем локально собранные пакеты
if err != nil { if err != nil {
log.Fatal("Error installing package").Err(err).Send() log.Fatal("Error installing package").Err(err).Send()
// Логируем и завершаем выполнение при ошибке установки // Логируем и завершаем выполнение при ошибке установки
} }
} }
} }

@ -23,10 +23,11 @@ import (
"os" "os"
"strings" "strings"
"plemya-x.ru/alr/internal/shutils/handlers"
"mvdan.cc/sh/v3/expand" "mvdan.cc/sh/v3/expand"
"mvdan.cc/sh/v3/interp" "mvdan.cc/sh/v3/interp"
"mvdan.cc/sh/v3/syntax" "mvdan.cc/sh/v3/syntax"
"plemya-x.ru/alr/internal/shutils/handlers"
) )
// OSRelease contains information from an os-release file // OSRelease contains information from an os-release file

@ -1,20 +1,20 @@
package gen package gen
import ( import (
"strings" "strings"
"text/template" "text/template"
) )
// Определяем переменную funcs типа template.FuncMap, которая будет использоваться для // Определяем переменную funcs типа template.FuncMap, которая будет использоваться для
// предоставления пользовательских функций в шаблонах // предоставления пользовательских функций в шаблонах
var funcs = template.FuncMap{ var funcs = template.FuncMap{
// Функция "tolower" использует strings.ToLower // Функция "tolower" использует strings.ToLower
// для преобразования строки в нижний регистр // для преобразования строки в нижний регистр
"tolower": strings.ToLower, "tolower": strings.ToLower,
// Функция "firstchar" — это лямбда-функция, которая берет строку // Функция "firstchar" — это лямбда-функция, которая берет строку
// и возвращает её первый символ // и возвращает её первый символ
"firstchar": func(s string) string { "firstchar": func(s string) string {
return s[:1] return s[:1]
}, },
} }

@ -1,98 +1,99 @@
package gen package gen
import ( import (
_ "embed" // Пакет для встраивания содержимого файлов в бинарники Go, использовав откладку //go:embed _ "embed" // Пакет для встраивания содержимого файлов в бинарники Go, использовав откладку //go:embed
"encoding/json" // Пакет для работы с JSON: декодирование и кодирование "encoding/json" // Пакет для работы с JSON: декодирование и кодирование
"errors" // Пакет для создания и обработки ошибок "errors" // Пакет для создания и обработки ошибок
"fmt" // Пакет для форматированного ввода и вывода "fmt" // Пакет для форматированного ввода и вывода
"io" // Пакет для интерфейсов ввода и вывода "io" // Пакет для интерфейсов ввода и вывода
"net/http" // Пакет для HTTP-клиентов и серверов "net/http" // Пакет для HTTP-клиентов и серверов
"text/template" // Пакет для обработки текстовых шаблонов "text/template" // Пакет для обработки текстовых шаблонов
) )
// Используем директиву //go:embed для встраивания содержимого файла шаблона в строку pipTmpl // Используем директиву //go:embed для встраивания содержимого файла шаблона в строку pipTmpl
// Встраивание файла tmpls/pip.tmpl.sh // Встраивание файла tmpls/pip.tmpl.sh
//
//go:embed tmpls/pip.tmpl.sh //go:embed tmpls/pip.tmpl.sh
var pipTmpl string var pipTmpl string
// PipOptions содержит параметры, которые будут переданы в шаблон // PipOptions содержит параметры, которые будут переданы в шаблон
type PipOptions struct { type PipOptions struct {
Name string // Имя пакета Name string // Имя пакета
Version string // Версия пакета Version string // Версия пакета
Description string // Описание пакета Description string // Описание пакета
} }
// pypiAPIResponse представляет структуру ответа от API PyPI // pypiAPIResponse представляет структуру ответа от API PyPI
type pypiAPIResponse struct { type pypiAPIResponse struct {
Info pypiInfo `json:"info"` // Информация о пакете Info pypiInfo `json:"info"` // Информация о пакете
URLs []pypiURL `json:"urls"` // Список URL-адресов для загрузки пакета URLs []pypiURL `json:"urls"` // Список URL-адресов для загрузки пакета
} }
// Метод SourceURL ищет и возвращает URL исходного distribution для пакета, если он существует // Метод SourceURL ищет и возвращает URL исходного distribution для пакета, если он существует
func (res pypiAPIResponse) SourceURL() (pypiURL, error) { func (res pypiAPIResponse) SourceURL() (pypiURL, error) {
for _, url := range res.URLs { for _, url := range res.URLs {
if url.PackageType == "sdist" { if url.PackageType == "sdist" {
return url, nil return url, nil
} }
} }
return pypiURL{}, errors.New("package doesn't have a source distribution") return pypiURL{}, errors.New("package doesn't have a source distribution")
} }
// pypiInfo содержит основную информацию о пакете, такую как имя, версия и пр. // pypiInfo содержит основную информацию о пакете, такую как имя, версия и пр.
type pypiInfo struct { type pypiInfo struct {
Name string `json:"name"` Name string `json:"name"`
Version string `json:"version"` Version string `json:"version"`
Summary string `json:"summary"` Summary string `json:"summary"`
Homepage string `json:"home_page"` Homepage string `json:"home_page"`
License string `json:"license"` License string `json:"license"`
} }
// pypiURL представляет информацию об одном из доступных для загрузки URL // pypiURL представляет информацию об одном из доступных для загрузки URL
type pypiURL struct { type pypiURL struct {
Digests map[string]string `json:"digests"` // Контрольные суммы для файлов Digests map[string]string `json:"digests"` // Контрольные суммы для файлов
Filename string `json:"filename"` // Имя файла Filename string `json:"filename"` // Имя файла
PackageType string `json:"packagetype"` // Тип пакета (например sdist) PackageType string `json:"packagetype"` // Тип пакета (например sdist)
} }
// Функция Pip загружает информацию о пакете из PyPI и использует шаблон для вывода информации // Функция Pip загружает информацию о пакете из PyPI и использует шаблон для вывода информации
func Pip(w io.Writer, opts PipOptions) error { func Pip(w io.Writer, opts PipOptions) error {
// Создаем новый шаблон с добавлением функций из FuncMap // Создаем новый шаблон с добавлением функций из FuncMap
tmpl, err := template.New("pip"). tmpl, err := template.New("pip").
Funcs(funcs). Funcs(funcs).
Parse(pipTmpl) Parse(pipTmpl)
if err != nil { if err != nil {
return err return err
} }
// Формируем URL для запроса к PyPI на основании имени и версии пакета // Формируем URL для запроса к PyPI на основании имени и версии пакета
url := fmt.Sprintf( url := fmt.Sprintf(
"https://pypi.org/pypi/%s/%s/json", "https://pypi.org/pypi/%s/%s/json",
opts.Name, opts.Name,
opts.Version, opts.Version,
) )
// Выполняем HTTP GET запрос к PyPI // Выполняем HTTP GET запрос к PyPI
res, err := http.Get(url) res, err := http.Get(url)
if err != nil { if err != nil {
return err return err
} }
defer res.Body.Close() // Закрываем тело ответа после завершения работы defer res.Body.Close() // Закрываем тело ответа после завершения работы
if res.StatusCode != 200 { if res.StatusCode != 200 {
return fmt.Errorf("pypi: %s", res.Status) return fmt.Errorf("pypi: %s", res.Status)
} }
// Раскодируем ответ JSON от PyPI в структуру pypiAPIResponse // Раскодируем ответ JSON от PyPI в структуру pypiAPIResponse
var resp pypiAPIResponse var resp pypiAPIResponse
err = json.NewDecoder(res.Body).Decode(&resp) err = json.NewDecoder(res.Body).Decode(&resp)
if err != nil { if err != nil {
return err return err
} }
// Если в opts указано описание, используем его вместо описания из PyPI // Если в opts указано описание, используем его вместо описания из PyPI
if opts.Description != "" { if opts.Description != "" {
resp.Info.Summary = opts.Description resp.Info.Summary = opts.Description
} }
// Выполняем шаблон с использованием данных из resp и записываем результат в w // Выполняем шаблон с использованием данных из resp и записываем результат в w
return tmpl.Execute(w, resp) return tmpl.Execute(w, resp)
} }

@ -107,6 +107,7 @@ func (a *APTRpm) UpgradeAll(opts *Opts) error {
} }
return nil return nil
} }
func (y *APTRpm) ListInstalled(opts *Opts) (map[string]string, error) { func (y *APTRpm) ListInstalled(opts *Opts) (map[string]string, error) {
out := map[string]string{} out := map[string]string{}
cmd := exec.Command("rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n") cmd := exec.Command("rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")

@ -19,154 +19,154 @@
package manager package manager
import ( import (
"bufio" "bufio"
"fmt" "fmt"
"os/exec" "os/exec"
"strings" "strings"
) )
// DNF представляет менеджер пакетов DNF // DNF представляет менеджер пакетов DNF
type DNF struct { type DNF struct {
rootCmd string // rootCmd хранит команду, используемую для выполнения команд с правами root rootCmd string // rootCmd хранит команду, используемую для выполнения команд с правами root
} }
// Exists проверяет, доступен ли DNF в системе, возвращает true если да // Exists проверяет, доступен ли DNF в системе, возвращает true если да
func (*DNF) Exists() bool { func (*DNF) Exists() bool {
_, err := exec.LookPath("dnf") _, err := exec.LookPath("dnf")
return err == nil return err == nil
} }
// Name возвращает имя менеджера пакетов, в данном случае "dnf" // Name возвращает имя менеджера пакетов, в данном случае "dnf"
func (*DNF) Name() string { func (*DNF) Name() string {
return "dnf" return "dnf"
} }
// Format возвращает формат пакетов "rpm", используемый DNF // Format возвращает формат пакетов "rpm", используемый DNF
func (*DNF) Format() string { func (*DNF) Format() string {
return "rpm" return "rpm"
} }
// SetRootCmd устанавливает команду, используемую для выполнения операций с правами root // SetRootCmd устанавливает команду, используемую для выполнения операций с правами root
func (d *DNF) SetRootCmd(s string) { func (d *DNF) SetRootCmd(s string) {
d.rootCmd = s d.rootCmd = s
} }
// Sync выполняет upgrade всех установленных пакетов, обновляя их до более новых версий // Sync выполняет upgrade всех установленных пакетов, обновляя их до более новых версий
func (d *DNF) Sync(opts *Opts) error { func (d *DNF) Sync(opts *Opts) error {
opts = ensureOpts(opts) // Гарантирует, что opts не равен nil и содержит допустимые значения opts = ensureOpts(opts) // Гарантирует, что opts не равен nil и содержит допустимые значения
cmd := d.getCmd(opts, "dnf", "upgrade") cmd := d.getCmd(opts, "dnf", "upgrade")
setCmdEnv(cmd) // Устанавливает переменные окружения для команды setCmdEnv(cmd) // Устанавливает переменные окружения для команды
err := cmd.Run() // Выполняет команду err := cmd.Run() // Выполняет команду
if err != nil { if err != nil {
return fmt.Errorf("dnf: sync: %w", err) return fmt.Errorf("dnf: sync: %w", err)
} }
return nil return nil
} }
// Install устанавливает указанные пакеты с помощью DNF // Install устанавливает указанные пакеты с помощью DNF
func (d *DNF) Install(opts *Opts, pkgs ...string) error { func (d *DNF) Install(opts *Opts, pkgs ...string) error {
opts = ensureOpts(opts) opts = ensureOpts(opts)
cmd := d.getCmd(opts, "dnf", "install", "--allowerasing") cmd := d.getCmd(opts, "dnf", "install", "--allowerasing")
cmd.Args = append(cmd.Args, pkgs...) // Добавляем названия пакетов к команде cmd.Args = append(cmd.Args, pkgs...) // Добавляем названия пакетов к команде
setCmdEnv(cmd) setCmdEnv(cmd)
err := cmd.Run() err := cmd.Run()
if err != nil { if err != nil {
return fmt.Errorf("dnf: install: %w", err) return fmt.Errorf("dnf: install: %w", err)
} }
return nil return nil
} }
// InstallLocal расширяет метод Install для установки пакетов, расположенных локально // InstallLocal расширяет метод Install для установки пакетов, расположенных локально
func (d *DNF) InstallLocal(opts *Opts, pkgs ...string) error { func (d *DNF) InstallLocal(opts *Opts, pkgs ...string) error {
opts = ensureOpts(opts) opts = ensureOpts(opts)
return d.Install(opts, pkgs...) return d.Install(opts, pkgs...)
} }
// Remove удаляет указанные пакеты с помощью DNF // Remove удаляет указанные пакеты с помощью DNF
func (d *DNF) Remove(opts *Opts, pkgs ...string) error { func (d *DNF) Remove(opts *Opts, pkgs ...string) error {
opts = ensureOpts(opts) opts = ensureOpts(opts)
cmd := d.getCmd(opts, "dnf", "remove") cmd := d.getCmd(opts, "dnf", "remove")
cmd.Args = append(cmd.Args, pkgs...) cmd.Args = append(cmd.Args, pkgs...)
setCmdEnv(cmd) setCmdEnv(cmd)
err := cmd.Run() err := cmd.Run()
if err != nil { if err != nil {
return fmt.Errorf("dnf: remove: %w", err) return fmt.Errorf("dnf: remove: %w", err)
} }
return nil return nil
} }
// Upgrade обновляет указанные пакеты до более новых версий // Upgrade обновляет указанные пакеты до более новых версий
func (d *DNF) Upgrade(opts *Opts, pkgs ...string) error { func (d *DNF) Upgrade(opts *Opts, pkgs ...string) error {
opts = ensureOpts(opts) opts = ensureOpts(opts)
cmd := d.getCmd(opts, "dnf", "upgrade") cmd := d.getCmd(opts, "dnf", "upgrade")
cmd.Args = append(cmd.Args, pkgs...) cmd.Args = append(cmd.Args, pkgs...)
setCmdEnv(cmd) setCmdEnv(cmd)
err := cmd.Run() err := cmd.Run()
if err != nil { if err != nil {
return fmt.Errorf("dnf: upgrade: %w", err) return fmt.Errorf("dnf: upgrade: %w", err)
} }
return nil return nil
} }
// UpgradeAll обновляет все установленные пакеты // UpgradeAll обновляет все установленные пакеты
func (d *DNF) UpgradeAll(opts *Opts) error { func (d *DNF) UpgradeAll(opts *Opts) error {
opts = ensureOpts(opts) opts = ensureOpts(opts)
cmd := d.getCmd(opts, "dnf", "upgrade") cmd := d.getCmd(opts, "dnf", "upgrade")
setCmdEnv(cmd) setCmdEnv(cmd)
err := cmd.Run() err := cmd.Run()
if err != nil { if err != nil {
return fmt.Errorf("dnf: upgradeall: %w", err) return fmt.Errorf("dnf: upgradeall: %w", err)
} }
return nil return nil
} }
// ListInstalled возвращает список установленных пакетов и их версий // ListInstalled возвращает список установленных пакетов и их версий
func (d *DNF) ListInstalled(opts *Opts) (map[string]string, error) { func (d *DNF) ListInstalled(opts *Opts) (map[string]string, error) {
out := map[string]string{} out := map[string]string{}
cmd := exec.Command("rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n") cmd := exec.Command("rpm", "-qa", "--queryformat", "%{NAME}\u200b%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\\n")
stdout, err := cmd.StdoutPipe() stdout, err := cmd.StdoutPipe()
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = cmd.Start() err = cmd.Start()
if err != nil { if err != nil {
return nil, err return nil, err
} }
scanner := bufio.NewScanner(stdout) scanner := bufio.NewScanner(stdout)
for scanner.Scan() { for scanner.Scan() {
name, version, ok := strings.Cut(scanner.Text(), "\u200b") name, version, ok := strings.Cut(scanner.Text(), "\u200b")
if !ok { if !ok {
continue continue
} }
version = strings.TrimPrefix(version, "0:") version = strings.TrimPrefix(version, "0:")
out[name] = version out[name] = version
} }
err = scanner.Err() err = scanner.Err()
if err != nil { if err != nil {
return nil, err return nil, err
} }
return out, nil return out, nil
} }
// getCmd создает и возвращает команду exec.Cmd для менеджера пакетов DNF // getCmd создает и возвращает команду exec.Cmd для менеджера пакетов DNF
func (d *DNF) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd { func (d *DNF) getCmd(opts *Opts, mgrCmd string, args ...string) *exec.Cmd {
var cmd *exec.Cmd var cmd *exec.Cmd
if opts.AsRoot { if opts.AsRoot {
cmd = exec.Command(getRootCmd(d.rootCmd), mgrCmd) cmd = exec.Command(getRootCmd(d.rootCmd), mgrCmd)
cmd.Args = append(cmd.Args, opts.Args...) cmd.Args = append(cmd.Args, opts.Args...)
cmd.Args = append(cmd.Args, args...) cmd.Args = append(cmd.Args, args...)
} else { } else {
cmd = exec.Command(mgrCmd, args...) cmd = exec.Command(mgrCmd, args...)
} }
if opts.NoConfirm { if opts.NoConfirm {
cmd.Args = append(cmd.Args, "-y") // Добавляет параметр автоматического подтверждения (-y) cmd.Args = append(cmd.Args, "-y") // Добавляет параметр автоматического подтверждения (-y)
} }
return cmd return cmd
} }

@ -35,6 +35,10 @@ import (
"github.com/go-git/go-git/v5/plumbing/format/diff" "github.com/go-git/go-git/v5/plumbing/format/diff"
"github.com/pelletier/go-toml/v2" "github.com/pelletier/go-toml/v2"
"go.elara.ws/vercmp" "go.elara.ws/vercmp"
"mvdan.cc/sh/v3/expand"
"mvdan.cc/sh/v3/interp"
"mvdan.cc/sh/v3/syntax"
"plemya-x.ru/alr/internal/config" "plemya-x.ru/alr/internal/config"
"plemya-x.ru/alr/internal/db" "plemya-x.ru/alr/internal/db"
"plemya-x.ru/alr/internal/shutils/decoder" "plemya-x.ru/alr/internal/shutils/decoder"
@ -42,9 +46,6 @@ import (
"plemya-x.ru/alr/internal/types" "plemya-x.ru/alr/internal/types"
"plemya-x.ru/alr/pkg/distro" "plemya-x.ru/alr/pkg/distro"
"plemya-x.ru/alr/pkg/loggerctx" "plemya-x.ru/alr/pkg/loggerctx"
"mvdan.cc/sh/v3/expand"
"mvdan.cc/sh/v3/interp"
"mvdan.cc/sh/v3/syntax"
) )
// Pull pulls the provided repositories. If a repo doesn't exist, it will be cloned // Pull pulls the provided repositories. If a repo doesn't exist, it will be cloned

@ -24,12 +24,13 @@ import (
"github.com/pelletier/go-toml/v2" "github.com/pelletier/go-toml/v2"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"golang.org/x/exp/slices"
"plemya-x.ru/alr/internal/config" "plemya-x.ru/alr/internal/config"
"plemya-x.ru/alr/internal/db" "plemya-x.ru/alr/internal/db"
"plemya-x.ru/alr/internal/types" "plemya-x.ru/alr/internal/types"
"plemya-x.ru/alr/pkg/loggerctx" "plemya-x.ru/alr/pkg/loggerctx"
"plemya-x.ru/alr/pkg/repos" "plemya-x.ru/alr/pkg/repos"
"golang.org/x/exp/slices"
) )
var addrepoCmd = &cli.Command{ var addrepoCmd = &cli.Command{

@ -23,6 +23,10 @@ import (
"fmt" "fmt"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"go.elara.ws/vercmp"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
"plemya-x.ru/alr/internal/config" "plemya-x.ru/alr/internal/config"
"plemya-x.ru/alr/internal/db" "plemya-x.ru/alr/internal/db"
"plemya-x.ru/alr/internal/types" "plemya-x.ru/alr/internal/types"
@ -31,9 +35,6 @@ import (
"plemya-x.ru/alr/pkg/loggerctx" "plemya-x.ru/alr/pkg/loggerctx"
"plemya-x.ru/alr/pkg/manager" "plemya-x.ru/alr/pkg/manager"
"plemya-x.ru/alr/pkg/repos" "plemya-x.ru/alr/pkg/repos"
"go.elara.ws/vercmp"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
) )
var upgradeCmd = &cli.Command{ var upgradeCmd = &cli.Command{