diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a05422b5..a77b5c06 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,6 +13,7 @@ jobs: outputs: documentation: ${{ steps.filter.outputs.documentation }} cli: ${{ steps.filter.outputs.cli }} + go: ${{ steps.filter.outputs.go }} steps: - uses: actions/checkout@v4 with: diff --git a/commands/commands.go b/cmd/main.go similarity index 97% rename from commands/commands.go rename to cmd/main.go index 7b8c9b35..8fb9673a 100644 --- a/commands/commands.go +++ b/cmd/main.go @@ -1,4 +1,4 @@ -package commands +package cmd import ( "fmt" diff --git a/config.go b/config.go new file mode 100644 index 00000000..be46c7f5 --- /dev/null +++ b/config.go @@ -0,0 +1,105 @@ +package main + +import ( + "context" + "strings" + + "github.com/mitchellh/go-homedir" + "github.com/sethvargo/go-envconfig" + "gopkg.in/ini.v1" +) + +// Not sure how best to represent this enum type +//type PluginRepositoryLastCheckDuration struct { +// Never bool +// Every int +//} + +type Settings struct { + LegacyVersionFile bool + // I don't think this setting should be supported in the Golang implementation + //UseReleaseCandidates bool + AlwaysKeepDownload bool + PluginRepositoryLastCheckDuration string + DisablePluginShortNameRepository bool +} + +type Config struct { + Home string + ConfigFile string `env:"ASDF_CONFIG_FILE"` + DefaultToolVersionsFilename string `env:"ASDF_DEFAULT_TOOL_VERSIONS_FILENAME"` + // Unclear if this value will be needed with the golang implementation. + //AsdfDir string + DataDir string `env:"ASDF_DATA_DIR"` + ForcePrepend bool `env:"ASDF_FORCE_PREPEND"` +} + +func LoadConfig() (Config, error) { + config, err := loadConfigEnv() + + if err != nil { + return config, err + } + + homeDir, err := homedir.Dir() + + if err != nil { + return config, err + } + + config.Home = homeDir + + return config, nil +} + +func loadConfigEnv() (Config, error) { + context := context.Background() + config := Config{} + + err := envconfig.Process(context, &config) + + return config, err +} + +func LoadSettings(asdfrcPath string) (Settings, error) { + // asdfrc is effectively formatted as ini + config, err := ini.Load(asdfrcPath) + + if err != nil { + return Settings{}, err + } + + mainConf := config.Section("") + checkDuration := stringToCheckDuration(mainConf.Key("plugin_repository_last_check_duration").String(), "60") + + return Settings{ + LegacyVersionFile: YesNoToBool(mainConf, "legacy_version_file", false), + AlwaysKeepDownload: YesNoToBool(mainConf, "use_release_candidates", false), + PluginRepositoryLastCheckDuration: checkDuration, + DisablePluginShortNameRepository: YesNoToBool(mainConf, "disable_plugin_short_name_repository", false), + }, nil +} + +func YesNoToBool(section *ini.Section, key string, defaultValue bool) bool { + yesOrNo := section.Key(key).String() + lcYesOrNo := strings.ToLower(yesOrNo) + if lcYesOrNo == "yes" { + return true + } + if lcYesOrNo == "no" { + return false + } + + return defaultValue +} + +// Eventually this should return a custom type but I need to figure out how to +// represent the (never string, duration int) type. For now it just returns a +// string. +func stringToCheckDuration(checkDuration string, defaultValue string) string { + if checkDuration != "" { + return checkDuration + } + + return defaultValue +} diff --git a/config_test.go b/config_test.go new file mode 100644 index 00000000..34d66f7c --- /dev/null +++ b/config_test.go @@ -0,0 +1,65 @@ +package main + +import ( + "testing" +) + +func TestLoadConfig(t *testing.T) { + config, err := LoadConfig() + + assert(t, err == nil, "Returned error when building config") + + assert(t, config.Home != "", "Expected Home to be set") +} + +func TestLoadConfigEnv(t *testing.T) { + config, err := loadConfigEnv() + + assert(t, err == nil, "Returned error when loading env for config") + + assert(t, config.Home == "", "Shouldn't set Home property when loading config") +} + +func TestLoadSettings(t *testing.T) { + t.Run("When given invalid path returns error", func(t *testing.T) { + _, err := LoadSettings("./foobar") + + if err == nil { + t.Fatal("Didn't get an error") + } + }) + + t.Run("When given path to populated asdfrc returns populated settings struct", func(t *testing.T) { + settings, err := LoadSettings("testdata/asdfrc") + + refuteError(t, err) + + assert(t, settings.LegacyVersionFile == true, "LegacyVersionFile field has wrong value") + assert(t, settings.AlwaysKeepDownload == true, "AlwaysKeepDownload field has wrong value") + assert(t, settings.PluginRepositoryLastCheckDuration == "never", "PluginRepositoryLastCheckDuration field has wrong value") + assert(t, settings.DisablePluginShortNameRepository == true, "DisablePluginShortNameRepository field has wrong value") + }) + + t.Run("When given path to empty file returns settings struct with defaults", func(t *testing.T) { + settings, err := LoadSettings("testdata/empty-asdfrc") + + refuteError(t, err) + + assert(t, settings.LegacyVersionFile == false, "LegacyVersionFile field has wrong value") + assert(t, settings.AlwaysKeepDownload == false, "AlwaysKeepDownload field has wrong value") + assert(t, settings.PluginRepositoryLastCheckDuration == "60", "PluginRepositoryLastCheckDuration field has wrong value") + assert(t, settings.DisablePluginShortNameRepository == false, "DisablePluginShortNameRepository field has wrong value") + }) +} + +func assert(t *testing.T, expr bool, message string) { + if !expr { + t.Error(message) + } +} + +func refuteError(t *testing.T, err error) { + if err != nil { + t.Fatal("Returned unexpected error", err) + } +} diff --git a/go.mod b/go.mod index 5fbbb330..d91459ae 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,14 @@ module asdf go 1.21.5 require ( - github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/mitchellh/go-homedir v1.1.0 + github.com/sethvargo/go-envconfig v1.0.0 github.com/spf13/cobra v1.8.0 + gopkg.in/ini.v1 v1.67.0 +) + +require ( + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/stretchr/testify v1.8.4 // indirect ) diff --git a/go.sum b/go.sum index d0e8c2c3..858080f4 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,25 @@ github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +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/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +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/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sethvargo/go-envconfig v1.0.0 h1:1C66wzy4QrROf5ew4KdVw942CQDa55qmlYmw9FZxZdU= +github.com/sethvargo/go-envconfig v1.0.0/go.mod h1:Lzc75ghUn5ucmcRGIdGQ33DKJrcjk4kihFYgSTBmjIc= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +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/main.go b/main.go index e23893f4..e8e681cf 100644 --- a/main.go +++ b/main.go @@ -1,10 +1,10 @@ package main import ( - "asdf/commands" + "asdf/cmd" ) // Placeholder for the real code func main() { - commands.Execute() + cmd.Execute() } diff --git a/testdata/asdfrc b/testdata/asdfrc new file mode 100644 index 00000000..d7e1c992 --- /dev/null +++ b/testdata/asdfrc @@ -0,0 +1,7 @@ +# This is a test asdfrc file containing all possible values. Each field to set +# to a value that is different than the default. +legacy_version_file = yes +use_release_candidates = yes +always_keep_download = yes +plugin_repository_last_check_duration = never +disable_plugin_short_name_repository = yes diff --git a/testdata/empty-asdfrc b/testdata/empty-asdfrc new file mode 100644 index 00000000..e69de29b