draft overhaul #8

Merged
stephen merged 3 commits from draft into master 7 years ago
  1. 6
      caddyhugo.go
  2. 42
      client.go
  3. 5
      deltas.go
  4. 21
      http.go
  5. 73
      hugo.go

@ -75,9 +75,13 @@ func (ch *CaddyHugo) docFilename(orig string) string {
// Publish really renders new content into the public directory // Publish really renders new content into the public directory
func (ch *CaddyHugo) Publish() error { func (ch *CaddyHugo) Publish() error {
err := ch.persistAllEdits()
if err != nil {
return err
}
cmd := exec.Command("hugo") cmd := exec.Command("hugo")
cmd.Dir = ch.Dir cmd.Dir = ch.Dir
_, err := cmd.CombinedOutput() _, err = cmd.CombinedOutput()
if err != nil { if err != nil {
return err return err
} }

@ -1,14 +1,14 @@
package caddyhugo package caddyhugo
import ( import (
"encoding/base64"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"os"
"path" "path"
"sync" "sync"
"github.com/spf13/afero"
"git.stephensearles.com/stephen/acedoc" "git.stephensearles.com/stephen/acedoc"
"git.stephensearles.com/stephen/idleshut" "git.stephensearles.com/stephen/idleshut"
) )
@ -18,7 +18,7 @@ type editSession struct {
docname string docname string
filename string filename string
doc *acedoc.Document doc *acedoc.Document
tmpdir string tmpfs afero.Fs
mtx sync.Mutex mtx sync.Mutex
} }
@ -38,14 +38,11 @@ func (ch *CaddyHugo) newEditSession(docName string) (*editSession, error) {
return nil, err return nil, err
} }
draftPrefix := fmt.Sprintf("draft-%s", base64.RawURLEncoding.EncodeToString([]byte(docName)))
tmpdir := path.Join(os.TempDir(), draftPrefix)
es := &editSession{ es := &editSession{
docname: docName, docname: docName,
filename: filename, filename: filename,
doc: acedoc.NewString(string(contents)), doc: acedoc.NewString(string(contents)),
tmpdir: tmpdir, tmpfs: afero.NewCopyOnWriteFs(afero.NewOsFs(), afero.NewMemMapFs()),
} }
err = es.doc.LogToFile(path.Join(ch.Dir, "logs", docName)) err = es.doc.LogToFile(path.Join(ch.Dir, "logs", docName))
@ -56,7 +53,10 @@ func (ch *CaddyHugo) newEditSession(docName string) (*editSession, error) {
ch.docs[filename] = es ch.docs[filename] = es
ch.renderDraft(es) err = ch.renderDraft(es)
if err != nil {
return nil, err
}
return es, nil return es, nil
} }
@ -70,8 +70,32 @@ func (ch *CaddyHugo) renderDraft(es *editSession) error {
} }
} }
proc = idleshut.New(HugoCmdProcessConfig(ch, es, f)) cfg, err := HugoInternalProcessConfig(ch, es, f)
if err != nil {
return fmt.Errorf("rendering draft: %v", err)
}
proc = idleshut.New(cfg)
return proc.Start()
}
func (ch *CaddyHugo) persistAllEdits() error {
ch.mtx.Lock()
defer ch.mtx.Unlock()
for _, es := range ch.docs {
err := ch.persistEditsForSession(es)
if err != nil {
return err
}
}
return nil
}
func (ch *CaddyHugo) persistEditsForSession(es *editSession) error {
err := afero.WriteFile(afero.NewOsFs(), es.filename, []byte(es.doc.Contents()), 0644)
if err != nil {
return err
}
return nil return nil
} }

@ -16,11 +16,6 @@ type DeltaConn interface {
WriteJSON(v interface{}) error WriteJSON(v interface{}) error
} }
const (
IdleWebsocketTimeout = 10 * time.Minute
WebsocketFileTicker = 1 * time.Second
)
func (ch *CaddyHugo) ObserveLTime(ltime uint64) uint64 { func (ch *CaddyHugo) ObserveLTime(ltime uint64) uint64 {
ch.mtx.Lock() ch.mtx.Lock()

@ -5,6 +5,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"os"
"path" "path"
"path/filepath" "path/filepath"
"strings" "strings"
@ -12,6 +13,7 @@ import (
"git.stephensearles.com/stephen/caddy-hugo2/assets" "git.stephensearles.com/stephen/caddy-hugo2/assets"
"github.com/mholt/caddy" "github.com/mholt/caddy"
"github.com/mholt/caddy/caddyhttp/httpserver" "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) { 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) r.URL.Path = strings.ToLower(r.URL.Path)
prefix := "/hugo/draft/" + encoded 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)) page := ch.HugoSites.GetContentPage(ch.docFilename(name))
if page == nil { if page == nil {
@ -208,7 +210,22 @@ func (ch *CaddyHugo) serveDraft(w http.ResponseWriter, r *http.Request) (int, er
} }
r.URL.Path = page.RelPermalink() r.URL.Path = page.RelPermalink()
http.FileServer(http.Dir(docref.tmpdir)).ServeHTTP(w, r) http.FileServer(aferoHTTP{afero.NewBasePathFs(docref.tmpfs, path.Join(ch.Dir, "public"))}).ServeHTTP(w, r)
return 200, nil return 200, nil
} }
type aferoHTTP struct {
afero.Fs
}
func (a aferoHTTP) Open(name string) (http.File, error) {
af, err := a.Fs.Open(name)
if os.IsExist(err) {
err = os.ErrExist
}
if os.IsNotExist(err) {
err = os.ErrNotExist
}
return af, err
}

@ -2,13 +2,22 @@ package caddyhugo
import ( import (
"fmt" "fmt"
"io/ioutil" "path"
"os" "time"
"os/exec"
"github.com/gohugoio/hugo/deps"
"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/hugolib"
"github.com/spf13/afero"
"git.stephensearles.com/stephen/idleshut" "git.stephensearles.com/stephen/idleshut"
) )
const (
IdleWebsocketTimeout = 10 * time.Minute
WebsocketFileTicker = 1 * time.Second
)
type HugoInteractor interface { type HugoInteractor interface {
Render(srcdir, workdir string) HugoRenderer Render(srcdir, workdir string) HugoRenderer
} }
@ -19,39 +28,37 @@ type HugoRenderer interface {
Stop() error Stop() error
} }
func HugoCmdProcessConfig(ch *CaddyHugo, es *editSession, touchFn func()) idleshut.Config { func HugoInternalProcessConfig(ch *CaddyHugo, es *editSession, touchFn func()) (idleshut.Config, error) {
cmd := exec.Command("hugo", "--watch", "-D", "-d", es.tmpdir)
cmd.Dir = es.tmpdir
return idleshut.Config{
TickDuration: WebsocketFileTicker,
MaxIdleTicks: uint(IdleWebsocketTimeout/WebsocketFileTicker) + 1,
Start: func() error {
err := cmd.Start() var err error
hugoCfg := &deps.DepsCfg{Fs: hugofs.NewFrom(es.tmpfs, ch.HugoCfg.Cfg)}
fmt.Println(ch.Dir)
hugoCfg.Cfg, err = hugolib.LoadConfig(es.tmpfs, "", path.Join(ch.Dir, "config.toml"))
if err != nil { if err != nil {
return fmt.Errorf("error starting hugo: %v", err) return idleshut.Config{}, fmt.Errorf("caddy-hugo: loading site configuration: %v", err)
}
return nil
},
Stop: func() error {
if cmd == nil {
return nil
} }
err := cmd.Process.Signal(os.Interrupt) hugoCfg.Cfg.Set("workingDir", ch.Dir)
hugoSites, err := hugolib.NewHugoSites(*hugoCfg)
if err != nil { if err != nil {
return fmt.Errorf("error signalling hugo to stop: %v", err) return idleshut.Config{}, fmt.Errorf("caddy-hugo: initializing site: %v", err)
} }
err = cmd.Wait()
err = hugoSites.Build(hugolib.BuildCfg{ResetState: true})
if err != nil { if err != nil {
return fmt.Errorf("error waiting for hugo to stop: %v", err) return idleshut.Config{}, fmt.Errorf("caddy-hugo: building site: %v", err)
return err
} }
return idleshut.Config{
TickDuration: WebsocketFileTicker,
MaxIdleTicks: uint(IdleWebsocketTimeout/WebsocketFileTicker) + 1,
Stop: func() error {
ch.persistEditsForSession(es)
es.doc.Close() es.doc.Close()
os.RemoveAll(es.tmpdir)
ch.mtx.Lock()
defer ch.mtx.Unlock()
delete(ch.docs, es.filename) delete(ch.docs, es.filename)
return nil return nil
@ -60,7 +67,17 @@ func HugoCmdProcessConfig(ch *CaddyHugo, es *editSession, touchFn func()) idlesh
fmt.Println("error processing draft:", err) fmt.Println("error processing draft:", err)
}, },
Tick: func() error { Tick: func() error {
return ioutil.WriteFile(es.filename, []byte(es.doc.Contents()), 0644) 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
} }

Loading…
Cancel
Save