| 1 | package main
|
| 2 |
|
| 3 | import (
|
| 4 | "fmt"
|
| 5 | "os"
|
| 6 | "os/exec"
|
| 7 | "path/filepath"
|
| 8 | "strings"
|
| 9 | )
|
| 10 |
|
| 11 | func 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 |
|
| 23 | func 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 |
|
| 68 | func 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
|
| 92 | func 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 |
|
| 124 | func 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
|
| 154 | func 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 |
|