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
28func (cl CommitLog) Len() int { return len(cl) }
29func (cl CommitLog) Swap(i, j int) { cl[i], cl[j] = cl[j], cl[i] }
30func (cl CommitLog) Less(i, j int) bool {
31 return cl[i].Commit.Author.When.After(cl[j].Commit.Author.When)
32}
33
34type RepoSource struct {
35 mtx sync.Mutex
36 path string
37 repos []*Repo
38 commits CommitLog
39}
40
41func NewRepoSource(repoPath string, poll time.Duration) *RepoSource {
42 rs := &RepoSource{path: repoPath}
43 go func() {
44 for {
45 rs.loadRepos()
46 time.Sleep(poll)
47 }
48 }()
49 return rs
50}
51
52func (rs *RepoSource) AllRepos() []*Repo {
53 rs.mtx.Lock()
54 defer rs.mtx.Unlock()
55 return rs.repos
56}
57
58func (rs *RepoSource) GetCommits(limit int) []RepoCommit {
59 rs.mtx.Lock()
60 defer rs.mtx.Unlock()
61 if limit > len(rs.commits) {
62 limit = len(rs.commits)
63 }
64 return rs.commits[:limit]
65}
66
67func (rs *RepoSource) loadRepos() {
68 rs.mtx.Lock()
69 defer rs.mtx.Unlock()
70 rd, err := os.ReadDir(rs.path)
71 if err != nil {
72 return
73 }
74 rs.repos = make([]*Repo, 0)
75 rs.commits = make([]RepoCommit, 0)
76 for _, de := range rd {
77 rn := de.Name()
78 r := &Repo{Name: rn}
79 rg, err := git.PlainOpen(rs.path + string(os.PathSeparator) + rn)
80 if err != nil {
81 log.Fatal(err)
82 }
83 r.Repository = rg
84 l, err := rg.Log(&git.LogOptions{All: true})
85 if err != nil {
86 log.Fatal(err)
87 }
88 l.ForEach(func(c *object.Commit) error {
89 if r.LastUpdated == nil {
90 r.LastUpdated = &c.Author.When
91 }
92 rs.commits = append(rs.commits, RepoCommit{Name: rn, Commit: c})
93 return nil
94 })
95 sort.Sort(rs.commits)
96 rs.repos = append(rs.repos, r)
97 }
98}