1package main23import (4 "embed"5 "flag"6 "fmt"7 "html/template"8 "log"9 "os"10 "path/filepath"11 "sort"12 "time"1314 "github.com/nmeum/depp/css"15 "github.com/nmeum/depp/gitweb"16)1718type Repo struct {19 Name string20 Title string21 Desc string22 Modified time.Time23}2425type Page struct {26 CurPage int27 NumPages int2829 Title string30 Desc string31 Repos []Repo32}3334//go:embed tmpl35var templates embed.FS3637var (38 desc = flag.String("s", "", "short description of git host")39 title = flag.String("t", "depp-index", "page title")40 dest = flag.String("d", "./www", "output directory for HTML files")41 strip = flag.Bool("x", false, "strip .git extension from repository name in link")42 items = flag.Int("p", 20, "amount of repos per HTML page, a zero value disables pagination")43)4445func usage() {46 fmt.Fprintf(flag.CommandLine.Output(),47 "USAGE: %s [FLAGS] REPOSITORY...\n\n"+48 "The following flags are supported:\n\n", os.Args[0])4950 flag.PrintDefaults()51 os.Exit(2)52}5354func repoLink(repo *Repo) string {55 if *strip {56 // Return a post-processed repository name without .git57 return repo.Title58 } else {59 // Return the raw file name, potentially including .git60 return repo.Name61 }62}6364func pageName(page int) string {65 if page == 0 {66 return "index.html"67 } else {68 return fmt.Sprintf("page-%d.html", page)69 }70}7172func pageRefs(page Page) []int {73 pages := make([]int, page.NumPages)74 for i := 0; i < page.NumPages; i++ {75 pages[i] = i76 }77 return pages78}7980func createHTML(page Page, path string) error {81 const name = "base.tmpl"8283 html := template.New(name)84 html.Funcs(template.FuncMap{85 "repoLink": repoLink,86 "pageName": pageName,87 "pageRefs": pageRefs,88 })8990 tmpl, err := html.ParseFS(templates, "tmpl/*.tmpl")91 if err != nil {92 return err93 }9495 file, err := os.Create(path)96 if err != nil {97 return err98 }99 defer file.Close()100101 err = tmpl.Execute(file, page)102 if err != nil {103 return err104 }105106 return nil107}108109func getRepos(fps []string) ([]Repo, error) {110 repos := make([]Repo, len(fps))111 for i, fp := range fps {112 r, err := gitweb.NewRepo(fp, nil, 0)113 if err != nil {114 return []Repo{}, err115 }116117 commit, err := r.Tip()118 if err != nil {119 return []Repo{}, err120 }121 desc, err := r.Description()122 if err != nil {123 return []Repo{}, err124 }125126 sig := commit.Committer127 repos[i] = Repo{128 Name: filepath.Base(fp),129 Title: r.Title,130 Desc: desc,131 Modified: sig.When,132 }133 }134135 sort.Sort(byModified(repos))136 return repos, nil137}138139func getPages(repos []Repo) []Page {140 var numPages int141 if *items == 0 {142 numPages = 1143 } else {144 // division is not truncated towards zero145 numPages = 1 + (len(repos)-1) / *items146 }147148 pages := make([]Page, numPages)149 for i := 0; i < numPages; i++ {150 var maxrepos int151 if *items == 0 {152 maxrepos = len(repos)153 } else {154 maxrepos = min(*items, len(repos))155 }156157 pages[i] = Page{158 CurPage: i,159 NumPages: numPages,160 Title: *title,161 Desc: *desc,162 Repos: repos[0:maxrepos],163 }164165 repos = repos[maxrepos:]166 }167168 return pages169}170171func main() {172 flag.Usage = usage173 flag.Parse()174175 log.SetFlags(log.Lshortfile)176 if flag.NArg() == 0 {177 usage()178 }179180 repos, err := getRepos(flag.Args())181 if err != nil {182 log.Fatal(err)183 }184 pages := getPages(repos)185186 err = os.MkdirAll(*dest, 0755)187 if err != nil {188 log.Fatal(err)189 }190191 err = css.Create(filepath.Join(*dest, "style.css"))192 if err != nil {193 log.Fatal(err)194 }195196 for _, page := range pages {197 fp := filepath.Join(*dest, pageName(page.CurPage))198 err = createHTML(page, fp)199 if err != nil {200 log.Fatal(err)201 }202 }203}