package fhelp import ( "errors" "time" "github.com/go-cmd/cmd" "go.wit.com/log" ) func RunRealtime(args []string) cmd.Status { return PathRunRealtime("", args) } func RunRealtimeError(args []string) (*cmd.Status, error) { result := PathRunRealtime("", args) if result.Error != nil { log.Warn(args, "wow. golang is cool. an os.Error:", result.Error) return &result, result.Error } if result.Exit != 0 { // log.Warn(cmd, "failed with", result.Exit, repo.GetGoPath()) return &result, errors.New(log.Sprintf("%v failed with %v", args, result.Exit)) } return &result, nil } // this is stuff from a long time ago that there must be a replacement for func RemoveFirstElement(slice []string) (string, []string) { if len(slice) == 0 { return "", slice // Return the original slice if it's empty } return slice[0], slice[1:] // Return the slice without the first element } // echos twice a second if anything sends to STDOUT or STDERR // not great, but it's really just for watching things run in real time anyway // TODO: fix \r handling for things like git-clone so the terminal doesn't // have to do a \n newline each time. // TODO: add timeouts and status of things hanging around forever func PathRunRealtime(pwd string, args []string) cmd.Status { // Check if the slice has at least one element (the command name) if len(args) == 0 { var s cmd.Status s.Error = errors.New("Error: Command slice is empty.") return s } // Start a long-running process, capture stdout and stderr a, b := RemoveFirstElement(args) findCmd := cmd.NewCmd(a, b...) if pwd != "" { findCmd.Dir = pwd } statusChan := findCmd.Start() // non-blocking ticker := time.NewTicker(100 * time.Microsecond) // this is interesting, maybe useful, but wierd, but neat. interesting even // Print last line of stdout every 2s go func() { // loop very quickly, but only print the line if it changes var lastout string var lasterr string for range ticker.C { status := findCmd.Status() n := len(status.Stdout) if n != 0 { newline := status.Stdout[n-1] if lastout != newline { lastout = newline log.Info(lastout) } } n = len(status.Stderr) if n != 0 { newline := status.Stderr[n-1] if lasterr != newline { lasterr = newline log.Info(lasterr) } } if status.Complete { return } } }() // Stop command after 1 hour go func() { <-time.After(1 * time.Hour) findCmd.Stop() }() // Check if command is done select { case finalStatus := <-statusChan: log.Info("finalStatus =", finalStatus.Exit, finalStatus.Error) return finalStatus // done default: // no, still running } // Block waiting for command to exit, be stopped, or be killed // there are things being left around here. debug this finalStatus := <-statusChan if len(finalStatus.Cmd) != 0 { if string(finalStatus.Cmd) != "go" { log.Info("shell.Run() ok goroutine end?", finalStatus.Cmd, finalStatus.Exit) } } return findCmd.Status() }