Better Watcher; butt it restarts 1000 times on one file change

This commit is contained in:
Simon Martens
2024-11-15 13:59:44 +01:00
parent 90eac53ef2
commit 20a660077f
3 changed files with 103 additions and 50 deletions

View File

@@ -1,38 +1,93 @@
package helpers package helpers
import ( import (
"errors"
"io/fs"
"log" "log"
"path/filepath"
"sync"
"time"
"github.com/fsnotify/fsnotify" "github.com/fsnotify/fsnotify"
) )
var NotInitializedError = errors.New("FileWatcher not initialized")
var NoWatchFunctionError = errors.New("No watch function provided")
type IFileWatcher interface { type IFileWatcher interface {
GetEvents() chan string RecursiveDir(path string) error
Dir(path string) error
Append(fn func(string))
Prepend(fn func(string))
Watch() error
Close()
Restart()
} }
type FileWatcher struct { type FileWatcher struct {
path []string mu sync.Mutex
events chan string wf []func(string)
paths []string
watcher *fsnotify.Watcher watcher *fsnotify.Watcher
} }
func NewFileWatcher(path []string) (*FileWatcher, error) { func NewFileWatcher() (*FileWatcher, error) {
fw := &FileWatcher{path: path, events: make(chan string, 48)}
err := fw.Watch(path) fw := &FileWatcher{mu: sync.Mutex{}}
if err != nil { fw.Watch()
return nil, err
}
return fw, nil return fw, nil
} }
func (fw *FileWatcher) Watch(paths []string) error { func (fw *FileWatcher) RecursiveDir(path string) error {
fw.events = make(chan string, 48) err := filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error {
if d.IsDir() {
err := fw.Dir(path)
if err != nil {
return err
}
}
return nil
})
return err
}
func (fw *FileWatcher) Dir(path string) error {
fw.mu.Lock()
defer fw.mu.Unlock()
if fw.watcher != nil {
err := fw.watcher.Add(path)
if err != nil {
return err
}
}
fw.paths = append(fw.paths, path)
return nil
}
func (fw *FileWatcher) Append(fn func(string)) {
fw.mu.Lock()
defer fw.mu.Unlock()
fw.wf = append(fw.wf, fn)
}
func (fw *FileWatcher) Prepend(fn func(string)) {
fw.mu.Lock()
defer fw.mu.Unlock()
fw.wf = append([]func(string){fn}, fw.wf...)
}
func (fw *FileWatcher) Watch() error {
watcher, err := fsnotify.NewWatcher() watcher, err := fsnotify.NewWatcher()
if err != nil { if err != nil {
return err return err
} }
fw.mu.Lock()
fw.watcher = watcher fw.watcher = watcher
fw.mu.Unlock()
// Start listening for events. // Start listening for events.
go func() { go func() {
@@ -44,7 +99,12 @@ func (fw *FileWatcher) Watch(paths []string) error {
} }
log.Println("event:", event) log.Println("event:", event)
if !event.Has(fsnotify.Chmod) { if !event.Has(fsnotify.Chmod) {
fw.events <- event.Name time.Sleep(50 * time.Millisecond)
fw.mu.Lock()
for _, wf := range fw.wf {
wf(event.Name)
}
fw.mu.Unlock()
} }
case err, ok := <-watcher.Errors: case err, ok := <-watcher.Errors:
if !ok { if !ok {
@@ -55,21 +115,23 @@ func (fw *FileWatcher) Watch(paths []string) error {
} }
}() }()
for _, path := range paths {
err = watcher.Add(path)
if err != nil {
return err
}
}
return nil return nil
} }
func (fw *FileWatcher) GetEvents() chan string { // INFO: After closing the watcher, you can't use it anymore.
return fw.events // Also, after a restart, you need to re add the paths
func (fw *FileWatcher) Close() {
fw.mu.Lock()
defer fw.mu.Unlock()
if fw.watcher != nil {
fw.watcher.Close()
}
fw.watcher = nil
fw.paths = nil
} }
func (fw *FileWatcher) Close() { func (fw *FileWatcher) Restart() {
fw.watcher.Close() fw.Close()
close(fw.events) fw.Watch()
} }

View File

@@ -2,8 +2,7 @@ package server
import ( import (
"fmt" "fmt"
"io/fs" "log"
"path/filepath"
"sync" "sync"
"time" "time"
@@ -47,6 +46,8 @@ type Server struct {
running chan bool running chan bool
shutdown *sync.WaitGroup shutdown *sync.WaitGroup
cache *memory.Storage cache *memory.Storage
watcher *helpers.FileWatcher
} }
func Start(k *app.KGPZ, c *providers.ConfigProvider) *Server { func Start(k *app.KGPZ, c *providers.ConfigProvider) *Server {
@@ -55,33 +56,23 @@ func Start(k *app.KGPZ, c *providers.ConfigProvider) *Server {
} }
} }
// INFO: this is a hacky way to add watchers to the server, which will restart the server if the files change func (s *Server) Watcher() error {
// It is very rudimentary and just restarts everything watcher, err := helpers.NewFileWatcher()
// TODO: send a reload on a websocket s.watcher = watcher
func (e *Server) AddWatchers(paths []string) error { s.watcher.Append(func(path string) {
var dirs []string log.Println("Restarting server")
for _, path := range paths { s.Restart()
// Get all subdirectories for paths })
filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error {
if d.IsDir() {
dirs = append(dirs, path)
}
return nil
})
}
watcher, err := helpers.NewFileWatcher(dirs) err = s.watcher.RecursiveDir(ROUTES_FILEPATH)
if err != nil { if err != nil {
return err return err
} }
go func() { err = s.watcher.RecursiveDir(LAYOUT_FILEPATH)
w := watcher.GetEvents() if err != nil {
<-w return err
watcher.Close() }
time.Sleep(200 * time.Millisecond)
e.Restart()
}()
return nil return nil
} }
@@ -149,7 +140,7 @@ func (s *Server) Start() {
s.runner(srv) s.runner(srv)
if s.Config.Debug { if s.Config.Debug {
err := s.AddWatchers([]string{ROUTES_FILEPATH, LAYOUT_FILEPATH}) err := s.Watcher()
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
} }

View File

@@ -1,3 +1,3 @@
{{ define "body" }} {{ define "body" }}
<p>Changed again! Hello from body</p> <p>Change! Hello from body</p>
{{ end }} {{ end }}