diff --git a/go.mod b/go.mod index 5422161..308ee5c 100644 --- a/go.mod +++ b/go.mod @@ -3,20 +3,22 @@ module github.com/gorcon/rcon-cli go 1.21 require ( + github.com/adrg/xdg v0.5.3 github.com/gorcon/rcon v1.3.5 github.com/gorcon/telnet v1.2.3 github.com/gorcon/websocket v1.1.3 github.com/gorilla/websocket v1.5.1 - github.com/stretchr/testify v1.7.1 + github.com/stretchr/testify v1.9.0 github.com/urfave/cli/v2 v2.27.1 gopkg.in/yaml.v3 v3.0.1 ) require ( github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect - github.com/davecgh/go-spew v1.1.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e // indirect golang.org/x/net v0.20.0 // indirect + golang.org/x/sys v0.26.0 // indirect ) diff --git a/go.sum b/go.sum index ac8b163..4b3b94b 100644 --- a/go.sum +++ b/go.sum @@ -1,45 +1,32 @@ -github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78= +github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ= github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gorcon/rcon v1.3.4 h1:TExNhWI2mJlqpCg49vajUgznvEZbEzQWKujY1Sy+/AY= -github.com/gorcon/rcon v1.3.4/go.mod h1:46+oSXgPwlRAkcAPStkNnIL1dlcxJweKVNWshy3hDJI= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gorcon/rcon v1.3.5 h1:YE/Vrw6R99uEP08wp0EjdPAP3Jwz/ys3J8qxI1nYoeU= github.com/gorcon/rcon v1.3.5/go.mod h1:zR1qfKZttF8vAgH1NsP6CdpachOvLDq8jE64NboTpIM= -github.com/gorcon/telnet v1.2.2 h1:Oyo5CBpZv5sVr8WfUerAZuUfOImvRJ5XZwrsqKNCtvg= -github.com/gorcon/telnet v1.2.2/go.mod h1:DKmih80eUSG39WBM4F/xbl6BwDvn1RLBfsqPO9w7IWk= github.com/gorcon/telnet v1.2.3 h1:qzMFpGn7UVJUQzYyoWNzfhMAzb9CubhtocoTOSd6aa4= github.com/gorcon/telnet v1.2.3/go.mod h1:eZGICW4Mdyh81CakCja9YwXv4SWoAiBUP7mMDMbwheE= -github.com/gorcon/websocket v1.1.2 h1:/OflFZiT8/vY3AJmehpAhSlQw771AzbFKf0wiUA9uUM= -github.com/gorcon/websocket v1.1.2/go.mod h1:iRDDKfIeot5GeM5dt35VTURslbxICv+3uP5OeO66ruo= github.com/gorcon/websocket v1.1.3 h1:wZRidsL/ib6yKLqNdZ9YJKHq12K7nzypomswBXxgRzo= github.com/gorcon/websocket v1.1.3/go.mod h1:FjrAj9v6QXV0ZZUPrjK9HgUwgXUVlw7YyFKKbvYEesk= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= 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/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/urfave/cli/v2 v2.25.0 h1:ykdZKuQey2zq0yin/l7JOm9Mh+pg72ngYMeB0ABn6q8= -github.com/urfave/cli/v2 v2.25.0/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho= github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e h1:+SOyEddqYF09QP7vr7CgJ1eti3pY9Fn3LHO1M1r/0sI= github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -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= diff --git a/internal/config/config.go b/internal/config/config.go index 5c398cd..1c86954 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -9,6 +9,8 @@ import ( "path/filepath" "gopkg.in/yaml.v3" + + "github.com/adrg/xdg" ) // DefaultConfigName sets the default config file name. @@ -27,6 +29,8 @@ var ( ErrUnsupportedFileExt = errors.New("unsupported file extension") ) +var AllowXDGConfig = true + // Config allows to take a remote server address and password from // the configuration file. This enables not to specify these flags when // running the CLI. @@ -45,7 +49,7 @@ type Config map[string]Session func NewConfig(name string) (*Config, error) { cfg := new(Config) if err := cfg.ParseFromFile(name); err != nil { - return nil, fmt.Errorf("parse file: %w", err) + return nil, err } if err := cfg.Validate(); err != nil { @@ -62,14 +66,35 @@ func (cfg *Config) ParseFromFile(name string) error { return cfg.parse(name) } - home, err := filepath.Abs(filepath.Dir(os.Args[0])) - if err != nil { - return fmt.Errorf("get abs path: %w", err) + var err error + configPath := "" + if AllowXDGConfig { + configPath, err = xdg.ConfigFile(filepath.Join("gorcon", DefaultConfigName)) + if err != nil { + return err + } } - name = home + "/" + DefaultConfigName - if err = cfg.parse(name); err != nil && !errors.Is(err, os.ErrNotExist) { - return err + return cfg.parseFirstExist( + configPath, + DefaultConfigName, + ) +} + +// Parse the first file that exists from the provided names. +func (cfg *Config) parseFirstExist(names ...string) error { + var err error + for _, name := range names { + if name == "" { + continue + } + + if err = cfg.parse(name); err == nil { + return nil + } + if !errors.Is(err, os.ErrNotExist) { + return err + } } *cfg = Config{DefaultConfigEnv: {}} @@ -97,7 +122,7 @@ func (cfg *Config) Validate() error { func (cfg *Config) parse(name string) error { file, err := os.ReadFile(name) if err != nil { - return fmt.Errorf("read file: %w", err) + return fmt.Errorf("read file %s: %w", name, err) } switch ext := path.Ext(name); ext { @@ -109,5 +134,9 @@ func (cfg *Config) parse(name string) error { err = fmt.Errorf("%w %s", ErrUnsupportedFileExt, ext) } - return err + if err != nil { + return fmt.Errorf("parse file %s: %w", name, err) + } + + return nil } diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 61a2db2..84a99c5 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -17,6 +17,8 @@ const ConfigLayoutJSON = `{"%s": {"address": "%s", "password": "%s", "log": "%s" const ConfigLayoutYAML = "%s:\n address: %s\n password: %s\n log: %s\n type: %s" func TestNewConfig(t *testing.T) { + config.AllowXDGConfig = false // Disable XDG config for testing + t.Run("no errors yaml", func(t *testing.T) { configFileName := "rcon-test-local.yaml" stringBody := fmt.Sprintf(ConfigLayoutYAML, config.DefaultConfigEnv, "", "", DefaultTestLogName, "") @@ -74,7 +76,7 @@ func TestNewConfig(t *testing.T) { defer os.Remove(configFileName) cfg, err := config.NewConfig(configFileName) - assert.EqualError(t, err, "parse file: yaml: line 1: did not find expected key") + assert.EqualError(t, err, "parse file rcon-test-local.yaml: yaml: line 1: did not find expected key") assert.Nil(t, cfg) }) @@ -86,7 +88,7 @@ func TestNewConfig(t *testing.T) { defer os.Remove(configFileName) cfg, err := config.NewConfig(configFileName) - assert.EqualError(t, err, "parse file: unsupported file extension .ini") + assert.EqualError(t, err, "parse file unsupported-local.ini: unsupported file extension .ini") assert.Nil(t, cfg) }) diff --git a/internal/executor/executor.go b/internal/executor/executor.go index 1f07c71..fee02cb 100644 --- a/internal/executor/executor.go +++ b/internal/executor/executor.go @@ -289,7 +289,7 @@ func (executor *Executor) getFlags() []cli.Flag { Name: "config", Aliases: []string{"c"}, Usage: "Path to the configuration file", - Value: config.DefaultConfigName, + Value: "", }, &cli.StringFlag{ Name: "env",