all repos — legit @ subdir

web frontend for git

routes/util.go (view raw)

  1package routes
  2
  3import (
  4	"context"
  5	"io/fs"
  6	"log"
  7	"os"
  8	"path/filepath"
  9	"sort"
 10	"strings"
 11
 12	"git.icyphox.sh/legit/git"
 13	"github.com/alexedwards/flow"
 14	"github.com/dustin/go-humanize"
 15)
 16
 17func isGoModule(gr *git.GitRepo) bool {
 18	_, err := gr.FileContent("go.mod")
 19	return err == nil
 20}
 21
 22func getDescription(path string) (desc string) {
 23	db, err := os.ReadFile(filepath.Join(path, "description"))
 24	if err == nil {
 25		desc = string(db)
 26	} else {
 27		desc = ""
 28	}
 29	return
 30}
 31
 32func (d *deps) isIgnored(name string) bool {
 33	for _, i := range d.c.Repo.Ignore {
 34		if name == i {
 35			return true
 36		}
 37	}
 38
 39	return false
 40}
 41
 42type repository struct {
 43	Name        string
 44	Category    string
 45	Path        string
 46	Slug        string
 47	Description string
 48	LastCommit  string
 49}
 50
 51type entry struct {
 52	Name         string
 53	Repositories []*repository
 54}
 55
 56type entries struct {
 57	Children []*entry
 58	c        map[string]*entry
 59}
 60
 61func (ent *entries) Add(r repository) {
 62	if r.Category == "" {
 63		ent.Children = append(ent.Children, &entry{
 64			Name:         r.Name,
 65			Repositories: []*repository{&r},
 66		})
 67		return
 68	}
 69	t, ok := ent.c[r.Category]
 70	if !ok {
 71		t := &entry{
 72			Name:         r.Category,
 73			Repositories: []*repository{&r},
 74		}
 75		ent.c[r.Category] = t
 76		ent.Children = append(ent.Children, t)
 77		return
 78	}
 79	t.Repositories = append(t.Repositories, &r)
 80}
 81
 82func (d *deps) getAllRepos() (*entries, error) {
 83	entries := &entries{
 84		Children: []*entry{},
 85		c:        map[string]*entry{},
 86	}
 87	max := strings.Count(d.c.Repo.ScanPath, string(os.PathSeparator)) + 2
 88
 89	err := filepath.WalkDir(d.c.Repo.ScanPath, func(path string, de fs.DirEntry, err error) error {
 90		if err != nil {
 91			return err
 92		}
 93
 94		if de.IsDir() {
 95			// Check if we've exceeded our recursion depth
 96			if strings.Count(path, string(os.PathSeparator)) > max {
 97				return fs.SkipDir
 98			}
 99
100			if d.isIgnored(path) {
101				return fs.SkipDir
102			}
103
104			// A bare repo should always have at least a HEAD file, if it
105			// doesn't we can continue recursing
106			if _, err := os.Lstat(filepath.Join(path, "HEAD")); err == nil {
107				repo, err := git.Open(path, "")
108				if err != nil {
109					log.Println(err)
110				} else {
111					relpath, _ := filepath.Rel(d.c.Repo.ScanPath, path)
112					category := strings.Split(relpath, string(os.PathSeparator))[0]
113					r := repository{
114						Name:        filepath.Base(path),
115						Category:    category,
116						Path:        path,
117						Slug:        relpath,
118						Description: getDescription(path),
119					}
120					if c, err := repo.LastCommit(); err == nil {
121						r.LastCommit = humanize.Time(c.Author.When)
122					}
123					entries.Add(r)
124					// Since we found a Git repo, we don't want to recurse
125					// further
126					return fs.SkipDir
127				}
128			}
129		}
130		return nil
131	})
132	sort.Slice(entries.Children, func(i, j int) bool {
133		return entries.Children[i].Name < entries.Children[j].Name
134	})
135	return entries, err
136}
137
138func repoPath(ctx context.Context) string {
139	return filepath.Join(
140		filepath.Clean(flow.Param(ctx, "category")),
141		filepath.Clean(flow.Param(ctx, "name")),
142	)
143}