github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/cmd/config/set/set.go (about)

     1  package set
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"github.com/MakeNowJust/heredoc"
     9  	"github.com/ungtb10d/cli/v2/internal/config"
    10  	"github.com/ungtb10d/cli/v2/pkg/cmdutil"
    11  	"github.com/ungtb10d/cli/v2/pkg/iostreams"
    12  	"github.com/spf13/cobra"
    13  )
    14  
    15  type SetOptions struct {
    16  	IO     *iostreams.IOStreams
    17  	Config config.Config
    18  
    19  	Key      string
    20  	Value    string
    21  	Hostname string
    22  }
    23  
    24  func NewCmdConfigSet(f *cmdutil.Factory, runF func(*SetOptions) error) *cobra.Command {
    25  	opts := &SetOptions{
    26  		IO: f.IOStreams,
    27  	}
    28  
    29  	cmd := &cobra.Command{
    30  		Use:   "set <key> <value>",
    31  		Short: "Update configuration with a value for the given key",
    32  		Example: heredoc.Doc(`
    33  			$ gh config set editor vim
    34  			$ gh config set editor "code --wait"
    35  			$ gh config set git_protocol ssh --host github.com
    36  			$ gh config set prompt disabled
    37  		`),
    38  		Args: cobra.ExactArgs(2),
    39  		RunE: func(cmd *cobra.Command, args []string) error {
    40  			config, err := f.Config()
    41  			if err != nil {
    42  				return err
    43  			}
    44  			opts.Config = config
    45  			opts.Key = args[0]
    46  			opts.Value = args[1]
    47  
    48  			if runF != nil {
    49  				return runF(opts)
    50  			}
    51  
    52  			return setRun(opts)
    53  		},
    54  	}
    55  
    56  	cmd.Flags().StringVarP(&opts.Hostname, "host", "h", "", "Set per-host setting")
    57  
    58  	return cmd
    59  }
    60  
    61  func setRun(opts *SetOptions) error {
    62  	err := ValidateKey(opts.Key)
    63  	if err != nil {
    64  		warningIcon := opts.IO.ColorScheme().WarningIcon()
    65  		fmt.Fprintf(opts.IO.ErrOut, "%s warning: '%s' is not a known configuration key\n", warningIcon, opts.Key)
    66  	}
    67  
    68  	err = ValidateValue(opts.Key, opts.Value)
    69  	if err != nil {
    70  		var invalidValue InvalidValueError
    71  		if errors.As(err, &invalidValue) {
    72  			var values []string
    73  			for _, v := range invalidValue.ValidValues {
    74  				values = append(values, fmt.Sprintf("'%s'", v))
    75  			}
    76  			return fmt.Errorf("failed to set %q to %q: valid values are %v", opts.Key, opts.Value, strings.Join(values, ", "))
    77  		}
    78  	}
    79  
    80  	opts.Config.Set(opts.Hostname, opts.Key, opts.Value)
    81  
    82  	err = opts.Config.Write()
    83  	if err != nil {
    84  		return fmt.Errorf("failed to write config to disk: %w", err)
    85  	}
    86  	return nil
    87  }
    88  
    89  func ValidateKey(key string) error {
    90  	for _, configKey := range config.ConfigOptions() {
    91  		if key == configKey.Key {
    92  			return nil
    93  		}
    94  	}
    95  
    96  	return fmt.Errorf("invalid key")
    97  }
    98  
    99  type InvalidValueError struct {
   100  	ValidValues []string
   101  }
   102  
   103  func (e InvalidValueError) Error() string {
   104  	return "invalid value"
   105  }
   106  
   107  func ValidateValue(key, value string) error {
   108  	var validValues []string
   109  
   110  	for _, v := range config.ConfigOptions() {
   111  		if v.Key == key {
   112  			validValues = v.AllowedValues
   113  			break
   114  		}
   115  	}
   116  
   117  	if validValues == nil {
   118  		return nil
   119  	}
   120  
   121  	for _, v := range validValues {
   122  		if v == value {
   123  			return nil
   124  		}
   125  	}
   126  
   127  	return InvalidValueError{ValidValues: validValues}
   128  }