// NewDaemonPen creates a new DaemonPen func NewDaemonPen(block conf.Block, vars map[string]string, log termlog.TermLog) (*DaemonPen, error) { d := make([]*daemon, len(block.Daemons)) for i, dmn := range block.Daemons { vcmd := varcmd.VarCmd{Block: nil, Mod: nil, Vars: vars} finalcmd, err := vcmd.Render(dmn.Command) if err != nil { return nil, err } dmn.Command = finalcmd var indir string if block.InDir != "" { indir = block.InDir } else { indir, err = os.Getwd() if err != nil { return nil, err } } d[i] = &daemon{ conf: dmn, log: log.Stream(niceHeader("daemon: ", dmn.Command)), shell: vars[shellVarName], indir: indir, } } return &DaemonPen{daemons: d}, nil }
// RunPreps runs all commands in sequence. Stops if any command returns an error. func RunPreps(b conf.Block, vars map[string]string, mod *watch.Mod, log termlog.TermLog) error { vcmd := varcmd.VarCmd{Block: &b, Mod: mod, Vars: vars} for _, p := range b.Preps { cmd, err := vcmd.Render(p.Command) if err != nil { return err } err = RunProc(cmd, log.Stream(niceHeader("prep: ", cmd))) if err != nil { return err } } return nil }
// Start starts set of daemons, each specified by a command func (dp *DaemonPen) Start(daemons []conf.Daemon, vars map[string]string, log termlog.TermLog) { dp.Lock() defer dp.Unlock() d := make([]daemon, len(daemons)) for i, dmn := range daemons { d[i] = daemon{ conf: dmn, vars: vars, log: log.Stream( niceHeader("daemon: ", dmn.Command), ), } go d[i].Run() } dp.daemons = &d }
// NewDaemonPen creates a new DaemonPen func NewDaemonPen(block conf.Block, vars map[string]string, log termlog.TermLog) (*DaemonPen, error) { d := make([]*daemon, len(block.Daemons)) for i, dmn := range block.Daemons { vcmd := varcmd.VarCmd{Block: nil, Mod: nil, Vars: vars} finalcmd, err := vcmd.Render(dmn.Command) if err != nil { return nil, err } dmn.Command = finalcmd d[i] = &daemon{ conf: dmn, log: log.Stream(niceHeader("daemon: ", dmn.Command)), } } return &DaemonPen{daemons: d}, nil }
// WrapHandler wraps an httpctx.Handler in the paraphernalia needed by devd for // logging, latency, and so forth. func (dd *Devd) WrapHandler(log termlog.TermLog, next httpctx.Handler) http.Handler { h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { revertOriginalHost(r) timr := timer.Timer{} sublog := log.Group() defer func() { timing := termlog.DefaultPalette.Timestamp.SprintFunc()("timing: ") sublog.SayAs("timer", timing+timr.String()) sublog.Done() }() if matchStringAny(dd.IgnoreLogs, fmt.Sprintf("%s%s", r.URL.Host, r.RequestURI)) { sublog.Quiet() } timr.RequestHeaders() time.Sleep(time.Millisecond * time.Duration(dd.Latency)) dpath := r.URL.String() if !strings.HasPrefix(dpath, "/") { dpath = "/" + dpath } sublog.Say("%s %s", r.Method, dpath) LogHeader(sublog, r.Header) ctx := timr.NewContext(context.Background()) ctx = termlog.NewContext(ctx, sublog) if dd.AddHeaders != nil { for h, vals := range *dd.AddHeaders { if strings.ToLower(h) == "host" { if len(vals) > 0 { r.Host = vals[0] } } else { for _, v := range vals { w.Header().Set(h, v) } } } } next.ServeHTTPContext( ctx, &ResponseLogWriter{Log: sublog, Resp: w, Timer: &timr}, r, ) }) return h }
// RunPreps runs all commands in sequence. Stops if any command returns an error. func RunPreps(b conf.Block, vars map[string]string, mod *watch.Mod, log termlog.TermLog, notifiers []notify.Notifier) error { vcmd := varcmd.VarCmd{Block: &b, Mod: mod, Vars: vars} for _, p := range b.Preps { cmd, err := vcmd.Render(p.Command) if err != nil { return err } err = RunProc(cmd, log.Stream(niceHeader("prep: ", cmd))) if err != nil { if pe, ok := err.(ProcError); ok { for _, n := range notifiers { n.Push("modd error", pe.Output, "") } } return err } } return nil }
// Returns a (continue, error) tuple. If continue is true, execution of the // remainder of the block should proceed. If error is not nil, modd should // exit. func prepsAndNotify(b conf.Block, vars map[string]string, lmod *watch.Mod, log termlog.TermLog) (bool, error) { err := modd.RunPreps(b, vars, lmod, log) if pe, ok := err.(modd.ProcError); ok { if *beep { fmt.Print("\a") } if *doNotify { n := notify.NewNotifier() if n == nil { log.Shout("Could not find a desktop notifier") } else { n.Push("modd error", pe.Output, "") } } return false, nil } else if err != nil { return false, err } return true, nil }
// Start starts set of daemons, each specified by a command func (dp *DaemonPen) Start(daemons []conf.Daemon, vars map[string]string, log termlog.TermLog) { dp.Lock() defer dp.Unlock() d := make([]daemon, len(daemons)) for i, dmn := range daemons { vcmd := varcmd.VarCmd{Block: nil, Mod: nil, Vars: vars} finalcmd, err := vcmd.Render(dmn.Command) if err != nil { log.Shout("%s", err) continue } dmn.Command = finalcmd d[i] = daemon{ conf: dmn, log: log.Stream( niceHeader("daemon: ", dmn.Command), ), } go d[i].Run() } dp.daemons = &d }
// RunPreps runs all commands in sequence. Stops if any command returns an error. func RunPreps(b conf.Block, vars map[string]string, mod *moddwatch.Mod, log termlog.TermLog, notifiers []notify.Notifier, initial bool) error { shell := vars[shellVarName] vcmd := varcmd.VarCmd{Block: &b, Mod: mod, Vars: vars} for _, p := range b.Preps { cmd, err := vcmd.Render(p.Command) if initial && p.Onchange { log.Say(niceHeader("skipping prep: ", cmd)) continue } if err != nil { return err } err = RunProc(cmd, shell, log.Stream(niceHeader("prep: ", cmd))) if err != nil { if pe, ok := err.(ProcError); ok { for _, n := range notifiers { n.Push("modd error", pe.Output, "") } } return err } } return nil }
// Serve starts the devd server. The callback is called with the serving URL // just before service starts. func (dd *Devd) Serve(address string, port int, certFile string, logger termlog.TermLog, callback func(string)) error { templates, err := ricetemp.MakeTemplates(rice.MustFindBox("templates")) if err != nil { return fmt.Errorf("Error loading templates: %s", err) } mux, err := dd.Router(logger, templates) if err != nil { return err } var tlsConfig *tls.Config var tlsEnabled bool if certFile != "" { tlsConfig, err = getTLSConfig(certFile) if err != nil { return fmt.Errorf("Could not load certs: %s", err) } tlsEnabled = true } var hl net.Listener if port > 0 { hl, err = net.Listen("tcp", fmt.Sprintf("%v:%d", address, port)) } else { hl, err = pickPort(address, portLow, portHigh, tlsEnabled) } if err != nil { return err } if tlsConfig != nil { hl = tls.NewListener(hl, tlsConfig) } hl = slowdown.NewSlowListener(hl, dd.UpKbps*1024, dd.DownKbps*1024) url := formatURL(tlsEnabled, address, hl.Addr().(*net.TCPAddr).Port) logger.Say("Listening on %s (%s)", url, hl.Addr().String()) server := &http.Server{Addr: hl.Addr().String(), Handler: mux} callback(url) if dd.HasLivereload() { c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGHUP) go func() { for { <-c logger.Say("Received signal - reloading") dd.lrserver.Reload([]string{"*"}) } }() } err = server.Serve(hl) logger.Shout("Server stopped: %v", err) return 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 }