all repos

mugit @ a02c4e8b021b8808df85e7e1e2a09e9673ea2d17

🐮 git server that your cow will love

mugit/internal/config/config.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
git: set default branch; dont replay on list of default branches (#7)..., 2 months ago
1
package config
2
3
import (
4
	"errors"
5
	"fmt"
6
	"os"
7
	"path/filepath"
8
	"strings"
9
	"time"
10
11
	"gopkg.in/yaml.v2"
12
)
13
14
var (
15
	ErrConfigNotFound = errors.New("no config file found")
16
	ErrUnsetEnv       = errors.New("environment variable is not set")
17
	ErrFileNotFound   = errors.New("provided file path is invalid")
18
)
19
20
type ServerConfig struct {
21
	Host string `yaml:"host"`
22
	Port int    `yaml:"port"`
23
}
24
25
type MetaConfig struct {
26
	Title       string `yaml:"title"`
27
	Description string `yaml:"description"`
28
	Host        string `yaml:"host"`
29
}
30
31
type RepoConfig struct {
32
	Dir     string   `yaml:"dir"`
33
	Readmes []string `yaml:"readmes"`
34
}
35
36
type SSHConfig struct {
37
	Enable bool     `yaml:"enable"`
38
	User   string   `yaml:"user"`
39
	Keys   []string `yaml:"keys"`
40
}
41
42
type MirrorConfig struct {
43
	Enable      bool          `yaml:"enable"`
44
	Interval    time.Duration `yaml:"interval"`
45
	GithubToken string        `yaml:"github_token"`
46
}
47
48
type CacheConfig struct {
49
	HomePage time.Duration `yaml:"home_page"`
50
	Readme   time.Duration `yaml:"readme"`
51
	Diff     time.Duration `yaml:"diff"`
52
}
53
54
type Config struct {
55
	Server ServerConfig `yaml:"server"`
56
	Meta   MetaConfig   `yaml:"meta"`
57
	Repo   RepoConfig   `yaml:"repo"`
58
	SSH    SSHConfig    `yaml:"ssh"`
59
	Mirror MirrorConfig `yaml:"mirror"`
60
	Cache  CacheConfig  `yaml:"cache"`
61
}
62
63
func Load(fpath string) (*Config, error) {
64
	configBytes, err := os.ReadFile(fpath)
65
	if err != nil {
66
		return nil, err
67
	}
68
69
	var config Config
70
	if cerr := yaml.Unmarshal(configBytes, &config); cerr != nil {
71
		return nil, fmt.Errorf("parsing config: %w", cerr)
72
	}
73
74
	if config.Repo.Dir, err = filepath.Abs(config.Repo.Dir); err != nil {
75
		return nil, err
76
	}
77
78
	config.ensureDefaults()
79
80
	if perr := config.parseValues(); perr != nil {
81
		return nil, perr
82
	}
83
84
	if verr := config.validate(); verr != nil {
85
		return nil, verr
86
	}
87
88
	return &config, nil
89
}
90
91
// PathOrDefault uses userPath, if it's "", or invalid path, will default to one of those(in priority order)
92
// 1. ./config.yaml
93
// 2. /etc/mugit.yaml
94
// 3. /var/lib/mugit/config.yaml
95
func PathOrDefault(userPath string) string {
96
	return pathOrDefaultWithCandidates(userPath, []string{
97
		"./config.yaml",
98
		"/etc/mugit.yaml",
99
		"/var/lib/mugit/config.yaml",
100
	})
101
}
102
103
func pathOrDefaultWithCandidates(path string, candidates []string) string {
104
	if isFileExists(path) {
105
		return path
106
	}
107
108
	for _, fpath := range candidates {
109
		if isFileExists(fpath) {
110
			return fpath
111
		}
112
	}
113
114
	return ""
115
}
116
117
func (c *Config) ensureDefaults() {
118
	// http
119
	if c.Server.Port == 0 {
120
		c.Server.Port = 8080
121
	}
122
123
	// meta
124
	if c.Meta.Title == "" {
125
		c.Meta.Title = "my mugit"
126
	}
127
128
	// repos
129
	if len(c.Repo.Readmes) == 0 {
130
		c.Repo.Readmes = []string{
131
			"README.md", "readme.md",
132
			"README.html", "readme.html",
133
			"README.txt", "readme.txt",
134
			"readme",
135
		}
136
	}
137
138
	// ssh
139
	if c.SSH.User == "" {
140
		c.SSH.User = "git"
141
	}
142
143
	// mirroring
144
	if c.Mirror.Interval == 0 {
145
		c.Mirror.Interval = 8 * time.Hour
146
	}
147
148
	// cache
149
	if c.Cache.HomePage == 0 {
150
		c.Cache.HomePage = 5 * time.Minute
151
	}
152
153
	if c.Cache.Readme == 0 {
154
		c.Cache.Readme = 1 * time.Minute
155
	}
156
157
	if c.Cache.Diff == 0 {
158
		c.Cache.Diff = 15 * time.Minute
159
	}
160
}
161
162
func (c *Config) parseValues() error {
163
	if c.Mirror.Enable {
164
		ghToken, err := parseValue(c.Mirror.GithubToken)
165
		if err != nil {
166
			return err
167
		}
168
		c.Mirror.GithubToken = ghToken
169
	}
170
	return nil
171
}
172
173
func parseValue(value string) (string, error) {
174
	envPrefix := "$env:"
175
	filePrefix := "$file:"
176
177
	switch {
178
	case strings.HasPrefix(value, envPrefix):
179
		env := os.Getenv(os.ExpandEnv(value[len(envPrefix):]))
180
		if env == "" {
181
			return "", ErrUnsetEnv
182
		}
183
		return env, nil
184
185
	case strings.HasPrefix(value, filePrefix):
186
		// supports only absolute paths
187
188
		fpath := value[len(filePrefix):]
189
		if !isFileExists(fpath) {
190
			return "", ErrFileNotFound
191
		}
192
193
		data, err := os.ReadFile(fpath)
194
		if err != nil {
195
			return "", err
196
		}
197
198
		return strings.TrimSpace(string(data)), nil
199
200
	default:
201
		return value, nil
202
	}
203
}
204
205
func isFileExists(path string) bool {
206
	_, err := os.Stat(path)
207
	return err == nil
208
}
209
210
func isDirExists(path string) bool {
211
	i, err := os.Stat(path)
212
	if err != nil {
213
		return false
214
	}
215
	return i.IsDir()
216
}