mirror of
				https://github.com/gohugoio/hugo.git
				synced 2024-05-11 05:54:58 +00:00 
			
		
		
		
	Added batching behavior for page building.
Quite often file watcher gets many changes and each change triggered a build. One build per second should be sufficient. Also added tracking for new folders.
This commit is contained in:
		@@ -15,11 +15,11 @@ package commands
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/howeyc/fsnotify"
 | 
			
		||||
	"github.com/mostafah/fsync"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
	"github.com/spf13/hugo/hugolib"
 | 
			
		||||
	"github.com/spf13/hugo/utils"
 | 
			
		||||
	"github.com/spf13/hugo/watcher"
 | 
			
		||||
	"github.com/spf13/nitro"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
@@ -155,7 +155,7 @@ func buildSite(watching ...bool) (err error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewWatcher(port int) error {
 | 
			
		||||
	watcher, err := fsnotify.NewWatcher()
 | 
			
		||||
	watcher, err := watcher.New(1 * time.Second)
 | 
			
		||||
	var wg sync.WaitGroup
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -166,15 +166,56 @@ func NewWatcher(port int) error {
 | 
			
		||||
	defer watcher.Close()
 | 
			
		||||
 | 
			
		||||
	wg.Add(1)
 | 
			
		||||
 | 
			
		||||
	for _, d := range getDirList() {
 | 
			
		||||
		if d != "" {
 | 
			
		||||
			_ = watcher.Watch(d)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		for {
 | 
			
		||||
			select {
 | 
			
		||||
			case ev := <-watcher.Event:
 | 
			
		||||
			case evs := <-watcher.Event:
 | 
			
		||||
				if Verbose {
 | 
			
		||||
					fmt.Println(ev)
 | 
			
		||||
					fmt.Println(evs)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				static_changed := false
 | 
			
		||||
				dynamic_changed := false
 | 
			
		||||
 | 
			
		||||
				for _, ev := range evs {
 | 
			
		||||
					ext := filepath.Ext(ev.Name)
 | 
			
		||||
					istemp := strings.HasSuffix(ext, "~") || (ext == ".swp") || (ext == ".tmp")
 | 
			
		||||
					if istemp {
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
					// renames are always followed with Create/Modify
 | 
			
		||||
					if ev.IsRename() {
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					isstatic := strings.HasPrefix(ev.Name, Config.GetAbsPath(Config.StaticDir))
 | 
			
		||||
					static_changed = static_changed || isstatic
 | 
			
		||||
					dynamic_changed = dynamic_changed || !isstatic
 | 
			
		||||
 | 
			
		||||
					// add new directory to watch list
 | 
			
		||||
					if s, err := os.Stat(ev.Name); err == nil && s.Mode().IsDir() {
 | 
			
		||||
						if ev.IsCreate() {
 | 
			
		||||
							watcher.Watch(ev.Name)
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if static_changed {
 | 
			
		||||
					fmt.Println("Static file changed, syncing\n")
 | 
			
		||||
					utils.CheckErr(copyStatic(), fmt.Sprintf("Error copying static files to %s", Config.GetAbsPath(Config.PublishDir)))
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if dynamic_changed {
 | 
			
		||||
					fmt.Println("Change detected, rebuilding site\n")
 | 
			
		||||
					utils.StopOnErr(buildSite(true))
 | 
			
		||||
				}
 | 
			
		||||
				watchChange(ev)
 | 
			
		||||
				// TODO add newly created directories to the watch list
 | 
			
		||||
			case err := <-watcher.Error:
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					fmt.Println("error:", err)
 | 
			
		||||
@@ -183,12 +224,6 @@ func NewWatcher(port int) error {
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	for _, d := range getDirList() {
 | 
			
		||||
		if d != "" {
 | 
			
		||||
			_ = watcher.Watch(d)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if port > 0 {
 | 
			
		||||
		go serve(port)
 | 
			
		||||
	}
 | 
			
		||||
@@ -196,22 +231,3 @@ func NewWatcher(port int) error {
 | 
			
		||||
	wg.Wait()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func watchChange(ev *fsnotify.FileEvent) {
 | 
			
		||||
	ext := filepath.Ext(ev.Name)
 | 
			
		||||
	// ignore temp files
 | 
			
		||||
	istemp := strings.HasSuffix(ext, "~") || (ext == ".swp") || (ext == ".tmp")
 | 
			
		||||
	if istemp {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if strings.HasPrefix(ev.Name, Config.GetAbsPath(Config.StaticDir)) {
 | 
			
		||||
		fmt.Println("Static file changed, syncing\n")
 | 
			
		||||
		utils.CheckErr(copyStatic(), fmt.Sprintf("Error copying static files to %s", Config.GetAbsPath(Config.PublishDir)))
 | 
			
		||||
	} else {
 | 
			
		||||
		if !ev.IsRename() { // Rename is always accompanied by a create or modify
 | 
			
		||||
			fmt.Println("Change detected, rebuilding site\n")
 | 
			
		||||
			utils.StopOnErr(buildSite(true))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										56
									
								
								watcher/batcher.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								watcher/batcher.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
			
		||||
package watcher
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/howeyc/fsnotify"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Batcher struct {
 | 
			
		||||
	*fsnotify.Watcher
 | 
			
		||||
	interval time.Duration
 | 
			
		||||
	done     chan struct{}
 | 
			
		||||
 | 
			
		||||
	Event chan []*fsnotify.FileEvent // Events are returned on this channel
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(interval time.Duration) (*Batcher, error) {
 | 
			
		||||
	watcher, err := fsnotify.NewWatcher()
 | 
			
		||||
 | 
			
		||||
	batcher := &Batcher{}
 | 
			
		||||
	batcher.Watcher = watcher
 | 
			
		||||
	batcher.interval = interval
 | 
			
		||||
	batcher.done = make(chan struct{}, 1)
 | 
			
		||||
	batcher.Event = make(chan []*fsnotify.FileEvent, 1)
 | 
			
		||||
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		go batcher.run()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return batcher, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Batcher) run() {
 | 
			
		||||
	tick := time.Tick(b.interval)
 | 
			
		||||
	evs := make([]*fsnotify.FileEvent, 0)
 | 
			
		||||
OuterLoop:
 | 
			
		||||
	for {
 | 
			
		||||
		select {
 | 
			
		||||
		case ev := <-b.Watcher.Event:
 | 
			
		||||
			evs = append(evs, ev)
 | 
			
		||||
		case <-tick:
 | 
			
		||||
			if len(evs) == 0 {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			b.Event <- evs
 | 
			
		||||
			evs = make([]*fsnotify.FileEvent, 0)
 | 
			
		||||
		case <-b.done:
 | 
			
		||||
			break OuterLoop
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	close(b.done)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *Batcher) Close() {
 | 
			
		||||
	b.done <- struct{}{}
 | 
			
		||||
	b.Watcher.Close()
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user