// Run is the top-level runner for modd func Run(log termlog.TermLog, cnf *conf.Config, watchconf string, notifiers []notify.Notifier) (*conf.Config, error) { shellMethod := cnf.GetVariables()[shellVarName] if !shell.Has(shellMethod) { return nil, fmt.Errorf("No shell interface %q", shellMethod) } modchan := make(chan *watch.Mod, 1024) return runOnChan(modchan, func() {}, log, cnf, watchconf, notifiers) }
// PrepOnly runs all prep functions and exits func PrepOnly(log termlog.TermLog, cnf *conf.Config, notifiers []notify.Notifier) error { for _, b := range cnf.Blocks { err := RunPreps(b, cnf.GetVariables(), nil, log, notifiers) if err != nil { return err } } return nil }
// NewDaemonWorld creates a DaemonWorld func NewDaemonWorld(cnf *conf.Config, log termlog.TermLog) (*DaemonWorld, error) { daemonPens := make([]*DaemonPen, len(cnf.Blocks)) for i, b := range cnf.Blocks { d, err := NewDaemonPen(b, cnf.GetVariables(), log) if err != nil { return nil, err } daemonPens[i] = d } return &DaemonWorld{daemonPens}, nil }
// Gives control of chan to caller func runOnChan(modchan chan *watch.Mod, readyCallback func(), log termlog.TermLog, cnf *conf.Config, watchconf string, notifiers []notify.Notifier) (*conf.Config, error) { err := PrepOnly(log, cnf, notifiers) if err != nil { return nil, err } dworld, err := NewDaemonWorld(cnf, log) if err != nil { return nil, err } c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, os.Kill) defer signal.Reset(os.Interrupt, os.Kill) defer dworld.Shutdown(os.Kill) go func() { dworld.Shutdown(<-c) os.Exit(0) }() dworld.Start() watchpaths := cnf.WatchPatterns() if watchconf != "" { watchpaths = append(watchpaths, filepath.Dir(watchconf)) } // FIXME: This takes a long time. We could start it in parallel with the // first process run in a goroutine watcher, err := watch.Watch(watchpaths, lullTime, modchan) if err != nil { return nil, fmt.Errorf("Error watching: %s", err) } defer watcher.Stop() go readyCallback() for mod := range modchan { if mod == nil { break } if watchconf != "" && mod.Has(watchconf) { ret, err := ioutil.ReadFile(watchconf) if err != nil { log.Warn("Reloading config - error reading %s: %s", watchconf, err) continue } newcnf, err := conf.Parse(watchconf, string(ret)) if err != nil { log.Warn("Reloading config - error reading %s: %s", watchconf, err) continue } log.Notice("Reloading config %s", watchconf) return newcnf, nil } log.SayAs("debug", "Delta: \n%s", mod.String()) for i, b := range cnf.Blocks { lmod, err := mod.Filter(b.Include, b.Exclude) if err != nil { log.Shout("Error filtering events: %s", err) continue } if lmod.Empty() { continue } err = RunPreps(b, cnf.GetVariables(), lmod, log, notifiers) if err != nil { if _, ok := err.(ProcError); ok { continue } else { return nil, err } } dworld.DaemonPens[i].Restart() } } return nil, nil }
func run(log termlog.TermLog, cnf *conf.Config, watchconf string) *conf.Config { modchan := make(chan *watch.Mod, 1024) if *ignores { for _, patt := range watch.CommonExcludes { fmt.Println(patt) } os.Exit(0) } daemonPens := make([]*modd.DaemonPen, len(cnf.Blocks)) for i, b := range cnf.Blocks { if !b.NoCommonFilter { b.Exclude = append(b.Exclude, watch.CommonExcludes...) } cnf.Blocks[i] = b _, err := prepsAndNotify(b, cnf.GetVariables(), nil, log) if err != nil { log.Shout("%s", err) return nil } d := modd.DaemonPen{} c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, os.Kill) go func() { d.Shutdown(<-c) os.Exit(0) }() if !*prep { d.Start(b.Daemons, cnf.GetVariables(), log) } daemonPens[i] = &d } if *prep { os.Exit(0) } watchpaths := cnf.WatchPaths() if watchconf != "" { watchpaths = append(watchpaths, watchconf) } // FIXME: This takes a long time. We could start it in parallel with the // first process run in a goroutine watcher, err := watch.Watch(watchpaths, lullTime, modchan) defer watcher.Stop() if err != nil { kingpin.Fatalf("Fatal error: %s", err) } for mod := range modchan { if watchconf != "" && mod.Has(watchconf) { ret, err := ioutil.ReadFile(watchconf) if err != nil { log.Warn("Reloading config - error reading %s: %s", watchconf, err) continue } newcnf, err := conf.Parse(*file, string(ret)) if err != nil { log.Warn("Reloading config - error reading %s: %s", watchconf, err) continue } log.Notice("Reloading config %s", watchconf) return newcnf } if mod == nil { break } log.SayAs("debug", "Delta: \n%s", mod.String()) for i, b := range cnf.Blocks { lmod, err := mod.Filter(b.Include, b.Exclude) if err != nil { log.Shout("Error filtering events: %s", err) continue } if lmod.Empty() { continue } proceed, err := prepsAndNotify(b, cnf.GetVariables(), lmod, log) if err != nil { log.Shout("%s", err) return nil } if !proceed { continue } daemonPens[i].Restart() } } return nil }