diff --git a/client.go b/client.go index 5c3f536..8963aa8 100644 --- a/client.go +++ b/client.go @@ -1,14 +1,14 @@ package caddyhugo import ( - "encoding/base64" "fmt" "io/ioutil" "net/http" - "os" "path" "sync" + "github.com/spf13/afero" + "git.stephensearles.com/stephen/acedoc" "git.stephensearles.com/stephen/idleshut" ) @@ -18,7 +18,7 @@ type editSession struct { docname string filename string doc *acedoc.Document - tmpdir string + tmpfs afero.Fs mtx sync.Mutex } @@ -38,16 +38,16 @@ func (ch *CaddyHugo) newEditSession(docName string) (*editSession, error) { return nil, err } - draftPrefix := fmt.Sprintf("draft-%s", base64.RawURLEncoding.EncodeToString([]byte(docName))) - tmpdir := path.Join(os.TempDir(), draftPrefix) - es := &editSession{ docname: docName, filename: filename, doc: acedoc.NewString(string(contents)), - tmpdir: tmpdir, + // tmpfs: afero.NewCopyOnWriteFs(afero.NewBasePathFs(afero.NewOsFs(), ch.Dir+"/"), afero.NewMemMapFs()), + tmpfs: afero.NewCopyOnWriteFs(afero.NewReadOnlyFs(afero.NewBasePathFs(afero.NewOsFs(), ch.Dir)), afero.NewMemMapFs()), } + printTree(es.tmpfs) + err = es.doc.LogToFile(path.Join(ch.Dir, "logs", docName)) if err != nil { fmt.Println(err) @@ -56,7 +56,10 @@ func (ch *CaddyHugo) newEditSession(docName string) (*editSession, error) { ch.docs[filename] = es - ch.renderDraft(es) + err = ch.renderDraft(es) + if err != nil { + return nil, err + } return es, nil } @@ -70,9 +73,13 @@ func (ch *CaddyHugo) renderDraft(es *editSession) error { } } - proc = idleshut.New(HugoCmdProcessConfig(ch, es, f)) + cfg, err := HugoCmdProcessConfig(ch, es, f) + if err != nil { + return fmt.Errorf("rendering draft: %v", err) + } - return nil + proc = idleshut.New(cfg) + return proc.Start() } func (ch *CaddyHugo) hasEditSession(docName string) (*editSession, bool) { diff --git a/deltas.go b/deltas.go index 7ab91fd..5955bfb 100644 --- a/deltas.go +++ b/deltas.go @@ -16,11 +16,6 @@ type DeltaConn interface { WriteJSON(v interface{}) error } -const ( - IdleWebsocketTimeout = 10 * time.Minute - WebsocketFileTicker = 1 * time.Second -) - func (ch *CaddyHugo) ObserveLTime(ltime uint64) uint64 { ch.mtx.Lock() diff --git a/http.go b/http.go index 2a17c96..352c53c 100644 --- a/http.go +++ b/http.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "net/http" + "os" "path" "path/filepath" "strings" @@ -12,6 +13,7 @@ import ( "git.stephensearles.com/stephen/caddy-hugo2/assets" "github.com/mholt/caddy" "github.com/mholt/caddy/caddyhttp/httpserver" + "github.com/spf13/afero" ) func (ch *CaddyHugo) ServeHTTPWithNext(next httpserver.Handler, c *caddy.Controller, w http.ResponseWriter, r *http.Request) (int, error) { @@ -199,7 +201,7 @@ func (ch *CaddyHugo) serveDraft(w http.ResponseWriter, r *http.Request) (int, er r.URL.Path = strings.ToLower(r.URL.Path) prefix := "/hugo/draft/" + encoded - r.URL.Path = r.URL.Path[len(prefix):] + r.URL.Path = path.Join("public", r.URL.Path[len(prefix):]) page := ch.HugoSites.GetContentPage(ch.docFilename(name)) if page == nil { @@ -208,7 +210,48 @@ func (ch *CaddyHugo) serveDraft(w http.ResponseWriter, r *http.Request) (int, er } r.URL.Path = page.RelPermalink() - http.FileServer(http.Dir(docref.tmpdir)).ServeHTTP(w, r) + http.FileServer(aferoHTTP{afero.NewBasePathFs(docref.tmpfs, "public")}).ServeHTTP(w, r) return 200, nil } + +func printTree(fs afero.Fs) { + const tab = " " + + afero.Walk(fs, "/", filepath.WalkFunc(func(path string, info os.FileInfo, err error) error { + if path == "/" { + return nil + } + if filepath.Base(path) == ".git" { + return filepath.SkipDir + } + + if err != nil && !os.IsNotExist(err) { + fmt.Println(err) + return err + } else if err != nil { + return nil + } + + nIndent := len(strings.Split(path, string(filepath.Separator))) - 1 + indent := strings.Repeat(tab, nIndent) + + if info.IsDir() { + fmt.Printf("%s├ %s\n", indent, info.Name()) + nIndent++ + } else { + fmt.Printf("%s| %s\t%d\n", indent, info.Name(), info.Size()) + } + + return nil + })) +} + +type aferoHTTP struct { + afero.Fs +} + +func (a aferoHTTP) Open(name string) (http.File, error) { + af, err := a.Fs.Open(name) + return af, err +} diff --git a/hugo.go b/hugo.go index 36c06cd..46f253a 100644 --- a/hugo.go +++ b/hugo.go @@ -2,13 +2,21 @@ package caddyhugo import ( "fmt" - "io/ioutil" - "os" - "os/exec" + "time" + + "github.com/gohugoio/hugo/deps" + "github.com/gohugoio/hugo/hugofs" + "github.com/gohugoio/hugo/hugolib" + "github.com/spf13/afero" "git.stephensearles.com/stephen/idleshut" ) +const ( + IdleWebsocketTimeout = 10 * time.Minute + WebsocketFileTicker = 1 * time.Second +) + type HugoInteractor interface { Render(srcdir, workdir string) HugoRenderer } @@ -19,48 +27,52 @@ type HugoRenderer interface { Stop() error } -func HugoCmdProcessConfig(ch *CaddyHugo, es *editSession, touchFn func()) idleshut.Config { - cmd := exec.Command("hugo", "--watch", "-D", "-d", es.tmpdir) - cmd.Dir = es.tmpdir +func HugoCmdProcessConfig(ch *CaddyHugo, es *editSession, touchFn func()) (idleshut.Config, error) { + + printTree(es.tmpfs) + var err error + hugoCfg := &deps.DepsCfg{Fs: hugofs.NewFrom(es.tmpfs, ch.HugoCfg.Cfg)} + hugoCfg.Cfg, err = hugolib.LoadConfig(es.tmpfs, "/", "config.toml") + if err != nil { + return idleshut.Config{}, fmt.Errorf("caddy-hugo: loading site configuration: %v", err) + } + + hugoCfg.Cfg.Set("workingdir", "/") + hugoSites, err := hugolib.NewHugoSites(*hugoCfg) + if err != nil { + return idleshut.Config{}, fmt.Errorf("caddy-hugo: initializing site: %v", err) + } + + err = hugoSites.Build(hugolib.BuildCfg{ResetState: true}) + if err != nil { + // TODO better + return idleshut.Config{}, fmt.Errorf("caddy-hugo: building site: %v", err) + } return idleshut.Config{ TickDuration: WebsocketFileTicker, MaxIdleTicks: uint(IdleWebsocketTimeout/WebsocketFileTicker) + 1, - Start: func() error { - - err := cmd.Start() - if err != nil { - return fmt.Errorf("error starting hugo: %v", err) - } - - return nil - }, Stop: func() error { - if cmd == nil { - return nil - } - - err := cmd.Process.Signal(os.Interrupt) - if err != nil { - return fmt.Errorf("error signalling hugo to stop: %v", err) - } - err = cmd.Wait() - if err != nil { - return fmt.Errorf("error waiting for hugo to stop: %v", err) - return err - } - es.doc.Close() - os.RemoveAll(es.tmpdir) delete(ch.docs, es.filename) - return nil }, TickError: func(err error) { fmt.Println("error processing draft:", err) }, Tick: func() error { - return ioutil.WriteFile(es.filename, []byte(es.doc.Contents()), 0644) + fmt.Println("TICK") + err := afero.WriteFile(es.tmpfs, es.filename, []byte(es.doc.Contents()), 0644) + if err != nil { + return err + } + + err = hugoSites.Build(hugolib.BuildCfg{ResetState: true}) + if err != nil { + return err + } + + return nil }, - } + }, nil }