Builder diagnostics go to stderr; stdout is the result alone
The logging added a line to stdout, and the genesis path parses the builder's stdout as JSON — so the first log line broke the parse with "invalid character 'c'", the c from "[clone]". A build that had worked stopped working because of a print statement. The installer's runner captures stdout alone (cmd.Output), and the contract was already stdout=result, stderr=everything else. The fix is to honour it: every builder diagnostic — the step log, the per-command echo, the module path's own lines — goes to stderr. Stdout carries only once.go's result JSON. And a unit test now fails if any fmt.Print to stdout appears in the two builder command files, except the three that belong there: the result, --version, and --help. A guard, because this was invisible until a 20-minute run hit it, and the same class of mistake should fail in milliseconds next time. Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
This commit is contained in:
@@ -139,7 +139,7 @@ func run() error {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("building for the mesh, publishing to %s\n", registry)
|
||||
fmt.Fprintf(os.Stderr, "building for the mesh, publishing to %s\n", registry)
|
||||
publisher := builder.Registry{Address: registry, Run: builder.Command}
|
||||
|
||||
for {
|
||||
@@ -164,14 +164,14 @@ func answer(ctx context.Context, channel *amqp.Channel, publisher builder.Publis
|
||||
// until it either finishes or fails is indistinguishable from one that never arrived — which
|
||||
// cost a long diagnosis against a running mesh, chasing "the handler never fired" when the
|
||||
// truth was only that the handler said nothing until the end.
|
||||
fmt.Printf("a build request arrived (%d bytes)\n", len(delivery.Body))
|
||||
fmt.Fprintf(os.Stderr, "a build request arrived (%d bytes)\n", len(delivery.Body))
|
||||
|
||||
var request link.BuildRequest
|
||||
if err := json.Unmarshal(delivery.Body, &request); err != nil {
|
||||
// Unreadable. Acknowledged and dropped rather than requeued: a message this builder
|
||||
// cannot parse will not become parseable by being delivered again, and requeueing it
|
||||
// would put it in front of every real request for ever.
|
||||
fmt.Printf("a request could not be read and was dropped: %v\n", err)
|
||||
fmt.Fprintf(os.Stderr, "a request could not be read and was dropped: %v\n", err)
|
||||
_ = delivery.Ack(false)
|
||||
return
|
||||
}
|
||||
@@ -180,19 +180,19 @@ func answer(ctx context.Context, channel *amqp.Channel, publisher builder.Publis
|
||||
ID: request.ID, Repository: request.Repository, Path: request.Path,
|
||||
Ref: request.Ref, On: on,
|
||||
}
|
||||
fmt.Printf("building %s", request.Repository)
|
||||
fmt.Fprintf(os.Stderr, "building %s", request.Repository)
|
||||
if request.Path != "" {
|
||||
fmt.Printf(" at %s", request.Path)
|
||||
fmt.Fprintf(os.Stderr, " at %s", request.Path)
|
||||
}
|
||||
if request.Ref != "" {
|
||||
fmt.Printf(" at %s", request.Ref)
|
||||
fmt.Fprintf(os.Stderr, " at %s", request.Ref)
|
||||
}
|
||||
fmt.Println()
|
||||
fmt.Fprintln(os.Stderr)
|
||||
|
||||
built, err := builder.Build(ctx, builder.Command, publisher,
|
||||
request.Repository, request.Path, request.Ref, workspace, request.Held,
|
||||
func(step, message string) {
|
||||
fmt.Printf(" [%s] %s\n", step, message)
|
||||
fmt.Fprintf(os.Stderr, " [%s] %s\n", step, message)
|
||||
})
|
||||
if err != nil {
|
||||
// A failure is a result. A build that fails and says nothing is indistinguishable from a
|
||||
@@ -212,7 +212,7 @@ func answer(ctx context.Context, channel *amqp.Channel, publisher builder.Publis
|
||||
})
|
||||
}
|
||||
result.Against = built.Against
|
||||
fmt.Printf(" built %s from %s\n", built.Manifest.Module, short(built.Commit))
|
||||
fmt.Fprintf(os.Stderr, " built %s from %s\n", built.Manifest.Module, short(built.Commit))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user