new config infrastructure
- adds config get and config set commands - supports arbitrary k/v strings set at top and host level - supports writing an updated config, preserving comments - supports mostly lazy evaluation of yaml
This commit is contained in:
parent
82bd7b97cf
commit
a325db3051
15 changed files with 858 additions and 150 deletions
|
|
@ -10,57 +10,159 @@ import (
|
|||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
const defaultHostname = "github.com"
|
||||
|
||||
type configEntry struct {
|
||||
User string
|
||||
Token string `yaml:"oauth_token"`
|
||||
}
|
||||
|
||||
func parseOrSetupConfigFile(fn string) (*configEntry, error) {
|
||||
entry, err := parseConfigFile(fn)
|
||||
func parseOrSetupConfigFile(fn string) (Config, error) {
|
||||
config, err := ParseConfig(fn)
|
||||
if err != nil && errors.Is(err, os.ErrNotExist) {
|
||||
return setupConfigFile(fn)
|
||||
}
|
||||
return entry, err
|
||||
return config, err
|
||||
}
|
||||
|
||||
func parseConfigFile(fn string) (*configEntry, error) {
|
||||
func ParseDefaultConfig() (Config, error) {
|
||||
return ParseConfig(configFile())
|
||||
}
|
||||
|
||||
var ReadConfigFile = func(fn string) ([]byte, error) {
|
||||
f, err := os.Open(fn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
return parseConfig(f)
|
||||
}
|
||||
|
||||
// ParseDefaultConfig reads the configuration file
|
||||
func ParseDefaultConfig() (*configEntry, error) {
|
||||
return parseConfigFile(configFile())
|
||||
}
|
||||
|
||||
func parseConfig(r io.Reader) (*configEntry, error) {
|
||||
data, err := ioutil.ReadAll(r)
|
||||
data, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var config yaml.Node
|
||||
err = yaml.Unmarshal(data, &config)
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
var WriteConfigFile = func(fn string, data []byte) error {
|
||||
cfgFile, err := os.OpenFile(configFile(), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) // cargo coded from setup
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
if len(config.Content) < 1 {
|
||||
return nil, fmt.Errorf("malformed config")
|
||||
defer cfgFile.Close()
|
||||
|
||||
n, err := cfgFile.Write(data)
|
||||
if err == nil && n < len(data) {
|
||||
err = io.ErrShortWrite
|
||||
}
|
||||
for i := 0; i < len(config.Content[0].Content)-1; i = i + 2 {
|
||||
if config.Content[0].Content[i].Value == defaultHostname {
|
||||
var entries []configEntry
|
||||
err = config.Content[0].Content[i+1].Decode(&entries)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &entries[0], nil
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
var BackupConfigFile = func(fn string) error {
|
||||
return os.Rename(fn, fn+".bak")
|
||||
}
|
||||
|
||||
func parseConfigFile(fn string) ([]byte, *yaml.Node, error) {
|
||||
data, err := ReadConfigFile(fn)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var root yaml.Node
|
||||
err = yaml.Unmarshal(data, &root)
|
||||
if err != nil {
|
||||
return data, nil, err
|
||||
}
|
||||
if len(root.Content) < 1 {
|
||||
return data, &root, fmt.Errorf("malformed config")
|
||||
}
|
||||
if root.Content[0].Kind != yaml.MappingNode {
|
||||
return data, &root, fmt.Errorf("expected a top level map")
|
||||
}
|
||||
|
||||
return data, &root, nil
|
||||
}
|
||||
|
||||
func isLegacy(root *yaml.Node) bool {
|
||||
for _, v := range root.Content[0].Content {
|
||||
if v.Value == "hosts" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("could not find config entry for %q", defaultHostname)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func migrateConfig(fn string, root *yaml.Node) error {
|
||||
// TODO i'm sorry
|
||||
newConfigData := map[string]map[string]map[string]string{}
|
||||
newConfigData["hosts"] = map[string]map[string]string{}
|
||||
|
||||
topLevelKeys := root.Content[0].Content
|
||||
|
||||
for i, x := range topLevelKeys {
|
||||
if x.Value == "" {
|
||||
continue
|
||||
}
|
||||
if i+1 == len(topLevelKeys) {
|
||||
break
|
||||
}
|
||||
hostname := x.Value
|
||||
newConfigData["hosts"][hostname] = map[string]string{}
|
||||
|
||||
authKeys := topLevelKeys[i+1].Content[0].Content
|
||||
|
||||
for j, y := range authKeys {
|
||||
if j+1 == len(authKeys) {
|
||||
break
|
||||
}
|
||||
switch y.Value {
|
||||
case "user":
|
||||
newConfigData["hosts"][hostname]["user"] = authKeys[j+1].Value
|
||||
case "oauth_token":
|
||||
newConfigData["hosts"][hostname]["oauth_token"] = authKeys[j+1].Value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := newConfigData["hosts"][defaultHostname]; !ok {
|
||||
return errors.New("could not find default host configuration")
|
||||
}
|
||||
|
||||
defaultHostConfig := newConfigData["hosts"][defaultHostname]
|
||||
|
||||
if _, ok := defaultHostConfig["user"]; !ok {
|
||||
return errors.New("default host configuration missing user")
|
||||
}
|
||||
|
||||
if _, ok := defaultHostConfig["oauth_token"]; !ok {
|
||||
return errors.New("default host configuration missing oauth_token")
|
||||
}
|
||||
|
||||
newConfig, err := yaml.Marshal(newConfigData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = BackupConfigFile(fn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to back up existing config: %s", err)
|
||||
}
|
||||
|
||||
return WriteConfigFile(fn, newConfig)
|
||||
}
|
||||
|
||||
func ParseConfig(fn string) (Config, error) {
|
||||
_, root, err := parseConfigFile(fn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if isLegacy(root) {
|
||||
err = migrateConfig(fn, root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, root, err = parseConfigFile(fn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to reparse migrated config: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
return NewConfig(root), nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue