shroomgit

generate static pages of git repos
git clone https://git.davidvoz.net/shroomgit.git
index
logs
tree

shared.go
1package main
2
3import (
4 "bufio"
5 "html"
6 "os"
7 "path/filepath"
8 "strconv"
9 "strings"
10)
11
12// prints an html section for each file inputted
13// TODO maybe see if you can add pandoc program coloring theme to it
14func writePerFile(file *os.File, path string) {
15 notFile, _ := os.Open(path)
16 defer notFile.Close()
17
18 scanner := bufio.NewScanner(notFile)
19 numLine := 1
20 var strNumLine string
21
22 fileName := filepath.Base(path)
23 file.WriteString("<b>" + fileName + "</b>")
24
25 file.WriteString("\n<table style=\"white-space:pre\">")
26 for scanner.Scan() {
27 line := scanner.Text()
28 escaped := html.EscapeString(line)
29 strNumLine = strconv.Itoa(numLine)
30
31 file.WriteString("<tr>")
32 file.WriteString("<td class=\"codeline\">")
33 file.WriteString("<a href=\"#" + strNumLine + "\" id=\"" + strNumLine + "\">")
34 file.WriteString(strNumLine)
35 file.WriteString("</a>")
36 file.WriteString("</td>")
37 file.WriteString("<td>")
38 file.WriteString(escaped + "\n")
39 file.WriteString("</td>")
40
41 file.WriteString("</tr>")
42
43 numLine++
44 }
45 file.WriteString("</table>")
46 file.WriteString("<br>")
47}
48
49// the common HTML lines that all files will share
50var genTopPart = func(repo string, depth int) string {
51 var b strings.Builder
52 config := shgit
53 repoName, desc := getRepoNameAndDesc(repo)
54
55 b.WriteString("<!DOCTYPE html>\n")
56 b.WriteString("<html>\n")
57 b.WriteString("<head>\n")
58 b.WriteString("<link rel=\"stylesheet\" href=\"")
59 for range depth {
60 b.WriteString("../")
61 }
62 b.WriteString("style.css\" \\>\n")
63 b.WriteString("<link rel=\"icon\" type=\"image/png\" href=\"")
64 for range depth {
65 b.WriteString("../")
66 }
67 b.WriteString(config.favicon)
68 b.WriteString("\" \\>\n")
69
70 b.WriteString("<style>")
71 b.WriteString(styles["codeline"])
72 b.WriteString("</style>\n")
73
74 b.WriteString("</head>\n")
75
76 b.WriteString("<body>\n")
77
78 b.WriteString("<table>")
79 b.WriteString("<tr>\n")
80
81 if config.logo != "" {
82 b.WriteString("<td>")
83 b.WriteString("<a href=\"")
84 for range depth {
85 b.WriteString("../")
86 }
87
88 b.WriteString(config.logoHref)
89
90 b.WriteString("\">")
91
92 b.WriteString("<img src=\"")
93 for range depth {
94 b.WriteString("../")
95 }
96
97 b.WriteString(config.logo)
98 b.WriteString("\" width=")
99 b.WriteString(strconv.Itoa(config.logoWidth))
100 b.WriteString(" height=")
101 b.WriteString(strconv.Itoa(config.logoHeight))
102 b.WriteString("/>")
103 b.WriteString("</a>")
104 b.WriteString("</td>")
105 }
106
107 b.WriteString("\n<td><h2 style=\"margin-left:10px\">")
108 b.WriteString(repoName)
109 b.WriteString("</h2>")
110
111 b.WriteString("</td>")
112
113 b.WriteString("</tr>")
114 b.WriteString("</table>")
115 b.WriteString(desc)
116
117 b.WriteString("<hr>")
118 b.WriteString("<table>\n")
119
120 b.WriteString("<tr>\n<td>\n<code style=\"background-color: #222; color: white; padding: 4px; user-select: all; border: none;\">")
121 b.WriteString("git clone " + config.url + repoName + ".git")
122 b.WriteString("\n</code></td>")
123
124 b.WriteString("</tr>\n")
125 b.WriteString("</table>")
126
127 b.WriteString("\n<div style=\"display:flex; flex-wrap:wrap; gap:30px;margin-top:3px;\">\n")
128
129 b.WriteString("\n<div style=\"display:flex;padding-left:20px;\">\n")
130 b.WriteString("<a href=\"")
131 for range depth {
132 b.WriteString("../")
133 }
134 b.WriteString("index.html\">")
135 b.WriteString("index\n")
136 b.WriteString("</a>")
137 b.WriteString("</div>\n")
138
139 b.WriteString("\n<div style=\"display:flex;\">\n")
140 b.WriteString("<a href=\"")
141 for range depth {
142 b.WriteString("../")
143 }
144 b.WriteString("log.html\">")
145 b.WriteString("logs\n")
146 b.WriteString("</a>")
147 b.WriteString("</div>\n")
148
149 b.WriteString("\n<div style=\"display:flex;\">\n")
150 b.WriteString("<a href=\"")
151 for range depth {
152 b.WriteString("../")
153 }
154 b.WriteString("tree.html\">")
155 b.WriteString("tree\n")
156 b.WriteString("</a>")
157 b.WriteString("</div>\n")
158
159 files := []string{"LICENSE", "LICENSE.txt", "LICENSE.md", "license", "COPYING"}
160
161 for _, f := range files {
162 possibleL := filepath.Join(repo, f)
163 if _, err := os.ReadFile(possibleL); err == nil {
164 b.WriteString("\n<div style=\"display:flex;\">\n")
165 b.WriteString("<a href=\"")
166 for range depth {
167 b.WriteString("../")
168 }
169 b.WriteString("files/")
170 b.WriteString(filepath.Base(possibleL))
171 b.WriteString(".html\">")
172 b.WriteString("license\n")
173 b.WriteString("</a>")
174 b.WriteString("</div>\n")
175 break
176 }
177 }
178
179 b.WriteString("\n</div>\n")
180
181 b.WriteString("<hr>")
182
183 return b.String()
184}
185
186func getRepoNameAndDesc(repo string) (string, string) {
187 descFile := filepath.Join(repo, ".git", "description")
188 desc := ""
189 if data, err := os.ReadFile(descFile); err == nil {
190 desc = string(data)
191 }
192
193 gitConfig := filepath.Join(repo, ".git", "config")
194 data, err := os.ReadFile(gitConfig)
195 if err != nil {
196 return filepath.Base(repo), desc
197 }
198
199 content := string(data)
200
201 for line := range strings.SplitSeq(content, "\n") {
202 line = strings.TrimSpace(line)
203 if strings.HasPrefix(line, "url =") {
204 parts := strings.Fields(line)
205 if len(parts) < 3 {
206 continue
207 }
208 url := parts[2]
209 url = strings.TrimSuffix(url, ".git")
210 segments := strings.Split(url, "/")
211 return segments[len(segments)-1], desc
212 }
213 }
214
215 return filepath.Base(repo), desc
216}
217