Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(proxy): use muesli for config paths (#162) #180

Merged
merged 1 commit into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.21
require (
github.com/charmbracelet/bubbletea v0.25.0
github.com/charmbracelet/lipgloss v0.9.1
github.com/muesli/go-app-paths v0.2.2
github.com/onsi/ginkgo/v2 v2.15.0
github.com/onsi/gomega v1.31.1
github.com/pkg/errors v0.9.1
Expand Down Expand Up @@ -32,6 +33,7 @@ require (
github.com/mattn/go-isatty v0.0.18 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+Ei
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
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/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
Expand All @@ -78,6 +80,8 @@ github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTd
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/muesli/go-app-paths v0.2.2 h1:NqG4EEZwNIhBq/pREgfBmgDmt3h1Smr1MjZiXbpZUnI=
github.com/muesli/go-app-paths v0.2.2/go.mod h1:SxS3Umca63pcFcLtbjVb+J0oD7cl4ixQWoBKhGEtEho=
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
Expand Down
50 changes: 49 additions & 1 deletion src/app/cfg/config-runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"path/filepath"

gap "github.com/muesli/go-app-paths"
"github.com/samber/lo"
"github.com/snivilised/cobrass/src/assistant/configuration"
ci18n "github.com/snivilised/cobrass/src/assistant/i18n"
Expand Down Expand Up @@ -45,6 +46,7 @@ func New(
applicationName: applicationName,
home: home,
vfs: vfs,
useXDG: common.IsUsingXDG(ci.Viper),
}, err
}

Expand All @@ -55,6 +57,7 @@ type configRunner struct {
applicationName string
home string
vfs storage.VirtualFS
useXDG bool
}

func (c *configRunner) DefaultPath() string {
Expand All @@ -65,7 +68,6 @@ func (c *configRunner) Run() error {
c.vc.SetConfigName(c.ci.Name)
c.vc.SetConfigType(c.ci.ConfigType)
c.vc.AutomaticEnv()
c.vc.AddConfigPath(c.path())

err := c.read()

Expand Down Expand Up @@ -93,6 +95,9 @@ func (c *configRunner) read() error {
var (
err error
)

c.vc.AddConfigPath(c.path())

// the returned error from vc.ReadInConfig() does not support standard
// golang error identity via errors.Is, so we are forced to assume
// that if we get an error, it is viper.ConfigFileNotFoundError
Expand All @@ -111,6 +116,49 @@ func (c *configRunner) read() error {

return nil
},
func() error {
// try standard or XDG
//
if c.useXDG {
// manual XDG: ["~/.local/share/app", "/usr/local/share/app", "/usr/share/app"]
// https://github.com/muesli/go-app-paths?tab=readme-ov-file#directories
//
paths := []string{
filepath.Join(c.home, ".local", "share"),
filepath.Join(string(filepath.Separator), "usr", "local", "share"),
filepath.Join(string(filepath.Separator), "usr", "share"),
}

for _, dir := range paths {
c.vc.AddConfigPath(filepath.Join(dir, common.Definitions.Pixa.AppName))
}
} else {
// use standard muesli; ie platform specific
//
scope := lo.TernaryF(c.ci.Scope != nil,
func() common.ConfigScope {
return c.ci.Scope
},
func() common.ConfigScope {
return gap.NewVendorScope(gap.User,
common.Definitions.Pixa.Org, common.Definitions.Pixa.AppName,
)
},
)

paths, e := scope.ConfigDirs()

if e == nil {
for _, dir := range paths {
c.vc.AddConfigPath(dir)
}
} else {
return e
}
}

return nil
},
func() error {
// not found in home, therefore export default to
// home path, which has already been added in previous
Expand Down
99 changes: 99 additions & 0 deletions src/app/cfg/config-runner_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cfg_test

import (
"errors"
"fmt"
"path/filepath"

Expand All @@ -19,12 +20,39 @@ import (
var (
sourceID = "github.com/snivilised/pixa"
environment = "PIXA_HOME"
useXDG = ""
)

type testScope struct {
}

func (f *testScope) ConfigDirs() ([]string, error) {
return []string{
filepath.Join(string(filepath.Separator), "foo"),
filepath.Join(string(filepath.Separator), "bar"),
}, nil
}

func (f *testScope) LogPath(filename string) (string, error) {
return filename, nil
}

type errorScope struct {
}

func (f *errorScope) ConfigDirs() ([]string, error) {
return []string{}, errors.New("fake could not get config dirs")
}

func (f *errorScope) LogPath(filename string) (string, error) {
return filename, nil
}

type runnerTE struct {
given string
should string
path string
scope common.ConfigScope
arrange func(entry *runnerTE, path string)
created func(entry *runnerTE, runner common.ConfigRunner)
assert func(entry *runnerTE, runner common.ConfigRunner, err error)
Expand Down Expand Up @@ -59,6 +87,7 @@ var _ = Describe("ConfigRunner", func() {
ConfigType: common.Definitions.Pixa.ConfigType,
ConfigPath: entry.path,
Viper: mock,
Scope: entry.scope,
}
// this is why I hate mocking, requires too much
// knowledge of the implementation, making the tests
Expand Down Expand Up @@ -94,6 +123,10 @@ var _ = Describe("ConfigRunner", func() {
given: "config file present at PIXA_HOME",
should: "use config at PIXA_HOME",
arrange: func(_ *runnerTE, path string) {
mock.EXPECT().Get(gomock.Eq(common.Definitions.Environment.UseXDG)).DoAndReturn(func(_ string) string {
return ""
}).AnyTimes()

mock.EXPECT().ReadInConfig().Times(1)
mock.EXPECT().AddConfigPath(path).AnyTimes()
mock.EXPECT().Get(gomock.Eq(environment)).DoAndReturn(func(_ string) string {
Expand All @@ -110,6 +143,10 @@ var _ = Describe("ConfigRunner", func() {
given: "config file present as configured by client, PIXA_HOME not defined",
should: "use config at specified path",
arrange: func(_ *runnerTE, _ string) {
mock.EXPECT().Get(gomock.Eq(common.Definitions.Environment.UseXDG)).DoAndReturn(func(_ string) string {
return ""
}).AnyTimes()

mock.EXPECT().ReadInConfig().Times(1)
mock.EXPECT().AddConfigPath(gomock.Any()).AnyTimes()
mock.EXPECT().Get(gomock.Eq(environment)).DoAndReturn(func(_ string) string {
Expand All @@ -126,6 +163,10 @@ var _ = Describe("ConfigRunner", func() {
given: "config file missing, but at default location, PIXA_HOME not defined",
should: "use config at default location",
arrange: func(_ *runnerTE, _ string) {
mock.EXPECT().Get(gomock.Eq(common.Definitions.Environment.UseXDG)).DoAndReturn(func(_ string) string {
return ""
}).AnyTimes()

mock.EXPECT().Get(gomock.Eq(environment)).DoAndReturn(func(_ string) string {
return ""
}).AnyTimes()
Expand Down Expand Up @@ -158,6 +199,64 @@ var _ = Describe("ConfigRunner", func() {
given: "config file completely missing",
should: "use default exported config",
arrange: func(_ *runnerTE, _ string) {
mock.EXPECT().Get(gomock.Eq(common.Definitions.Environment.UseXDG)).DoAndReturn(func(_ string) string {
return ""
}).AnyTimes()

mock.EXPECT().Get(gomock.Eq(environment)).DoAndReturn(func(_ string) string {
return ""
}).AnyTimes()

mock.EXPECT().ReadInConfig().Times(2).DoAndReturn(func() error {
return viper.ConfigFileNotFoundError{}
})
mock.EXPECT().AddConfigPath(gomock.Any()).AnyTimes()
mock.EXPECT().ReadInConfig().Times(1).DoAndReturn(func() error {
return nil
})
},
assert: func(_ *runnerTE, runner common.ConfigRunner, err error) {
Expect(err).Error().To(BeNil())
Expect(runner).NotTo(BeNil())
},
}),

Entry(nil, &runnerTE{
given: "use XDG, config file completely missing",
should: "use default exported config",
scope: &testScope{},
arrange: func(_ *runnerTE, _ string) {
mock.EXPECT().Get(gomock.Eq(common.Definitions.Environment.UseXDG)).DoAndReturn(func(_ string) string {
return "true"
}).AnyTimes()

mock.EXPECT().Get(gomock.Eq(environment)).DoAndReturn(func(_ string) string {
return ""
}).AnyTimes()

mock.EXPECT().ReadInConfig().Times(2).DoAndReturn(func() error {
return viper.ConfigFileNotFoundError{}
})
mock.EXPECT().AddConfigPath(gomock.Any()).AnyTimes()
mock.EXPECT().ReadInConfig().Times(1).DoAndReturn(func() error {
return nil
})
},
assert: func(_ *runnerTE, runner common.ConfigRunner, err error) {
Expect(err).Error().To(BeNil())
Expect(runner).NotTo(BeNil())
},
}),

Entry(nil, &runnerTE{
given: "scope returns error, config file completely missing",
should: "use default exported config",
scope: &errorScope{},
arrange: func(_ *runnerTE, _ string) {
mock.EXPECT().Get(gomock.Eq(common.Definitions.Environment.UseXDG)).DoAndReturn(func(_ string) string {
return ""
}).AnyTimes()

mock.EXPECT().Get(gomock.Eq(environment)).DoAndReturn(func(_ string) string {
return ""
}).AnyTimes()
Expand Down
31 changes: 18 additions & 13 deletions src/app/command/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/spf13/viper"
"golang.org/x/text/language"

gap "github.com/muesli/go-app-paths"
"github.com/snivilised/cobrass/src/assistant"
"github.com/snivilised/cobrass/src/assistant/configuration"
ci18n "github.com/snivilised/cobrass/src/assistant/i18n"
Expand Down Expand Up @@ -71,7 +72,7 @@ type Bootstrap struct {

type ConfigureOptionsInfo struct {
Detector LocaleDetector
Config common.ConfigInfo
Config *common.ConfigInfo
Runner common.ConfigRunner
}

Expand All @@ -81,14 +82,26 @@ type ConfigureOptionFn func(*ConfigureOptionsInfo)
// to be executed.
func (b *Bootstrap) Root(options ...ConfigureOptionFn) *cobra.Command {
vc := &configuration.GlobalViperConfig{}
ci := common.ConfigInfo{
ci := &common.ConfigInfo{
Name: common.Definitions.Pixa.AppName,
ConfigType: common.Definitions.Pixa.ConfigType,
Viper: vc,
Scope: gap.NewVendorScope(gap.User,
common.Definitions.Pixa.Org, common.Definitions.Pixa.AppName,
),
}

b.OptionsInfo = ConfigureOptionsInfo{
Detector: &Jabber{},
Config: ci,
}

for _, fo := range options {
fo(&b.OptionsInfo)
}

runner, err := cfg.New(
&ci,
ci,
common.Definitions.Pixa.SourceID,
common.Definitions.Pixa.AppName,
b.Vfs,
Expand All @@ -102,19 +115,11 @@ func (b *Bootstrap) Root(options ...ConfigureOptionFn) *cobra.Command {
os.Exit(1)
}

b.OptionsInfo = ConfigureOptionsInfo{
Detector: &Jabber{},
Config: ci,
Runner: runner,
}

for _, fo := range options {
fo(&b.OptionsInfo)
}
b.OptionsInfo.Runner = runner

b.configure()
b.viper()
b.Logger = plog.New(b.Configs.Logging, b.Vfs)
b.Logger = plog.New(b.Configs.Logging, b.Vfs, ci.Scope, vc)

b.Container = assistant.NewCobraContainer(
&cobra.Command{
Expand Down
Loading
Loading