Skip to content

Commit ade57f1

Browse files
committed
Merge branch 'feature/dev-3093-create-a-cli-command-core-library' of https://github.com/cloudposse/atmos into feature/dev-3093-create-a-cli-command-core-library
2 parents 4a64fb7 + 247ab4c commit ade57f1

File tree

2 files changed

+153
-4
lines changed

2 files changed

+153
-4
lines changed

pkg/config/config_new.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,7 @@ func (c *ConfigHandler) AddConfig(cmd *cobra.Command, opts *ConfigOptions) {
7676

7777
// Handle environment variable binding
7878
if opts.EnvVar != "" {
79-
if err := c.v.BindEnv(key, opts.EnvVar); err != nil {
80-
panic(err)
81-
}
79+
c.BindEnv(key, opts.EnvVar)
8280
}
8381
}
8482

@@ -119,7 +117,9 @@ func (c *ConfigHandler) Get() *schema.AtmosConfiguration {
119117
}
120118

121119
func (c *ConfigHandler) BindEnv(key ...string) {
122-
c.v.BindEnv(key...)
120+
if err := c.v.BindEnv(key...); err != nil {
121+
panic(err)
122+
}
123123
}
124124

125125
// GetString retrieves a string value from the config.

pkg/config/config_viper.go

+149
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package config
2+
3+
import (
4+
"fmt"
5+
"path/filepath"
6+
"strings"
7+
8+
"github.com/cloudposse/atmos/pkg/schema"
9+
"github.com/spf13/cobra"
10+
"github.com/spf13/viper"
11+
)
12+
13+
var DefaultConfigHandler, err = New()
14+
15+
// ConfigHandler holds the application's configuration
16+
type ConfigHandler struct {
17+
atmosConfig *schema.AtmosConfiguration
18+
v *viper.Viper
19+
}
20+
21+
// ConfigOptions defines options for adding a configuration parameter
22+
type ConfigOptions struct {
23+
FlagName string // Custom flag name (optional)
24+
EnvVar string // Custom environment variable (optional)
25+
Description string // Flag description
26+
Key string // Key of the data in atmosConfiguration
27+
DefaultValue interface{} // Default value of the data in atmosConfiguration
28+
}
29+
30+
// New creates a new Config instance with initialized Viper
31+
func New() (*ConfigHandler, error) {
32+
v := viper.New()
33+
v.SetConfigType("yaml")
34+
v.SetTypeByDefaultValue(true)
35+
configHandler := &ConfigHandler{
36+
v: v,
37+
atmosConfig: &schema.AtmosConfiguration{},
38+
}
39+
return configHandler, configHandler.load()
40+
}
41+
42+
// AddConfig adds a configuration parameter to both Cobra and Viper with options
43+
func (c *ConfigHandler) AddConfig(cmd *cobra.Command, opts *ConfigOptions) error {
44+
key := opts.Key
45+
defaultValue := opts.DefaultValue
46+
// Set default value in Viper
47+
c.v.SetDefault(key, defaultValue)
48+
49+
// Determine flag name
50+
flagName := opts.FlagName
51+
if flagName == "" {
52+
flagName = strings.ReplaceAll(key, ".", "-")
53+
}
54+
55+
// Register flag with Cobra
56+
flagSet := cmd.PersistentFlags()
57+
58+
switch defaultValue.(type) {
59+
case string:
60+
flagSet.String(flagName, defaultValue.(string), opts.Description)
61+
case int:
62+
flagSet.Int(flagName, defaultValue.(int), opts.Description)
63+
case bool:
64+
flagSet.Bool(flagName, defaultValue.(bool), opts.Description)
65+
default:
66+
return fmt.Errorf("unsupported type for key %s", key)
67+
}
68+
69+
// Bind the flag to Viper
70+
if err := c.v.BindPFlag(key, flagSet.Lookup(flagName)); err != nil {
71+
return fmt.Errorf("failed to bind %s: %w", key, err)
72+
}
73+
74+
// Handle environment variable binding
75+
if opts.EnvVar != "" {
76+
if err := c.v.BindEnv(key, opts.EnvVar); err != nil {
77+
return fmt.Errorf("failed to bind custom env var %s for %s: %w", opts.EnvVar, key, err)
78+
}
79+
} else {
80+
if err := c.v.BindEnv(key); err != nil {
81+
return fmt.Errorf("failed to bind default env var for %s: %w", key, err)
82+
}
83+
}
84+
85+
return nil
86+
}
87+
88+
// load reads and merges the configuration
89+
func (c *ConfigHandler) load() error {
90+
// Read config file if exists (non-blocking)
91+
if err := loadConfigSources(c.v, ""); err != nil {
92+
return err
93+
}
94+
if c.v.ConfigFileUsed() != "" {
95+
// get dir of atmosConfigFilePath
96+
atmosConfigDir := filepath.Dir(c.v.ConfigFileUsed())
97+
c.atmosConfig.CliConfigPath = atmosConfigDir
98+
// Set the CLI config path in the atmosConfig struct
99+
if !filepath.IsAbs(c.atmosConfig.CliConfigPath) {
100+
absPath, err := filepath.Abs(c.atmosConfig.CliConfigPath)
101+
if err != nil {
102+
return err
103+
}
104+
c.atmosConfig.CliConfigPath = absPath
105+
}
106+
}
107+
// TODO: This is copy paste need to set this in the right place
108+
// We want the editorconfig color by default to be true
109+
c.atmosConfig.Validate.EditorConfig.Color = true
110+
111+
viper.AutomaticEnv()
112+
113+
// Unmarshal into AtmosConfiguration struct
114+
if err := c.v.Unmarshal(c.atmosConfig); err != nil {
115+
return err
116+
}
117+
c.atmosConfig.ProcessSchemas()
118+
119+
return nil
120+
}
121+
122+
// Get returns the populated AtmosConfiguration
123+
func (c *ConfigHandler) Get() *schema.AtmosConfiguration {
124+
return c.atmosConfig
125+
}
126+
127+
func (c *ConfigHandler) BindEnv(key ...string) {
128+
c.v.BindEnv(key...)
129+
}
130+
131+
// GetString retrieves a string value from the config
132+
func (c *ConfigHandler) GetString(key string) string {
133+
return c.v.GetString(key)
134+
}
135+
136+
// GetInt retrieves an int value from the config
137+
func (c *ConfigHandler) GetInt(key string) int {
138+
return c.v.GetInt(key)
139+
}
140+
141+
// GetBool retrieves a bool value from the config
142+
func (c *ConfigHandler) GetBool(key string) bool {
143+
return c.v.GetBool(key)
144+
}
145+
146+
// GetStringSlice retrieves a string slice value from the config
147+
func (c *ConfigHandler) SetDefault(key string, value any) {
148+
c.v.SetDefault(key, value)
149+
}

0 commit comments

Comments
 (0)