Beispiel #1
0
// Expands glob patterns.
func (task *Task) expandGlobs() {

	// runs once lazily
	if len(task.SrcFiles) > 0 {
		return
	}

	files, regexps, err := glob.Glob(task.SrcGlobs)
	if err != nil {
		util.Error(task.Name, "%v", err)
		return
	}

	task.SrcRegexps = regexps
	task.SrcFiles = files

	if len(task.DestGlobs) > 0 {
		files, regexps, err := glob.Glob(task.DestGlobs)
		if err != nil {
			util.Error(task.Name, "%v", err)
			return
		}
		task.DestRegexps = regexps
		task.DestFiles = files
	}
}
Beispiel #2
0
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)
}
Beispiel #3
0
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)
		}
	}

}
Beispiel #4
0
func killSpawned(command string) {
	process := Processes[command]
	if process == nil {
		return
	}

	err := process.Kill()
	delete(Processes, command)
	if err != nil && !strings.Contains(err.Error(), "process already finished") {
		util.Error("Start", "Could not kill existing process %+v\n%s\n", process, err.Error())
		return
	}
	if verbose {
		util.Debug("#", "Processes[%q] killed\n", command)
	}
}
Beispiel #5
0
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
		}
	}
}
Beispiel #6
0
// Deps are task dependencies and must specify how to run tasks in series or in parallel.
func (task *Task) Deps(names ...interface{}) {
	for _, name := range names {
		switch dep := name.(type) {
		default:
			util.Error(task.Name, "Dependency types must be (string | P | Parallel | S | Series)")
		case string:
			task.dependencies = append(task.dependencies, dep)
		case P:
			task.dependencies = append(task.dependencies, Parallel(dep))
		case Parallel:
			task.dependencies = append(task.dependencies, dep)
		case S:
			task.dependencies = append(task.dependencies, Series(dep))
		case Series:
			task.dependencies = append(task.dependencies, dep)
		}
	}
}
Beispiel #7
0
// Watch watches the Files of a task and reruns the task on a watch event. Any
// direct dependency is also watched. Returns true if watching.
//
//
// TODO:
// 1. Only the parent task watches, but it gathers wath info from all dependencies.
//
// 2. Anything without src files always run when a dependency is triggered by a glob match.
//
//		build [generate{*.go} compile] => go file changes =>  build, generate and compile
//
// 3. Tasks with src only run if it matches a src
//
//       build [generate{*.go} css{*.scss} compile] => go file changes => build, generate and compile
//       css does not need to run since no SCSS files ran
//
// X depends on [A:txt, B]	=> txt changes	A runs, X runs without deps
// X:txt on [A, B]			=> txt changes	A, B, X runs
//
func (project *Project) Watch(names []string, isParent bool) bool {
	// fixes a bug where the first debounce prevents the task from running because
	// all tasks are run once before Watch() is called
	project.reset()

	funcs := []func(){}

	taskClosure := func(project *Project, task *Task, taskname string, logName string) func() {
		paths := calculateWatchPaths(task.EffectiveWatchGlobs)
		return func() {
			if len(paths) == 0 {
				return
			}
			for _, pth := range paths {
				go func(path string) {
					project.watchTask(task, path, logName, func(e *watcher.FileEvent) {
						err := project.run(taskname, taskname, e)
						if err != nil {
							util.Error("ERR", "%s\n", err.Error())
						}
					})
				}(pth)
			}
		}
	}

	for _, taskname := range names {
		proj, task, _ := project.mustTask(taskname)
		// updates effectiveWatchGlobs
		proj.gatherWatchInfo(task)
		if len(task.EffectiveWatchGlobs) > 0 {
			funcs = append(funcs, taskClosure(project, task, taskname, taskname))
		}
	}

	if len(funcs) > 0 {
		<-all(funcs)
		return true
	}
	return false
}
Beispiel #8
0
func pgTasks(p *Project) {
	Env = `
	DAT_DRIVER=postgres
	DAT_DSN="dbname=dbr_test user=dbr password=!test host=localhost sslmode=disable"
	`

	p.Task("file", nil, func(c *Context) {
		filename := c.Args.Leftover()[0]
		if !util.FileExists(filename) {
			util.Error("ERR", "file not found %s", filename)
			return
		}
		b, err := ioutil.ReadFile(filename)
		if err != nil {
			panic(err)
		}
		parts := strings.Split(string(b), "---\n")
		if len(parts) != 2 {
			panic("sql file must have frontmatter")
		}
		var args []interface{}
		err = json.Unmarshal([]byte(parts[0]), &args)
		if err != nil {
			panic(err)
		}
		sql := parts[1]
		sql, args, _ = dat.Interpolate(sql, args)
		querySQL(sql, args)
	}).Desc("Executes a SQL file with placeholders")

	p.Task("query", nil, func(c *Context) {
		if len(c.Args.Leftover()) != 1 {
			fmt.Println(`usage: godo query -- "SELECT * ..." `)
			return
		}
		sql := c.Args.Leftover()[0]
		querySQL(sql, nil)

	}).Desc("Executes a query against the database")
}
Beispiel #9
0
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")
}
Beispiel #10
0
func checkError(err error, format string, args ...interface{}) {
	if err != nil {
		util.Error("ERR", format, args...)
		os.Exit(1)
	}
}
Beispiel #11
0
// 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
}
Beispiel #12
0
// used for testing to switch out exitFn
func godoExit(tasksFunc func(*Project), argv []string, exitFn func(int)) {
	if argv == nil {
		argm = minimist.Parse()
	} else {
		argm = minimist.ParseArgv(argv)
	}

	dump := argm.AsBool("dump")
	help = argm.AsBool("help", "h", "?")
	verbose = argm.AsBool("verbose", "v")
	version = argm.AsBool("version", "V")
	watching = argm.AsBool("watch", "w")
	deprecatedWarnings = argm.AsBool("D")
	contextArgm := minimist.ParseArgv(argm.Unparsed())

	project := NewProject(tasksFunc, exitFn, contextArgm)

	if help {
		Usage(project.usage())
		exitFn(0)
	}

	if version {
		fmt.Printf("godo %s\n", Version)
		exitFn(0)
	}

	if dump {
		project.dump(os.Stdout, "", "  ")
		exitFn(0)
	}

	// env vars are any nonflag key=value pair
	addToOSEnviron(argm.NonFlags())

	// Run each task including their dependencies.
	args := []string{}
	for _, s := range argm.NonFlags() {
		// skip env vars
		if !strings.Contains(s, "=") {
			args = append(args, s)
		}
	}

	if len(args) == 0 {
		if project.Tasks["default"] != nil {
			args = append(args, "default")
		} else {
			Usage(project.usage())
			exitFn(0)
		}
	}

	for _, name := range args {
		err := project.Run(name)
		if err != nil {
			util.Error("ERR", "%s\n", err.Error())
			exitFn(1)
		}
	}

	if watching {
		if project.Watch(args, true) {
			runnerWaitGroup.Add(1)
			waitExit = true
		} else {
			fmt.Println("Nothing to watch. Use Task#Src() to specify watch patterns")
			exitFn(0)
		}
	}

	if waitExit {
		// Ctrl+C handler
		csig := make(chan os.Signal, 1)
		signal.Notify(csig, syscall.SIGQUIT)
		go func() {
			for sig := range csig {
				fmt.Println("SIG caught")
				if sig == syscall.SIGQUIT {
					fmt.Println("SIG caught B")
					project.Exit(0)
					break
				}
			}
		}()

		runnerWaitGroup.Wait()
	}
	exitFn(0)
}