Sanitized root snapshot from private source commit a72903c63e1753f9e6ffbf40453c0830bdfc05c5 and tree 295641e67eef5979da76746d8ae271249568263e. Private development history and workflows are excluded by the exact allowlist. AI-assisted: OpenAI Codex helped implement, test, and audit this preview. Signed-off-by: Cole Speelman <crspeelman@gmail.com>
73 lines
1.6 KiB
Go
73 lines
1.6 KiB
Go
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
package process
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
const maxOutput = 4 << 20
|
|
|
|
type Runner interface {
|
|
Run(ctx context.Context, dir string, env map[string]string, name string, args ...string) ([]byte, error)
|
|
}
|
|
|
|
type ExecRunner struct{}
|
|
|
|
func (ExecRunner) Run(ctx context.Context, dir string, env map[string]string, name string, args ...string) ([]byte, error) {
|
|
cmd := exec.CommandContext(ctx, name, args...)
|
|
cmd.Dir = dir
|
|
cmd.Env = mergeEnv(os.Environ(), env)
|
|
var output limitedBuffer
|
|
cmd.Stdout = &output
|
|
cmd.Stderr = &output
|
|
err := cmd.Run()
|
|
if err != nil {
|
|
return output.Bytes(), fmt.Errorf("%s failed: %w: %s", name, err, strings.TrimSpace(output.String()))
|
|
}
|
|
return output.Bytes(), nil
|
|
}
|
|
|
|
type limitedBuffer struct{ bytes.Buffer }
|
|
|
|
func (b *limitedBuffer) Write(p []byte) (int, error) {
|
|
written := len(p)
|
|
remaining := maxOutput - b.Len()
|
|
if remaining > 0 {
|
|
if len(p) > remaining {
|
|
p = p[:remaining]
|
|
}
|
|
_, _ = b.Buffer.Write(p)
|
|
}
|
|
return written, nil
|
|
}
|
|
|
|
func mergeEnv(base []string, extra map[string]string) []string {
|
|
values := make(map[string]string, len(base)+len(extra))
|
|
for _, pair := range base {
|
|
key, value, ok := strings.Cut(pair, "=")
|
|
if ok {
|
|
values[key] = value
|
|
}
|
|
}
|
|
for key, value := range extra {
|
|
values[key] = value
|
|
}
|
|
keys := make([]string, 0, len(values))
|
|
for key := range values {
|
|
keys = append(keys, key)
|
|
}
|
|
sort.Strings(keys)
|
|
out := make([]string, 0, len(keys))
|
|
for _, key := range keys {
|
|
out = append(out, key+"="+values[key])
|
|
}
|
|
return out
|
|
}
|