fix sentinel collision, backslash escape, and timeout cleanup
This commit is contained in:
parent
57fba7acb0
commit
c7730bcaa6
2
parse.go
2
parse.go
|
|
@ -12,6 +12,8 @@ func stripComment(line string) string {
|
|||
for i := 0; i < len(line); i++ {
|
||||
ch := line[i]
|
||||
switch {
|
||||
case ch == '\\' && !inSingle:
|
||||
i++ // skip escaped character (backslash escapes in double-quoted and unquoted contexts)
|
||||
case ch == '\'' && !inDouble:
|
||||
inSingle = !inSingle
|
||||
case ch == '"' && !inSingle:
|
||||
|
|
|
|||
29
run.go
29
run.go
|
|
@ -1,6 +1,8 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
|
@ -11,7 +13,11 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
const sentinelPrefix = "__SHOUT_SENTINEL_"
|
||||
func newSentinelPrefix() string {
|
||||
b := make([]byte, 8)
|
||||
_, _ = rand.Read(b)
|
||||
return fmt.Sprintf("__SHOUT_%x_", b)
|
||||
}
|
||||
|
||||
type RunOptions struct {
|
||||
CleanEnv bool
|
||||
|
|
@ -22,7 +28,7 @@ type RunOptions struct {
|
|||
OnCommand func(Command)
|
||||
}
|
||||
|
||||
func buildScript(commands []Command) string {
|
||||
func buildScript(commands []Command, sentinelPrefix string) string {
|
||||
var b strings.Builder
|
||||
b.WriteString("exec 2>&1\n")
|
||||
for i, cmd := range commands {
|
||||
|
|
@ -33,7 +39,7 @@ func buildScript(commands []Command) string {
|
|||
return b.String()
|
||||
}
|
||||
|
||||
func parseSentinelOutput(raw string, commandCount int) (outputs [][]string, exitCodes []int) {
|
||||
func parseSentinelOutput(raw string, commandCount int, sentinelPrefix string) (outputs [][]string, exitCodes []int) {
|
||||
re := regexp.MustCompile(regexp.QuoteMeta(sentinelPrefix) + `(\d+)_(\d+)__`)
|
||||
|
||||
remaining := raw
|
||||
|
|
@ -91,7 +97,8 @@ func runFile(file ShoutFile, opts RunOptions) FileResult {
|
|||
return FileResult{File: file, TmpDir: tmpDir}
|
||||
}
|
||||
|
||||
script := buildScript(file.Commands)
|
||||
sentinel := newSentinelPrefix()
|
||||
script := buildScript(file.Commands, sentinel)
|
||||
|
||||
// Build environment
|
||||
var envMap map[string]string
|
||||
|
|
@ -115,7 +122,12 @@ func runFile(file ShoutFile, opts RunOptions) FileResult {
|
|||
|
||||
if len(opts.PathDirs) > 0 {
|
||||
existing := envMap["PATH"]
|
||||
envMap["PATH"] = strings.Join(opts.PathDirs, ":") + ":" + existing
|
||||
prepend := strings.Join(opts.PathDirs, ":")
|
||||
if existing != "" {
|
||||
envMap["PATH"] = prepend + ":" + existing
|
||||
} else {
|
||||
envMap["PATH"] = prepend
|
||||
}
|
||||
}
|
||||
|
||||
envSlice := make([]string, 0, len(envMap))
|
||||
|
|
@ -172,12 +184,17 @@ func runFile(file ShoutFile, opts RunOptions) FileResult {
|
|||
}
|
||||
output = r.data
|
||||
case <-time.After(totalTimeout):
|
||||
if cmd.Process != nil {
|
||||
_ = syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
|
||||
}
|
||||
<-ch // drain the reader goroutine so pipe FDs are released
|
||||
_ = cmd.Wait()
|
||||
return FileResult{File: file, TmpDir: tmpDir, Error: "Timeout reading output"}
|
||||
}
|
||||
|
||||
_ = cmd.Wait()
|
||||
|
||||
outputs, exitCodesList := parseSentinelOutput(string(output), len(file.Commands))
|
||||
outputs, exitCodesList := parseSentinelOutput(string(output), len(file.Commands), sentinel)
|
||||
|
||||
results := make([]CommandResult, len(file.Commands))
|
||||
for i, c := range file.Commands {
|
||||
|
|
|
|||
12
update.go
12
update.go
|
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
|
@ -20,6 +21,13 @@ func rewriteFile(file ShoutFile, results []CommandResult, originalContent string
|
|||
output = append(output, line)
|
||||
|
||||
if cmdIdx >= len(file.Commands) || cmdIdx >= len(results) {
|
||||
// Out of bounds — skip past expected output, preserving original lines
|
||||
j := i + 1
|
||||
for j < len(lines) && !strings.HasPrefix(lines[j], "$ ") {
|
||||
j++
|
||||
}
|
||||
output = append(output, lines[i+1:j]...)
|
||||
i = j - 1
|
||||
cmdIdx++
|
||||
continue
|
||||
}
|
||||
|
|
@ -62,6 +70,10 @@ func rewriteFile(file ShoutFile, results []CommandResult, originalContent string
|
|||
output = append(output, result.Actual...)
|
||||
if oldExitMarker != "" {
|
||||
output = append(output, oldExitMarker)
|
||||
} else if len(result.Actual) > 0 && exitCodeMarkerRe.MatchString(result.Actual[len(result.Actual)-1]) {
|
||||
// Actual output's last line looks like an exit code marker.
|
||||
// Add an explicit marker to prevent parser from consuming it as one.
|
||||
output = append(output, fmt.Sprintf("[%d]", result.ExitCode))
|
||||
}
|
||||
for k := 0; k < trailingBlanks; k++ {
|
||||
output = append(output, "")
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user