depp

No frills static page generator for Git repositories

git clone https://git.8pit.net/depp.git

  1package main
  2
  3import (
  4	"embed"
  5	"flag"
  6	"fmt"
  7	"html/template"
  8	"log"
  9	"net/url"
 10	"os"
 11	"path/filepath"
 12	"time"
 13
 14	"github.com/nmeum/depp/css"
 15	"github.com/nmeum/depp/gitweb"
 16)
 17
 18//go:embed tmpl
 19var templates embed.FS
 20
 21var (
 22	commits     = flag.Uint("c", 5, "amount of recent commits to include")
 23	gitURL      = flag.String("u", "", "clone URL for the Git repository")
 24	destination = flag.String("d", "./www", "output directory for HTML files")
 25)
 26
 27var tmpl *template.Template
 28
 29func usage() {
 30	fmt.Fprintf(flag.CommandLine.Output(),
 31		"USAGE: %s [FLAGS] REPOSITORY\n\n"+
 32			"The following flags are supported:\n\n", os.Args[0])
 33
 34	flag.PrintDefaults()
 35	os.Exit(2)
 36}
 37
 38func walkPages(page *gitweb.RepoPage) error {
 39	name := page.CurrentFile.Path
 40	if isIndexPage(page) {
 41		name = "index"
 42	}
 43
 44	fp := filepath.Join(*destination, name+".html")
 45	err := os.MkdirAll(filepath.Dir(fp), 0755)
 46	if err != nil {
 47		return err
 48	}
 49	file, err := os.Create(fp)
 50	if err != nil {
 51		return err
 52	}
 53	defer file.Close()
 54
 55	err = tmpl.Execute(file, page)
 56	if err != nil {
 57		return err
 58	}
 59
 60	return nil
 61}
 62
 63func buildHTML() (*template.Template, error) {
 64	var err error
 65
 66	const name = "base.tmpl"
 67	tmpl := template.New(name)
 68
 69	funcMap := template.FuncMap{
 70		"isBinary":     isBinary,
 71		"getRelPath":   getRelPath,
 72		"increment":    increment,
 73		"decrement":    decrement,
 74		"getLines":     getLines,
 75		"padNumber":    padNumber,
 76		"relIndex":     relIndex,
 77		"isIndexPage":  isIndexPage,
 78		"renderReadme": renderReadme,
 79	}
 80	tmpl = tmpl.Funcs(funcMap)
 81
 82	tmpl, err = tmpl.ParseFS(templates, "tmpl/*.tmpl")
 83	if err != nil {
 84		return nil, err
 85	}
 86
 87	return tmpl, nil
 88}
 89
 90func generate(repo *gitweb.Repo) error {
 91	var err error
 92	tmpl, err = buildHTML()
 93	if err != nil {
 94		return err
 95	}
 96	err = repo.Walk(walkPages)
 97	if err != nil {
 98		return err
 99	}
100
101	err = css.Create(filepath.Join(*destination, "style.css"))
102	if err != nil {
103		return err
104	}
105
106	return nil
107}
108
109func main() {
110	// Time **before** start of file generation.
111	// Will later be used as the mtime/atime of `index.html`.
112	startTime := time.Now().Add(-1 * time.Second)
113
114	flag.Usage = usage
115	flag.Parse()
116
117	log.SetFlags(log.Lshortfile)
118	if flag.NArg() != 1 {
119		usage()
120	}
121
122	gitURL, err := url.Parse(*gitURL)
123	if err != nil {
124		log.Fatal(err)
125	}
126
127	path := flag.Arg(0)
128	repo, err := gitweb.NewRepo(path, gitURL, *commits)
129	if err != nil {
130		log.Fatal(err)
131	}
132	err = generate(repo)
133	if err != nil {
134		log.Fatal(err)
135	}
136
137	// Reset mtime/atime of index.html to detect untouched files.
138	index := filepath.Join(*destination, "index.html")
139	err = os.Chtimes(index, startTime, startTime)
140	if err != nil {
141		log.Fatal(err)
142	}
143}