Skip to content

Commit

Permalink
fix(proxy): use muesli for config paths (#162)
Browse files Browse the repository at this point in the history
  • Loading branch information
plastikfan committed Feb 23, 2024
1 parent e3a4c63 commit 5cee447
Show file tree
Hide file tree
Showing 10 changed files with 334 additions and 44 deletions.
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

0 comments on commit 5cee447

Please sign in to comment.