Answer a single pull request's check with its request id on clean standard output (issue 1311) #403
@@ -902,16 +902,14 @@ func deliveryCommand(ctx context.Context, args []string) error {
|
||||
if *group == "" && len(members) == 1 {
|
||||
// One head, checked again as the forge's announcement would have it (a recheck): the controller's
|
||||
// own path for a pull request, run because the delivery's owner asked.
|
||||
// Its prose goes to standard error: standard output is the answer's JSON alone, or the seat verb
|
||||
// reads no answer from it (novox/hq issue 1311).
|
||||
var asked string
|
||||
if err := sayingOnTheBus(ctx, inv, func() (err error) {
|
||||
asked, err = (following{open: open, oneShot: true}).pullUpdated(ctx, members[0].pull(), os.Stderr)
|
||||
return err
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return answer(recheckAnswer(asked, members[0].ID))
|
||||
return recheckOneHead(members[0].ID, answer, func() (string, error) {
|
||||
var asked string
|
||||
err := sayingOnTheBus(ctx, inv, func() (err error) {
|
||||
asked, err = (following{open: open, oneShot: true}).pullUpdated(ctx, members[0].pull(), os.Stderr)
|
||||
return err
|
||||
})
|
||||
return asked, err
|
||||
})
|
||||
}
|
||||
if *group == "" || len(members) < 2 {
|
||||
return errors.New("delivery check --group <id> --members <json> (two heads or more), or --members <one head>")
|
||||
@@ -1388,3 +1386,19 @@ func overNATS(conn *nats.Conn) (link.Bus, error) {
|
||||
func recheckAnswer(asked, member string) map[string]any {
|
||||
return map[string]any{"asked": asked, "rechecked": member}
|
||||
}
|
||||
|
||||
// recheckOneHead runs a one-head `delivery check` and answers it as data. Standard output is the answer's JSON alone,
|
||||
// or the seat verb reads no answer from it (novox/hq issue 1311): while the check runs, whatever it prints to standard
|
||||
// output (the controller's own path prints its prose there, as do the stores it reads) goes to standard error.
|
||||
func recheckOneHead(member string, answer func(any) error, ask func() (string, error)) error {
|
||||
real := os.Stdout
|
||||
os.Stdout = os.Stderr
|
||||
asked, err := func() (string, error) {
|
||||
defer func() { os.Stdout = real }()
|
||||
return ask()
|
||||
}()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return answer(recheckAnswer(asked, member))
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -44,3 +46,40 @@ func TestSayCheckedWritesTheSameLineToItsWriter(t *testing.T) {
|
||||
t.Fatalf("sayChecked said %q; want %q", w.String(), want)
|
||||
}
|
||||
}
|
||||
|
||||
// Whatever the check prints to standard output while it runs (checks.go namedForTheCheck, the inventory's pointer
|
||||
// repair) must not reach the answer's standard output: it is exactly one JSON document (novox/hq issue 1311).
|
||||
func TestOneHeadRecheckLeavesStandardOutputTheAnswerAlone(t *testing.T) {
|
||||
r, w, err := os.Pipe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
e, ew, err := os.Pipe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
realOut, realErr := os.Stdout, os.Stderr
|
||||
os.Stdout, os.Stderr = w, ew
|
||||
runErr := recheckOneHead("novox/x@abc", func(v any) error { return json.NewEncoder(os.Stdout).Encode(v) },
|
||||
func() (string, error) {
|
||||
fmt.Println(" the earlier checks' builds could not be read, so the check builds")
|
||||
fmt.Fprintln(os.Stdout, "repaired a pointer")
|
||||
return "check-42", nil
|
||||
})
|
||||
os.Stdout, os.Stderr = realOut, realErr
|
||||
w.Close()
|
||||
ew.Close()
|
||||
if runErr != nil {
|
||||
t.Fatal(runErr)
|
||||
}
|
||||
var out, errOut bytes.Buffer
|
||||
_, _ = out.ReadFrom(r)
|
||||
_, _ = errOut.ReadFrom(e)
|
||||
var doc map[string]any
|
||||
if err := json.Unmarshal(out.Bytes(), &doc); err != nil || doc["asked"] != "check-42" || doc["rechecked"] != "novox/x@abc" {
|
||||
t.Fatalf("standard output %q is not the answer alone (%v)", out.String(), err)
|
||||
}
|
||||
if !strings.Contains(errOut.String(), "repaired a pointer") {
|
||||
t.Fatalf("the prose should be on standard error: %q", errOut.String())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user