package main import ( "embed" "flag" "fmt" "io" "io/fs" "log" "os" "path/filepath" "strings" ) var ( Version string ) func main() { if len(os.Args) >= 2 { switch os.Args[1] { case "build": build() return case "new": if len(os.Args) < 3 { fmt.Println("usage: kiln new ") os.Exit(1) } newSite(os.Args[2]) return case "version": fmt.Println("kiln", Version) return } } fmt.Println("usage: kiln args...") os.Exit(1) } func build() { var ( config string ) flags := flag.NewFlagSet("kiln build", flag.ExitOnError) flags.StringVar(&config, "c", "config.toml", "the configuration file to use") flags.Parse(os.Args[2:]) site, err := LoadSite(config) if err != nil { log.Fatalf("ERROR: %v", err) } if err := site.run(); err != nil { log.Fatalf("ERROR: %v", err) } } func (site *Site) run() error { for _, task := range site.Tasks { err := site.runTask(task) if err != nil { return err } } return nil } func (s *Site) runTask(task *Task) error { // Read content s.Root = &Page{Path: "/", FilePath: ".", URL: task.URL + "/"} if err := s.Root.read("content", task, s); err != nil { return err } s.Root.sort() // Process content if err := s.Root.process(s, task); err != nil { return err } // Write content if err := s.Root.write(task.OutputDir, task); err != nil { return err } // Copy static files if task.StaticDir != "" { err := copyAll(task.StaticDir, task.OutputDir) if err != nil { if os.IsNotExist(err) { log.Printf("static_dir '%s' does not exist\n", task.StaticDir) return nil } return err } } return nil } func copyAll(srcDir, dstDir string) error { return filepath.Walk(srcDir, func(path string, info fs.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { // Do nothing return nil } src, err := os.Open(path) if err != nil { return err } defer src.Close() sinfo, err := src.Stat() if err != nil { return err } mode := sinfo.Mode() dstPath := filepath.Join(dstDir, strings.TrimPrefix(path, srcDir)) os.MkdirAll(filepath.Dir(dstPath), 0755) dst, err := os.OpenFile(dstPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, mode) if err != nil { return err } defer dst.Close() if _, err := io.Copy(dst, src); err != nil { return err } return nil }) } //go:embed templates/_default config.toml var builtin embed.FS func newSite(name string) { name = filepath.Clean(name) os.Mkdir(name, 0755) os.Mkdir(filepath.Join(name, "content"), 0755) os.Mkdir(filepath.Join(name, "templates"), 0755) os.Mkdir(filepath.Join(name, "templates", "_default"), 0755) os.Mkdir(filepath.Join(name, "static"), 0755) os.Mkdir(filepath.Join(name, "public"), 0755) config, _ := builtin.ReadFile("config.toml") os.WriteFile(filepath.Join(name, "config.toml"), config, 0644) index := []byte("---\ntitle: Hello, world!\n---\n") os.WriteFile(filepath.Join(name, "content", "_index.gmi"), index, 0644) templates := []string{"atom.xml", "index.gmi", "page.gmi"} for _, template := range templates { b, _ := builtin.ReadFile(filepath.Join("templates", "_default", template)) os.WriteFile(filepath.Join(name, "templates", "_default", template), b, 0644) } }