-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexecutor.go
More file actions
139 lines (116 loc) · 3.97 KB
/
Copy pathexecutor.go
File metadata and controls
139 lines (116 loc) · 3.97 KB
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package executor
import (
"fmt"
"strings"
"github.com/sleipi/cli-t/internal/assert"
"github.com/sleipi/cli-t/internal/runner"
"github.com/sleipi/cli-t/internal/types"
"github.com/sleipi/cli-t/internal/vars"
)
// Entry runs a single entry's command and evaluates all assertions.
// It also stores any captures into the provided captures map.
func Entry(entry types.Entry, captures map[string]string) Result {
cmd := vars.SubstituteCaptures(entry.Command, captures)
if entry.ExitNever {
res, _ := backgroundEntry(entry, cmd, captures)
return res
}
if len(entry.Prompts) > 0 {
return promptEntry(entry, cmd, captures)
}
result := runner.RunWithEnv(cmd, entry.Directives.Env, entry.Directives.Workdir)
return evaluateResult(entry, result, captures, nil)
}
// promptEntry runs a command with interactive prompts.
func promptEntry(entry types.Entry, cmd string, captures map[string]string) Result {
timeout := 30000 // default 30s
if entry.Directives.Timeout != nil {
if *entry.Directives.Timeout == 0 {
timeout = 0 // explicitly no timeout
} else {
timeout = *entry.Directives.Timeout
}
}
prompts := make([]runner.PromptDef, len(entry.Prompts))
for i, p := range entry.Prompts {
prompts[i] = runner.PromptDef{
Pattern: p.Pattern,
IsRegex: p.IsRegex,
Response: p.Response,
Repeat: p.Repeat,
}
}
pr := runner.RunWithPrompts(cmd, prompts, timeout, entry.Directives.Env, entry.Directives.Workdir)
var failures []string
if pr.TimedOut {
failures = append(failures, "timeout waiting for prompts")
}
if pr.AmbiguousMatch != "" {
failures = append(failures, fmt.Sprintf("ambiguous prompt match: %s", pr.AmbiguousMatch))
}
for _, u := range pr.UnmatchedPrompts {
failures = append(failures, fmt.Sprintf("prompt %q was never matched", u))
}
result := pr.Result
// Strip trailing newline for consistency with regular runner
result.Stdout = strings.TrimRight(result.Stdout, "\n")
result.Stderr = strings.TrimRight(result.Stderr, "\n")
return evaluateResult(entry, result, captures, failures)
}
// evaluateResult checks exit code, body, asserts, and captures against a runner result.
// Any pre-existing failures (e.g. from prompt timeouts) are passed via prefailures.
func evaluateResult(entry types.Entry, result runner.Result, captures map[string]string, prefailures []string) Result {
passed := len(prefailures) == 0
failures := prefailures
if result.ExitCode != entry.ExitCode {
passed = false
failures = append(failures, fmt.Sprintf("exit code: expected %d, got %d", entry.ExitCode, result.ExitCode))
}
if len(entry.Body) > 0 {
res := assert.EvaluateBody(entry.Body, result)
if !res.Pass {
passed = false
failures = append(failures, res.Message)
}
}
for _, a := range entry.Asserts {
res := assert.Evaluate(a, result)
if !res.Pass {
passed = false
failures = append(failures, res.Message)
}
}
for _, c := range entry.Captures {
val := vars.ResolveCapture(c.Query, result)
captures[c.Name] = val
}
return Result{Pass: passed, Failures: failures, Runner: result}
}
// SplitDeferEntries separates defer entries from regular entries.
// Returns (regular, defers) where defers are in LIFO order.
func SplitDeferEntries(entries []types.Entry) (regular, defers []types.Entry) {
for _, e := range entries {
if e.Directives.Defer {
defers = append(defers, e)
} else {
regular = append(regular, e)
}
}
// Reverse defers for LIFO execution
for i, j := 0, len(defers)-1; i < j; i, j = i+1, j-1 {
defers[i], defers[j] = defers[j], defers[i]
}
return
}
// ExecuteDefers runs all defer entries, logging errors but not failing.
func ExecuteDefers(defers []types.Entry, captures map[string]string) []string {
var logs []string
for _, entry := range defers {
cmd := vars.SubstituteCaptures(entry.Command, captures)
result := runner.RunWithEnv(cmd, entry.Directives.Env, entry.Directives.Workdir)
if result.ExitCode != 0 {
logs = append(logs, fmt.Sprintf("defer %q: exit code %d", cmd, result.ExitCode))
}
}
return logs
}