repos / pgit

static site generator for git
git clone https://github.com/picosh/pgit.git

commit
134e2ae
parent
fe38cd8
author
Eric Bower
date
2023-08-07 13:47:05 +0000 UTC
copy changes
6 files changed,  +148, -141
M config.yaml
+4, -0
1@@ -9,3 +9,7 @@ repos:
2     refs:
3       - main
4     desc: infra for pico services
5+  - path: /home/erock/dev/app-ui
6+    refs:
7+      - main
8+    desc: aptible's paas web application
M html/header.partial.tmpl
+1, -1
1@@ -12,7 +12,7 @@
2   <a href="{{.Repo.LogURL}}">log</a>
3 </div>
4 
5-<div class="my">
6+<div class="mt-lg">
7   <div class="text-lg">{{.Repo.Desc}}</div>
8   <pre style="margin: 0;">git clone {{.Repo.CloneURL}}</pre>
9 </div>
M html/log.page.tmpl
+9, -3
 1@@ -8,10 +8,16 @@
 2   <div>
 3   {{range .Data.Log}}
 4     <div class="box">
 5-      <div>
 6-        <a href="{{.URL}}">{{.SummaryStr}}</a>
 7+      <div class="flex justify-between items-center">
 8+        <div>
 9+          <a href="{{.URL}}">{{.SummaryStr}}</a>
10+        </div>
11+        <div>
12+          {{.ShortID}}
13+        </div>
14       </div>
15-      <div>
16+
17+      <div class="flex items-center">
18         <span>{{.AuthorStr}}</span>
19         <span>&nbsp;committed&nbsp;</span>
20         <span>{{.WhenStr}}</span>
M html/tree.page.tmpl
+6, -6
 1@@ -7,17 +7,17 @@
 2 
 3   <div>
 4   {{range .Data.Tree}}
 5-    <div class="flex justify-between">
 6+    <div class="flex justify-between items-center gap my-sm border-b">
 7       <div class="flex-1">
 8         <a href="{{.URL}}">{{.Path}}</a>
 9       </div>
10 
11-      <div class="flex">
12-        <div>
13-          <a href="{{.CommitURL}}">{{.When}}</a>
14+      <div class="flex items-center gap">
15+        <div class="flex-1">
16+          <a href="{{.CommitURL}}" title="{{.Summary}}">{{.When}}</a>
17         </div>
18-        <div class="mono font-bold">
19-          L{{.NumLines}}
20+        <div class="tree-size">
21+          {{if .IsTextFile}}{{.NumLines}} L{{else}}{{.Size}}{{end}}
22         </div>
23       </div>
24     </div>
M main.go
+101, -64
  1@@ -9,9 +9,11 @@ import (
  2 	"sort"
  3 	"strings"
  4 
  5+	"github.com/dustin/go-humanize"
  6 	git "github.com/gogs/git-module"
  7 	"github.com/mergestat/timediff"
  8 	"github.com/picosh/pico/pastes"
  9+	"github.com/picosh/pico/shared"
 10 	"github.com/spf13/viper"
 11 )
 12 
 13@@ -38,35 +40,39 @@ type RepoData struct {
 14 	RefsURL    string
 15 	CloneURL   string
 16 	MaxCommits int
 17-	RevName string
 18+	RevName    string
 19+	Readme     string
 20 }
 21 
 22 type CommitData struct {
 23 	SummaryStr string
 24-	URL string
 25-	WhenStr string
 26-	AuthorStr string
 27+	URL        string
 28+	WhenStr    string
 29+	AuthorStr  string
 30+	ShortID    string
 31 	*git.Commit
 32 }
 33 
 34 type TreeItem struct {
 35-	NumLines  int
 36-	URL       string
 37-	Path      string
 38-	Entry     *git.TreeEntry
 39-	CommitURL string
 40-	Desc      string
 41-	When      string
 42+	IsTextFile bool
 43+	Size       string
 44+	NumLines   int
 45+	URL        string
 46+	Path       string
 47+	Entry      *git.TreeEntry
 48+	CommitURL  string
 49+	Summary    string
 50+	When       string
 51 }
 52 
 53 type PageData struct {
 54-	Repo     *RepoData
 55-	Log      []*CommitData
 56-	Tree     []*TreeItem
 57-	Readme   template.HTML
 58-	Rev      *git.Reference
 59-	RevName  string
 60-	Refs     []*RefInfo
 61+	Repo    *RepoData
 62+	Log     []*CommitData
 63+	Tree    []*TreeItem
 64+	Readme  template.HTML
 65+	Rev     *git.Reference
 66+	RevName string
 67+	Refs    []*RefInfo
 68 }
 69 
 70 type CommitPageData struct {
 71@@ -107,6 +113,35 @@ type DiffRenderFile struct {
 72 	NumDeletions int
 73 }
 74 
 75+type FileData struct {
 76+	Contents template.HTML
 77+	Name     string
 78+}
 79+
 80+type RefInfo struct {
 81+	Refspec string
 82+	URL     template.URL
 83+}
 84+
 85+type BranchOutput struct {
 86+	Readme     string
 87+	LastCommit *git.Commit
 88+}
 89+
 90+type RepoConfig struct {
 91+	Path       string   `mapstructure:"path"`
 92+	Refs       []string `mapstructure:"refs"`
 93+	Desc       string   `mapstructure:"desc"`
 94+	MaxCommits int      `mapstructure:"max_commits"`
 95+	Readme     string   `mapstructure:"readme"`
 96+}
 97+
 98+type Config struct {
 99+	Repos []*RepoConfig `mapstructure:"repos"`
100+	URL   string        `mapstructure:"url"`
101+	Cache map[string]bool
102+}
103+
104 func diffFileType(_type git.DiffFileType) string {
105 	if _type == git.DiffFileAdd {
106 		return "A"
107@@ -127,6 +162,10 @@ func bail(err error) {
108 	}
109 }
110 
111+func toPretty(b int64) string {
112+	return humanize.Bytes(uint64(b))
113+}
114+
115 func commitURL(repo string, commitID string) string {
116 	return fmt.Sprintf("/%s/commits/%s.html", repo, commitID)
117 }
118@@ -135,6 +174,15 @@ func repoName(root string) string {
119 	_, file := filepath.Split(root)
120 	return file
121 }
122+
123+func readmeFile(repo *RepoData) string {
124+	if repo.Readme == "" {
125+		return "readme.md"
126+	}
127+
128+	return strings.ToLower(repo.Readme)
129+}
130+
131 func findDefaultBranch(config *RepoConfig, refs []*git.Reference) *git.Reference {
132 	branches := config.Refs
133 	if len(branches) == 0 {
134@@ -165,6 +213,7 @@ func walkTree(tree *git.Tree, branch string, curpath string, aggregate []*TreeIt
135 
136 		if entry.IsBlob() {
137 			aggregate = append(aggregate, &TreeItem{
138+				Size:  toPretty(entry.Size()),
139 				Path:  fname,
140 				Entry: entry,
141 				URL:   filepath.Join("/", "tree", branch, "item", fname),
142@@ -188,12 +237,13 @@ func writeHtml(data *WriteData) {
143 
144 	outdir := viper.GetString("outdir")
145 	dir := filepath.Join(outdir, data.RepoName, data.Subdir)
146-	fmt.Println(dir)
147-	fmt.Println(data.Name)
148 	err = os.MkdirAll(dir, os.ModePerm)
149 	bail(err)
150 
151-	w, err := os.OpenFile(filepath.Join(dir, data.Name), os.O_WRONLY|os.O_CREATE, 0755)
152+	fp := filepath.Join(dir, data.Name)
153+	fmt.Printf("writing (%s)\n", fp)
154+
155+	w, err := os.OpenFile(fp, os.O_WRONLY|os.O_CREATE, 0755)
156 	bail(err)
157 
158 	err = ts.Execute(w, data)
159@@ -213,11 +263,13 @@ func writeIndex(data *IndexPage) {
160 
161 	outdir := viper.GetString("outdir")
162 	dir := filepath.Join(outdir)
163-	fmt.Println(dir)
164 	err = os.MkdirAll(dir, os.ModePerm)
165 	bail(err)
166 
167-	w, err := os.OpenFile(filepath.Join(dir, "index.html"), os.O_WRONLY|os.O_CREATE, 0755)
168+	fp := filepath.Join(dir, "index.html")
169+	fmt.Printf("writing (%s)\n", fp)
170+
171+	w, err := os.OpenFile(fp, os.O_WRONLY|os.O_CREATE, 0755)
172 	bail(err)
173 
174 	err = ts.Execute(w, data)
175@@ -264,30 +316,36 @@ func writeRefs(data *PageData) {
176 	})
177 }
178 
179-type FileData struct {
180-	Contents template.HTML
181-}
182-
183 func writeHTMLTreeFiles(data *PageData) string {
184 	readme := ""
185 	for _, file := range data.Tree {
186 		b, err := file.Entry.Blob().Bytes()
187 		bail(err)
188-		file.NumLines = len(strings.Split(string(b), "\n"))
189+		str := string(b)
190+
191+		file.IsTextFile = shared.IsTextFile(str)
192+
193+		if file.IsTextFile {
194+			file.NumLines = len(strings.Split(str, "\n"))
195+		}
196 
197 		d := filepath.Dir(file.Path)
198 		contents, err := pastes.ParseText(file.Entry.Name(), string(b))
199 		bail(err)
200 
201 		nameLower := strings.ToLower(file.Entry.Name())
202-		if nameLower == "readme.md" {
203+		summary := readmeFile(data.Repo)
204+		if nameLower == summary {
205 			readme = contents
206 		}
207 
208 		writeHtml(&WriteData{
209 			Name:     fmt.Sprintf("%s.html", file.Entry.Name()),
210 			Template: "./html/file.page.tmpl",
211-			Data:     &FileData{Contents: template.HTML(contents)},
212+			Data: &FileData{
213+				Contents: template.HTML(contents),
214+				Name:     file.Entry.Name(),
215+			},
216 			RepoName: data.Repo.Name,
217 			Subdir:   filepath.Join("tree", data.RevName, "item", d),
218 			Repo:     data.Repo,
219@@ -381,11 +439,6 @@ func (c *Config) writeLogDiffs(repo *git.Repository, pageData *PageData) {
220 	}
221 }
222 
223-type RefInfo struct {
224-	Refspec string
225-	URL     template.URL
226-}
227-
228 func (c *Config) writeRepo(config *RepoConfig) *BranchOutput {
229 	repo, err := git.Open(config.Path)
230 	bail(err)
231@@ -410,7 +463,7 @@ func (c *Config) writeRepo(config *RepoConfig) *BranchOutput {
232 		LogURL:     fmt.Sprintf("/%s/logs/%s/index.html", name, revName),
233 		RefsURL:    fmt.Sprintf("/%s/refs.html", name),
234 		CloneURL:   fmt.Sprintf("https://%s/%s.git", c.URL, name),
235-		RevName: revName,
236+		RevName:    revName,
237 	}
238 
239 	refInfoMap := map[string]*RefInfo{}
240@@ -436,7 +489,7 @@ func (c *Config) writeRepo(config *RepoConfig) *BranchOutput {
241 				LogURL:     fmt.Sprintf("/%s/logs/%s/index.html", name, revn),
242 				RefsURL:    fmt.Sprintf("/%s/refs.html", name),
243 				CloneURL:   fmt.Sprintf("https://%s/%s.git", c.URL, name),
244-				RevName: revn,
245+				RevName:    revn,
246 			}
247 
248 			data := &PageData{
249@@ -479,22 +532,17 @@ func (c *Config) writeRepo(config *RepoConfig) *BranchOutput {
250 	})
251 
252 	data := &PageData{
253-		Rev:      rev,
254-		RevName:  revName,
255-		Repo:     repoData,
256-		Readme:   template.HTML(mainOutput.Readme),
257-		Refs:     refInfoList,
258+		Rev:     rev,
259+		RevName: revName,
260+		Repo:    repoData,
261+		Readme:  template.HTML(mainOutput.Readme),
262+		Refs:    refInfoList,
263 	}
264 	writeRefs(data)
265 	writeRootSummary(data)
266 	return mainOutput
267 }
268 
269-type BranchOutput struct {
270-	Readme     string
271-	LastCommit *git.Commit
272-}
273-
274 func (c *Config) writeBranch(repo *git.Repository, pageData *PageData) *BranchOutput {
275 	output := &BranchOutput{}
276 	pageSize := pageData.Repo.MaxCommits
277@@ -512,11 +560,12 @@ func (c *Config) writeBranch(repo *git.Repository, pageData *PageData) *BranchOu
278 		}
279 
280 		logs = append(logs, &CommitData{
281-			URL:    commitURL(pageData.Repo.Name, commit.ID.String()),
282+			URL:        commitURL(pageData.Repo.Name, commit.ID.String()),
283+			ShortID:    commit.ID.String()[:7],
284 			SummaryStr: commit.Summary(),
285-			AuthorStr: commit.Author.Name,
286-			WhenStr: timediff.TimeDiff(commit.Author.When),
287-			Commit: commit,
288+			AuthorStr:  commit.Author.Name,
289+			WhenStr:    timediff.TimeDiff(commit.Author.When),
290+			Commit:     commit,
291 		})
292 	}
293 
294@@ -537,7 +586,7 @@ func (c *Config) writeBranch(repo *git.Repository, pageData *PageData) *BranchOu
295 			lc = lastCommits[0]
296 		}
297 		entry.CommitURL = commitURL(pageData.Repo.Name, lc.ID.String())
298-		entry.Desc = lc.Summary()
299+		entry.Summary = lc.Summary()
300 		entry.When = timediff.TimeDiff(lc.Author.When)
301 		entry.URL = filepath.Join(
302 			"/",
303@@ -562,18 +611,6 @@ func (c *Config) writeBranch(repo *git.Repository, pageData *PageData) *BranchOu
304 	return output
305 }
306 
307-type RepoConfig struct {
308-	Path       string   `mapstructure:"path"`
309-	Refs       []string `mapstructure:"refs"`
310-	Desc       string   `mapstructure:"desc"`
311-	MaxCommits int      `mapstructure:"max_commits"`
312-}
313-type Config struct {
314-	Repos []*RepoConfig `mapstructure:"repos"`
315-	URL   string        `mapstructure:"url"`
316-	Cache map[string]bool
317-}
318-
319 func main() {
320 	viper.SetDefault("outdir", "./public")
321 	viper.SetConfigName("config")
M static/main.css
+27, -67
  1@@ -115,32 +115,6 @@ pre {
  2   background-color: var(--pre) !important;
  3 }
  4 
  5-.my {
  6-  margin: 1rem 0;
  7-}
  8-
  9-.mt {
 10-  margin-top: 1rem;
 11-}
 12-
 13-.my-sm {
 14-  margin: 5px 0;
 15-}
 16-
 17-.mono {
 18-  font-family: ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", Menlo,
 19-    monospace;
 20-}
 21-
 22-.box {
 23-  font-size: 0.8rem;
 24-  border-radius: 5px;
 25-  padding: 1rem;
 26-  margin: 1rem 0;
 27-  overflow-x: auto;
 28-  background-color: var(--pre) !important;
 29-}
 30-
 31 small {
 32   font-size: 0.8rem;
 33 }
 34@@ -263,6 +237,19 @@ figure {
 35   margin: 0;
 36 }
 37 
 38+.mono {
 39+  font-family: ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", Menlo,
 40+    monospace;
 41+}
 42+
 43+.box {
 44+  border-radius: 5px;
 45+  padding: 1rem;
 46+  margin: 1rem 0;
 47+  overflow-x: auto;
 48+  background-color: var(--pre) !important;
 49+}
 50+
 51 .color-green {
 52   color: var(--success);
 53 }
 54@@ -271,10 +258,6 @@ figure {
 55   color: var(--error);
 56 }
 57 
 58-.post-date {
 59-  width: 130px;
 60-}
 61-
 62 .text-grey {
 63   color: var(--grey);
 64 }
 65@@ -315,10 +298,6 @@ figure {
 66   font-style: italic;
 67 }
 68 
 69-.inline {
 70-  display: inline;
 71-}
 72-
 73 .flex {
 74   display: flex;
 75 }
 76@@ -335,16 +314,8 @@ figure {
 77   margin-top: 0.5rem;
 78 }
 79 
 80-.mb {
 81-  margin-bottom: 0.5rem;
 82-}
 83-
 84-.mr {
 85-  margin-right: 0.5rem;
 86-}
 87-
 88-.ml {
 89-  margin-left: 0.5rem;
 90+.mt-lg {
 91+  margin-top: 1.35rem;
 92 }
 93 
 94 .my {
 95@@ -357,6 +328,11 @@ figure {
 96   margin-bottom: 1rem;
 97 }
 98 
 99+.my-sm {
100+  margin-top: 5px;
101+  margin-bottom: 5px;
102+}
103+
104 .mx {
105   margin-left: 0.5rem;
106   margin-right: 0.5rem;
107@@ -375,23 +351,19 @@ figure {
108   flex: 1;
109 }
110 
111-.layout-aside {
112-  max-width: 50rem;
113+.gap {
114+  gap: 1rem;
115 }
116 
117-.layout-aside aside {
118-  width: 200px;
119+.border-b {
120+  border-bottom: 1px solid #666;
121 }
122 
123-.layout-aside img {
124-  border-radius: 5px;
125+.tree-size {
126+  width: 60px;
127 }
128 
129-#readme {
130-  display: none;
131-}
132-
133-@media only screen and (max-width: 600px) {
134+@media only screen and (max-width: 900px) {
135   body {
136     padding: 1rem;
137   }
138@@ -399,16 +371,4 @@ figure {
139   header {
140     margin: 0;
141   }
142-
143-  .layout-aside main {
144-    flex-direction: column;
145-  }
146-
147-  aside {
148-    display: none;
149-  }
150-
151-  #readme {
152-    display: block;
153-  }
154 }