Skip to content
62 changes: 55 additions & 7 deletions internal/config/config.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package config

import (
"bytes"
"fmt"
"os"
"path/filepath"
"text/template"

"gopkg.in/yaml.v3"

Expand All @@ -16,10 +18,37 @@ const (

DefaultConfigFileName = "cli.yaml"
defaultConfigPermission = 0644

DefaultOutput = "human"
configFileTemplate = `# Scaleway CLI config file
# This config file can be used only with Scaleway CLI (>2.0.0) (https://github.com/scaleway/scaleway-cli)
# Output sets the output format for all commands you run
{{ if .Output }}output: {{ .Output }}{{ else }}# output: human{{ end }}

# Alias creates custom aliases for your Scaleway CLI commands
{{- if .Alias }}
alias:
aliases:
{{- range $alias, $commands := .Alias.Aliases }}
{{ $alias }}:
{{- range $index, $command := $commands }}
- {{ $command }}
{{- end }}
{{- end }}
{{- else }}
# alias:
# aliases:
# isl:
# - instance
# - server
# - list
{{- end }}
`
)

type Config struct {
Alias *alias.Config `json:"alias"`
Alias *alias.Config `json:"alias"`
Output string `json:"output"`

path string
}
Expand All @@ -32,15 +61,17 @@ func LoadConfig(configPath string) (*Config, error) {
if err != nil {
if os.IsNotExist(err) {
return &Config{
Alias: alias.EmptyConfig(),
path: configPath,
Alias: alias.EmptyConfig(),
Output: DefaultOutput,
path: configPath,
}, nil
}
return nil, fmt.Errorf("failed to read cli config file: %w", err)
}
config := &Config{
Alias: alias.EmptyConfig(),
path: configPath,
Alias: alias.EmptyConfig(),
Output: DefaultOutput,
path: configPath,
}
err = yaml.Unmarshal(file, &config)
if err != nil {
Expand All @@ -52,15 +83,32 @@ func LoadConfig(configPath string) (*Config, error) {

// Save marshal config to config file
func (c *Config) Save() error {
config, err := yaml.Marshal(c)
file, err := c.HumanConfig()
if err != nil {
return err
}

err = os.MkdirAll(filepath.Dir(c.path), 0700)
if err != nil {
return err
}
return os.WriteFile(c.path, config, defaultConfigPermission)
return os.WriteFile(c.path, []byte(file), defaultConfigPermission)
}

// HumanConfig will generate a config file with documented arguments
func (c *Config) HumanConfig() (string, error) {
tmpl, err := template.New("configuration").Parse(configFileTemplate)
if err != nil {
return "", err
}

var buf bytes.Buffer
err = tmpl.Execute(&buf, c)
if err != nil {
return "", err
}

return buf.String(), nil
}

func FilePath() (string, error) {
Expand Down
22 changes: 15 additions & 7 deletions internal/core/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,6 @@ type BootstrapConfig struct {
BetaMode bool
}

const (
defaultOutput = "human"
)

// Bootstrap is the main entry point. It is directly called from main.
// BootstrapConfig.Args is usually os.Args
// BootstrapConfig.Commands is a list of command available in CLI.
Expand All @@ -87,7 +83,7 @@ func Bootstrap(config *BootstrapConfig) (exitCode int, result interface{}, err e
flags := pflag.NewFlagSet(config.Args[0], pflag.ContinueOnError)
flags.StringVarP(&profileFlag, "profile", "p", "", "The config profile to use")
flags.StringVarP(&configPathFlag, "config", "c", "", "The path to the config file")
flags.StringVarP(&outputFlag, "output", "o", defaultOutput, "Output format: json or human")
flags.StringVarP(&outputFlag, "output", "o", cliConfig.DefaultOutput, "Output format: json or human")
flags.BoolVarP(&debug, "debug", "D", os.Getenv("SCW_DEBUG") == "true", "Enable debug mode")
// Ignore unknown flag
flags.ParseErrorsWhitelist.UnknownFlags = true
Expand All @@ -104,7 +100,7 @@ func Bootstrap(config *BootstrapConfig) (exitCode int, result interface{}, err e

// If debug flag is set enable debug mode in SDK logger
logLevel := logger.LogLevelWarning
if outputFlag != defaultOutput {
if outputFlag != cliConfig.DefaultOutput {
logLevel = logger.LogLevelError
}

Expand Down Expand Up @@ -207,6 +203,18 @@ func Bootstrap(config *BootstrapConfig) (exitCode int, result interface{}, err e
return 1, nil, err
}
meta.CliConfig = cliCfg
if cliCfg.Output != cliConfig.DefaultOutput {
outputFlag = cliCfg.Output
printer, err = NewPrinter(&PrinterConfig{
OutputFlag: outputFlag,
Stdout: config.Stdout,
Stderr: config.Stderr,
})
if err != nil {
_, _ = fmt.Fprintln(config.Stderr, err)
return 1, nil, err
}
}

// Check CLI new version when exiting the bootstrap
defer func() { // if we plan to remove defer, do not forget logger is not set until cobra pre init func
Expand Down Expand Up @@ -242,7 +250,7 @@ func Bootstrap(config *BootstrapConfig) (exitCode int, result interface{}, err e
// declaration in order for them to be shown in the cobra usage documentation.
rootCmd.PersistentFlags().StringVarP(&profileFlag, "profile", "p", "", "The config profile to use")
rootCmd.PersistentFlags().StringVarP(&configPathFlag, "config", "c", "", "The path to the config file")
rootCmd.PersistentFlags().StringVarP(&outputFlag, "output", "o", "human", "Output format: json or human, see 'scw help output' for more info")
rootCmd.PersistentFlags().StringVarP(&outputFlag, "output", "o", cliConfig.DefaultOutput, "Output format: json or human, see 'scw help output' for more info")
rootCmd.PersistentFlags().BoolVarP(&debug, "debug", "D", false, "Enable debug mode")
rootCmd.SetArgs(args)
rootCmd.SetHelpCommand(&cobra.Command{Hidden: true})
Expand Down