1package git
2
3import (
4 "log"
5 "os"
6 "sort"
7 "sync"
8 "time"
9
10 "github.com/go-git/go-git/v5"
11 "github.com/go-git/go-git/v5/plumbing/object"
12)
13
14type Repo struct {
15 Name string
16 Repository *git.Repository
17 Readme string
18 LastUpdated *time.Time
19}
20
21type RepoCommit struct {
22 Name string
23 Commit *object.Commit
24}
25
26type CommitLog []RepoCommit
27
28type ReadmeTransform func(string) string
29
30func (cl CommitLog) Len() int { return len(cl) }
31func (cl CommitLog) Swap(i, j int) { cl[i], cl[j] = cl[j], cl[i] }
32func (cl CommitLog) Less(i, j int) bool {
33 return cl[i].Commit.Author.When.After(cl[j].Commit.Author.When)
34}
35
36type RepoSource struct {
37 mtx sync.Mutex
38 path string
39 repos []*Repo
40 commits CommitLog
41 readmeTransform ReadmeTransform
42}
43
44func NewRepoSource(repoPath string, poll time.Duration, rf ReadmeTransform) *RepoSource {
45 rs := &RepoSource{path: repoPath, readmeTransform: rf}
46 go func() {
47 for {
48 rs.loadRepos()
49 time.Sleep(poll)
50 }
51 }()
52 return rs
53}
54
55func (rs *RepoSource) AllRepos() []*Repo {
56 rs.mtx.Lock()
57 defer rs.mtx.Unlock()
58 return rs.repos
59}
60
61func (rs *RepoSource) GetCommits(limit int) []RepoCommit {
62 rs.mtx.Lock()
63 defer rs.mtx.Unlock()
64 if limit > len(rs.commits) {
65 limit = len(rs.commits)
66 }
67 return rs.commits[:limit]
68}
69
70func (rs *RepoSource) loadRepos() {
71 rs.mtx.Lock()
72 defer rs.mtx.Unlock()
73 rd, err := os.ReadDir(rs.path)
74 if err != nil {
75 return
76 }
77 rs.repos = make([]*Repo, 0)
78 rs.commits = make([]RepoCommit, 0)
79 for _, de := range rd {
80 rn := de.Name()
81 r := &Repo{Name: rn}
82 rg, err := git.PlainOpen(rs.path + string(os.PathSeparator) + rn)
83 if err != nil {
84 log.Fatal(err)
85 }
86 r.Repository = rg
87 l, err := rg.Log(&git.LogOptions{All: true})
88 if err != nil {
89 log.Fatal(err)
90 }
91 l.ForEach(func(c *object.Commit) error {
92 if r.LastUpdated == nil {
93 r.LastUpdated = &c.Author.When
94 rf, err := c.File("README.md")
95 if err == nil {
96 rmd, err := rf.Contents()
97 if err == nil {
98 r.Readme = rs.readmeTransform(rmd)
99 }
100 }
101 }
102 rs.commits = append(rs.commits, RepoCommit{Name: rn, Commit: c})
103 return nil
104 })
105 sort.Sort(rs.commits)
106 rs.repos = append(rs.repos, r)
107 }
108}