1package main23import (4 "embed"5 "errors"6 "flag"7 "fmt"8 "html/template"9 "io/fs"10 "log"11 "net/url"12 "os"13 "path/filepath"1415 "github.com/nmeum/depp/css"16 "github.com/nmeum/depp/gitweb"17)1819//go:embed tmpl20var templates embed.FS2122var (23 commits = flag.Uint("c", 5, "amount of recent commits to include")24 force = flag.Bool("f", false, "force rebuilding of all HTML files")25 gitURL = flag.String("u", "", "clone URL for the Git repository")26 destination = flag.String("d", "./www", "output directory for HTML files")27 verbose = flag.Bool("v", false, "print the name of each changed file")28)2930var tmpl *template.Template3132const (33 // Name of file used to record the hash of the generated tree object.34 stateFile = ".tree"35)3637func usage() {38 fmt.Fprintf(flag.CommandLine.Output(),39 "USAGE: %s [FLAGS] REPOSITORY\n\n"+40 "The following flags are supported:\n\n", os.Args[0])4142 flag.PrintDefaults()43 os.Exit(2)44}4546func walkPages(name string, page *gitweb.RepoPage) error {47 if *verbose {48 fmt.Println(name)49 }5051 dest := filepath.Join(*destination, name+".html")52 if page == nil { // file was removed53 err := os.Remove(dest)54 if err != nil && !errors.Is(err, fs.ErrNotExist) {55 return err56 }5758 // In case name refers to a (now empty) directory:59 os.Remove(filepath.Join(*destination, name))6061 return nil62 } else if isIndexPage(page) {63 dest = filepath.Join(*destination, "index.html")64 }6566 err := os.MkdirAll(filepath.Dir(dest), 0755)67 if err != nil {68 return err69 }70 file, err := os.Create(dest)71 if err != nil {72 return err73 }74 defer file.Close()7576 err = tmpl.Execute(file, page)77 if err != nil {78 return err79 }8081 return nil82}8384func buildHTML() (*template.Template, error) {85 var err error8687 const name = "base.tmpl"88 tmpl := template.New(name)8990 funcMap := template.FuncMap{91 "summarize": summarize,92 "getRelPath": getRelPath,93 "increment": increment,94 "decrement": decrement,95 "getLines": getLines,96 "padNumber": padNumber,97 "relIndex": relIndex,98 "isIndexPage": isIndexPage,99 "renderReadme": renderReadme,100 }101 tmpl = tmpl.Funcs(funcMap)102103 tmpl, err = tmpl.ParseFS(templates, "tmpl/*.tmpl")104 if err != nil {105 return nil, err106 }107108 return tmpl, nil109}110111func generate(repo *gitweb.Repo) error {112 var err error113 tmpl, err = buildHTML()114 if err != nil {115 return err116 }117 err = repo.Walk(walkPages)118 if err != nil {119 return err120 }121122 cssPath := filepath.Join(*destination, "style.css")123 _, err = os.Stat(cssPath)124 if *force || errors.Is(err, os.ErrNotExist) {125 err = css.Create(cssPath)126 if err != nil {127 return err128 }129 }130131 return nil132}133134func main() {135 flag.Usage = usage136 flag.Parse()137138 log.SetFlags(log.Lshortfile)139 if flag.NArg() != 1 {140 usage()141 }142143 gitURL, err := url.Parse(*gitURL)144 if err != nil {145 log.Fatal(err)146 }147148 path := flag.Arg(0)149 statePath := filepath.Join(*destination, stateFile)150151 repo, err := gitweb.NewRepo(path, gitURL, *commits)152 if err != nil {153 log.Fatal(err)154 }155 if !*force {156 err = repo.ReadState(statePath)157 if err != nil && !errors.Is(err, os.ErrNotExist) {158 log.Fatal(err)159 }160 }161162 err = generate(repo)163 if err != nil {164 log.Fatal(err)165 }166 err = repo.WriteState(statePath)167 if err != nil {168 log.Fatal(err)169 }170}