This commit brings a greater experience for developers working on ZQDGR, and also fixes a number of bugs. The following bugs have been fixed: - Scripts that died in watch mode did not restart once changes were made - Created and deleted files did not cause a reload - script.Stop is now used in every place where we kill the process, this ensures that the process is *eventually* actually killed
164 lines
3.3 KiB
Go
164 lines
3.3 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"log/slog"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/bmatcuk/doublestar"
|
|
"github.com/fsnotify/fsnotify"
|
|
)
|
|
|
|
type globList []string
|
|
|
|
func (g *globList) Matches(value string) bool {
|
|
for _, v := range *g {
|
|
// if the pattern matches a filepath pattern
|
|
if match, err := filepath.Match(v, value); err != nil {
|
|
log.Fatalf("Bad pattern \"%s\": %s", v, err.Error())
|
|
} else if match {
|
|
return true
|
|
}
|
|
|
|
// or if the path starts with the pattern
|
|
if strings.HasSuffix(value, v) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func matchesPattern(pattern []string, path string) bool {
|
|
for _, p := range pattern {
|
|
slog.Debug("checking path against pattern", "pattern", p, "path", path)
|
|
if matched, _ := doublestar.Match(p, path); matched {
|
|
slog.Debug("path matches pattern", "pattern", p, "path", path)
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func pathShouldBeTracked(cfg *WatcherConfig, path string) bool {
|
|
base := filepath.Dir(path)
|
|
|
|
slog.Debug("checking file against path", "path", path, "base", base)
|
|
|
|
if cfg.excludedGlobs.Matches(base) {
|
|
slog.Debug("file is excluded", "path", path)
|
|
return false
|
|
}
|
|
|
|
return matchesPattern(cfg.pattern, path)
|
|
}
|
|
|
|
func pathMatches(cfg *WatcherConfig, path string) bool {
|
|
return matchesPattern(cfg.pattern, path)
|
|
}
|
|
|
|
type WatcherConfig struct {
|
|
excludedGlobs globList
|
|
pattern globList
|
|
}
|
|
|
|
type FileWatcher interface {
|
|
Close() error
|
|
AddFiles() error
|
|
AddFile(path string) error
|
|
add(path string) error
|
|
getConfig() *WatcherConfig
|
|
}
|
|
|
|
type NotifyWatcher struct {
|
|
watcher *fsnotify.Watcher
|
|
cfg *WatcherConfig
|
|
}
|
|
|
|
func (n NotifyWatcher) Close() error {
|
|
return n.watcher.Close()
|
|
}
|
|
|
|
func (n NotifyWatcher) AddFile(path string) error {
|
|
slog.Debug("manually adding file", "file", path)
|
|
|
|
return n.add(path)
|
|
}
|
|
|
|
func (n NotifyWatcher) AddFiles() error {
|
|
return addFiles(n)
|
|
}
|
|
|
|
func (n NotifyWatcher) add(path string) error {
|
|
return n.watcher.Add(path)
|
|
}
|
|
|
|
func (n NotifyWatcher) getConfig() *WatcherConfig {
|
|
return n.cfg
|
|
}
|
|
|
|
func NewWatcher(cfg *WatcherConfig) (FileWatcher, error) {
|
|
if cfg == nil {
|
|
err := errors.New("no config specified")
|
|
return nil, err
|
|
}
|
|
|
|
w, err := fsnotify.NewWatcher()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return NotifyWatcher{
|
|
watcher: w,
|
|
cfg: cfg,
|
|
}, nil
|
|
}
|
|
|
|
func addFiles(fw FileWatcher) error {
|
|
cfg := fw.getConfig()
|
|
for _, pattern := range cfg.pattern {
|
|
slog.Debug("adding pattern", "pattern", pattern)
|
|
|
|
matches, err := doublestar.Glob(pattern)
|
|
if err != nil {
|
|
log.Fatalf("Bad pattern \"%s\": %s", pattern, err.Error())
|
|
}
|
|
|
|
trackedDirs := make(map[string]bool)
|
|
for _, match := range matches {
|
|
base := filepath.Dir(match)
|
|
// this allows us to track file creations and deletions
|
|
if !trackedDirs[base] {
|
|
if cfg.excludedGlobs.Matches(base) {
|
|
slog.Debug("directory is excluded", "file", match)
|
|
continue
|
|
}
|
|
|
|
trackedDirs[base] = true
|
|
|
|
slog.Debug("adding directory", "dir", base)
|
|
|
|
if err := fw.add(base); err != nil {
|
|
return fmt.Errorf("FileWatcher.Add(): %v", err)
|
|
}
|
|
}
|
|
|
|
slog.Debug("adding file", "file", match)
|
|
|
|
if pathShouldBeTracked(cfg, match) {
|
|
slog.Debug("path should be tracked", "file", match)
|
|
|
|
if err := fw.add(match); err != nil {
|
|
return fmt.Errorf("FileWatcher.Add(): %v", err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|