summaryrefslogtreecommitdiff
path: root/run.go
blob: 5fbeb7c94d1b1298d115213984dfb8b76488e3ad (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
package fhelp

import (
	"errors"
	"time"

	"github.com/go-cmd/cmd"
	"go.wit.com/log"
)

func RunRealtime(args []string) cmd.Status {
	return PathRunRealtime("", args)
}

// 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()
}