mirror of
https://github.com/Jguer/yay.git
synced 2026-02-17 13:52:14 +01:00
feat(ini): support ini config (#2774)
* support ini config * support user ini config * Update pkg/settings/ini.go --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
parent
3d7701fa35
commit
3e3d5aa111
@ -66,6 +66,7 @@ linters:
|
||||
- gochecknoinits
|
||||
- gocritic
|
||||
- godot
|
||||
- gosec
|
||||
- govet
|
||||
- lll
|
||||
- revive
|
||||
|
||||
3
go.mod
3
go.mod
@ -16,6 +16,7 @@ require (
|
||||
golang.org/x/sys v0.40.0
|
||||
golang.org/x/term v0.39.0
|
||||
gopkg.in/h2non/gock.v1 v1.1.2
|
||||
gopkg.in/ini.v1 v1.67.1
|
||||
)
|
||||
|
||||
require (
|
||||
@ -26,7 +27,7 @@ require (
|
||||
github.com/itchyny/gojq v0.12.18 // indirect
|
||||
github.com/itchyny/timefmt-go v0.1.7 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/ohler55/ojg v1.27.0 // indirect
|
||||
github.com/ohler55/ojg v1.28.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
7
go.sum
7
go.sum
@ -1,7 +1,5 @@
|
||||
github.com/Jguer/aur v1.3.0 h1:skdjp/P9kB75TBaJmn9PKK/kCeA9QsgjdUrORZ3gldU=
|
||||
github.com/Jguer/aur v1.3.0/go.mod h1:F8Awo+WKzTxlXtNOO4pDQjMkePLZ+oMSbu+1fKLTTLo=
|
||||
github.com/Jguer/dyalpm v0.1.0 h1:cGajPBZvjZmCG1B1hJmFdNwLoezrIqfiOaAOURM+Kc4=
|
||||
github.com/Jguer/dyalpm v0.1.0/go.mod h1:eUPJQ/zSclJKTxOPihpspulI+S8WQNsxHJoIBiBgogw=
|
||||
github.com/Jguer/dyalpm v0.1.1 h1:38JkmJuHIGXVZedXIDGz/nhVcn8DtMk4zM+GRGipC7w=
|
||||
github.com/Jguer/dyalpm v0.1.1/go.mod h1:eUPJQ/zSclJKTxOPihpspulI+S8WQNsxHJoIBiBgogw=
|
||||
github.com/Jguer/votar v1.0.0 h1:drPYpV5Py5BeAQS8xezmT6uCEfLzotNjLf5yfmlHKTg=
|
||||
@ -42,11 +40,14 @@ github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
||||
github.com/ohler55/ojg v1.27.0 h1:1JzdkMpDc/X9bzRaN1+8AFLnrSiFy96yDSaeACCGD5U=
|
||||
github.com/ohler55/ojg v1.27.0/go.mod h1:/Y5dGWkekv9ocnUixuETqiL58f+5pAsUfg5P8e7Pa2o=
|
||||
github.com/ohler55/ojg v1.28.0 h1:8xClBgMIRRJGDUC9xNe7NprP4kD2C3mQMeon3wY4KXA=
|
||||
github.com/ohler55/ojg v1.28.0/go.mod h1:/Y5dGWkekv9ocnUixuETqiL58f+5pAsUfg5P8e7Pa2o=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4=
|
||||
github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
@ -67,6 +68,8 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=
|
||||
gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
|
||||
gopkg.in/ini.v1 v1.67.1 h1:tVBILHy0R6e4wkYOn3XmiITt/hEVH4TFMYvAX2Ytz6k=
|
||||
gopkg.in/ini.v1 v1.67.1/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
5
main.go
5
main.go
@ -80,11 +80,6 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
if errS := cfg.RunMigrations(fallbackLog,
|
||||
settings.DefaultMigrations(), configPath, yayVersion); errS != nil {
|
||||
fallbackLog.Errorln(errS)
|
||||
}
|
||||
|
||||
cmdArgs := parser.MakeArguments()
|
||||
|
||||
// Parse command line
|
||||
|
||||
@ -2,6 +2,7 @@ package settings
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
@ -13,8 +14,12 @@ import (
|
||||
"github.com/Jguer/yay/v12/pkg/text"
|
||||
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
//go:embed yay.conf
|
||||
var defaultsINI []byte
|
||||
|
||||
// HideMenus indicates if pacman's provider menus must be hidden.
|
||||
var HideMenus = false
|
||||
|
||||
@ -23,91 +28,70 @@ var NoConfirm = false
|
||||
|
||||
// Configuration stores yay's config.
|
||||
type Configuration struct {
|
||||
AURURL string `json:"aururl"`
|
||||
AURRPCURL string `json:"aurrpcurl"`
|
||||
BuildDir string `json:"buildDir"`
|
||||
Editor string `json:"editor"`
|
||||
EditorFlags string `json:"editorflags"`
|
||||
MakepkgBin string `json:"makepkgbin"`
|
||||
MakepkgConf string `json:"makepkgconf"`
|
||||
PacmanBin string `json:"pacmanbin"`
|
||||
PacmanConf string `json:"pacmanconf"`
|
||||
ReDownload string `json:"redownload"`
|
||||
AnswerClean string `json:"answerclean"`
|
||||
AnswerDiff string `json:"answerdiff"`
|
||||
AnswerEdit string `json:"answeredit"`
|
||||
AnswerUpgrade string `json:"answerupgrade"`
|
||||
GitBin string `json:"gitbin"`
|
||||
GpgBin string `json:"gpgbin"`
|
||||
GpgFlags string `json:"gpgflags"`
|
||||
MFlags string `json:"mflags"`
|
||||
SortBy string `json:"sortby"`
|
||||
SearchBy string `json:"searchby"`
|
||||
GitFlags string `json:"gitflags"`
|
||||
RemoveMake string `json:"removemake"`
|
||||
SudoBin string `json:"sudobin"`
|
||||
SudoFlags string `json:"sudoflags"`
|
||||
Version string `json:"version"`
|
||||
RequestSplitN int `json:"requestsplitn"`
|
||||
CompletionInterval int `json:"completionrefreshtime"`
|
||||
MaxConcurrentDownloads int `json:"maxconcurrentdownloads"`
|
||||
BottomUp bool `json:"bottomup"`
|
||||
SudoLoop bool `json:"sudoloop"`
|
||||
TimeUpdate bool `json:"timeupdate"`
|
||||
Devel bool `json:"devel"`
|
||||
CleanAfter bool `json:"cleanAfter"`
|
||||
KeepSrc bool `json:"keepSrc"`
|
||||
Provides bool `json:"provides"`
|
||||
PGPFetch bool `json:"pgpfetch"`
|
||||
CleanMenu bool `json:"cleanmenu"`
|
||||
DiffMenu bool `json:"diffmenu"`
|
||||
EditMenu bool `json:"editmenu"`
|
||||
CombinedUpgrade bool `json:"combinedupgrade"`
|
||||
UseAsk bool `json:"useask"`
|
||||
BatchInstall bool `json:"batchinstall"`
|
||||
SingleLineResults bool `json:"singlelineresults"`
|
||||
SeparateSources bool `json:"separatesources"`
|
||||
Debug bool `json:"debug"`
|
||||
UseRPC bool `json:"rpc"`
|
||||
DoubleConfirm bool `json:"doubleconfirm"` // confirm install before and after build
|
||||
AURURL string `json:"aururl" ini:"AurUrl"`
|
||||
AURRPCURL string `json:"aurrpcurl" ini:"AurRpcUrl"`
|
||||
BuildDir string `json:"buildDir" ini:"BuildDir"`
|
||||
Editor string `json:"editor" ini:"Editor"`
|
||||
EditorFlags string `json:"editorflags" ini:"EditorFlags"`
|
||||
MakepkgBin string `json:"makepkgbin" ini:"MakepkgBin"`
|
||||
MakepkgConf string `json:"makepkgconf" ini:"MakepkgConf"`
|
||||
PacmanBin string `json:"pacmanbin" ini:"PacmanBin"`
|
||||
PacmanConf string `json:"pacmanconf" ini:"PacmanConf"`
|
||||
ReDownload string `json:"redownload" ini:"ReDownload"`
|
||||
AnswerClean string `json:"answerclean" ini:"AnswerClean"`
|
||||
AnswerDiff string `json:"answerdiff" ini:"AnswerDiff"`
|
||||
AnswerEdit string `json:"answeredit" ini:"AnswerEdit"`
|
||||
AnswerUpgrade string `json:"answerupgrade" ini:"AnswerUpgrade"`
|
||||
GitBin string `json:"gitbin" ini:"GitBin"`
|
||||
GpgBin string `json:"gpgbin" ini:"GpgBin"`
|
||||
GpgFlags string `json:"gpgflags" ini:"GpgFlags"`
|
||||
MFlags string `json:"mflags" ini:"MFlags"`
|
||||
SortBy string `json:"sortby" ini:"SortBy"`
|
||||
SearchBy string `json:"searchby" ini:"SearchBy"`
|
||||
GitFlags string `json:"gitflags" ini:"GitFlags"`
|
||||
RemoveMake string `json:"removemake" ini:"RemoveMake"`
|
||||
SudoBin string `json:"sudobin" ini:"SudoBin"`
|
||||
SudoFlags string `json:"sudoflags" ini:"SudoFlags"`
|
||||
Version string `json:"version" ini:"-"`
|
||||
RequestSplitN int `json:"requestsplitn" ini:"RequestSplitN"`
|
||||
CompletionInterval int `json:"completionrefreshtime" ini:"CompletionInterval"`
|
||||
MaxConcurrentDownloads int `json:"maxconcurrentdownloads" ini:"MaxConcurrentDownloads"`
|
||||
BottomUp bool `json:"bottomup" ini:"BottomUp"`
|
||||
SudoLoop bool `json:"sudoloop" ini:"SudoLoop"`
|
||||
TimeUpdate bool `json:"timeupdate" ini:"TimeUpdate"`
|
||||
Devel bool `json:"devel" ini:"Devel"`
|
||||
CleanAfter bool `json:"cleanAfter" ini:"CleanAfter"`
|
||||
KeepSrc bool `json:"keepSrc" ini:"KeepSrc"`
|
||||
Provides bool `json:"provides" ini:"Provides"`
|
||||
PGPFetch bool `json:"pgpfetch" ini:"PgpFetch"`
|
||||
CleanMenu bool `json:"cleanmenu" ini:"CleanMenu"`
|
||||
DiffMenu bool `json:"diffmenu" ini:"DiffMenu"`
|
||||
EditMenu bool `json:"editmenu" ini:"EditMenu"`
|
||||
CombinedUpgrade bool `json:"combinedupgrade" ini:"CombinedUpgrade"`
|
||||
UseAsk bool `json:"useask" ini:"UseAsk"`
|
||||
BatchInstall bool `json:"batchinstall" ini:"BatchInstall"`
|
||||
SingleLineResults bool `json:"singlelineresults" ini:"SingleLineResults"`
|
||||
SeparateSources bool `json:"separatesources" ini:"SeparateSources"`
|
||||
Debug bool `json:"debug" ini:"Debug"`
|
||||
UseRPC bool `json:"rpc" ini:"Rpc"`
|
||||
DoubleConfirm bool `json:"doubleconfirm" ini:"DoubleConfirm"` // confirm install before and after build
|
||||
|
||||
CompletionPath string `json:"-"`
|
||||
VCSFilePath string `json:"-"`
|
||||
// ConfigPath string `json:"-"`
|
||||
SaveConfig bool `json:"-"`
|
||||
Mode parser.TargetMode `json:"-"`
|
||||
ReBuild parser.RebuildMode `json:"rebuild"`
|
||||
CompletionPath string `json:"-" ini:"-"`
|
||||
VCSFilePath string `json:"-" ini:"-"`
|
||||
SaveConfig bool `json:"-" ini:"-"`
|
||||
Mode parser.TargetMode `json:"-" ini:"-"`
|
||||
ReBuild parser.RebuildMode `json:"rebuild" ini:"ReBuild"`
|
||||
}
|
||||
|
||||
// SaveConfig writes yay config to file.
|
||||
// Save writes yay config to INI file.
|
||||
func (c *Configuration) Save(configPath, version string) error {
|
||||
c.Version = version
|
||||
|
||||
marshalledinfo, err := json.MarshalIndent(c, "", "\t")
|
||||
if err != nil {
|
||||
return err
|
||||
// Use INI config path instead of JSON
|
||||
iniPath := GetINIConfigPath()
|
||||
if iniPath == "" {
|
||||
return fmt.Errorf("unable to determine config path")
|
||||
}
|
||||
|
||||
// https://github.com/Jguer/yay/issues/1325
|
||||
marshalledinfo = append(marshalledinfo, '\n')
|
||||
// https://github.com/Jguer/yay/issues/1399
|
||||
if _, err = os.Stat(filepath.Dir(configPath)); os.IsNotExist(err) && err != nil {
|
||||
if mkErr := os.MkdirAll(filepath.Dir(configPath), 0o755); mkErr != nil {
|
||||
return mkErr
|
||||
}
|
||||
}
|
||||
|
||||
in, err := os.OpenFile(configPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer in.Close()
|
||||
|
||||
if _, err = in.Write(marshalledinfo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return in.Sync()
|
||||
return c.SaveINI(iniPath)
|
||||
}
|
||||
|
||||
func (c *Configuration) expandEnv() {
|
||||
@ -190,55 +174,34 @@ func (c *Configuration) setPrivilegeElevator() error {
|
||||
}
|
||||
|
||||
func DefaultConfig(version string) *Configuration {
|
||||
return &Configuration{
|
||||
AURURL: "https://aur.archlinux.org",
|
||||
BuildDir: os.ExpandEnv("$HOME/.cache/yay"),
|
||||
CleanAfter: false,
|
||||
KeepSrc: false,
|
||||
Editor: "",
|
||||
EditorFlags: "",
|
||||
Devel: false,
|
||||
MakepkgBin: "makepkg",
|
||||
MakepkgConf: "",
|
||||
PacmanBin: "pacman",
|
||||
PGPFetch: true,
|
||||
PacmanConf: "/etc/pacman.conf",
|
||||
GpgFlags: "",
|
||||
MFlags: "",
|
||||
GitFlags: "",
|
||||
BottomUp: true,
|
||||
CompletionInterval: 7,
|
||||
MaxConcurrentDownloads: 1,
|
||||
SortBy: "",
|
||||
SearchBy: "name-desc",
|
||||
SudoLoop: false,
|
||||
GitBin: "git",
|
||||
GpgBin: "gpg",
|
||||
SudoBin: "sudo",
|
||||
SudoFlags: "",
|
||||
TimeUpdate: false,
|
||||
RequestSplitN: 150,
|
||||
ReDownload: "no",
|
||||
ReBuild: "no",
|
||||
BatchInstall: false,
|
||||
AnswerClean: "",
|
||||
AnswerDiff: "",
|
||||
AnswerEdit: "",
|
||||
AnswerUpgrade: "",
|
||||
RemoveMake: "ask",
|
||||
Provides: true,
|
||||
CleanMenu: true,
|
||||
DiffMenu: true,
|
||||
EditMenu: false,
|
||||
UseAsk: false,
|
||||
CombinedUpgrade: true,
|
||||
SeparateSources: true,
|
||||
Version: version,
|
||||
Debug: false,
|
||||
UseRPC: true,
|
||||
DoubleConfirm: true,
|
||||
Mode: parser.ModeAny,
|
||||
cfg := &Configuration{
|
||||
Version: version,
|
||||
Mode: parser.ModeAny,
|
||||
}
|
||||
|
||||
// Load defaults from embedded INI
|
||||
iniCfg, err := ini.LoadSources(ini.LoadOptions{
|
||||
AllowBooleanKeys: true,
|
||||
Insensitive: true,
|
||||
InsensitiveSections: true,
|
||||
IgnoreInlineComment: true,
|
||||
}, defaultsINI)
|
||||
if err != nil {
|
||||
// Fallback to minimal defaults if embedded config fails
|
||||
cfg.AURURL = "https://aur.archlinux.org"
|
||||
cfg.BuildDir = os.ExpandEnv("$HOME/.cache/yay")
|
||||
return cfg
|
||||
}
|
||||
|
||||
// Map the default section
|
||||
_ = iniCfg.Section("").MapTo(cfg)
|
||||
|
||||
// Also map [options] section if present
|
||||
if iniCfg.HasSection("options") {
|
||||
_ = iniCfg.Section("options").MapTo(cfg)
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
func NewConfig(logger *text.Logger, configPath, version string) (*Configuration, error) {
|
||||
@ -252,8 +215,23 @@ func NewConfig(logger *text.Logger, configPath, version string) (*Configuration,
|
||||
newConfig.BuildDir = cacheHome
|
||||
newConfig.CompletionPath = filepath.Join(cacheHome, completionFileName)
|
||||
newConfig.VCSFilePath = filepath.Join(cacheHome, vcsFileName)
|
||||
|
||||
// Load system-wide INI config first (silently ignored if not present)
|
||||
if err := newConfig.loadINI(SystemConfigPath); err != nil && logger != nil {
|
||||
logger.Errorln(err)
|
||||
}
|
||||
|
||||
// Load user JSON config (legacy, overrides system config)
|
||||
newConfig.load(configPath)
|
||||
|
||||
// Load user INI config (takes priority over JSON when both exist)
|
||||
userINIPath := GetINIConfigPath()
|
||||
if userINIPath != "" {
|
||||
if err := newConfig.loadINI(userINIPath); err != nil && logger != nil {
|
||||
logger.Errorln(err)
|
||||
}
|
||||
}
|
||||
|
||||
if aurdest := os.Getenv("AURDEST"); aurdest != "" {
|
||||
newConfig.BuildDir = aurdest
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import (
|
||||
|
||||
const (
|
||||
configFileName string = "config.json" // configFileName holds the name of the config file.
|
||||
iniConfigFileName string = "yay.conf" // iniConfigFileName holds the name of the INI config file.
|
||||
vcsFileName string = "vcs.json" // vcsFileName holds the name of the vcs file.
|
||||
completionFileName string = "completion.cache"
|
||||
systemdCache string = "/var/cache/yay" // systemd should handle cache creation
|
||||
@ -30,6 +31,26 @@ func GetConfigPath() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetINIConfigPath returns the path to the user's INI config file (yay.conf).
|
||||
// This is used for both loading (with priority over JSON) and saving.
|
||||
func GetINIConfigPath() string {
|
||||
if configHome := os.Getenv("XDG_CONFIG_HOME"); configHome != "" {
|
||||
configDir := filepath.Join(configHome, "yay")
|
||||
if err := initDir(configDir); err == nil {
|
||||
return filepath.Join(configDir, iniConfigFileName)
|
||||
}
|
||||
}
|
||||
|
||||
if configHome := os.Getenv("HOME"); configHome != "" {
|
||||
configDir := filepath.Join(configHome, ".config", "yay")
|
||||
if err := initDir(configDir); err == nil {
|
||||
return filepath.Join(configDir, iniConfigFileName)
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func getCacheHome() (string, error) {
|
||||
uid := os.Geteuid()
|
||||
|
||||
|
||||
73
pkg/settings/ini.go
Normal file
73
pkg/settings/ini.go
Normal file
@ -0,0 +1,73 @@
|
||||
package settings
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
// SystemConfigPath is the path to the system-wide INI configuration file.
|
||||
const SystemConfigPath = "/etc/yay.conf"
|
||||
|
||||
// loadINI parses an INI configuration file and applies values to the Configuration.
|
||||
// It silently returns nil if the file doesn't exist.
|
||||
// Uses struct tags for mapping (e.g., `ini:"AurUrl"`).
|
||||
func (c *Configuration) loadINI(path string) error {
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
cfg, err := ini.LoadSources(ini.LoadOptions{
|
||||
AllowBooleanKeys: true,
|
||||
Insensitive: true,
|
||||
InsensitiveSections: true,
|
||||
IgnoreInlineComment: true,
|
||||
}, path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load INI config file '%s': %w", path, err)
|
||||
}
|
||||
|
||||
// Map the default section to the config struct
|
||||
if err := cfg.Section("").MapTo(c); err != nil {
|
||||
return fmt.Errorf("failed to map INI config '%s': %w", path, err)
|
||||
}
|
||||
|
||||
// Also map [options] section if present (for compatibility)
|
||||
if cfg.HasSection("options") {
|
||||
if err := cfg.Section("options").MapTo(c); err != nil {
|
||||
return fmt.Errorf("failed to map INI [options] section '%s': %w", path, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SaveINI writes the configuration to an INI file at the specified path.
|
||||
func (c *Configuration) SaveINI(path string) error {
|
||||
cfg := ini.Empty(ini.LoadOptions{
|
||||
AllowBooleanKeys: true,
|
||||
})
|
||||
|
||||
// Use [options] section for compatibility with system config
|
||||
section, err := cfg.NewSection("options")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create INI section: %w", err)
|
||||
}
|
||||
|
||||
if err := section.ReflectFrom(c); err != nil {
|
||||
return fmt.Errorf("failed to reflect config to INI: %w", err)
|
||||
}
|
||||
|
||||
// Ensure parent directory exists
|
||||
if dir := filepath.Dir(path); dir != "" {
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
if mkErr := os.MkdirAll(dir, 0o755); mkErr != nil {
|
||||
return fmt.Errorf("failed to create config directory: %w", mkErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cfg.SaveTo(path)
|
||||
}
|
||||
362
pkg/settings/ini_test.go
Normal file
362
pkg/settings/ini_test.go
Normal file
@ -0,0 +1,362 @@
|
||||
package settings
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestConfigurationLoadINI(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("load nonexistent file returns nil", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
cfg := DefaultConfig("test")
|
||||
|
||||
err := cfg.loadINI("/nonexistent/path/yay.conf")
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("load valid INI file", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
content := `# System-wide yay configuration
|
||||
[options]
|
||||
AurUrl = https://custom.aur.org
|
||||
BuildDir = /var/cache/yay
|
||||
Editor = vim
|
||||
Devel = true
|
||||
SudoLoop = yes
|
||||
RequestSplitN = 200
|
||||
BottomUp = false
|
||||
|
||||
; This is also a comment
|
||||
CleanAfter = 1
|
||||
`
|
||||
tmpDir := t.TempDir()
|
||||
iniPath := filepath.Join(tmpDir, "yay.conf")
|
||||
require.NoError(t, os.WriteFile(iniPath, []byte(content), 0o644))
|
||||
|
||||
cfg := DefaultConfig("test")
|
||||
err := cfg.loadINI(iniPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "https://custom.aur.org", cfg.AURURL)
|
||||
assert.Equal(t, "/var/cache/yay", cfg.BuildDir)
|
||||
assert.Equal(t, "vim", cfg.Editor)
|
||||
assert.True(t, cfg.Devel)
|
||||
assert.True(t, cfg.SudoLoop)
|
||||
assert.Equal(t, 200, cfg.RequestSplitN)
|
||||
assert.False(t, cfg.BottomUp)
|
||||
assert.True(t, cfg.CleanAfter)
|
||||
})
|
||||
|
||||
t.Run("load INI file with all option types", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
content := `
|
||||
# String options
|
||||
AurUrl = https://aur.example.com
|
||||
AurRpcUrl = https://aur.example.com/rpc
|
||||
BuildDir = /tmp/build
|
||||
Editor = nvim
|
||||
EditorFlags = -p
|
||||
MakepkgBin = /usr/bin/makepkg
|
||||
MakepkgConf = /etc/makepkg.conf
|
||||
PacmanBin = /usr/bin/pacman
|
||||
PacmanConf = /etc/pacman.conf
|
||||
GitBin = /usr/bin/git
|
||||
GpgBin = /usr/bin/gpg
|
||||
GpgFlags = --keyserver-options
|
||||
MFlags = -s
|
||||
SortBy = votes
|
||||
SearchBy = name
|
||||
GitFlags = --depth=1
|
||||
RemoveMake = yes
|
||||
SudoBin = doas
|
||||
SudoFlags = -n
|
||||
ReDownload = all
|
||||
AnswerClean = All
|
||||
AnswerDiff = None
|
||||
AnswerEdit = None
|
||||
AnswerUpgrade = None
|
||||
ReBuild = all
|
||||
|
||||
# Integer options
|
||||
RequestSplitN = 100
|
||||
CompletionInterval = 3
|
||||
MaxConcurrentDownloads = 4
|
||||
|
||||
# Boolean options
|
||||
BottomUp = true
|
||||
SudoLoop = false
|
||||
TimeUpdate = yes
|
||||
Devel = no
|
||||
CleanAfter = true
|
||||
KeepSrc = false
|
||||
Provides = true
|
||||
PgpFetch = false
|
||||
CleanMenu = yes
|
||||
DiffMenu = no
|
||||
EditMenu = true
|
||||
CombinedUpgrade = false
|
||||
UseAsk = true
|
||||
BatchInstall = false
|
||||
SingleLineResults = true
|
||||
SeparateSources = false
|
||||
Debug = no
|
||||
Rpc = yes
|
||||
DoubleConfirm = false
|
||||
`
|
||||
tmpDir := t.TempDir()
|
||||
iniPath := filepath.Join(tmpDir, "yay.conf")
|
||||
require.NoError(t, os.WriteFile(iniPath, []byte(content), 0o644))
|
||||
|
||||
cfg := DefaultConfig("test")
|
||||
err := cfg.loadINI(iniPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
// String options
|
||||
assert.Equal(t, "https://aur.example.com", cfg.AURURL)
|
||||
assert.Equal(t, "https://aur.example.com/rpc", cfg.AURRPCURL)
|
||||
assert.Equal(t, "/tmp/build", cfg.BuildDir)
|
||||
assert.Equal(t, "nvim", cfg.Editor)
|
||||
assert.Equal(t, "-p", cfg.EditorFlags)
|
||||
assert.Equal(t, "/usr/bin/makepkg", cfg.MakepkgBin)
|
||||
assert.Equal(t, "/etc/makepkg.conf", cfg.MakepkgConf)
|
||||
assert.Equal(t, "/usr/bin/pacman", cfg.PacmanBin)
|
||||
assert.Equal(t, "/etc/pacman.conf", cfg.PacmanConf)
|
||||
assert.Equal(t, "/usr/bin/git", cfg.GitBin)
|
||||
assert.Equal(t, "/usr/bin/gpg", cfg.GpgBin)
|
||||
assert.Equal(t, "--keyserver-options", cfg.GpgFlags)
|
||||
assert.Equal(t, "-s", cfg.MFlags)
|
||||
assert.Equal(t, "votes", cfg.SortBy)
|
||||
assert.Equal(t, "name", cfg.SearchBy)
|
||||
assert.Equal(t, "--depth=1", cfg.GitFlags)
|
||||
assert.Equal(t, "yes", cfg.RemoveMake)
|
||||
assert.Equal(t, "doas", cfg.SudoBin)
|
||||
assert.Equal(t, "-n", cfg.SudoFlags)
|
||||
assert.Equal(t, "all", cfg.ReDownload)
|
||||
assert.Equal(t, "All", cfg.AnswerClean)
|
||||
assert.Equal(t, "None", cfg.AnswerDiff)
|
||||
assert.Equal(t, "None", cfg.AnswerEdit)
|
||||
assert.Equal(t, "None", cfg.AnswerUpgrade)
|
||||
assert.Equal(t, "all", string(cfg.ReBuild))
|
||||
|
||||
// Integer options
|
||||
assert.Equal(t, 100, cfg.RequestSplitN)
|
||||
assert.Equal(t, 3, cfg.CompletionInterval)
|
||||
assert.Equal(t, 4, cfg.MaxConcurrentDownloads)
|
||||
|
||||
// Boolean options
|
||||
assert.True(t, cfg.BottomUp)
|
||||
assert.False(t, cfg.SudoLoop)
|
||||
assert.True(t, cfg.TimeUpdate)
|
||||
assert.False(t, cfg.Devel)
|
||||
assert.True(t, cfg.CleanAfter)
|
||||
assert.False(t, cfg.KeepSrc)
|
||||
assert.True(t, cfg.Provides)
|
||||
assert.False(t, cfg.PGPFetch)
|
||||
assert.True(t, cfg.CleanMenu)
|
||||
assert.False(t, cfg.DiffMenu)
|
||||
assert.True(t, cfg.EditMenu)
|
||||
assert.False(t, cfg.CombinedUpgrade)
|
||||
assert.True(t, cfg.UseAsk)
|
||||
assert.False(t, cfg.BatchInstall)
|
||||
assert.True(t, cfg.SingleLineResults)
|
||||
assert.False(t, cfg.SeparateSources)
|
||||
assert.False(t, cfg.Debug)
|
||||
assert.True(t, cfg.UseRPC)
|
||||
assert.False(t, cfg.DoubleConfirm)
|
||||
})
|
||||
|
||||
t.Run("load INI without section header", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
content := `# Config without section header
|
||||
AurUrl = https://custom.aur.org
|
||||
Devel = true
|
||||
RequestSplitN = 250
|
||||
`
|
||||
tmpDir := t.TempDir()
|
||||
iniPath := filepath.Join(tmpDir, "yay.conf")
|
||||
require.NoError(t, os.WriteFile(iniPath, []byte(content), 0o644))
|
||||
|
||||
cfg := DefaultConfig("test")
|
||||
err := cfg.loadINI(iniPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "https://custom.aur.org", cfg.AURURL)
|
||||
assert.True(t, cfg.Devel)
|
||||
assert.Equal(t, 250, cfg.RequestSplitN)
|
||||
})
|
||||
|
||||
t.Run("load INI with boolean keys (no value)", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
content := `# Boolean keys without values are treated as true
|
||||
Devel
|
||||
SudoLoop
|
||||
CleanAfter
|
||||
`
|
||||
tmpDir := t.TempDir()
|
||||
iniPath := filepath.Join(tmpDir, "yay.conf")
|
||||
require.NoError(t, os.WriteFile(iniPath, []byte(content), 0o644))
|
||||
|
||||
cfg := DefaultConfig("test")
|
||||
cfg.Devel = false
|
||||
cfg.SudoLoop = false
|
||||
cfg.CleanAfter = false
|
||||
|
||||
err := cfg.loadINI(iniPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.True(t, cfg.Devel)
|
||||
assert.True(t, cfg.SudoLoop)
|
||||
assert.True(t, cfg.CleanAfter)
|
||||
})
|
||||
|
||||
t.Run("unknown options are ignored", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
content := `AurUrl = https://custom.aur.org
|
||||
unknownoption = somevalue
|
||||
anotherunknown = 123
|
||||
`
|
||||
tmpDir := t.TempDir()
|
||||
iniPath := filepath.Join(tmpDir, "yay.conf")
|
||||
require.NoError(t, os.WriteFile(iniPath, []byte(content), 0o644))
|
||||
|
||||
cfg := DefaultConfig("test")
|
||||
err := cfg.loadINI(iniPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "https://custom.aur.org", cfg.AURURL)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSystemConfigPath(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert.Equal(t, "/etc/yay.conf", SystemConfigPath)
|
||||
}
|
||||
|
||||
func TestConfigurationSaveINI(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("save and reload config", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg := DefaultConfig("test")
|
||||
cfg.AURURL = "https://custom.aur.org"
|
||||
cfg.BuildDir = "/custom/build"
|
||||
cfg.Editor = "nvim"
|
||||
cfg.Devel = true
|
||||
cfg.SudoLoop = true
|
||||
cfg.RequestSplitN = 150
|
||||
cfg.BottomUp = true
|
||||
cfg.CleanAfter = false
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
iniPath := filepath.Join(tmpDir, "yay.conf")
|
||||
|
||||
err := cfg.SaveINI(iniPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify file was created
|
||||
_, err = os.Stat(iniPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Load into new config and verify values
|
||||
cfg2 := DefaultConfig("test")
|
||||
err = cfg2.loadINI(iniPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "https://custom.aur.org", cfg2.AURURL)
|
||||
assert.Equal(t, "/custom/build", cfg2.BuildDir)
|
||||
assert.Equal(t, "nvim", cfg2.Editor)
|
||||
assert.True(t, cfg2.Devel)
|
||||
assert.True(t, cfg2.SudoLoop)
|
||||
assert.Equal(t, 150, cfg2.RequestSplitN)
|
||||
assert.True(t, cfg2.BottomUp)
|
||||
assert.False(t, cfg2.CleanAfter)
|
||||
})
|
||||
|
||||
t.Run("save creates directory if not exists", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg := DefaultConfig("test")
|
||||
cfg.AURURL = "https://test.aur.org"
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
nestedPath := filepath.Join(tmpDir, "nested", "dir", "yay.conf")
|
||||
|
||||
err := cfg.SaveINI(nestedPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify file was created
|
||||
_, err = os.Stat(nestedPath)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("roundtrip preserves all field types", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg := DefaultConfig("test")
|
||||
// String fields
|
||||
cfg.AURURL = "https://roundtrip.aur.org"
|
||||
cfg.AURRPCURL = "https://roundtrip.aur.org/rpc"
|
||||
cfg.BuildDir = "/roundtrip/build"
|
||||
cfg.Editor = "emacs"
|
||||
cfg.EditorFlags = "-nw"
|
||||
cfg.SudoBin = "doas"
|
||||
cfg.SudoFlags = "-n"
|
||||
cfg.ReDownload = "all"
|
||||
cfg.ReBuild = "tree"
|
||||
|
||||
// Integer fields
|
||||
cfg.RequestSplitN = 75
|
||||
cfg.CompletionInterval = 5
|
||||
cfg.MaxConcurrentDownloads = 8
|
||||
|
||||
// Boolean fields
|
||||
cfg.BottomUp = true
|
||||
cfg.SudoLoop = false
|
||||
cfg.Devel = true
|
||||
cfg.CleanAfter = false
|
||||
cfg.UseRPC = true
|
||||
cfg.BatchInstall = true
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
iniPath := filepath.Join(tmpDir, "yay.conf")
|
||||
|
||||
err := cfg.SaveINI(iniPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
cfg2 := DefaultConfig("test")
|
||||
err = cfg2.loadINI(iniPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify all fields
|
||||
assert.Equal(t, cfg.AURURL, cfg2.AURURL)
|
||||
assert.Equal(t, cfg.AURRPCURL, cfg2.AURRPCURL)
|
||||
assert.Equal(t, cfg.BuildDir, cfg2.BuildDir)
|
||||
assert.Equal(t, cfg.Editor, cfg2.Editor)
|
||||
assert.Equal(t, cfg.EditorFlags, cfg2.EditorFlags)
|
||||
assert.Equal(t, cfg.SudoBin, cfg2.SudoBin)
|
||||
assert.Equal(t, cfg.SudoFlags, cfg2.SudoFlags)
|
||||
assert.Equal(t, cfg.ReDownload, cfg2.ReDownload)
|
||||
assert.Equal(t, cfg.ReBuild, cfg2.ReBuild)
|
||||
assert.Equal(t, cfg.RequestSplitN, cfg2.RequestSplitN)
|
||||
assert.Equal(t, cfg.CompletionInterval, cfg2.CompletionInterval)
|
||||
assert.Equal(t, cfg.MaxConcurrentDownloads, cfg2.MaxConcurrentDownloads)
|
||||
assert.Equal(t, cfg.BottomUp, cfg2.BottomUp)
|
||||
assert.Equal(t, cfg.SudoLoop, cfg2.SudoLoop)
|
||||
assert.Equal(t, cfg.Devel, cfg2.Devel)
|
||||
assert.Equal(t, cfg.CleanAfter, cfg2.CleanAfter)
|
||||
assert.Equal(t, cfg.UseRPC, cfg2.UseRPC)
|
||||
assert.Equal(t, cfg.BatchInstall, cfg2.BatchInstall)
|
||||
})
|
||||
}
|
||||
@ -1,90 +0,0 @@
|
||||
package settings
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/db"
|
||||
"github.com/Jguer/yay/v12/pkg/text"
|
||||
|
||||
"github.com/leonelquinteros/gotext"
|
||||
)
|
||||
|
||||
type configMigration interface {
|
||||
// Description of what the migration does
|
||||
fmt.Stringer
|
||||
// return true if migration was done
|
||||
Do(config *Configuration) bool
|
||||
// Target version of the migration (e.g. "11.2.1")
|
||||
// Should match the version of yay releasing this migration
|
||||
TargetVersion() string
|
||||
}
|
||||
|
||||
type configProviderMigration struct{}
|
||||
|
||||
func (migration *configProviderMigration) String() string {
|
||||
return gotext.Get("Disable 'provides' setting by default")
|
||||
}
|
||||
|
||||
func (migration *configProviderMigration) Do(config *Configuration) bool {
|
||||
if config.Provides {
|
||||
config.Provides = false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (migration *configProviderMigration) TargetVersion() string {
|
||||
return "11.2.1"
|
||||
}
|
||||
|
||||
type configSortByMigration struct{}
|
||||
|
||||
func (migration *configSortByMigration) String() string {
|
||||
return gotext.Get("Reset 'sortby' setting to default")
|
||||
}
|
||||
|
||||
func (migration *configSortByMigration) Do(config *Configuration) bool {
|
||||
if config.SortBy != "" {
|
||||
config.SortBy = ""
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (migration *configSortByMigration) TargetVersion() string {
|
||||
return "13.0.0"
|
||||
}
|
||||
|
||||
func DefaultMigrations() []configMigration {
|
||||
return []configMigration{
|
||||
&configProviderMigration{},
|
||||
&configSortByMigration{},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Configuration) RunMigrations(logger *text.Logger, migrations []configMigration,
|
||||
configPath, newVersion string,
|
||||
) error {
|
||||
saveConfig := false
|
||||
|
||||
for _, migration := range migrations {
|
||||
if db.VerCmp(migration.TargetVersion(), c.Version) > 0 {
|
||||
if migration.Do(c) {
|
||||
logger.Infoln("Config migration executed (",
|
||||
migration.TargetVersion(), "):", migration)
|
||||
|
||||
saveConfig = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if saveConfig {
|
||||
return c.Save(configPath, newVersion)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -1,275 +0,0 @@
|
||||
//go:build !integration
|
||||
// +build !integration
|
||||
|
||||
package settings
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/Jguer/yay/v12/pkg/text"
|
||||
)
|
||||
|
||||
func newTestLogger() *text.Logger {
|
||||
return text.NewLogger(io.Discard, io.Discard, strings.NewReader(""), true, "test")
|
||||
}
|
||||
|
||||
func TestMigrationNothingToDo(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Create temporary file for config
|
||||
configFile, err := os.CreateTemp("/tmp", "yay-*-config.json")
|
||||
require.NoError(t, err)
|
||||
|
||||
testFilePath := configFile.Name()
|
||||
defer os.Remove(testFilePath)
|
||||
// Create config with configVersion
|
||||
config := Configuration{
|
||||
Version: "99.0.0",
|
||||
// Create runtime with runtimeVersion
|
||||
}
|
||||
|
||||
// Run Migration
|
||||
err = config.RunMigrations(newTestLogger(), DefaultMigrations(), testFilePath, "20.0.0")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check file contents if wantSave otherwise check file empty
|
||||
cfile, err := os.Open(testFilePath)
|
||||
require.NoError(t, err)
|
||||
defer cfile.Close()
|
||||
|
||||
decoder := json.NewDecoder(cfile)
|
||||
newConfig := Configuration{}
|
||||
err = decoder.Decode(&newConfig)
|
||||
require.Error(t, err)
|
||||
assert.Empty(t, newConfig.Version)
|
||||
}
|
||||
|
||||
func TestProvidesMigrationDo(t *testing.T) {
|
||||
migration := &configProviderMigration{}
|
||||
config := Configuration{
|
||||
Provides: true,
|
||||
}
|
||||
|
||||
assert.True(t, migration.Do(&config))
|
||||
|
||||
falseConfig := Configuration{Provides: false}
|
||||
|
||||
assert.False(t, migration.Do(&falseConfig))
|
||||
}
|
||||
|
||||
func TestProvidesMigration(t *testing.T) {
|
||||
t.Parallel()
|
||||
type testCase struct {
|
||||
desc string
|
||||
testConfig *Configuration
|
||||
newVersion string
|
||||
wantSave bool
|
||||
}
|
||||
|
||||
testCases := []testCase{
|
||||
{
|
||||
desc: "to upgrade",
|
||||
testConfig: &Configuration{
|
||||
Version: "11.0.1",
|
||||
Provides: true,
|
||||
},
|
||||
newVersion: "11.2.1",
|
||||
wantSave: true,
|
||||
},
|
||||
{
|
||||
desc: "to upgrade-git",
|
||||
testConfig: &Configuration{
|
||||
Version: "11.2.0.r7.g6f60892",
|
||||
Provides: true,
|
||||
},
|
||||
newVersion: "11.2.1",
|
||||
wantSave: true,
|
||||
},
|
||||
{
|
||||
desc: "to not upgrade",
|
||||
testConfig: &Configuration{
|
||||
Version: "11.2.0",
|
||||
Provides: false,
|
||||
},
|
||||
newVersion: "11.2.1",
|
||||
wantSave: false,
|
||||
},
|
||||
{
|
||||
desc: "to not upgrade - target version",
|
||||
testConfig: &Configuration{
|
||||
Version: "11.2.1",
|
||||
Provides: true,
|
||||
},
|
||||
newVersion: "11.2.1",
|
||||
wantSave: false,
|
||||
},
|
||||
{
|
||||
desc: "to not upgrade - new version",
|
||||
testConfig: &Configuration{
|
||||
Version: "11.3.0",
|
||||
Provides: true,
|
||||
},
|
||||
newVersion: "11.3.0",
|
||||
wantSave: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
// Create temporary file for config
|
||||
configFile, err := os.CreateTemp("/tmp", "yay-*-config.json")
|
||||
require.NoError(t, err)
|
||||
|
||||
testFilePath := configFile.Name()
|
||||
defer os.Remove(testFilePath)
|
||||
// Create config with configVersion and provides
|
||||
tcConfig := Configuration{
|
||||
Version: tc.testConfig.Version,
|
||||
Provides: tc.testConfig.Provides,
|
||||
// Create runtime with runtimeVersion
|
||||
}
|
||||
|
||||
// Run Migration
|
||||
err = tcConfig.RunMigrations(newTestLogger(),
|
||||
[]configMigration{&configProviderMigration{}},
|
||||
testFilePath, tc.newVersion)
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check file contents if wantSave otherwise check file empty
|
||||
cfile, err := os.Open(testFilePath)
|
||||
require.NoError(t, err)
|
||||
defer cfile.Close()
|
||||
|
||||
decoder := json.NewDecoder(cfile)
|
||||
newConfig := Configuration{}
|
||||
err = decoder.Decode(&newConfig)
|
||||
if tc.wantSave {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tc.newVersion, newConfig.Version)
|
||||
assert.Equal(t, false, newConfig.Provides)
|
||||
} else {
|
||||
require.Error(t, err)
|
||||
assert.Empty(t, newConfig.Version)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSortByMigrationDo(t *testing.T) {
|
||||
migration := &configSortByMigration{}
|
||||
config := Configuration{
|
||||
SortBy: "name",
|
||||
}
|
||||
|
||||
assert.True(t, migration.Do(&config))
|
||||
|
||||
falseConfig := Configuration{SortBy: ""}
|
||||
|
||||
assert.False(t, migration.Do(&falseConfig))
|
||||
}
|
||||
|
||||
func TestSortByMigration(t *testing.T) {
|
||||
t.Parallel()
|
||||
type testCase struct {
|
||||
desc string
|
||||
testConfig *Configuration
|
||||
newVersion string
|
||||
wantSave bool
|
||||
}
|
||||
|
||||
testCases := []testCase{
|
||||
{
|
||||
desc: "to upgrade",
|
||||
testConfig: &Configuration{
|
||||
Version: "12.9.0",
|
||||
SortBy: "name",
|
||||
},
|
||||
newVersion: "13.0.0",
|
||||
wantSave: true,
|
||||
},
|
||||
{
|
||||
desc: "to upgrade-git",
|
||||
testConfig: &Configuration{
|
||||
Version: "12.9.0.r7.g6f60892",
|
||||
SortBy: "votes",
|
||||
},
|
||||
newVersion: "13.0.0",
|
||||
wantSave: true,
|
||||
},
|
||||
{
|
||||
desc: "to not upgrade",
|
||||
testConfig: &Configuration{
|
||||
Version: "12.9.0",
|
||||
SortBy: "",
|
||||
},
|
||||
newVersion: "13.0.0",
|
||||
wantSave: false,
|
||||
},
|
||||
{
|
||||
desc: "to not upgrade - target version",
|
||||
testConfig: &Configuration{
|
||||
Version: "13.0.0",
|
||||
SortBy: "name",
|
||||
},
|
||||
newVersion: "13.0.0",
|
||||
wantSave: false,
|
||||
},
|
||||
{
|
||||
desc: "to not upgrade - new version",
|
||||
testConfig: &Configuration{
|
||||
Version: "13.1.0",
|
||||
SortBy: "name",
|
||||
},
|
||||
newVersion: "13.1.0",
|
||||
wantSave: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
// Create temporary file for config
|
||||
configFile, err := os.CreateTemp("/tmp", "yay-*-config.json")
|
||||
require.NoError(t, err)
|
||||
|
||||
testFilePath := configFile.Name()
|
||||
defer os.Remove(testFilePath)
|
||||
// Create config with configVersion and sortby
|
||||
tcConfig := Configuration{
|
||||
Version: tc.testConfig.Version,
|
||||
SortBy: tc.testConfig.SortBy,
|
||||
// Create runtime with runtimeVersion
|
||||
}
|
||||
|
||||
// Run Migration
|
||||
err = tcConfig.RunMigrations(newTestLogger(),
|
||||
[]configMigration{&configSortByMigration{}},
|
||||
testFilePath, tc.newVersion)
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check file contents if wantSave otherwise check file empty
|
||||
cfile, err := os.Open(testFilePath)
|
||||
require.NoError(t, err)
|
||||
defer cfile.Close()
|
||||
|
||||
decoder := json.NewDecoder(cfile)
|
||||
newConfig := Configuration{}
|
||||
err = decoder.Decode(&newConfig)
|
||||
if tc.wantSave {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tc.newVersion, newConfig.Version)
|
||||
assert.Equal(t, "", newConfig.SortBy)
|
||||
} else {
|
||||
require.Error(t, err)
|
||||
assert.Empty(t, newConfig.Version)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
74
pkg/settings/yay.conf
Normal file
74
pkg/settings/yay.conf
Normal file
@ -0,0 +1,74 @@
|
||||
# /etc/yay.conf - System-wide yay configuration
|
||||
[options]
|
||||
|
||||
# AUR Settings
|
||||
AurUrl = https://aur.archlinux.org
|
||||
#AurRpcUrl = https://aur.archlinux.org/rpc
|
||||
|
||||
# Directories
|
||||
BuildDir = ~/.cache/yay
|
||||
|
||||
# Binaries
|
||||
#Editor = vim
|
||||
#EditorFlags =
|
||||
MakepkgBin = makepkg
|
||||
#MakepkgConf =
|
||||
PacmanBin = pacman
|
||||
PacmanConf = /etc/pacman.conf
|
||||
GitBin = git
|
||||
GpgBin = gpg
|
||||
SudoBin = sudo
|
||||
|
||||
# Flags
|
||||
# GpgFlags =
|
||||
# MFlags =
|
||||
# GitFlags =
|
||||
# SudoFlags =
|
||||
|
||||
# Search/Display
|
||||
BottomUp
|
||||
# SingleLineResults
|
||||
SeparateSources
|
||||
|
||||
# Sorting
|
||||
#SortBy =
|
||||
SearchBy = name-desc
|
||||
|
||||
# Build options
|
||||
#Devel
|
||||
#CleanAfter
|
||||
#KeepSrc
|
||||
#BatchInstall
|
||||
|
||||
RemoveMake = ask
|
||||
|
||||
# Download options
|
||||
ReDownload = no
|
||||
ReBuild = no
|
||||
PgpFetch
|
||||
|
||||
# Menus
|
||||
CleanMenu
|
||||
DiffMenu
|
||||
# EditMenu
|
||||
|
||||
# Prompts
|
||||
#AnswerClean =
|
||||
#AnswerDiff =
|
||||
#AnswerEdit =
|
||||
#AnswerUpgrade =
|
||||
|
||||
# Behavior
|
||||
CombinedUpgrade
|
||||
#SudoLoop
|
||||
#TimeUpdate
|
||||
Provides
|
||||
#UseAsk
|
||||
DoubleConfirm
|
||||
Rpc
|
||||
#Debug
|
||||
|
||||
# Limits
|
||||
RequestSplitN = 150
|
||||
CompletionInterval = 7
|
||||
MaxConcurrentDownloads = 1
|
||||
Loading…
x
Reference in New Issue
Block a user