internal/pkg: exec prefix verbose output
All checks were successful
Test / Create distribution (push) Successful in 47s
Test / Sandbox (push) Successful in 2m52s
Test / ShareFS (push) Successful in 4m50s
Test / Hpkg (push) Successful in 5m10s
Test / Sandbox (race detector) (push) Successful in 5m18s
Test / Hakurei (push) Successful in 5m25s
Test / Hakurei (race detector) (push) Successful in 7m32s
Test / Flake checks (push) Successful in 1m41s

This proxies program output through msg with a name and fd prefix. This also avoids introducing additional information to the container via process stdout/stderr.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
2026-01-12 22:15:01 +09:00
parent 41a8d03dd2
commit 5fe166a4a7

View File

@@ -1,11 +1,14 @@
package pkg package pkg
import ( import (
"bufio"
"bytes" "bytes"
"context" "context"
"errors" "errors"
"fmt" "fmt"
"io"
"os" "os"
"os/exec"
"path" "path"
"slices" "slices"
"strconv" "strconv"
@@ -16,6 +19,7 @@ import (
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/fhs" "hakurei.app/container/fhs"
"hakurei.app/container/std" "hakurei.app/container/std"
"hakurei.app/message"
) )
// AbsWork is the container pathname [CureContext.GetWorkDir] is mounted on. // AbsWork is the container pathname [CureContext.GetWorkDir] is mounted on.
@@ -57,7 +61,8 @@ const (
// Methods of execArtifact does not modify any struct field or underlying arrays // Methods of execArtifact does not modify any struct field or underlying arrays
// referred to by slices. // referred to by slices.
type execArtifact struct { type execArtifact struct {
// Caller-supplied user-facing reporting name. // Caller-supplied user-facing reporting name, guaranteed to be nonzero
// during initialisation.
name string name string
// Caller-supplied inner mount points. // Caller-supplied inner mount points.
paths []ExecPath paths []ExecPath
@@ -219,6 +224,23 @@ const (
execWaitDelay = time.Nanosecond execWaitDelay = time.Nanosecond
) )
// scanVerbose prefixes program output for a verbose [message.Msg].
func scanVerbose(
msg message.Msg,
done chan<- struct{},
prefix string,
r io.Reader,
) {
defer close(done)
s := bufio.NewScanner(r)
for s.Scan() {
msg.Verbose(prefix, s.Text())
}
if err := s.Err(); err != nil && !errors.Is(err, os.ErrClosed) {
msg.Verbose("*"+prefix, err)
}
}
// cure is like Cure but allows optional host net namespace. This is used for // cure is like Cure but allows optional host net namespace. This is used for
// the [KnownChecksum] variant where networking is allowed. // the [KnownChecksum] variant where networking is allowed.
func (a *execArtifact) cure(f *FContext, hostNet bool) (err error) { func (a *execArtifact) cure(f *FContext, hostNet bool) (err error) {
@@ -250,8 +272,26 @@ func (a *execArtifact) cure(f *FContext, hostNet bool) (err error) {
z.Hostname = "cure-net" z.Hostname = "cure-net"
} }
z.Uid, z.Gid = (1<<10)-1, (1<<10)-1 z.Uid, z.Gid = (1<<10)-1, (1<<10)-1
if f.GetMessage().IsVerbose() { if msg := f.GetMessage(); msg.IsVerbose() {
z.Stdout, z.Stderr = os.Stdout, os.Stderr var stdout, stderr io.ReadCloser
if stdout, err = z.StdoutPipe(); err != nil {
return
}
if stderr, err = z.StderrPipe(); err != nil {
_ = stdout.Close()
return
}
defer func() {
if err != nil && !errors.As(err, new(*exec.ExitError)) {
_ = stdout.Close()
_ = stderr.Close()
}
}()
stdoutDone, stderrDone := make(chan struct{}), make(chan struct{})
go scanVerbose(msg, stdoutDone, "("+a.name+":1)", stdout)
go scanVerbose(msg, stderrDone, "("+a.name+":2)", stderr)
defer func() { <-stdoutDone; <-stderrDone }()
} }
z.Dir, z.Env, z.Path, z.Args = a.dir, a.env, a.path, a.args z.Dir, z.Env, z.Path, z.Args = a.dir, a.env, a.path, a.args