shroomgit

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

shindex.go
1package main
2
3import (
4 "fmt"
5 "os"
6 "os/exec"
7 "path/filepath"
8 "strings"
9)
10
11func main() {
12 args := os.Args[1:]
13
14 if len(args) == 0 {
15 printHelp(1)
16 os.Exit(1)
17 }
18
19 basicPart()
20 gitTable(args)
21}
22
23func gitTable(args []string) {
24 fmt.Println("<div style=\"display:flex; flex-wrap:wrap; gap;9px;\">")
25
26 for _, repo := range args {
27 repoName, desc := getRepoNameAndDesc(repo)
28 cmd := exec.Command(
29 "git",
30 "-C", repo,
31 "log", "-1",
32 "--pretty=format:%cd",
33 "--date=format:%Y-%m-%d",
34 )
35
36 lastCommit, _ := cmd.Output()
37 license := detectLicense(repo)
38
39 fmt.Println("\n<div style=\"width:350px;\">")
40 fmt.Println("<div style=\"display:flex;padding:5px;\">")
41 fmt.Println("<div style=\"width:32px;height:32px;margin-right:8px;\">")
42
43 // the line below assumes the directory path is the same as the repo's name, not arguments
44 // change the 'repoName' to 'repo' if you want the latter option for the line below
45 // other instances of such a case would have to be changed as well
46 fmt.Printf("<a href=\"%s/index.html\" style=\"color:inherit\">", repoName)
47
48 fmt.Println("<svg width=\"32\" height=\"32\">")
49 fmt.Println("<rect x=\"6\" y=\"10\" width=\"20\" height=\"20\"")
50 fmt.Println("fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" transform=\"rotate(45 20 20)\"/></svg></a></div><div>")
51 fmt.Printf("<a href=\"%s/index.html\" style=\"line-height:1.5;\">", repoName)
52
53 fmt.Printf("<b>%s", repoName)
54 fmt.Printf("</b></a><br>%s", desc)
55 fmt.Printf("<br><br>%s", string(lastCommit))
56 if license != "0" {
57 fmt.Printf(" - %s", license)
58 }
59
60 fmt.Println("</div></div></div>")
61 }
62
63 fmt.Println("\n</div>")
64 fmt.Println("</body>")
65 fmt.Print("</html>")
66}
67
68func basicPart() {
69 config := shindex
70 fmt.Println("<!DOCTYPE html>")
71 fmt.Println("<html>")
72 fmt.Println("<head>")
73 fmt.Printf("<title>%s</title>\n", config.title)
74 fmt.Println("<link rel=\"stylesheet\" href=\"style.css\" \\>")
75 fmt.Println("<link rel=\"icon\" type=\"image/png\" href=\"favicon.png\" \\>")
76 fmt.Println("</head>")
77 fmt.Println("<body>")
78 fmt.Println("<table>")
79 fmt.Printf("<tr><td><a href=\"%s\">", config.logoHref)
80 fmt.Printf("<img src=\"%s\" ", config.logo)
81 fmt.Printf("alt=\"\" width=%d ", config.logoWidth)
82 fmt.Printf("height=%d ", config.logoHeight)
83 fmt.Print("/></a></td>\n")
84 fmt.Printf("<td>%s", config.title)
85 fmt.Println("</td></tr>")
86 fmt.Println("</table>")
87 fmt.Println(config.desc)
88 fmt.Println("<hr>")
89}
90
91// Not very reliable, will fix later
92func detectLicense(repo string) string {
93 files := []string{"LICENSE", "LICENSE.txt", "LICENSE.md", "license"}
94
95 for _, f := range files {
96 path := filepath.Join(repo, f)
97 data, _ := os.ReadFile(path)
98
99 text := string(data)
100
101 switch {
102 case strings.Contains(text, "MIT License"):
103 return "MIT"
104 case strings.Contains(text, "Apache"):
105 return "Apache"
106 case strings.Contains(text, "GNU General"):
107 return "GPL"
108 case strings.Contains(text, "unlicense"):
109 return "unlicense"
110 case strings.Contains(text, "Mozilla"):
111 return "MPL"
112 case strings.Contains(text, "Creative Commons"):
113 return "CC"
114 case strings.Contains(text, "0BSD"), strings.Contains(text, "BSD-"):
115 return "BSD"
116 case strings.Contains(text, "license"):
117 return "other"
118 }
119 }
120
121 return "0"
122}
123
124func getRepoNameAndDesc(repo string) (string, string) {
125 descFile := filepath.Join(repo, ".git", "description")
126 desc := ""
127 if data, err := os.ReadFile(descFile); err == nil {
128 desc = string(data)
129 }
130
131 gitConfig := filepath.Join(repo, ".git", "config")
132 data, err := os.ReadFile(gitConfig)
133 if err != nil {
134 return filepath.Base(repo), desc
135 }
136
137 content := string(data)
138
139 for line := range strings.SplitSeq(content, "\n") {
140 line = strings.TrimSpace(line)
141 if strings.HasPrefix(line, "url =") {
142 parts := strings.Fields(line)
143 url := parts[2]
144 url = strings.TrimSuffix(url, ".git")
145 segments := strings.Split(url, "/")
146 return segments[len(segments)-1], desc
147 }
148 }
149
150 return filepath.Base(repo), desc
151}
152
153// TODO add more errors messages
154func printHelp(errorNum int) {
155 switch errorNum {
156 case 1:
157 fmt.Println("ERROR not enough arguments")
158 fmt.Println()
159 }
160 fmt.Println("Usage: shindex [repo_path...]")
161 fmt.Println("creates a static html page to display git repos")
162}
163