master
1package main
2
3import (
4 "fmt"
5 "os"
6 "os/exec"
7 "path/filepath"
8 "strings"
9
10 "github.com/BurntSushi/toml"
11)
12
13type SingleRepoConfig struct {
14 Name string `toml:"name"`
15 Path string `toml:"path"`
16 Description string `toml:"description"`
17 DefaultBranch string `toml:"default_branch"`
18}
19
20type RepoEntry struct {
21 Name string `toml:"name"`
22 Slug string `toml:"slug"`
23 Path string `toml:"path"`
24 Description string `toml:"description"`
25 DefaultBranch string `toml:"default_branch"`
26}
27
28type Config struct {
29 SiteName string `toml:"site_name"`
30 Repo *SingleRepoConfig `toml:"repo"`
31 Repos []RepoEntry `toml:"repos"`
32 Sort string `toml:"sort"`
33 Theme string `toml:"theme"`
34 ThemeLight string `toml:"theme_light"`
35 ThemeDark string `toml:"theme_dark"`
36 Branches string `toml:"branches"`
37 Output string `toml:"output"`
38 Minify bool `toml:"minify"`
39 Gzip bool `toml:"gzip"`
40 Git bool `toml:"git"`
41 InlineStyles bool `toml:"inline_styles"`
42}
43
44func parseConfig(path string) (*Config, error) {
45 absPath, err := filepath.Abs(path)
46 if err != nil {
47 return nil, fmt.Errorf("resolve config path: %w", err)
48 }
49
50 data, err := os.ReadFile(absPath)
51 if err != nil {
52 return nil, fmt.Errorf("read config file: %w", err)
53 }
54
55 var cfg Config
56 if err := toml.Unmarshal(data, &cfg); err != nil {
57 return nil, fmt.Errorf("parse config: %w", err)
58 }
59
60 if cfg.SiteName == "" {
61 cfg.SiteName = "Git repositories"
62 }
63
64 if hasRepo := cfg.Repo != nil; hasRepo {
65 if len(cfg.Repos) > 0 {
66 return nil, fmt.Errorf("config: cannot use both [repo] and [[repos]]")
67 }
68 if cfg.Repo.Name == "" {
69 return nil, fmt.Errorf("config: [repo].name is required")
70 }
71 if cfg.Repo.Path == "" {
72 return nil, fmt.Errorf("config: [repo].path is required")
73 }
74 if err := normalizeRepoPath(&cfg.Repo.Path); err != nil {
75 return nil, fmt.Errorf("config: [repo].path: %w", err)
76 }
77 if cfg.Repo.DefaultBranch == "" {
78 cfg.Repo.DefaultBranch = autoDefaultBranchName(cfg.Repo.Path)
79 }
80 }
81
82 if len(cfg.Repos) > 0 {
83 for i := range cfg.Repos {
84 repo := &cfg.Repos[i]
85 if repo.Name == "" {
86 return nil, fmt.Errorf("config: repos[%d].name is required", i)
87 }
88 if repo.Slug == "" {
89 return nil, fmt.Errorf("config: repos[%d].slug is required", i)
90 }
91 if repo.Path == "" {
92 return nil, fmt.Errorf("config: repos[%d].path is required", i)
93 }
94 if err := normalizeRepoPath(&repo.Path); err != nil {
95 return nil, fmt.Errorf("config: repos[%d].path: %w", i, err)
96 }
97 if repo.DefaultBranch == "" {
98 repo.DefaultBranch = autoDefaultBranchName(repo.Path)
99 }
100 }
101 }
102
103 return &cfg, nil
104}
105
106func normalizeRepoPath(path *string) error {
107 abs, err := filepath.Abs(*path)
108 if err != nil {
109 return err
110 }
111 *path = abs
112 return nil
113}
114
115func autoDefaultBranchName(repoPath string) string {
116 cmd := exec.Command("git", "symbolic-ref", "--short", "HEAD")
117 cmd.Dir = repoPath
118 out, err := cmd.Output()
119 if err != nil {
120 return ""
121 }
122 return strings.TrimSpace(string(out))
123}