// Kill kills the underlying application if its started func (h *ExecuteHandle) Kill(reason StepResult) { if reason == nil { reason = ErrorAppKilled } gwl.LogDebug("hitting kill lock") h.Lock() gwl.LogDebug("done with kill lock") if h.running { cmd := h.cmd proc := cmd.Process gwl.LogDebug("Killing") if proc != nil { if err := proc.Kill(); err != nil && err.Error() != errorProcessAlreadyFinished.Error() { gwl.LogDebug("process didn't seem to exit gracefully", err) reason = err } } h.writeError(reason) h.errorCode = reason h.running = false h.halted = true close(h.result) } else if h.errorCode == nil { gwl.LogDebug("process never started %s", reason.Error()) h.writeError(reason) } h.Unlock() }
func setupIgnorePaths(root string) []string { gwl.LogDebug("Ignore globs.") paths := strings.Split(*ignore, ",") expandedPaths := []string{} for _, path := range paths { abs := filepath.Join(root, path) gwl.LogDebug("\t%s\n", abs) expandedPaths = append(expandedPaths, abs) } return expandedPaths }
func (h *ExecuteHandle) writeError(reason StepResult) { if h.running { gwl.LogDebug("sending error") h.result <- reason } else { h.errorCode = reason } }
func (handle *WatchHandle) handleFileEvent(projectName string) { lastEvent, timeSinceLastEvent := "", time.Now().AddDate(-1, 0, 0) watcher := handle.watcher ignorePaths := handle.ignorePaths errorChan := watcher.Errors for { select { case <-time.After(time.Second): continue case err := <-errorChan: if err != nil { gwl.LogError("Closing file watcher error routine %s", err.Error()) } case event, ok := <-watcher.Events: if !ok { gwl.LogDebug("closing file event channel") return } _, filename := filepath.Split(event.Name) // ignore any files that have the same name as the package if projectName == filename || event.Op&fsnotify.Chmod == fsnotify.Chmod { gwl.LogDebug("ignoring go build artifacts") continue } //ignores individual files that may match any ignore paths if shouldIgnore(event.Name, ignorePaths) { gwl.LogDebug("%s in ignore path ", event.Name) continue } // debounces file events if event.Name == lastEvent && timeSinceLastEvent.Add(time.Second).After(time.Now()) { gwl.LogDebug("ignoring extra file watch events") timeSinceLastEvent = time.Now() continue } lastEvent = event.Name timeSinceLastEvent = time.Now() if handle.halted { gwl.LogDebug("\tfilewatcher halteds") return } else if handle.fileUpdateCb != nil { gwl.LogDebug("\tinvoking file callback with %s", event.Name) handle.Lock() handle.fileUpdateCb(event.Name) handle.Unlock() gwl.LogDebug("\tfile callback complete") } } } }
func test(projectDirectory string) bool { gwl.LogDebug("testing code...") cmd := exec.Command("go", "test") cmd.Dir = projectDirectory cmd.Env = os.Environ() cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { gwl.LogDebug("test failures: ", err) return false } return true }
func main() { flag.Parse() setupLogging() projectPath := getAbsPathToProject() gwl.LogDebug("watching", projectPath) handleWatch(projectPath, setupIgnorePaths(projectPath)) }
func (w *WatchHandle) Halt() { w.Lock() defer w.Unlock() if !w.halted { gwl.LogDebug("Halting watcher") w.halted = true w.fileUpdateCb = nil go func() { w.watcher.Close() }() } }
func addFilesToWatch(dir string, ignorePaths []string, watcher *fsnotify.Watcher) { filepath.Walk(dir, func(p string, info os.FileInfo, err error) error { if info.IsDir() { if shouldIgnore(filepath.Join(p, info.Name()), ignorePaths) { return filepath.SkipDir } else if err := watcher.Add(p); err != nil { gwl.LogDebug("error adding watched dir", p, info.Name(), err.Error()) return err } } return nil }) }
func shouldIgnore(file string, ignorePaths []string) bool { for _, pattern := range ignorePaths { matched, err := filepath.Match(strings.Replace(pattern, "/", "", -1), strings.Replace(file, "/", "", -1)) if err != nil { gwl.LogError(err.Error()) } if matched && err == nil { gwl.LogDebug("\tIgnore %s -> %s\n", pattern, file) return true } } return false }
func (h *ExecuteHandle) start(cmd *exec.Cmd) { h.Lock() h.cmd = cmd err := cmd.Start() h.running = true h.Unlock() if err != nil { h.Kill(err) } waiter := make(chan bool) go func() { close(waiter) if err := cmd.Wait(); err != nil { gwl.LogDebug("app exited prematurely") h.Kill(err) } }() <-waiter }
func handleWatch(projectPath string, ignorePaths []string) { watchHandle := watch.StartWatch(projectPath, ignorePaths) for { gwl.LogDebug("---Starting app monitor---") time.Sleep(*wait) execHandle := project.ExecuteBuildSteps(projectPath, *appArgs, *shouldTest, *shouldLint) gwl.LogDebug("---Setting up watch cb---") watchHandle.Subscribe(func(fileName string) { if !execHandle.Halted() { gwl.LogError("attempting to kill process") execHandle.Kill(nil) gwl.LogDebug("exiting file watch routine in main") } }) gwl.LogDebug("waiting on app to exit") err := execHandle.Error() gwl.LogDebug("---App exited---") watchHandle.Subscribe(nil) exitedSuccessfully := err == nil || err == project.ErrorAppKilled if exitedSuccessfully { color.Green("exited successfully\n") } else { color.Red("%s\n", err.Error()) } sync := make(chan bool) if (!exitedSuccessfully && !*restartOnError) || (!*restartOnExit && err != project.ErrorAppKilled) { watchHandle.Subscribe(func(fileName string) { close(sync) watchHandle.Subscribe(nil) }) gwl.LogDebug("waiting on file notification") <-sync } } }
func lint(projectDirectory string) bool { gwl.LogDebug("linting code...") lint := &linter.Linter{} files := make(map[string]map[string][]byte) filepath.Walk(projectDirectory, func(p string, info os.FileInfo, err error) error { if filepath.Ext(p) == ".go" { fileWithPackage := strings.TrimPrefix(p, projectDirectory) packageName := strings.Trim(strings.TrimSuffix(fileWithPackage, info.Name()), "/") if packageName == "" { packageName = "main" } files[packageName] = make(map[string][]byte) f, err := os.Open(p) if err != nil { return err } if files[packageName][p], err = ioutil.ReadAll(f); err != nil { return err } } return nil }) lintErrors := false for k, v := range files { gwl.LogDebug("linting package %s", k) problems, err := lint.LintFiles(v) if err != nil { color.Red("[ERROR]", err) lintErrors = true } else if len(problems) > 0 { gwl.LogDebug("lint issues found") color.Yellow("%d lint issue(s) found in %s\n\n", len(problems), k) linterConfidenceThresholdReached := false for i, p := range problems { position := p.Position fileWithPackage := strings.Trim(strings.TrimPrefix(position.Filename, projectDirectory), "/") lintInfo := strings.Split(p.String(), "\n") gwl.LogDebug("%d out of 3", len(lintInfo)) readableLintError := "" if len(lintInfo) >= 3 { readableLintError = fmt.Sprintf("- %s", lintInfo[2]) } lintLineOutput := fmt.Sprintf("\t%d. %s line %d %s\n\t%s\n\n", i+1, fileWithPackage, position.Line, readableLintError, lintInfo[0]) if p.Confidence > 0.5 { color.Red(lintLineOutput) linterConfidenceThresholdReached = true } else { color.Yellow(lintLineOutput) } } lintErrors = linterConfidenceThresholdReached } } return !lintErrors }