github.com/mattevans/edward@v1.9.2/runner/watch.go (about)

     1  package runner
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  
     8  	"github.com/mattevans/edward/home"
     9  
    10  	"github.com/pkg/errors"
    11  	"github.com/mattevans/edward/builder"
    12  	"github.com/mattevans/edward/services"
    13  	"github.com/mattevans/edward/tracker"
    14  	fsnotify "gopkg.in/fsnotify.v1"
    15  )
    16  
    17  // BeginWatch starts auto-restart watches for the provided services. The function returned will close the
    18  // watcher.
    19  func BeginWatch(dirConfig *home.EdwardConfiguration, service services.ServiceOrGroup, restart func() error) (func(), error) {
    20  	watches, err := service.Watch()
    21  	if err != nil {
    22  		return nil, errors.WithStack(err)
    23  	}
    24  	if len(watches) == 0 {
    25  		return nil, nil
    26  	}
    27  
    28  	var watchers []*fsnotify.Watcher
    29  	for _, watch := range watches {
    30  		watcher, err := startWatch(dirConfig, &watch, restart)
    31  		if err != nil {
    32  			return nil, errors.WithStack(err)
    33  		}
    34  		watchers = append(watchers, watcher)
    35  	}
    36  
    37  	closeAll := func() {
    38  		log.Printf("Closing watchers")
    39  		for _, watch := range watchers {
    40  			watch.Close()
    41  		}
    42  	}
    43  	return closeAll, nil
    44  }
    45  
    46  func startWatch(dirConfig *home.EdwardConfiguration, watch *services.ServiceWatch, restart func() error) (*fsnotify.Watcher, error) {
    47  	watcher, err := fsnotify.NewWatcher()
    48  	if err != nil {
    49  		return nil, errors.WithStack(err)
    50  	}
    51  
    52  	go func() {
    53  		for event := range watcher.Events {
    54  			if event.Op == fsnotify.Write {
    55  				var wasExcluded bool
    56  				for _, excluded := range watch.ExcludedPaths {
    57  					if strings.HasPrefix(event.Name, excluded) {
    58  						log.Printf("File is under excluded path: %v\n", excluded)
    59  						wasExcluded = true
    60  						break
    61  					}
    62  				}
    63  
    64  				if wasExcluded {
    65  					continue
    66  				}
    67  				fmt.Printf("Rebuilding %v\n", watch.Service.GetName())
    68  				err = rebuildService(dirConfig, watch.Service, restart)
    69  				if err != nil {
    70  					log.Printf("Could not rebuild %v: %v\n", watch.Service.GetName(), err)
    71  				}
    72  			}
    73  		}
    74  		log.Printf("No more events in watcher")
    75  	}()
    76  
    77  	for _, dir := range watch.IncludedPaths {
    78  		log.Printf("Adding: %s\n", dir)
    79  		err = watcher.Add(dir)
    80  		if err != nil {
    81  			log.Printf("%v\n", err)
    82  			watcher.Close()
    83  			return nil, errors.WithStack(err)
    84  		}
    85  	}
    86  	return watcher, nil
    87  }
    88  
    89  func rebuildService(dirConfig *home.EdwardConfiguration, service *services.ServiceConfig, restart func() error) error {
    90  	b := builder.New(services.OperationConfig{}, services.ContextOverride{})
    91  	err := b.BuildWithTracker(dirConfig, tracker.NewTask(func(updatedTask tracker.Task) {}), service, true)
    92  	if err != nil {
    93  		return fmt.Errorf("build failed: %v", err)
    94  	}
    95  	log.Printf("Build succeeded\n")
    96  	err = restart()
    97  	if err != nil {
    98  		return errors.WithStack(err)
    99  	}
   100  	return nil
   101  }