~emersion/hut

7406ef3f833d150fa57171c4e13d213faa69c5f0 — Simon Ser 4 months ago 499ef88
config: use scfg decoder to read config file
4 files changed, 58 insertions(+), 55 deletions(-)

M client.go
M config.go
M go.mod
M go.sum
M client.go => client.go +4 -1
@@ 73,7 73,10 @@ func createClientWithInstance(service string, cmd *cobra.Command, instanceName s
		token = inst.AccessToken
	}

	baseURL := inst.Origins[service]
	var baseURL string
	if serviceCfg := inst.Services()[service]; serviceCfg != nil {
		baseURL = serviceCfg.Origin
	}
	if baseURL == "" && strings.Contains(inst.Name, ".") && net.ParseIP(inst.Name) == nil {
		baseURL = fmt.Sprintf("https://%s.%s", service, inst.Name)
	}

M config.go => config.go +51 -51
@@ 18,67 18,88 @@ import (
)

type Config struct {
	Instances []*InstanceConfig
	Instances []*InstanceConfig `scfg:"instance"`
}

type InstanceConfig struct {
	Name string

	AccessToken    string
	AccessTokenCmd []string

	Origins map[string]string
	Name string `scfg:",param"`

	AccessToken    string   `scfg:"access-token"`
	AccessTokenCmd []string `scfg:"access-token-cmd"`

	Builds *ServiceConfig `scfg:"builds"`
	Git    *ServiceConfig `scfg:"git"`
	Hg     *ServiceConfig `scfg:"hg"`
	Lists  *ServiceConfig `scfg:"lists"`
	Meta   *ServiceConfig `scfg:"meta"`
	Pages  *ServiceConfig `scfg:"pages"`
	Paste  *ServiceConfig `scfg:"paste"`
	Todo   *ServiceConfig `scfg:"todo"`
}

func (instance InstanceConfig) match(name string) bool {
func (instance *InstanceConfig) match(name string) bool {
	if instancesEqual(name, instance.Name) {
		return true
	}

	for _, origin := range instance.Origins {
		if stripProtocol(origin) == name {
	for _, service := range instance.Services() {
		if service.Origin != "" && stripProtocol(service.Origin) == name {
			return true
		}
	}
	return false
}

func (instance *InstanceConfig) Services() map[string]*ServiceConfig {
	all := map[string]*ServiceConfig{
		"builds": instance.Builds,
		"git":    instance.Git,
		"hg":     instance.Hg,
		"lists":  instance.Lists,
		"meta":   instance.Meta,
		"pages":  instance.Pages,
		"paste":  instance.Paste,
		"todo":   instance.Todo,
	}

	m := make(map[string]*ServiceConfig)
	for name, service := range all {
		if service != nil {
			m[name] = service
		}
	}
	return m
}

type ServiceConfig struct {
	Origin string `scfg:"origin"`
}

func instancesEqual(a, b string) bool {
	return a == b || strings.HasSuffix(a, "."+b) || strings.HasSuffix(b, "."+a)
}

func loadConfigFile(filename string) (*Config, error) {
	rootBlock, err := scfg.Load(filename)
	f, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	cfg := new(Config)
	instanceNames := make(map[string]struct{})
	for _, instanceDir := range rootBlock.GetAll("instance") {
		instance := &InstanceConfig{
			Origins: make(map[string]string),
		}

		if err := instanceDir.ParseParams(&instance.Name); err != nil {
			return nil, err
		}
	if err := scfg.NewDecoder(f).Decode(cfg); err != nil {
		return nil, err
	}

	instanceNames := make(map[string]struct{})
	for _, instance := range cfg.Instances {
		if _, ok := instanceNames[instance.Name]; ok {
			return nil, fmt.Errorf("duplicate instance name %q", instance.Name)
		}
		instanceNames[instance.Name] = struct{}{}

		if dir := instanceDir.Children.Get("access-token"); dir != nil {
			if err := dir.ParseParams(&instance.AccessToken); err != nil {
				return nil, err
			}
		}
		if dir := instanceDir.Children.Get("access-token-cmd"); dir != nil {
			if len(dir.Params) == 0 {
				return nil, fmt.Errorf("instance %q: missing command name in access-token-cmd directive", instance.Name)
			}
			instance.AccessTokenCmd = dir.Params
		if instance.AccessTokenCmd != nil && len(instance.AccessTokenCmd) == 0 {
			return nil, fmt.Errorf("instance %q: missing command name in access-token-cmd directive", instance.Name)
		}
		if instance.AccessToken == "" && len(instance.AccessTokenCmd) == 0 {
			return nil, fmt.Errorf("instance %q: missing access-token or access-token-cmd", instance.Name)


@@ 86,27 107,6 @@ func loadConfigFile(filename string) (*Config, error) {
		if instance.AccessToken != "" && len(instance.AccessTokenCmd) > 0 {
			return nil, fmt.Errorf("instance %q: access-token and access-token-cmd can't be both specified", instance.Name)
		}

		for _, service := range []string{"builds", "git", "hg", "lists", "meta", "pages", "paste", "todo"} {
			serviceDir := instanceDir.Children.Get(service)
			if serviceDir == nil {
				continue
			}

			originDir := serviceDir.Children.Get("origin")
			if originDir == nil {
				continue
			}

			var origin string
			if err := originDir.ParseParams(&origin); err != nil {
				return nil, err
			}

			instance.Origins[service] = origin
		}

		cfg.Instances = append(cfg.Instances, instance)
	}

	return cfg, nil

M go.mod => go.mod +1 -1
@@ 3,7 3,7 @@ module git.sr.ht/~emersion/hut
go 1.17

require (
	git.sr.ht/~emersion/go-scfg v0.0.0-20231004133111-9dce55c8d63b
	git.sr.ht/~emersion/go-scfg v0.0.0-20231211181832-0b4e72d8ec3c
	git.sr.ht/~emersion/gqlclient v0.0.0-20230820050442-8873fe0204b9
	github.com/dustin/go-humanize v1.0.1
	github.com/juju/ansiterm v1.0.0

M go.sum => go.sum +2 -2
@@ 1,5 1,5 @@
git.sr.ht/~emersion/go-scfg v0.0.0-20231004133111-9dce55c8d63b h1:Lf4oYBOJVmbYzrfqWfXUvSpXQPNMgnbN0efn5A7bH3M=
git.sr.ht/~emersion/go-scfg v0.0.0-20231004133111-9dce55c8d63b/go.mod h1:ybgvEJTIx5XbaspSviB3KNa6OdPmAZqDoSud7z8fFlw=
git.sr.ht/~emersion/go-scfg v0.0.0-20231211181832-0b4e72d8ec3c h1:Cjy9/qASF8hogbKbWXgEQZxbYHrM9ksl76sGzsP8Zqo=
git.sr.ht/~emersion/go-scfg v0.0.0-20231211181832-0b4e72d8ec3c/go.mod h1:ybgvEJTIx5XbaspSviB3KNa6OdPmAZqDoSud7z8fFlw=
git.sr.ht/~emersion/gqlclient v0.0.0-20230820050442-8873fe0204b9 h1:QNwHP6WknvS7X6MEFxCpefQb1QJMqgIIt+vn/PVoMMg=
git.sr.ht/~emersion/gqlclient v0.0.0-20230820050442-8873fe0204b9/go.mod h1:kvl/JK0Z3VRmtbBxdOJR4ydyXVouUIcFIXgv4H6rVAY=
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=