summaryrefslogtreecommitdiff
path: root/pkg/commands.go
blob: 5403e5f80e7232080aada6cf80f490a9ca4a73aa (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
package gonf

import (
	"bytes"
	"log/slog"
	"os/exec"
)

// ----- Globals ---------------------------------------------------------------
var commands []*CommandPromise

// ----- Init ------------------------------------------------------------------
func init() {
	commands = make([]*CommandPromise, 0)
}

// ----- Public ----------------------------------------------------------------
func Command(cmd string, args ...string) *CommandPromise {
	return CommandWithEnv([]string{}, cmd, args...)
}

func CommandWithEnv(env []string, cmd string, args ...string) *CommandPromise {
	return &CommandPromise{
		args:   args,
		chain:  nil,
		cmd:    cmd,
		env:    env,
		err:    nil,
		status: PROMISED,
	}
}

type CommandPromise struct {
	args   []string
	chain  []Promise
	cmd    string
	env    []string
	err    error
	status Status
	Stdout bytes.Buffer
	Stderr bytes.Buffer
}

func (c *CommandPromise) IfRepaired(p ...Promise) Promise {
	c.chain = append(c.chain, p...)
	return c
}

func (c *CommandPromise) Promise() Promise {
	commands = append(commands, c)
	return c
}

func (c *CommandPromise) Resolve() {
	cmd := exec.Command(c.cmd, c.args...)
	for _, e := range c.env {
		cmd.Env = append(cmd.Environ(), e)
	}
	cmd.Stdout = &c.Stdout
	cmd.Stderr = &c.Stderr

	if c.err = cmd.Run(); c.err != nil {
		c.status = BROKEN
		slog.Error("command", "args", c.args, "cmd", c.cmd, "env", c.env, "err", c.err, "stdout", c.Stdout.String(), "stderr", c.Stderr.String(), "status", c.Status)
		return
	}
	if c.Stdout.Len() == 0 && c.Stderr.Len() > 0 {
		c.status = BROKEN
		slog.Error("command", "args", c.args, "cmd", c.cmd, "env", c.env, "stdout", c.Stdout.String(), "stderr", c.Stderr.String(), "status", c.Status)
		return
	}
	c.status = REPAIRED
	slog.Info("command", "args", c.args, "cmd", c.cmd, "env", c.env, "stderr", c.Stderr.String(), "status", c.Status)
	// TODO add a notion of repaired?
	for _, p := range c.chain {
		p.Resolve()
	}
}

func (c CommandPromise) Status() Status {
	return c.status
}

// ----- Internal --------------------------------------------------------------
func resolveCommands() (status Status) {
	status = KEPT
	for _, c := range commands {
		if c.status == PROMISED {
			c.Resolve()
			switch c.status {
			case BROKEN:
				return BROKEN
			case REPAIRED:
				status = REPAIRED
			}
		}
	}
	return
}