Bound retained Go command diagnostics
This commit is contained in:
@@ -95,7 +95,7 @@ func New(ctx context.Context, directory string) (*Tool, error) {
|
||||
// local Go toolchain. It preserves caller module-network settings and caches,
|
||||
// while disabling workspace discovery and automatic toolchain downloads.
|
||||
func PrepareBuildDependencies(ctx context.Context, directory string) error {
|
||||
captured := &combinedWriter{}
|
||||
captured := &combinedWriter{mode: captureStatus}
|
||||
err := process.Run(ctx, process.Invocation{
|
||||
Args: []string{"go", "mod", "download", "-modfile="},
|
||||
Dir: directory,
|
||||
@@ -107,7 +107,8 @@ func PrepareBuildDependencies(ctx context.Context, directory string) error {
|
||||
Stderr: captured,
|
||||
})
|
||||
if err != nil {
|
||||
if output := strings.TrimSpace(string(captured.take())); output != "" {
|
||||
omitted := captured.omitted()
|
||||
if output := diagnosticText(captured.take(), omitted); output != "" {
|
||||
return fmt.Errorf("go mod download: %w: %s", err, output)
|
||||
}
|
||||
return fmt.Errorf("go mod download: %w", err)
|
||||
@@ -140,7 +141,11 @@ func (tool *Tool) execute(ctx context.Context, directory string, mode Mode, targ
|
||||
return nil, errors.New("Go command requires arguments")
|
||||
}
|
||||
environment := tool.environment(mode, target)
|
||||
captured := &combinedWriter{output: output}
|
||||
modeCapture := captureStatus
|
||||
if keepOutput {
|
||||
modeCapture = captureFull
|
||||
}
|
||||
captured := &combinedWriter{output: output, mode: modeCapture}
|
||||
commandArgs := append([]string{"go"}, args...)
|
||||
err := process.Run(ctx, process.Invocation{
|
||||
Args: commandArgs,
|
||||
@@ -152,16 +157,28 @@ func (tool *Tool) execute(ctx context.Context, directory string, mode Mode, targ
|
||||
if forwardErr := captured.forwardingError(); forwardErr != nil {
|
||||
err = errors.Join(err, fmt.Errorf("forward Go output: %w", forwardErr))
|
||||
}
|
||||
if err == nil && !keepOutput {
|
||||
return nil, nil
|
||||
}
|
||||
omitted := captured.omitted()
|
||||
contents := captured.take()
|
||||
if err != nil {
|
||||
if len(contents) != 0 {
|
||||
return outputBytes(contents, keepOutput), fmt.Errorf("go %s: %w: %s", strings.Join(args, " "), err, strings.TrimSpace(string(contents)))
|
||||
if diagnostic := diagnosticText(contents, omitted); diagnostic != "" {
|
||||
return outputBytes(contents, keepOutput), fmt.Errorf("go %s: %w: %s", strings.Join(args, " "), err, diagnostic)
|
||||
}
|
||||
return outputBytes(contents, keepOutput), fmt.Errorf("go %s: %w", strings.Join(args, " "), err)
|
||||
}
|
||||
return outputBytes(contents, keepOutput), nil
|
||||
}
|
||||
|
||||
func diagnosticText(contents []byte, omitted bool) string {
|
||||
text := strings.TrimSpace(string(contents))
|
||||
if omitted {
|
||||
return "[earlier Go output omitted]\n" + text
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
func outputBytes(contents []byte, keep bool) []byte {
|
||||
if keep {
|
||||
return contents
|
||||
@@ -232,17 +249,63 @@ type combinedWriter struct {
|
||||
mu sync.Mutex
|
||||
output io.Writer
|
||||
buffer bytes.Buffer
|
||||
mode captureMode
|
||||
tail []byte
|
||||
head int
|
||||
truncated bool
|
||||
forwardErr error
|
||||
}
|
||||
|
||||
type captureMode uint8
|
||||
|
||||
const (
|
||||
captureFull captureMode = iota
|
||||
captureStatus
|
||||
statusDiagnosticLimit = 1 << 20
|
||||
)
|
||||
|
||||
func (writer *combinedWriter) capture(contents []byte) {
|
||||
if writer.mode == captureFull {
|
||||
_, _ = writer.buffer.Write(contents)
|
||||
return
|
||||
}
|
||||
if len(contents) >= statusDiagnosticLimit {
|
||||
writer.truncated = writer.truncated || len(writer.tail) != 0 || len(contents) > statusDiagnosticLimit
|
||||
if cap(writer.tail) < statusDiagnosticLimit {
|
||||
writer.tail = make([]byte, statusDiagnosticLimit)
|
||||
} else {
|
||||
writer.tail = writer.tail[:statusDiagnosticLimit]
|
||||
}
|
||||
copy(writer.tail, contents[len(contents)-statusDiagnosticLimit:])
|
||||
writer.head = 0
|
||||
return
|
||||
}
|
||||
if available := statusDiagnosticLimit - len(writer.tail); available > 0 {
|
||||
n := min(available, len(contents))
|
||||
if needed := len(writer.tail) + n; needed > cap(writer.tail) {
|
||||
capacity := min(statusDiagnosticLimit, max(needed, 2*cap(writer.tail)))
|
||||
grown := make([]byte, len(writer.tail), capacity)
|
||||
copy(grown, writer.tail)
|
||||
writer.tail = grown
|
||||
}
|
||||
writer.tail = append(writer.tail, contents[:n]...)
|
||||
contents = contents[n:]
|
||||
}
|
||||
if len(contents) == 0 {
|
||||
return
|
||||
}
|
||||
writer.truncated = true
|
||||
n := copy(writer.tail[writer.head:], contents)
|
||||
copy(writer.tail, contents[n:])
|
||||
writer.head = (writer.head + len(contents)) % statusDiagnosticLimit
|
||||
}
|
||||
|
||||
func (writer *combinedWriter) Write(contents []byte) (int, error) {
|
||||
writer.mu.Lock()
|
||||
defer writer.mu.Unlock()
|
||||
writer.capture(contents)
|
||||
if writer.output == nil {
|
||||
return writer.buffer.Write(contents)
|
||||
}
|
||||
if _, err := writer.buffer.Write(contents); err != nil {
|
||||
return 0, err
|
||||
return len(contents), nil
|
||||
}
|
||||
written, err := writer.output.Write(contents)
|
||||
if err == nil && written != len(contents) {
|
||||
@@ -254,6 +317,12 @@ func (writer *combinedWriter) Write(contents []byte) (int, error) {
|
||||
return written, err
|
||||
}
|
||||
|
||||
func (writer *combinedWriter) omitted() bool {
|
||||
writer.mu.Lock()
|
||||
defer writer.mu.Unlock()
|
||||
return writer.truncated
|
||||
}
|
||||
|
||||
func (writer *combinedWriter) forwardingError() error {
|
||||
writer.mu.Lock()
|
||||
defer writer.mu.Unlock()
|
||||
@@ -265,5 +334,18 @@ func (writer *combinedWriter) forwardingError() error {
|
||||
func (writer *combinedWriter) take() []byte {
|
||||
writer.mu.Lock()
|
||||
defer writer.mu.Unlock()
|
||||
return writer.buffer.Bytes()
|
||||
if writer.mode == captureFull {
|
||||
contents := writer.buffer.Bytes()
|
||||
writer.buffer = bytes.Buffer{}
|
||||
return contents
|
||||
}
|
||||
contents := writer.tail
|
||||
if writer.head != 0 {
|
||||
contents = make([]byte, 0, len(writer.tail))
|
||||
contents = append(contents, writer.tail[writer.head:]...)
|
||||
contents = append(contents, writer.tail[:writer.head]...)
|
||||
}
|
||||
writer.tail = nil
|
||||
writer.head = 0
|
||||
return contents
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user