Skip to content

Commit

Permalink
misc: use context and error group to properly handle watching
Browse files Browse the repository at this point in the history
  • Loading branch information
nervo committed May 25, 2024
1 parent 2851e71 commit 67dfc70
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 134 deletions.
55 changes: 24 additions & 31 deletions cmd/watch/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"manala/app/api"
"manala/internal/notifier"
"manala/internal/ui"
"os"
"os/signal"
"path/filepath"
"syscall"
Expand Down Expand Up @@ -72,40 +71,34 @@ func run(ctx context.Context, log *slog.Logger, api *api.Api, out ui.Output, not
return err
}

done := make(chan os.Signal, 1)
signal.Notify(done, syscall.SIGINT, syscall.SIGTERM)
ctx, stop := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM)
defer stop()

// Watch project
log.Info("watching project…")
if err = watch(log, project, all, func(project app.Project) app.Project {
// Load project
log.Info("loading project…")
if project, err = projectLoader.Load(project.Dir()); err != nil {
out.Error(err)
if notify {
notifier.Error(err)
return NewWatcher(log, all).
Watch(ctx, project, func(project app.Project) app.Project {
// Load project
log.Info("loading project…")
if project, err = projectLoader.Load(project.Dir()); err != nil {
out.Error(err)
if notify {
notifier.Error(err)
}
return nil
}
return nil
}

// Sync project
log.Info("syncing project…")
if err = api.NewProjectSyncer().Sync(project); err != nil {
out.Error(err)
if notify {
notifier.Error(err)
}
return project
}

if notify {
notifier.Message("Project synced")
}

return project
}, done); err != nil {
return err
}
// Sync project
log.Info("syncing project…")
if err = api.NewProjectSyncer().Sync(project); err != nil {
out.Error(err)
if notify {
notifier.Error(err)
}
} else {
notifier.Message("Project synced")
}

return nil
return project
})
}
103 changes: 0 additions & 103 deletions cmd/watch/watch.go

This file was deleted.

121 changes: 121 additions & 0 deletions cmd/watch/watcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package watch

import (
"context"
"github.com/fsnotify/fsnotify"
"golang.org/x/sync/errgroup"
"log/slog"
"manala/app"
"slices"
)

func NewWatcher(log *slog.Logger, recipe bool) *Watcher {
return &Watcher{
log: log,
recipe: recipe,
}
}

type Watcher struct {
log *slog.Logger
recipe bool
}

func (watcher *Watcher) Watch(ctx context.Context, project app.Project, fn func(project app.Project) app.Project) error {
// Create fs watcher
fsWatcher, err := fsnotify.NewWatcher()
if err != nil {
return err
}

//goland:noinspection GoUnhandledErrorResult
defer fsWatcher.Close()

group, ctx := errgroup.WithContext(ctx)
group.Go(func() error {
for {
select {
case <-ctx.Done():
return nil
case err := <-fsWatcher.Errors:
return err
case event, ok := <-fsWatcher.Events:
if !ok {
return nil
}

// Ignore chmod events & empty events
if event.Has(fsnotify.Chmod) || event.Name == "" {
watcher.log.Debug("ignore file event",
"path", event.Name,
"operation", event.Op,
)
break
}

watcher.log.Debug("file event",
"path", event.Name,
"operation", event.Op,
)

// Callback
if p := fn(project); p != nil {
project = p
}

if err := watcher.syncWatches(fsWatcher, project); err != nil {
return err
}
}
}
})

if err := watcher.syncWatches(fsWatcher, project); err != nil {
return err
}

return group.Wait()
}

func (watcher *Watcher) syncWatches(fsWatcher *fsnotify.Watcher, project app.Project) error {
// Start with project watches
watches, err := project.Watches()
if err != nil {
return err
}

// Eventually add recipe watches
if watcher.recipe {
recipeWatches, err := project.Recipe().Watches()
if err != nil {
return err
}

watches = append(watches, recipeWatches...)
}

// Get current watches
currentWatches := fsWatcher.WatchList()

// Add only new watches (not presents in current ones)
for _, path := range watches {
if !slices.Contains(currentWatches, path) {
watcher.log.Debug("add watch", "path", path)
if err := fsWatcher.Add(path); err != nil {
return err
}
}
}

// Remove only current watches
for _, path := range currentWatches {
if !slices.Contains(watches, path) {
watcher.log.Debug("remove watch", "path", path)
if err := fsWatcher.Remove(path); err != nil {
return err
}
}
}

return nil
}

0 comments on commit 67dfc70

Please sign in to comment.