181 lines
3.6 KiB
Go
181 lines
3.6 KiB
Go
package config
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"path"
|
|
|
|
"github.com/mitchellh/go-homedir"
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
func ConfigDir() string {
|
|
dir, _ := homedir.Expand("~/.config/gh")
|
|
return dir
|
|
}
|
|
|
|
func ConfigFile() string {
|
|
return path.Join(ConfigDir(), "config.yml")
|
|
}
|
|
|
|
func ParseOrSetupConfigFile(fn string) (Config, error) {
|
|
config, err := ParseConfig(fn)
|
|
if err != nil && errors.Is(err, os.ErrNotExist) {
|
|
return setupConfigFile(fn)
|
|
}
|
|
return config, err
|
|
}
|
|
|
|
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()
|
|
|
|
data, err := ioutil.ReadAll(f)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
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 err
|
|
}
|
|
defer cfgFile.Close()
|
|
|
|
n, err := cfgFile.Write(data)
|
|
if err == nil && n < len(data) {
|
|
err = io.ErrShortWrite
|
|
}
|
|
|
|
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 true
|
|
}
|
|
|
|
func migrateConfig(fn string, root *yaml.Node) error {
|
|
type ConfigEntry map[string]string
|
|
type ConfigHash map[string]ConfigEntry
|
|
|
|
newConfigData := map[string]ConfigHash{}
|
|
newConfigData["hosts"] = ConfigHash{}
|
|
|
|
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] = ConfigEntry{}
|
|
|
|
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: %w", 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: %w", err)
|
|
}
|
|
}
|
|
|
|
return NewConfig(root), nil
|
|
}
|