commit 39ab9877330c2743e9fe8f6d206836a2143bcb35
Author: David Voznyarskiy <davidv@no-reply@disroot.org>
Date: Fri Apr 3 22:00:48 2026 -0700
lot of refactoring due to a strange conflict I made
Signed-off-by: David Voznyarskiy <davidv@no-reply@disroot.org>
diff --git a/Makefile b/Makefile
index f81cb3c..923eae4 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@ all: shindex shgit
shindex:
@mkdir -p bin
- go build -o bin/shindex shindex/shindex.go
+ go build -o bin/shindex ./shindex
shgit:
@mkdir -p bin
diff --git a/config/config.go b/config/config.go
deleted file mode 100644
index e054c37..0000000
--- a/config/config.go
+++ /dev/null
@@ -1,41 +0,0 @@
-// Package config
-package config
-
-type Config struct {
- Title string
- Favicon string
- Logo string
- LogoHref string
- LogoWidth int
- LogoHeight int
- Desc string
- URL string
-}
-
-// Shindex config
-var Shindex = Config{
- Title: "Repositories",
- Logo: "logo.png",
- LogoHref: "https://davidvoz.net",
- LogoWidth: 88,
- LogoHeight: 36,
- Desc: "this is a static index of my repos, for more visit <a href='https://git.disroot.org/davidv'>here</a>",
-}
-
-// Shgit config
-var Shgit = Config{
- Favicon: "logo.png",
- Logo: "logo.png",
- LogoHref: "..",
- LogoWidth: 40,
- LogoHeight: 40,
- URL: "https://git.davidvoz.net/",
-}
-
-var Style string = `
- .codeline {
- text-align: right;
- vertical-align: top;
- user-select: none;
- }
-`
diff --git a/shgit/config.go b/shgit/config.go
new file mode 100644
index 0000000..15d10f5
--- /dev/null
+++ b/shgit/config.go
@@ -0,0 +1,29 @@
+package main
+
+type config struct {
+ favicon string
+ logo string
+ logoHref string
+ logoWidth int
+ logoHeight int
+ url string
+}
+
+var shgit = config{
+ favicon: "logo.png",
+ logo: "logo.png",
+ logoHref: "..",
+ logoWidth: 40,
+ logoHeight: 40,
+ url: "https://git.davidvoz.net/",
+}
+
+var styles = map[string]string{
+ "codeline": `
+ .codeline {
+ text-align: right;
+ vertical-align: top;
+ user-select: none;
+ }
+ `,
+}
diff --git a/shgit/logs.go b/shgit/logs.go
new file mode 100644
index 0000000..53607d0
--- /dev/null
+++ b/shgit/logs.go
@@ -0,0 +1,209 @@
+package main
+
+import (
+ "fmt"
+ "html"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strconv"
+ "strings"
+)
+
+// overall awful
+func logTest(repo string, path string) {
+ topPart := genTopPart(repo, 1)
+ logsDir := filepath.Join(path, "logs")
+
+ err := os.MkdirAll(logsDir, 0o755)
+ if err != nil {
+ panic(err)
+ }
+
+ cmd, err := exec.Command("git", "-C", repo, "rev-list", "--reverse", "--date-order", "HEAD").Output()
+ if err != nil {
+ panic(err)
+ }
+
+ hashes := strings.Split(strings.TrimSpace(string(cmd)), "\n")
+
+ // TODO fix formatting
+ // TODO add coloring and links
+ for _, hash := range hashes {
+ cmd, err := exec.Command(
+ "git",
+ "-C",
+ repo,
+ "show",
+ hash,
+ ).Output()
+ if err != nil {
+ panic(err)
+ }
+
+ lines := strings.Split(string(cmd), "\n")
+ var firstLine string
+ var hash string
+ if lines[0] != "" {
+ firstLine = lines[0]
+ } else {
+ continue
+ }
+
+ fields := strings.Fields(firstLine)
+ hash = fields[1]
+
+ outputFile := filepath.Join(logsDir, fmt.Sprintf("%s.html", hash))
+ file, _ := os.Create(outputFile)
+ defer file.Close()
+
+ file.WriteString(topPart)
+
+ commitPageWriter(file, lines)
+ }
+}
+
+func commitPageWriter(file *os.File, lines []string) {
+ file.WriteString("<pre style=\"padding:6px;border-radius:4px;font-family:monospace;\">\n")
+
+ for _, line := range lines {
+ escaped := html.EscapeString(line)
+ if strings.HasPrefix(line, "+") {
+ file.WriteString("<div style=\"color:ForestGreen\">")
+ file.WriteString(escaped)
+ file.WriteString("</div>")
+ } else if strings.HasPrefix(line, "-") {
+ file.WriteString("<div style=\"color:#e54e50\">")
+ file.WriteString(escaped)
+ file.WriteString("</div>")
+
+ } else {
+ file.WriteString(escaped)
+ file.WriteString("\n")
+ }
+ }
+
+ file.WriteString("</pre>\n")
+}
+
+func logPage(topPart string, repo string, path string) {
+ outputFile := filepath.Join(path, "log.html")
+ file, err := os.Create(outputFile)
+ if err != nil {
+ panic(err)
+ }
+ defer file.Close()
+
+ cmd, err := exec.Command(
+ "git",
+ "-C",
+ repo,
+ "rev-list",
+ "--count",
+ "HEAD",
+ ).Output()
+ if err != nil {
+ return
+ }
+
+ numofCommitsStr := strings.TrimSpace(string(cmd))
+ numofCommits, _ := strconv.Atoi(numofCommitsStr)
+
+ file.WriteString(topPart)
+ file.WriteString("<table>")
+
+ for i := range numofCommits {
+ file.WriteString("<tr>")
+
+ cmd, _ := exec.Command(
+ "git",
+ "-C", repo,
+ "log", "-1",
+ fmt.Sprintf("--skip=%d", i),
+ "--pretty=format:%cd",
+ "--date=format:%Y-%m-%d",
+ ).Output()
+ date := strings.TrimSpace(string(cmd))
+ file.WriteString("<td valign=\"top\">")
+ file.WriteString(date)
+
+ file.WriteString("</td>\n")
+
+ cmd, _ = exec.Command(
+ "git",
+ "-C", repo,
+ "show",
+ fmt.Sprintf("HEAD~%d", i),
+ ).Output()
+
+ // commit := strings.TrimSpace(string(cmd))
+
+ lines := strings.Split(string(cmd), "\n")
+ var firstLine string
+ var hash string
+ if lines[0] != "" {
+ firstLine = lines[0]
+ } else {
+ return
+ }
+
+ fields := strings.Fields(firstLine)
+ hash = fields[1]
+
+ cmd, _ = exec.Command(
+ "git",
+ "-C", repo,
+ "log", "-1",
+ fmt.Sprintf("--skip=%d", i),
+ "--pretty=format:%s",
+ ).Output()
+ commit := strings.TrimSpace(string(cmd))
+ file.WriteString("<td>")
+ file.WriteString("\n<a href=\"logs/")
+ file.WriteString(hash)
+ file.WriteString(".html\">" + commit + "</a>\n")
+ file.WriteString("</td>\n")
+
+ cmd, _ = exec.Command(
+ "git",
+ "-C", repo,
+ "log", "-1",
+ fmt.Sprintf("--skip=%d", i),
+ "--pretty=format:%an",
+ ).Output()
+ author := strings.TrimSpace(string(cmd))
+ file.WriteString("<td style=\"white-space:nowrap\" valign=\"top\">")
+ file.WriteString(author)
+ file.WriteString("</td>\n")
+
+ cmd, _ = exec.Command(
+ "git",
+ "-C", repo,
+ "log", "-1",
+ fmt.Sprintf("--skip=%d", i),
+ "--shortstat",
+ "--pretty=format:",
+ ).Output()
+ cmdOutput := strings.TrimSpace(string(cmd))
+ arr := strings.Fields(cmdOutput)
+ if len(arr) == 0 {
+ continue
+ }
+ file.WriteString("<td style=\"padding-left: 1em\" valign=\"top\" align=\"right\">")
+ file.WriteString(arr[0])
+ file.WriteString("</td>\n")
+ file.WriteString("<td style=\"color:ForestGreen\" valign=\"top\" align=\"right\">+")
+ file.WriteString(arr[3])
+ file.WriteString("</td>\n")
+ if len(arr) > 5 {
+ file.WriteString("<td style=\"color:Crimson\" valign=\"top\" align=\"right\">-")
+ file.WriteString(arr[5])
+ file.WriteString("</td>\n")
+ }
+
+ file.WriteString("\n</tr>")
+ }
+
+ file.WriteString("\n</table>")
+ file.WriteString("\n</body>")
+}
diff --git a/shgit/shared.go b/shgit/shared.go
index 0f112c6..00f78fd 100644
--- a/shgit/shared.go
+++ b/shgit/shared.go
@@ -7,12 +7,10 @@ import (
"path/filepath"
"strconv"
"strings"
-
- "shroomgit/config"
)
// prints an html section for each file inputted
-// TODO class option for lines instead of the clunky style elements
+// TODO maybe see if you can add pandoc program coloring theme to it
func writePerFile(file *os.File, path string) {
notFile, _ := os.Open(path)
defer notFile.Close()
@@ -21,6 +19,9 @@ func writePerFile(file *os.File, path string) {
numLine := 1
var strNumLine string
+ fileName := filepath.Base(path)
+ file.WriteString("<b>" + fileName + "</b>")
+
file.WriteString("\n<table style=\"white-space:pre\">")
for scanner.Scan() {
line := scanner.Text()
@@ -46,22 +47,28 @@ func writePerFile(file *os.File, path string) {
}
// the common HTML lines that all files will share
-var genTopPart = func(repo string) string {
+var genTopPart = func(repo string, depth int) string {
var b strings.Builder
- shgitStyle := config.Style
- config := config.Shgit
+ config := shgit
repoName, desc := getRepoNameAndDesc(repo)
b.WriteString("<!DOCTYPE html>\n")
b.WriteString("<html>\n")
b.WriteString("<head>\n")
- b.WriteString("<link rel=\"stylesheet\" href=\"style.css\" \\>\n")
+ b.WriteString("<link rel=\"stylesheet\" href=\"")
+ for range depth {
+ b.WriteString("../")
+ }
+ b.WriteString("style.css\" \\>\n")
b.WriteString("<link rel=\"icon\" type=\"image/png\" href=\"")
- b.WriteString(config.Favicon)
+ for range depth {
+ b.WriteString("../")
+ }
+ b.WriteString(config.favicon)
b.WriteString("\" \\>\n")
b.WriteString("<style>")
- b.WriteString(shgitStyle)
+ b.WriteString(styles["codeline"])
b.WriteString("</style>\n")
b.WriteString("</head>\n")
@@ -71,19 +78,27 @@ var genTopPart = func(repo string) string {
b.WriteString("<table>")
b.WriteString("<tr>\n")
- if config.Logo != "" {
+ if config.logo != "" {
b.WriteString("<td>")
b.WriteString("<a href=\"")
- b.WriteString(config.LogoHref)
+ for range depth {
+ b.WriteString("../")
+ }
+
+ b.WriteString(config.logoHref)
b.WriteString("\">")
b.WriteString("<img src=\"")
- b.WriteString(config.Logo)
+ for range depth {
+ b.WriteString("../")
+ }
+
+ b.WriteString(config.logo)
b.WriteString("\" width=")
- b.WriteString(strconv.Itoa(config.LogoWidth))
+ b.WriteString(strconv.Itoa(config.logoWidth))
b.WriteString(" height=")
- b.WriteString(strconv.Itoa(config.LogoHeight))
+ b.WriteString(strconv.Itoa(config.logoHeight))
b.WriteString("/>")
b.WriteString("</a>")
b.WriteString("</td>")
@@ -103,7 +118,7 @@ var genTopPart = func(repo string) string {
b.WriteString("<table>\n")
b.WriteString("<tr>\n<td>\n<code style=\"background-color: #222; color: white; padding: 4px; user-select: all; border: none;\">")
- b.WriteString("git clone " + config.URL + repoName + ".git")
+ b.WriteString("git clone " + config.url + repoName + ".git")
b.WriteString("\n</code></td>")
b.WriteString("</tr>\n")
@@ -112,31 +127,53 @@ var genTopPart = func(repo string) string {
b.WriteString("\n<div style=\"display:flex; flex-wrap:wrap; gap:30px;margin-top:3px;\">\n")
b.WriteString("\n<div style=\"display:flex;padding-left:20px;\">\n")
- b.WriteString("<a href=\"index.html\">")
+ b.WriteString("<a href=\"")
+ for range depth {
+ b.WriteString("../")
+ }
+ b.WriteString("index.html\">")
b.WriteString("index\n")
b.WriteString("</a>")
b.WriteString("</div>\n")
b.WriteString("\n<div style=\"display:flex;\">\n")
- b.WriteString("<a href=\"log.html\">")
+ b.WriteString("<a href=\"")
+ for range depth {
+ b.WriteString("../")
+ }
+ b.WriteString("log.html\">")
b.WriteString("logs\n")
b.WriteString("</a>")
b.WriteString("</div>\n")
b.WriteString("\n<div style=\"display:flex;\">\n")
- b.WriteString("<a>")
+ b.WriteString("<a href=\"")
+ for range depth {
+ b.WriteString("../")
+ }
+ b.WriteString("tree.html\">")
b.WriteString("tree\n")
b.WriteString("</a>")
b.WriteString("</div>\n")
- // TODO if license exists {
- if 1+1 == 2 {
- b.WriteString("\n<div style=\"display:flex;\">\n")
- b.WriteString("<a href=\"license.html\">")
- b.WriteString("license\n")
- b.WriteString("</a>")
- b.WriteString("</div>\n")
-
+ files := []string{"LICENSE", "LICENSE.txt", "LICENSE.md", "license", "COPYING"}
+
+ for _, f := range files {
+ possibleL := filepath.Join(repo, f)
+ if _, err := os.ReadFile(possibleL); err == nil {
+ b.WriteString("\n<div style=\"display:flex;\">\n")
+ b.WriteString("<a href=\"")
+ for range depth {
+ b.WriteString("../")
+ }
+ b.WriteString("files/")
+ b.WriteString(filepath.Base(possibleL))
+ b.WriteString(".html\">")
+ b.WriteString("license\n")
+ b.WriteString("</a>")
+ b.WriteString("</div>\n")
+ break
+ }
}
b.WriteString("\n</div>\n")
@@ -165,6 +202,9 @@ func getRepoNameAndDesc(repo string) (string, string) {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "url =") {
parts := strings.Fields(line)
+ if len(parts) < 3 {
+ continue
+ }
url := parts[2]
url = strings.TrimSuffix(url, ".git")
segments := strings.Split(url, "/")
@@ -174,3 +214,4 @@ func getRepoNameAndDesc(repo string) (string, string) {
return filepath.Base(repo), desc
}
+
diff --git a/shgit/shgit.go b/shgit/shgit.go
index 881c0b3..e2bd1aa 100644
--- a/shgit/shgit.go
+++ b/shgit/shgit.go
@@ -193,3 +193,4 @@ func indexPageCommitTable(numofCommits int, file *os.File, repo string, path str
file.WriteString("</table>")
file.WriteString("</body>")
}
+
diff --git a/shgit/tree.go b/shgit/tree.go
new file mode 100644
index 0000000..8eace16
--- /dev/null
+++ b/shgit/tree.go
@@ -0,0 +1,90 @@
+package main
+
+import (
+ "fmt"
+ "io/fs"
+ "log"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+// TODO make it more tree like
+func treeRepo(topPart string, repo string, output string) {
+ logsDir := filepath.Join(output, "files")
+
+ err := os.MkdirAll(logsDir, 0o755)
+ if err != nil {
+ panic(err)
+ }
+
+ outputFile := filepath.Join(output, "tree.html")
+ file, err := os.Create(outputFile)
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer file.Close()
+
+ file.WriteString(topPart)
+
+ file.WriteString("<table>\n")
+
+ file.WriteString("\n<tr>")
+ file.WriteString("<td>Mode</td>")
+ file.WriteString("<td>Bytes</td>")
+ file.WriteString("</tr>")
+
+ err = filepath.WalkDir(repo, func(path string, d fs.DirEntry, err error) error {
+ relPath, _ := filepath.Rel(repo, path)
+
+ if d.Name() == ".git" {
+ return filepath.SkipDir
+ } else if relPath == "." {
+ return nil
+ } else if d.IsDir() {
+ return nil
+ }
+ info, _ := d.Info()
+
+ file.WriteString("<tr>")
+ file.WriteString("<td>")
+ file.WriteString(info.Mode().String())
+ file.WriteString(" </td>")
+ file.WriteString("<td valign=\"top\" align=\"right\">")
+ fmt.Fprintf(file, " %d", info.Size())
+ file.WriteString("</td>\n")
+ file.WriteString("<td>")
+ file.WriteString("<a href=\"files/")
+ file.WriteString(relPath)
+ file.WriteString(".html\">")
+ file.WriteString(relPath)
+ treeFiles(path, output, relPath, repo)
+ file.WriteString("</a>")
+ file.WriteString("</td>")
+
+ file.WriteString("</tr>")
+
+ return nil
+ })
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ file.WriteString("</table>\n")
+
+ file.WriteString("</body>\n")
+ file.WriteString("</html>")
+}
+
+func treeFiles(path string, output string, relPath string, repo string) {
+ outputFile := filepath.Join(output, "files", fmt.Sprintf("%s.html", relPath))
+ os.MkdirAll(filepath.Dir(outputFile), 0o755)
+ file, _ := os.Create(outputFile)
+ defer file.Close()
+
+ depth := strings.Count(relPath, "/")
+ topPart := genTopPart(repo, depth+1)
+
+ file.WriteString(topPart)
+ writePerFile(file, path)
+}
diff --git a/shindex/config.go b/shindex/config.go
new file mode 100644
index 0000000..676cf3a
--- /dev/null
+++ b/shindex/config.go
@@ -0,0 +1,20 @@
+package main
+
+type config struct {
+ title string
+ logo string
+ logoHref string
+ logoWidth int
+ logoHeight int
+ desc string
+}
+
+// Shindex config
+var shindex = config{
+ title: "Repositories",
+ logo: "logo.png",
+ logoHref: "https://davidvoz.net",
+ logoWidth: 88,
+ logoHeight: 36,
+ desc: "this is a static index of my repos, for more visit <a href='https://git.disroot.org/davidv'>here</a>",
+}
diff --git a/shindex/shindex.go b/shindex/shindex.go
index 9640a8d..5c20994 100644
--- a/shindex/shindex.go
+++ b/shindex/shindex.go
@@ -6,8 +6,6 @@ import (
"os/exec"
"path/filepath"
"strings"
-
- "shroomgit/config"
)
func main() {
@@ -43,15 +41,14 @@ func gitTable(args []string) {
fmt.Println("<div style=\"width:32px;height:32px;margin-right:8px;\">")
// the line below assumes the directory path is the same as the repo's name, not arguments
- // change the 'repoName' to 'repo' if you want the latter option
- fmt.Printf("<a href=\"%s/log.html\" style=\"color:inherit\">", repoName)
+ // change the 'repoName' to 'repo' if you want the latter option for the line below
+ // other instances of such a case would have to be changed as well
+ fmt.Printf("<a href=\"%s/index.html\" style=\"color:inherit\">", repoName)
fmt.Println("<svg width=\"32\" height=\"32\">")
fmt.Println("<rect x=\"6\" y=\"10\" width=\"20\" height=\"20\"")
fmt.Println("fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" transform=\"rotate(45 20 20)\"/></svg></a></div><div>")
-
- // other instance of possible path argument issue
- fmt.Printf("<a href=\"%s/log.html\" style=\"line-height:1.5;\">", repoName)
+ fmt.Printf("<a href=\"%s/index.html\" style=\"line-height:1.5;\">", repoName)
fmt.Printf("<b>%s", repoName)
fmt.Printf("</b></a><br>%s", desc)
@@ -69,25 +66,25 @@ func gitTable(args []string) {
}
func basicPart() {
- config := config.Shindex
+ config := shindex
fmt.Println("<!DOCTYPE html>")
fmt.Println("<html>")
fmt.Println("<head>")
- fmt.Printf("<title>%s</title>\n", config.Title)
+ fmt.Printf("<title>%s</title>\n", config.title)
fmt.Println("<link rel=\"stylesheet\" href=\"style.css\" \\>")
fmt.Println("<link rel=\"icon\" type=\"image/png\" href=\"favicon.png\" \\>")
fmt.Println("</head>")
fmt.Println("<body>")
fmt.Println("<table>")
- fmt.Printf("<tr><td><a href=\"%s\">", config.LogoHref)
- fmt.Printf("<img src=\"%s\" ", config.Logo)
- fmt.Printf("alt=\"\" width=%d ", config.LogoWidth)
- fmt.Printf("height=%d ", config.LogoHeight)
+ fmt.Printf("<tr><td><a href=\"%s\">", config.logoHref)
+ fmt.Printf("<img src=\"%s\" ", config.logo)
+ fmt.Printf("alt=\"\" width=%d ", config.logoWidth)
+ fmt.Printf("height=%d ", config.logoHeight)
fmt.Print("/></a></td>\n")
- fmt.Printf("<td>%s", config.Title)
+ fmt.Printf("<td>%s", config.title)
fmt.Println("</td></tr>")
fmt.Println("</table>")
- fmt.Println(config.Desc)
+ fmt.Println(config.desc)
fmt.Println("<hr>")
}
@@ -153,6 +150,7 @@ func getRepoNameAndDesc(repo string) (string, string) {
return filepath.Base(repo), desc
}
+// TODO add more errors messages
func printHelp(errorNum int) {
switch errorNum {
case 1:
@@ -162,3 +160,4 @@ func printHelp(errorNum int) {
fmt.Println("Usage: shindex [repo_path...]")
fmt.Println("creates a static html page to display git repos")
}
+