git.go

  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}