2020-06-26 20:25:17 +03:00
|
|
|
package repository
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
gogit "github.com/go-git/go-git/v5"
|
2020-09-27 21:31:09 +03:00
|
|
|
"github.com/go-git/go-git/v5/config"
|
2020-06-26 20:25:17 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
var _ Config = &goGitConfig{}
|
|
|
|
|
|
|
|
type goGitConfig struct {
|
2020-09-27 21:31:09 +03:00
|
|
|
ConfigRead
|
|
|
|
ConfigWrite
|
2020-06-26 20:25:17 +03:00
|
|
|
}
|
|
|
|
|
2020-09-27 21:31:09 +03:00
|
|
|
func newGoGitLocalConfig(repo *gogit.Repository) *goGitConfig {
|
|
|
|
return &goGitConfig{
|
|
|
|
ConfigRead: &goGitConfigReader{getConfig: repo.Config},
|
|
|
|
ConfigWrite: &goGitConfigWriter{repo: repo},
|
|
|
|
}
|
2020-06-26 20:25:17 +03:00
|
|
|
}
|
|
|
|
|
2020-12-05 05:08:54 +03:00
|
|
|
func newGoGitGlobalConfig() *goGitConfig {
|
|
|
|
// TODO: replace that with go-git native implementation once it's supported
|
|
|
|
// see: https://github.com/go-git/go-git
|
|
|
|
// see: https://github.com/src-d/go-git/issues/760
|
|
|
|
|
2020-09-27 21:31:09 +03:00
|
|
|
return &goGitConfig{
|
|
|
|
ConfigRead: &goGitConfigReader{getConfig: func() (*config.Config, error) {
|
|
|
|
return config.LoadConfig(config.GlobalScope)
|
|
|
|
}},
|
|
|
|
ConfigWrite: &configPanicWriter{},
|
2020-06-26 20:25:17 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-27 21:31:09 +03:00
|
|
|
var _ ConfigRead = &goGitConfigReader{}
|
2020-06-26 20:25:17 +03:00
|
|
|
|
2020-09-27 21:31:09 +03:00
|
|
|
type goGitConfigReader struct {
|
|
|
|
getConfig func() (*config.Config, error)
|
2020-06-26 20:25:17 +03:00
|
|
|
}
|
|
|
|
|
2020-09-27 21:31:09 +03:00
|
|
|
func (cr *goGitConfigReader) ReadAll(keyPrefix string) (map[string]string, error) {
|
|
|
|
cfg, err := cr.getConfig()
|
2020-06-26 20:25:17 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
split := strings.Split(keyPrefix, ".")
|
2020-09-27 20:25:37 +03:00
|
|
|
result := make(map[string]string)
|
2020-06-26 20:25:17 +03:00
|
|
|
|
|
|
|
switch {
|
2020-09-27 20:25:37 +03:00
|
|
|
case keyPrefix == "":
|
|
|
|
for _, section := range cfg.Raw.Sections {
|
|
|
|
for _, option := range section.Options {
|
|
|
|
result[fmt.Sprintf("%s.%s", section.Name, option.Key)] = option.Value
|
|
|
|
}
|
|
|
|
for _, subsection := range section.Subsections {
|
|
|
|
for _, option := range subsection.Options {
|
|
|
|
result[fmt.Sprintf("%s.%s.%s", section.Name, subsection.Name, option.Key)] = option.Value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-06-26 20:25:17 +03:00
|
|
|
case len(split) == 1:
|
2020-09-27 20:25:37 +03:00
|
|
|
if !cfg.Raw.HasSection(split[0]) {
|
2020-09-27 21:31:09 +03:00
|
|
|
return nil, nil
|
2020-09-27 20:25:37 +03:00
|
|
|
}
|
|
|
|
section := cfg.Raw.Section(split[0])
|
|
|
|
for _, option := range section.Options {
|
|
|
|
result[fmt.Sprintf("%s.%s", section.Name, option.Key)] = option.Value
|
|
|
|
}
|
|
|
|
for _, subsection := range section.Subsections {
|
|
|
|
for _, option := range subsection.Options {
|
|
|
|
result[fmt.Sprintf("%s.%s.%s", section.Name, subsection.Name, option.Key)] = option.Value
|
|
|
|
}
|
|
|
|
}
|
2020-06-26 20:25:17 +03:00
|
|
|
default:
|
2020-09-27 20:25:37 +03:00
|
|
|
if !cfg.Raw.HasSection(split[0]) {
|
2020-09-27 21:31:09 +03:00
|
|
|
return nil, nil
|
2020-09-27 20:25:37 +03:00
|
|
|
}
|
|
|
|
section := cfg.Raw.Section(split[0])
|
|
|
|
rest := strings.Join(split[1:], ".")
|
2020-10-26 01:35:36 +03:00
|
|
|
rest = strings.TrimSuffix(rest, ".")
|
2020-09-27 20:25:37 +03:00
|
|
|
for _, subsection := range section.Subsections {
|
|
|
|
if strings.HasPrefix(subsection.Name, rest) {
|
|
|
|
for _, option := range subsection.Options {
|
|
|
|
result[fmt.Sprintf("%s.%s.%s", section.Name, subsection.Name, option.Key)] = option.Value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-06-26 20:25:17 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
2020-09-27 21:31:09 +03:00
|
|
|
func (cr *goGitConfigReader) ReadBool(key string) (bool, error) {
|
|
|
|
val, err := cr.ReadString(key)
|
2020-06-26 20:25:17 +03:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return strconv.ParseBool(val)
|
|
|
|
}
|
|
|
|
|
2020-09-27 21:31:09 +03:00
|
|
|
func (cr *goGitConfigReader) ReadString(key string) (string, error) {
|
|
|
|
cfg, err := cr.getConfig()
|
2020-06-26 20:25:17 +03:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
split := strings.Split(key, ".")
|
|
|
|
|
2020-08-30 12:56:34 +03:00
|
|
|
if len(split) <= 1 {
|
|
|
|
return "", fmt.Errorf("invalid key")
|
|
|
|
}
|
|
|
|
|
|
|
|
sectionName := split[0]
|
|
|
|
if !cfg.Raw.HasSection(sectionName) {
|
|
|
|
return "", ErrNoConfigEntry
|
|
|
|
}
|
|
|
|
section := cfg.Raw.Section(sectionName)
|
2020-06-26 20:25:17 +03:00
|
|
|
|
|
|
|
switch {
|
|
|
|
case len(split) == 2:
|
2020-08-30 12:56:34 +03:00
|
|
|
optionName := split[1]
|
|
|
|
if !section.HasOption(optionName) {
|
|
|
|
return "", ErrNoConfigEntry
|
|
|
|
}
|
|
|
|
if len(section.OptionAll(optionName)) > 1 {
|
|
|
|
return "", ErrMultipleConfigEntry
|
|
|
|
}
|
|
|
|
return section.Option(optionName), nil
|
2020-06-26 20:25:17 +03:00
|
|
|
default:
|
2021-03-28 23:18:01 +03:00
|
|
|
subsectionName := strings.Join(split[1:len(split)-1], ".")
|
2020-08-30 12:56:34 +03:00
|
|
|
optionName := split[len(split)-1]
|
|
|
|
if !section.HasSubsection(subsectionName) {
|
|
|
|
return "", ErrNoConfigEntry
|
|
|
|
}
|
|
|
|
subsection := section.Subsection(subsectionName)
|
|
|
|
if !subsection.HasOption(optionName) {
|
|
|
|
return "", ErrNoConfigEntry
|
|
|
|
}
|
|
|
|
if len(subsection.OptionAll(optionName)) > 1 {
|
|
|
|
return "", ErrMultipleConfigEntry
|
|
|
|
}
|
|
|
|
return subsection.Option(optionName), nil
|
2020-06-26 20:25:17 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-27 21:31:09 +03:00
|
|
|
func (cr *goGitConfigReader) ReadTimestamp(key string) (time.Time, error) {
|
|
|
|
value, err := cr.ReadString(key)
|
2020-06-26 20:25:17 +03:00
|
|
|
if err != nil {
|
|
|
|
return time.Time{}, err
|
|
|
|
}
|
|
|
|
return ParseTimestamp(value)
|
|
|
|
}
|
|
|
|
|
2020-09-27 21:31:09 +03:00
|
|
|
var _ ConfigWrite = &goGitConfigWriter{}
|
|
|
|
|
|
|
|
// Only works for the local config as go-git only support that
|
|
|
|
type goGitConfigWriter struct {
|
|
|
|
repo *gogit.Repository
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cw *goGitConfigWriter) StoreString(key, value string) error {
|
|
|
|
cfg, err := cw.repo.Config()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
split := strings.Split(key, ".")
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case len(split) <= 1:
|
|
|
|
return fmt.Errorf("invalid key")
|
|
|
|
case len(split) == 2:
|
|
|
|
cfg.Raw.Section(split[0]).SetOption(split[1], value)
|
|
|
|
default:
|
|
|
|
section := split[0]
|
|
|
|
subsection := strings.Join(split[1:len(split)-1], ".")
|
|
|
|
option := split[len(split)-1]
|
|
|
|
cfg.Raw.Section(section).Subsection(subsection).SetOption(option, value)
|
|
|
|
}
|
|
|
|
|
|
|
|
return cw.repo.SetConfig(cfg)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cw *goGitConfigWriter) StoreTimestamp(key string, value time.Time) error {
|
|
|
|
return cw.StoreString(key, strconv.Itoa(int(value.Unix())))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cw *goGitConfigWriter) StoreBool(key string, value bool) error {
|
|
|
|
return cw.StoreString(key, strconv.FormatBool(value))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cw *goGitConfigWriter) RemoveAll(keyPrefix string) error {
|
|
|
|
cfg, err := cw.repo.Config()
|
2020-06-26 20:25:17 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
split := strings.Split(keyPrefix, ".")
|
|
|
|
|
|
|
|
switch {
|
2020-09-27 20:25:37 +03:00
|
|
|
case keyPrefix == "":
|
|
|
|
cfg.Raw.Sections = nil
|
|
|
|
// warning: this does not actually remove everything as go-git config hold
|
|
|
|
// some entries in multiple places (cfg.User ...)
|
2020-06-26 20:25:17 +03:00
|
|
|
case len(split) == 1:
|
2020-09-27 20:25:37 +03:00
|
|
|
if cfg.Raw.HasSection(split[0]) {
|
2020-06-26 20:25:17 +03:00
|
|
|
cfg.Raw.RemoveSection(split[0])
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("invalid key prefix")
|
|
|
|
}
|
|
|
|
default:
|
2020-09-27 20:25:37 +03:00
|
|
|
if !cfg.Raw.HasSection(split[0]) {
|
|
|
|
return fmt.Errorf("invalid key prefix")
|
|
|
|
}
|
|
|
|
section := cfg.Raw.Section(split[0])
|
2020-06-26 20:25:17 +03:00
|
|
|
rest := strings.Join(split[1:], ".")
|
|
|
|
|
2020-09-27 20:25:37 +03:00
|
|
|
ok := false
|
|
|
|
if section.HasSubsection(rest) {
|
|
|
|
section.RemoveSubsection(rest)
|
|
|
|
ok = true
|
|
|
|
}
|
|
|
|
if section.HasOption(rest) {
|
|
|
|
section.RemoveOption(rest)
|
|
|
|
ok = true
|
|
|
|
}
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("invalid key prefix")
|
2020-06-26 20:25:17 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-27 21:31:09 +03:00
|
|
|
return cw.repo.SetConfig(cfg)
|
2020-06-26 20:25:17 +03:00
|
|
|
}
|