From dd2e795045d8021c46dd5ebb8a49d39b859c2f73 Mon Sep 17 00:00:00 2001 From: Kevin Franklin Kim Date: Thu, 20 Mar 2025 16:08:13 +0100 Subject: [PATCH] feat(sesamy): add config --- foomo/sesamy/README.md | 9 ++ foomo/sesamy/command.go | 186 +++++++++++++++++++++++++++++++--------- foomo/sesamy/config.go | 3 + go.mod | 9 +- go.sum | 18 +++- 5 files changed, 181 insertions(+), 44 deletions(-) create mode 100644 foomo/sesamy/config.go diff --git a/foomo/sesamy/README.md b/foomo/sesamy/README.md index 59c9a16..050efa3 100644 --- a/foomo/sesamy/README.md +++ b/foomo/sesamy/README.md @@ -28,6 +28,15 @@ func New(l log.Logger) (plugin.Plugin, error) { } ``` +### Config + +```yaml +sesamy: + default: + - path/to/sesamy.base.yaml + - path/to/sesamy.base.override.yaml +``` + ### Ownbrew To install binary locally, add: diff --git a/foomo/sesamy/command.go b/foomo/sesamy/command.go index 9dbe009..e183ce8 100644 --- a/foomo/sesamy/command.go +++ b/foomo/sesamy/command.go @@ -3,40 +3,80 @@ package sesamy import ( "bytes" "context" - "path" + "sort" "github.com/foomo/posh-providers/onepassword" - "github.com/foomo/posh/pkg/cache" "github.com/foomo/posh/pkg/command/tree" "github.com/foomo/posh/pkg/log" "github.com/foomo/posh/pkg/prompt/goprompt" "github.com/foomo/posh/pkg/readline" "github.com/foomo/posh/pkg/shell" - "github.com/foomo/posh/pkg/util/files" "github.com/foomo/posh/pkg/util/suggests" + "github.com/knadh/koanf/parsers/yaml" + "github.com/knadh/koanf/providers/file" + "github.com/knadh/koanf/v2" + "github.com/pkg/errors" + "github.com/pterm/pterm" + "github.com/samber/lo" + "github.com/spf13/viper" ) -type Command struct { - l log.Logger - op *onepassword.OnePassword - cache cache.Namespace - commandTree tree.Root +type ( + Command struct { + l log.Logger + name string + op *onepassword.OnePassword + config Config + configKey string + commandTree tree.Root + } + CommandOption func(*Command) +) + +// ------------------------------------------------------------------------------------------------ +// ~ Options +// ------------------------------------------------------------------------------------------------ + +func CommandWithName(v string) CommandOption { + return func(o *Command) { + o.name = v + } +} + +func CommandWithConfigKey(v string) CommandOption { + return func(o *Command) { + o.configKey = v + } } // ------------------------------------------------------------------------------------------------ // ~ Constructor // ------------------------------------------------------------------------------------------------ -func NewCommand(l log.Logger, op *onepassword.OnePassword, cache cache.Cache) *Command { +func NewCommand(l log.Logger, op *onepassword.OnePassword, opts ...CommandOption) (*Command, error) { inst := &Command{ - l: l.Named("sesamy"), - op: op, - - cache: cache.Get("sesamy"), + l: l.Named("sesamy"), + op: op, + name: "sesamy", + configKey: "sesamy", } + for _, opt := range opts { + if opt != nil { + opt(inst) + } + } + + if err := viper.UnmarshalKey(inst.configKey, &inst.config); err != nil { + return nil, err + } + + // if err := os.Setenv("SESAMY_SCOPE", inst.name); err != nil { + // return nil, err + // } + configArg := &tree.Arg{ - Name: "path", + Name: "config", Optional: true, Suggest: inst.completePaths, } @@ -46,14 +86,14 @@ func NewCommand(l log.Logger, op *onepassword.OnePassword, cache cache.Cache) *C } inst.commandTree = tree.New(&tree.Node{ - Name: "sesamy", + Name: inst.name, Description: "Run sesamy", Nodes: tree.Nodes{ { Name: "config", Description: "Dump config", Args: tree.Args{configArg}, - Execute: inst.config, + Execute: inst.conf, }, { Name: "tags", @@ -73,6 +113,16 @@ func NewCommand(l log.Logger, op *onepassword.OnePassword, cache cache.Cache) *C Name: "provision", Description: "Provision Google Tag Manager", Nodes: tree.Nodes{ + { + Name: "all", + Description: "Provision Web & Server Container", + Args: tree.Args{configArg}, + Flags: func(ctx context.Context, r *readline.Readline, fs *readline.FlagSets) error { + fs.Default().StringSlice("tags", nil, "list of tags to run") + return flags(ctx, r, fs) + }, + Execute: inst.provisionAll, + }, { Name: "web", Description: "Provision Web Container", @@ -109,6 +159,7 @@ func NewCommand(l log.Logger, op *onepassword.OnePassword, cache cache.Cache) *C Suggest: func(ctx context.Context, t tree.Root, r *readline.Readline) []goprompt.Suggest { return []goprompt.Suggest{ {Text: "built-in-variables"}, + {Text: "environments"}, {Text: "folders"}, {Text: "gtag-config"}, {Text: "status"}, @@ -118,6 +169,7 @@ func NewCommand(l log.Logger, op *onepassword.OnePassword, cache cache.Cache) *C {Text: "transformations"}, {Text: "triggers"}, {Text: "variables"}, + {Text: "workspaces"}, {Text: "zones"}, } }, @@ -136,6 +188,7 @@ func NewCommand(l log.Logger, op *onepassword.OnePassword, cache cache.Cache) *C Suggest: func(ctx context.Context, t tree.Root, r *readline.Readline) []goprompt.Suggest { return []goprompt.Suggest{ {Text: "built-in-variables"}, + {Text: "environments"}, {Text: "clients"}, {Text: "folders"}, {Text: "gtag-config"}, @@ -146,6 +199,7 @@ func NewCommand(l log.Logger, op *onepassword.OnePassword, cache cache.Cache) *C {Text: "transformations"}, {Text: "triggers"}, {Text: "variables"}, + {Text: "workspaces"}, {Text: "zones"}, } }, @@ -159,7 +213,7 @@ func NewCommand(l log.Logger, op *onepassword.OnePassword, cache cache.Cache) *C }, }) - return inst + return inst, nil } // ------------------------------------------------------------------------------------------------ @@ -190,7 +244,27 @@ func (c *Command) Help(ctx context.Context, r *readline.Readline) string { // ~ Private methods // ------------------------------------------------------------------------------------------------ -func (c *Command) config(ctx context.Context, r *readline.Readline) error { +func (c *Command) merge(ctx context.Context, path string) ([]byte, error) { + filenames, ok := c.config[path] + if !ok { + return nil, errors.New("invalid config key: " + path) + } + + var conf = koanf.Conf{ + Delim: "/", + } + var k = koanf.NewWithConf(conf) + + for _, filename := range filenames { + if err := k.Load(file.Provider(filename), yaml.Parser()); err != nil { + return nil, errors.Wrap(err, "error loading config file: "+filename) + } + } + + return k.Marshal(yaml.Parser()) +} + +func (c *Command) conf(ctx context.Context, r *readline.Readline) error { var paths []string if r.Args().HasIndex(1) { paths = []string{r.Args().At(1)} @@ -202,9 +276,15 @@ func (c *Command) config(ctx context.Context, r *readline.Readline) error { for _, value := range paths { c.l.Info("└ " + value) - out, err := c.op.RenderFile(ctx, value) + b, err := c.merge(ctx, value) if err != nil { - return err + return errors.Wrap(err, "failed to merge config") + } + + out, err := c.op.Render(ctx, string(b)) + if err != nil { + pterm.Error.Println(string(b)) + return errors.Wrap(err, "failed to render secrets") } if err := shell.New(ctx, c.l, "sesamy", "config"). @@ -212,7 +292,6 @@ func (c *Command) config(ctx context.Context, r *readline.Readline) error { Args("--config", "-"). Args(r.AdditionalArgs()...). Stdin(bytes.NewReader(out)). - Dir(path.Dir(value)). Run(); err != nil { return err } @@ -232,9 +311,15 @@ func (c *Command) tags(ctx context.Context, r *readline.Readline) error { for _, value := range paths { c.l.Info("└ " + value) - out, err := c.op.RenderFile(ctx, value) + b, err := c.merge(ctx, value) if err != nil { - return err + return errors.Wrap(err, "failed to merge config") + } + + out, err := c.op.Render(ctx, string(b)) + if err != nil { + pterm.Error.Println(string(b)) + return errors.Wrap(err, "failed to render secrets") } if err := shell.New(ctx, c.l, "sesamy", "tags"). @@ -242,7 +327,6 @@ func (c *Command) tags(ctx context.Context, r *readline.Readline) error { Args("--config", "-"). Args(r.AdditionalArgs()...). Stdin(bytes.NewReader(out)). - Dir(path.Dir(value)). Run(); err != nil { return err } @@ -262,9 +346,15 @@ func (c *Command) typescript(ctx context.Context, r *readline.Readline) error { for _, value := range paths { c.l.Info("└ " + value) - out, err := c.op.RenderFile(ctx, value) + b, err := c.merge(ctx, value) if err != nil { - return err + return errors.Wrap(err, "failed to merge config") + } + + out, err := c.op.Render(ctx, string(b)) + if err != nil { + pterm.Error.Println(string(b)) + return errors.Wrap(err, "failed to render secrets") } if err := shell.New(ctx, c.l, "sesamy", "typescript"). @@ -272,7 +362,6 @@ func (c *Command) typescript(ctx context.Context, r *readline.Readline) error { Args("--config", "-"). Args(r.AdditionalArgs()...). Stdin(bytes.NewReader(out)). - Dir(path.Dir(value)). Run(); err != nil { return err } @@ -292,9 +381,15 @@ func (c *Command) provision(ctx context.Context, r *readline.Readline, cmd strin for _, value := range paths { c.l.Info("└ " + value) - out, err := c.op.RenderFile(ctx, value) + b, err := c.merge(ctx, value) if err != nil { - return err + return errors.Wrap(err, "failed to merge config") + } + + out, err := c.op.Render(ctx, string(b)) + if err != nil { + pterm.Error.Println(string(b)) + return errors.Wrap(err, "failed to render secrets") } if err := shell.New(ctx, c.l, "sesamy", "provision", cmd). @@ -302,7 +397,6 @@ func (c *Command) provision(ctx context.Context, r *readline.Readline, cmd strin Args("--config", "-"). Args(r.AdditionalArgs()...). Stdin(bytes.NewReader(out)). - Dir(path.Dir(value)). Run(); err != nil { return err } @@ -310,6 +404,16 @@ func (c *Command) provision(ctx context.Context, r *readline.Readline, cmd strin return nil } +func (c *Command) provisionAll(ctx context.Context, r *readline.Readline) error { + if err := c.provision(ctx, r, "web"); err != nil { + return err + } + if err := c.provision(ctx, r, "server"); err != nil { + return err + } + return nil +} + func (c *Command) provisionWeb(ctx context.Context, r *readline.Readline) error { return c.provision(ctx, r, "web") } @@ -340,9 +444,15 @@ func (c *Command) list(ctx context.Context, r *readline.Readline, cmd string) er for _, value := range paths { c.l.Info("└ " + value) - out, err := c.op.RenderFile(ctx, value) + b, err := c.merge(ctx, value) if err != nil { - return err + return errors.Wrap(err, "failed to merge config") + } + + out, err := c.op.Render(ctx, string(b)) + if err != nil { + pterm.Error.Println(string(b)) + return errors.Wrap(err, "failed to render secrets") } if err := shell.New(ctx, c.l, "sesamy", "list", cmd, resource). @@ -350,7 +460,6 @@ func (c *Command) list(ctx context.Context, r *readline.Readline, cmd string) er Args("--config", "-"). Args(r.AdditionalArgs()...). Stdin(bytes.NewReader(out)). - Dir(path.Dir(value)). Run(); err != nil { return err } @@ -364,12 +473,7 @@ func (c *Command) completePaths(ctx context.Context, t tree.Root, r *readline.Re //nolint:forcetypeassert func (c *Command) paths(ctx context.Context) []string { - return c.cache.Get("paths", func() any { - if value, err := files.Find(ctx, ".", "sesamy*.yml"); err != nil { - c.l.Debug("failed to walk files", err.Error()) - return nil - } else { - return value - } - }).([]string) + keys := lo.Keys(c.config) + sort.Strings(keys) + return keys } diff --git a/foomo/sesamy/config.go b/foomo/sesamy/config.go new file mode 100644 index 0000000..a41b809 --- /dev/null +++ b/foomo/sesamy/config.go @@ -0,0 +1,3 @@ +package sesamy + +type Config map[string][]string diff --git a/go.mod b/go.mod index 4c2c1db..ef10050 100644 --- a/go.mod +++ b/go.mod @@ -10,11 +10,14 @@ require ( github.com/c-bata/go-prompt v0.2.6 github.com/cloudrecipes/packagejson v1.0.0 github.com/digitalocean/godo v1.139.0 - github.com/foomo/posh v0.10.0 + github.com/foomo/posh v0.10.1 github.com/goccy/go-json v0.10.5 github.com/golang-migrate/migrate/v4 v4.18.2 github.com/google/go-github/v47 v47.1.0 github.com/joho/godotenv v1.5.1 + github.com/knadh/koanf/parsers/yaml v0.1.0 + github.com/knadh/koanf/providers/file v1.1.2 + github.com/knadh/koanf/v2 v2.1.2 github.com/mitchellh/mapstructure v1.5.0 github.com/muesli/go-app-paths v0.2.2 github.com/pkg/errors v0.9.1 @@ -41,6 +44,7 @@ require ( github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-test/deep v1.1.0 // indirect + github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/gookit/color v1.5.4 // indirect @@ -50,6 +54,7 @@ require ( github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-retryablehttp v0.7.7 // indirect github.com/hashicorp/hcl v1.0.0 // indirect + github.com/knadh/koanf/maps v0.1.1 // indirect github.com/lithammer/fuzzysearch v1.1.8 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/magiconair/properties v1.8.9 // indirect @@ -57,7 +62,9 @@ require ( github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mattn/go-tty v0.0.7 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/neilotoole/slogt v1.1.0 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect diff --git a/go.sum b/go.sum index 797def5..228f9b7 100644 --- a/go.sum +++ b/go.sum @@ -42,8 +42,8 @@ github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZ github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= -github.com/foomo/posh v0.10.0 h1:iMCHgsUBZTMIeeCF0pnrT8vsWYguxtPXC4ndGrNpaMo= -github.com/foomo/posh v0.10.0/go.mod h1:iXdJ4j07pWlzrmINk7sn0hsl9b9cC90c6LcF4O96vMQ= +github.com/foomo/posh v0.10.1 h1:sIjCyEcFdKOHuyc/gcRPchHlHL8+qIkLvGfMkYy8cf0= +github.com/foomo/posh v0.10.1/go.mod h1:mHj/4ktsbdHOQTj6VDCsw/1lOA8qPeEsbJlKOMaZSmk= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/franklinkim/go-prompt v0.2.7-0.20210427061716-a8f4995d7aa5 h1:kXNtle4AoQnngdm+gwt4ku6Llbzw3EFHgZYpL618JaI= @@ -55,6 +55,8 @@ github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiU github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= +github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/golang-migrate/migrate/v4 v4.18.2 h1:2VSCMz7x7mjyTXx3m2zPokOY82LTRgxK1yQYKo6wWQ8= @@ -97,6 +99,14 @@ github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuOb github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU= github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs= +github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI= +github.com/knadh/koanf/parsers/yaml v0.1.0 h1:ZZ8/iGfRLvKSaMEECEBPM1HQslrZADk8fP1XFUxVI5w= +github.com/knadh/koanf/parsers/yaml v0.1.0/go.mod h1:cvbUDC7AL23pImuQP0oRw/hPuccrNBS2bps8asS0CwY= +github.com/knadh/koanf/providers/file v1.1.2 h1:aCC36YGOgV5lTtAFz2qkgtWdeQsgfxUkxDOe+2nQY3w= +github.com/knadh/koanf/providers/file v1.1.2/go.mod h1:/faSBcv2mxPVjFrXck95qeoyoZ5myJ6uxN8OOVNJJCI= +github.com/knadh/koanf/v2 v2.1.2 h1:I2rtLRqXRy1p01m/utEtpZSSA6dcJbgGVuE27kW2PzQ= +github.com/knadh/koanf/v2 v2.1.2/go.mod h1:Gphfaen0q1Fc1HTgJgSTC4oRX9R2R5ErYMZJy8fLJBo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -128,10 +138,14 @@ github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0= github.com/mattn/go-tty v0.0.7 h1:KJ486B6qI8+wBO7kQxYgmmEFDaFEE96JMBQ7h400N8Q= github.com/mattn/go-tty v0.0.7/go.mod h1:f2i5ZOvXBU/tCABmLmOfzLz9azMo5wdAaElRNnJKr+k= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= 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/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 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/neilotoole/slogt v1.1.0 h1:c7qE92sq+V0yvCuaxph+RQ2jOKL61c4hqS1Bv9W7FZE=