func runAndWatch(godoFile string) { done := make(chan bool, 1) run := func(forceBuild bool) (*exec.Cmd, string) { cmd, exe := buildCommand(godoFile, forceBuild) cmd.Start() go func() { err := cmd.Wait() done <- true if err != nil { if isVerbose { util.Debug("godo", "godo process killed\n") } } }() return cmd, exe } bufferSize := 2048 watchr, err := watcher.NewWatcher(bufferSize) if err != nil { util.Panic("project", "%v\n", err) } godoDir := filepath.Dir(godoFile) watchr.WatchRecursive(godoDir) watchr.ErrorHandler = func(err error) { util.Error("godo", "Watcher error %v\n", err) } cmd, exe := run(false) // this function will block forever, Ctrl+C to quit app // var lastHappenedTime int64 watchr.Start() util.Info("godo", "watching %s ...\n", godoDir) <-time.After(godo.GetWatchDelay() + (300 * time.Millisecond)) // forloop: for { select { case event := <-watchr.Event: if event.Path == exe { continue } util.Debug("watchmain", "%+v\n", event) syscall.Kill(cmd.Process.Pid, syscall.SIGQUIT) cmd.Process.Kill() <-done cmd, _ = run(true) } } }
func (project *Project) watchTask(task *Task, root string, logName string, handler func(e *watcher.FileEvent)) { ignorePathFn := func(p string) bool { return watcher.DefaultIgnorePathFn(p) || !task.isWatchedFile(p) } // if len(task.EffectiveWatchRegexps) == 0 { // util.Error("godo", "EffectiveWatchRegexps should not be zero") // } else { // ignorePathFn = func(p string) bool { // return watcher.DefaultIgnorePathFn(p) || !task.isWatchedFile(p) // } // } bufferSize := 2048 watchr, err := watcher.NewWatcher(bufferSize) if err != nil { util.Panic("project", "%v\n", err) } watchr.IgnorePathFn = ignorePathFn watchr.ErrorHandler = func(err error) { util.Error("project", "Watcher error %v\n", err) } watchr.WatchRecursive(root) // this function will block forever, Ctrl+C to quit app // var lastHappenedTime int64 util.Info(logName, "watching %s ...\n", root) // not sure why this need to be unbuffered, but it was blocking // on cquit <- true cquit := make(chan bool, 1) project.Lock() project.cwatchTasks[cquit] = true project.Unlock() watchr.Start() forloop: for { select { case event := <-watchr.Event: //util.Debug("DBG", "handling watchr.Event %+v\n", event) handler(event) case <-cquit: watchr.Stop() break forloop } } }
func buildMain(src string, forceBuild bool) string { mustBeMain(src) dir := filepath.Dir(src) exeFile := "godobin-" + godo.Version if isWindows { exeFile += ".exe" } exe := filepath.Join(dir, exeFile) build := false reasonFormat := "" if isRebuild || forceBuild { build = true reasonFormat = "Rebuilding %s...\n" } else { build = util.Outdated([]string{dir + "/**/*.go"}, []string{exe}) reasonFormat = "Godo tasks changed. Rebuilding %s...\n" } if build { util.Debug("godo", reasonFormat, exe) _, err := godo.Run("go build -a -o "+exeFile, godo.M{"$in": dir}) if err != nil { panic(fmt.Sprintf("Error building %s: %s\n", src, err.Error())) } // for some reason go build does not delete the exe named after the dir // which ends up with Gododir/Gododir if filepath.Base(dir) == "Gododir" { orphanedFile := filepath.Join(dir, filepath.Base(dir)) if _, err := os.Stat(orphanedFile); err == nil { os.Remove(orphanedFile) } } } if isRebuild { util.Info("godo", "ok\n") } return exe }
func main() { // v2 ONLY uses Gododir/main.go godoFiles := []string{"Gododir/main.go", "Gododir/Godofile.go", "tasks/Godofile.go"} src := "" for _, filename := range godoFiles { src = util.FindUp(".", filename) if src != "" { break } } if src == "" { fmt.Printf("\n\n%s not found\n", src) os.Exit(1) } wd, err := os.Getwd() if err != nil { util.Error("godo", "Could not get working directory: %s\n", err.Error()) } // parent of Gododir/main.go absParentDir, err := filepath.Abs(filepath.Dir(filepath.Dir(src))) if err != nil { util.Error("godo", "Could not get absolute parent of %s: %s\n", src, err.Error()) } if wd != absParentDir { relDir, _ := filepath.Rel(wd, src) os.Chdir(absParentDir) util.Info("godo", "Using %s\n", relDir) } os.Setenv("GODOFILE", src) argm := minimist.Parse() isRebuild = argm.AsBool("rebuild") isWatch = argm.AsBool("w", "watch") isVerbose = argm.AsBool("v", "verbose") hasTasks = len(argm.NonFlags()) > 0 run(src) }
func querySQL(sql string, args []interface{}) { fmt.Printf("%s: %s\n", cyan("sql"), sql) if len(args) > 0 { fmt.Printf("%s: %#v\n", cyan("args"), args) } conn := getConnection() if conn != nil { rows, err := conn.DB.Queryx(sql, args...) if err != nil { util.Error("pg", "Error executing SQL: %s", err.Error()) } for rows.Next() { m := map[string]interface{}{} rows.MapScan(m) mapBytesToString(m) b, _ := json.Marshal(m) json.Unmarshal(b, &m) printMap(m) } } util.Info("pg", "OK\n") }
// RunWithEvent runs this task when triggered from a watch. // *e* FileEvent contains information about the file/directory which changed // in watch mode. func (task *Task) RunWithEvent(logName string, e *watcher.FileEvent) (err error) { if task.RunOnce && task.Complete { util.Debug(task.Name, "Already ran\n") return nil } task.expandGlobs() if !task.shouldRun(e) { util.Info(logName, "up-to-date 0ms\n") return nil } start := time.Now() if len(task.SrcGlobs) > 0 && len(task.SrcFiles) == 0 { util.Error("task", "\""+task.Name+"\" '%v' did not match any files\n", task.SrcGlobs) } // Run this task only if the file matches watch Regexps rebuilt := "" if e != nil { rebuilt = "rebuilt " if !task.isWatchedFile(e.Path) && len(task.SrcGlobs) > 0 { return nil } if verbose { util.Debug(logName, "%s\n", e.String()) } } log := true if task.Handler != nil { context := Context{Task: task, Args: task.argm} defer func() { if p := recover(); p != nil { sp, ok := p.(*softPanic) if !ok { panic(p) } err = fmt.Errorf("%q: %s", logName, sp) } }() task.Handler.Handle(&context) if context.Error != nil { return fmt.Errorf("%q: %s", logName, context.Error.Error()) } } else if len(task.dependencies) > 0 { // no need to log if just dependency log = false } else { util.Info(task.Name, "Ignored. Task does not have a handler or dependencies.\n") return nil } if log { util.Info(logName, "%s%vms\n", rebuilt, time.Since(start).Nanoseconds()/1e6) } task.Complete = true return nil }