main.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. package main
  2. import (
  3. "html/template"
  4. "os"
  5. "os/exec"
  6. "path/filepath"
  7. "runtime"
  8. "runtime/pprof"
  9. "strconv"
  10. "strings"
  11. "time"
  12. "github.com/cdelorme/go-log"
  13. "github.com/cdelorme/go-maps"
  14. "github.com/cdelorme/go-option"
  15. )
  16. // check that a path exists
  17. // does not care if it is a directory
  18. // will not say whether user has rw access, but
  19. // will throw an error if the user cannot read the parent directory
  20. func exists(path string) (bool, error) {
  21. _, err := os.Stat(path)
  22. if err == nil {
  23. return true, nil
  24. }
  25. if os.IsNotExist(err) {
  26. return false, nil
  27. }
  28. return false, err
  29. }
  30. // if within a git repo, gets git version as a short-hash
  31. // otherwise falls back to a unix timestamp
  32. func version() string {
  33. version := strconv.FormatInt(time.Now().Unix(), 10)
  34. out, err := exec.Command("sh", "-c", "git rev-parse --short HEAD").Output()
  35. if err == nil {
  36. version = strings.Trim(string(out), "\n")
  37. }
  38. return version
  39. }
  40. // remove the path and extension from a given filename
  41. func basename(name string) string {
  42. return filepath.Base(strings.TrimSuffix(name, filepath.Ext(name)))
  43. }
  44. func main() {
  45. // get current directory
  46. cwd, _ := os.Getwd()
  47. // prepare staticmd with dependencies & defaults
  48. staticmd := Staticmd{
  49. Logger: log.Logger{Level: log.Error},
  50. Version: version(),
  51. Input: cwd,
  52. Output: filepath.Join(cwd, "public/"),
  53. }
  54. // optimize concurrent processing
  55. staticmd.MaxParallelism = runtime.NumCPU()
  56. runtime.GOMAXPROCS(staticmd.MaxParallelism)
  57. // prepare cli options
  58. appOptions := option.App{Description: "command line tool for generating deliverable static content"}
  59. appOptions.Flag("template", "path to the template file", "--template", "-t")
  60. appOptions.Flag("input", "path to the markdown files", "--input", "-i")
  61. appOptions.Flag("output", "path to place generated content", "--output", "-o")
  62. appOptions.Flag("book", "combine all content into a single file", "--book", "-b")
  63. appOptions.Flag("relative", "use relative paths instead of absolute paths", "--relative", "-r")
  64. appOptions.Flag("debug", "verbose debug output", "--debug", "-d")
  65. appOptions.Flag("profile", "produce profile output to supplied path", "--profile", "-p")
  66. appOptions.Example("-t template.tmpl -i . -b")
  67. appOptions.Example("-t template.tmpl -i src/ -o out/ -r")
  68. flags := appOptions.Parse()
  69. // apply flags
  70. t, _ := maps.String(&flags, "", "template")
  71. if tmpl, err := template.ParseFiles(t); err != nil {
  72. staticmd.Logger.Error("Failed to open template: %s", err)
  73. os.Exit(1)
  74. } else {
  75. staticmd.Template = *tmpl
  76. }
  77. staticmd.Input, _ = maps.String(&flags, staticmd.Input, "input")
  78. staticmd.Output, _ = maps.String(&flags, staticmd.Output, "output")
  79. staticmd.Book, _ = maps.Bool(&flags, staticmd.Book, "book")
  80. staticmd.Relative, _ = maps.Bool(&flags, staticmd.Relative, "relative")
  81. // sanitize input & output
  82. staticmd.Input, _ = filepath.Abs(staticmd.Input)
  83. staticmd.Output, _ = filepath.Abs(staticmd.Output)
  84. // optionally enable debugging
  85. if debug, _ := maps.Bool(&flags, false, "debug"); debug {
  86. staticmd.Logger.Level = log.Debug
  87. }
  88. // optionally enable profiling
  89. if profile, _ := maps.String(&flags, "", "profile"); profile != "" {
  90. f, _ := os.Create(profile)
  91. pprof.StartCPUProfile(f)
  92. defer pprof.StopCPUProfile()
  93. }
  94. // sanitize & validate properties
  95. staticmd.Input = filepath.Clean(staticmd.Input)
  96. staticmd.Output = filepath.Clean(staticmd.Output)
  97. // print debug status
  98. staticmd.Logger.Debug("Staticmd State: %+v", staticmd)
  99. // walk the file system
  100. if err := filepath.Walk(staticmd.Input, staticmd.Walk); err != nil {
  101. staticmd.Logger.Error("failed to walk directory: %s", err)
  102. }
  103. staticmd.Logger.Debug("Pages: %+v", staticmd.Pages)
  104. // build
  105. if staticmd.Book {
  106. staticmd.Single()
  107. } else {
  108. staticmd.Multi()
  109. }
  110. }