shroomgit

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

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(repoIconArt)
49 fmt.Println("</a></div><div>")
50 fmt.Printf("<a href=\"%s/index.html\" style=\"line-height:1.5;\">", repoName)
51
52 fmt.Printf("<b>%s", repoName)
53 fmt.Printf("</b></a><br>%s", desc)
54 fmt.Printf("<br><br>%s", string(lastCommit))
55 if license != "0" {
56 fmt.Printf(" - %s", license)
57 }
58
59 fmt.Println("</div></div></div>")
60 }
61
62 fmt.Println("\n</div>")
63 fmt.Println("</body>")
64 fmt.Print("</html>")
65}
66
67func basicPart() {
68 config := shindex
69 fmt.Println("<!DOCTYPE html>")
70 fmt.Println("<html>")
71 fmt.Println("<head>")
72 fmt.Printf("<title>%s</title>\n", config.title)
73 fmt.Println("<link rel=\"stylesheet\" href=\"style.css\" \\>")
74 fmt.Println("<link rel=\"icon\" type=\"image/png\" href=\"favicon.png\" \\>")
75 fmt.Println("</head>")
76 fmt.Println("<body>")
77 fmt.Println("<table>")
78 fmt.Printf("<tr><td><a href=\"%s\">", config.logoHref)
79 fmt.Printf("<img src=\"%s\" ", config.logo)
80 fmt.Printf("alt=\"\" width=%d ", config.logoWidth)
81 fmt.Printf("height=%d ", config.logoHeight)
82 fmt.Print("/></a></td>\n")
83 fmt.Printf("<td>%s", config.title)
84 fmt.Println("</td></tr>")
85 fmt.Println("</table>")
86 fmt.Println(config.desc)
87 fmt.Println("<hr>")
88}
89
90// Not very reliable, will fix later
91func detectLicense(repo string) string {
92 files := []string{"LICENSE", "LICENSE.txt", "LICENSE.md", "license"}
93
94 for _, f := range files {
95 path := filepath.Join(repo, f)
96 data, _ := os.ReadFile(path)
97
98 text := string(data)
99
100 switch {
101 case strings.Contains(text, "MIT License"):
102 return "MIT"
103 case strings.Contains(text, "Apache"):
104 return "Apache"
105 case strings.Contains(text, "GNU General"):
106 return "GPL"
107 case strings.Contains(text, "unlicense"):
108 return "unlicense"
109 case strings.Contains(text, "Mozilla"):
110 return "MPL"
111 case strings.Contains(text, "Creative Commons"):
112 return "CC"
113 case strings.Contains(text, "0BSD"), strings.Contains(text, "BSD-"):
114 return "BSD"
115 case strings.Contains(text, "license"):
116 return "other"
117 }
118 }
119
120 return "0"
121}
122
123func getRepoNameAndDesc(repo string) (string, string) {
124 descFile := filepath.Join(repo, ".git", "description")
125 desc := ""
126 if data, err := os.ReadFile(descFile); err == nil {
127 desc = string(data)
128 }
129
130 gitConfig := filepath.Join(repo, ".git", "config")
131 data, err := os.ReadFile(gitConfig)
132 if err != nil {
133 return filepath.Base(repo), desc
134 }
135
136 content := string(data)
137
138 for line := range strings.SplitSeq(content, "\n") {
139 line = strings.TrimSpace(line)
140 if strings.HasPrefix(line, "url =") {
141 parts := strings.Fields(line)
142 url := parts[2]
143 url = strings.TrimSuffix(url, ".git")
144 segments := strings.Split(url, "/")
145 return segments[len(segments)-1], desc
146 }
147 }
148
149 return filepath.Base(repo), desc
150}
151
152// TODO add more errors messages
153func printHelp(errorNum int) {
154 switch errorNum {
155 case 1:
156 fmt.Println("ERROR not enough arguments")
157 fmt.Println()
158 }
159 fmt.Println("Usage: shindex [repo_path...]")
160 fmt.Println("creates a static html page to display git repos")
161}