From 8411fb4a3acebe46acaf7a2ff6c4e58018737d65 Mon Sep 17 00:00:00 2001 From: Alan Pearce Date: Fri, 3 May 2024 00:10:23 +0200 Subject: feat: watch and live reload in development --- config.toml | 7 +---- go.mod | 1 + go.sum | 2 ++ gomod2nix.toml | 3 ++ internal/server/dev.go | 71 +++++++++++++++++++++++++++++++++++++++++++++++ internal/server/server.go | 24 ++++++++++++++-- justfile | 2 +- 7 files changed, 100 insertions(+), 10 deletions(-) create mode 100644 internal/server/dev.go diff --git a/config.toml b/config.toml index cdbe7d1..8c66184 100644 --- a/config.toml +++ b/config.toml @@ -2,19 +2,14 @@ base_url = "https://searchix.alanpearce.eu" [content-security-policy] default-src = [ - "'none'", + "'self'", ] image-src = [ - "'self'", "http://gc.zgo.at", ] script-src = [ - "'self'", "http://gc.zgo.at", ] -style-src = [ - "'unsafe-inline'", -] require-trusted-types-for = [ "'script'", ] diff --git a/go.mod b/go.mod index ac4a28b..c656f22 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.22.2 require ( github.com/ardanlabs/conf/v3 v3.1.7 github.com/crewjam/csp v0.0.2 + github.com/fsnotify/fsnotify v1.7.0 github.com/getsentry/sentry-go v0.27.0 github.com/osdevisnot/sorvor v0.4.4 github.com/pelletier/go-toml/v2 v2.2.1 diff --git a/go.sum b/go.sum index 0869ec4..082f7c5 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/evanw/esbuild v0.14.11/go.mod h1:GG+zjdi59yh3ehDn4ZWfPcATxjPDUH53iU4ZJbp7dkY= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= diff --git a/gomod2nix.toml b/gomod2nix.toml index 436a69c..c808744 100644 --- a/gomod2nix.toml +++ b/gomod2nix.toml @@ -7,6 +7,9 @@ schema = 3 [mod."github.com/crewjam/csp"] version = "v0.0.2" hash = "sha256-4vlGmDdQjPiXmueCV51fJH/hRcG8eqhCi9TENCXjzfA=" + [mod."github.com/fsnotify/fsnotify"] + version = "v1.7.0" + hash = "sha256-MdT2rQyQHspPJcx6n9ozkLbsktIOJutOqDuKpNAtoZY=" [mod."github.com/getsentry/sentry-go"] version = "v0.27.0" hash = "sha256-PTkTzVNogqFA/5rc6INLY6RxK5uR1AoJFOO+pOPdE7Q=" diff --git a/internal/server/dev.go b/internal/server/dev.go new file mode 100644 index 0000000..52ab29b --- /dev/null +++ b/internal/server/dev.go @@ -0,0 +1,71 @@ +package server + +import ( + "fmt" + "io/fs" + "log/slog" + "os" + "path/filepath" + "time" + + "github.com/fsnotify/fsnotify" + "github.com/pkg/errors" +) + +type FileWatcher struct { + *fsnotify.Watcher +} + +func NewFileWatcher() (*FileWatcher, error) { + watcher, err := fsnotify.NewWatcher() + if err != nil { + return nil, errors.WithMessage(err, "could not create watcher") + } + + return &FileWatcher{watcher}, nil +} + +func (watcher FileWatcher) AddRecursive(from string) error { + slog.Debug(fmt.Sprintf("watching files under %s", from)) + err := filepath.WalkDir(from, func(path string, entry fs.DirEntry, err error) error { + if err != nil { + return errors.WithMessagef(err, "could not walk directory %s", path) + } + if entry.IsDir() { + slog.Debug(fmt.Sprintf("adding directory %s to watcher", path)) + if err = watcher.Add(path); err != nil { + return errors.WithMessagef(err, "could not add directory %s to watcher", path) + } + } + + return nil + }) + + return errors.WithMessage(err, "error walking directory tree") +} + +func (watcher FileWatcher) Start(callback func()) { + for { + select { + case event := <-watcher.Events: + slog.Debug(fmt.Sprintf("event received: %s", event)) + if event.Has(fsnotify.Create) || event.Has(fsnotify.Rename) { + f, err := os.Stat(event.Name) + if err != nil { + slog.Error(fmt.Sprintf("error handling %s event: %v", event.Op.String(), err)) + } else if f.IsDir() { + err = watcher.Add(event.Name) + if err != nil { + slog.Error(fmt.Sprintf("error adding new folder to watcher: %v", err)) + } + } + } + if event.Has(fsnotify.Rename) || event.Has(fsnotify.Write) { + callback() + time.Sleep(500 * time.Millisecond) + } + case err := <-watcher.Errors: + slog.Error(fmt.Sprintf("error in watcher: %v", err)) + } + } +} diff --git a/internal/server/server.go b/internal/server/server.go index fc56d47..04ee94c 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -2,6 +2,7 @@ package server import ( "context" + "fmt" "html/template" "io" "log" @@ -87,7 +88,8 @@ func New(runtimeConfig *Config) (*Server, error) { Repanic: true, }) - tpl := template.Must(template.ParseGlob(path.Join("frontend", "templates", "*.tmpl"))) + templatePaths := path.Join("frontend", "templates", "*.tmpl") + tpl := template.Must(template.ParseGlob(templatePaths)) top := http.NewServeMux() mux := http.NewServeMux() @@ -108,8 +110,24 @@ func New(runtimeConfig *Config) (*Server, error) { applyDevModeOverrides(config) liveReload := livereload.New() liveReload.Start() - mux.Handle("/livereload", liveReload) - // liveReload.Reload() + top.Handle("/livereload", liveReload) + fw, err := NewFileWatcher() + if err != nil { + return nil, errors.WithMessage(err, "could not create file watcher") + } + err = fw.AddRecursive("frontend") + if err != nil { + return nil, errors.WithMessage(err, "could not add directory to file watcher") + } + go fw.Start(func() { + t, err := template.ParseGlob(path.Join("frontend", "templates", "*.tmpl")) + if err != nil { + slog.Error(fmt.Sprintf("could not parse template: %v", err)) + } else { + tpl = t + liveReload.Reload() + } + }) } var logWriter io.Writer diff --git a/justfile b/justfile index d78325f..45a5f11 100644 --- a/justfile +++ b/justfile @@ -16,4 +16,4 @@ precommit: nix-build -A pre-commit-check dev: - wgo run ./serve/ + wgo run ./serve/ --live -- cgit 1.4.1