Judge a group's controller by itself with the validator it vendors, and copy nothing of mesh-host's (issue 1391, 3 of 3)

judgeFromTheGroup copies nothing into the judge's vendor tree: a mesh-host member whose tree holds no validate/ does not fail the judge, and the verdict names the validator as mesh-sdk's at the pin of the controller that judges. replaceGoFiles and its test are gone.
This commit is contained in:
2026-10-20 09:35:38 +00:00
parent 52d5e38729
commit 0fc455aa65
2 changed files with 11 additions and 93 deletions
+11 -72
View File
@@ -1446,42 +1446,6 @@ func refWords(ref string) string {
// commitLike is a ref that names a commit. // commitLike is a ref that names a commit.
var commitLike = regexp.MustCompile(`^[0-9a-f]{7,40}$`) var commitLike = regexp.MustCompile(`^[0-9a-f]{7,40}$`)
// replaceGoFiles puts a package's Go files — never its tests — in place of another copy's.
func replaceGoFiles(from, into string) error {
old, err := filepath.Glob(filepath.Join(into, "*.go"))
if err != nil {
return err
}
for _, f := range old {
if err := os.Remove(f); err != nil {
return err
}
}
if err := os.MkdirAll(into, 0o755); err != nil {
return err
}
files, err := filepath.Glob(filepath.Join(from, "*.go"))
if err != nil {
return err
}
if len(files) == 0 {
return fmt.Errorf("%s holds no Go files", from)
}
for _, f := range files {
if strings.HasSuffix(f, "_test.go") {
continue
}
body, err := os.ReadFile(f)
if err != nil {
return err
}
if err := os.WriteFile(filepath.Join(into, filepath.Base(f)), body, 0o644); err != nil {
return err
}
}
return nil
}
// problemLines are the lines of a module check's output that name a problem: not a manifest found ok, not // problemLines are the lines of a module check's output that name a problem: not a manifest found ok, not
// the count, not the closing note. // the count, not the closing note.
func problemLines(s string) []string { func problemLines(s string) []string {
@@ -1794,20 +1758,20 @@ func groupDir(spec CheckSpec, root, tree, repo string) string {
// to), and false leaves the judge to the rules before it. // to), and false leaves the judge to the rules before it.
// //
// - A member with a mesh-controller sibling is judged by the controller built from that sibling's merge (read beside // - A member with a mesh-controller sibling is judged by the controller built from that sibling's merge (read beside
// the check), with the node-engine's validator from the member's own change when it is mesh-host's, else from a // the check), with the validator that controller takes from mesh-sdk at its pin.
// mesh-host sibling's merge, else the one that controller vendors. // - A mesh-controller member is judged by itself, and a mesh-host sibling changes nothing of it: the node-engine holds
// - A mesh-controller member is judged by itself, with a mesh-host sibling's validator in place of the one it vendors. // no validator any more (issue 1391, novox/hq ADR 0446 clause 5).
// //
// **The judge is unreviewed code** and is built where nothing the change's script does can reach it: from a copy of the // **The judge is unreviewed code** and is built where nothing the change's script does can reach it: from a copy of the
// merge made apart from the workspace's tree, with the judge-build environment (JudgeBuildEnv) — a home and Go cache of // merge made apart from the workspace's tree, with the judge-build environment (JudgeBuildEnv) — a home and Go cache of
// its own, no global or system git configuration, no version stamping from git — and kept in the judge cache, which no // its own, no global or system git configuration, no version stamping from git — and kept in the judge cache, which no
// check's container mounts, only for the same inputs: the controller merge and the base commits it was merged onto, the // check's container mounts, only for the same inputs: the controller merge and the base commits it was merged onto, the
// validator's source and commit. // controller's pin of the validator.
func judgeFromTheGroup(ctx context.Context, labelled, run Runner, spec CheckSpec, root, tree string, judged CheckVerdict, func judgeFromTheGroup(ctx context.Context, labelled, run Runner, spec CheckSpec, root, tree string, judged CheckVerdict,
said *siblingJudgement, inToolchain func(string, []string, ...string) []string, said *siblingJudgement, inToolchain func(string, []string, ...string) []string,
say func(step, format string, args ...any)) (string, string, bool, error) { say func(step, format string, args ...any)) (string, string, bool, error) {
controller, host := siblingIn(spec, "mesh-controller"), siblingIn(spec, "mesh-host") controller := siblingIn(spec, "mesh-controller")
var source, validatorDir, from, validator string var source, from, validator string
var inputs []string var inputs []string
merge := func(dir string, s *Sibling) string { merge := func(dir string, s *Sibling) string {
return fmt.Sprintf("sibling %s#%d merge %s", s.Repository, s.Number, judged.Beside[dir]) return fmt.Sprintf("sibling %s#%d merge %s", s.Repository, s.Number, judged.Beside[dir])
@@ -1815,33 +1779,19 @@ func judgeFromTheGroup(ctx context.Context, labelled, run Runner, spec CheckSpec
switch { switch {
case len(spec.Group) > 0 && groupDir(spec, root, tree, "mesh-controller") != "": case len(spec.Group) > 0 && groupDir(spec, root, tree, "mesh-controller") != "":
// **A delivery group's composed check is judged by the group's own controller** (novox/hq ADR 0239 §14), whatever // **A delivery group's composed check is judged by the group's own controller** (novox/hq ADR 0239 §14), whatever
// base it merges into: the member that moves mesh-controller, as merged, with the validator of the group's mesh-host member's // base it merges into: the member that moves mesh-controller, as merged, with the validator it takes from mesh-sdk at
// merge when it has one, else the one that controller vendors. // its pin. A mesh-host member brings no validator (issue 1391), and its tree need hold none.
source, validatorDir = groupDir(spec, root, tree, "mesh-controller"), groupDir(spec, root, tree, "mesh-host") source = groupDir(spec, root, tree, "mesh-controller")
from = "the group's own controller, merge " + headAt(ctx, run, source) from = "the group's own controller, merge " + headAt(ctx, run, source)
validator = "the validator the group's controller vendors" validator = "mesh-sdk's validator at the pin of the group's controller"
if validatorDir != "" {
validator = "the group's mesh-host merge " + headAt(ctx, run, validatorDir) + "'s validator"
}
inputs = append(inputs, "group member "+spec.Repo+" "+judged.Head+" onto "+judged.Trunk) inputs = append(inputs, "group member "+spec.Repo+" "+judged.Head+" onto "+judged.Trunk)
for _, m := range judged.Members { for _, m := range judged.Members {
inputs = append(inputs, "group member "+m.Repo+" "+m.Head+" onto "+m.Trunk) inputs = append(inputs, "group member "+m.Repo+" "+m.Head+" onto "+m.Trunk)
} }
case spec.Judge == "self" && host != nil:
source, validatorDir = tree, filepath.Join(root, "mesh-host")
from = "this change's own merge " + judged.Head
validator = merge("mesh-host", host) + "'s validator"
case spec.Judge != "self" && controller != nil: case spec.Judge != "self" && controller != nil:
source = filepath.Join(root, "mesh-controller") source = filepath.Join(root, "mesh-controller")
from = merge("mesh-controller", controller) from = merge("mesh-controller", controller)
switch { validator = "mesh-sdk's validator at the pin of " + merge("mesh-controller", controller)
case spec.Judge == "validator":
validatorDir, validator = tree, "the change's own validator"
case host != nil:
validatorDir, validator = filepath.Join(root, "mesh-host"), merge("mesh-host", host)+"'s validator"
default:
validator = "the validator " + merge("mesh-controller", controller) + " vendors"
}
default: default:
return "", "", false, nil return "", "", false, nil
} }
@@ -1851,9 +1801,6 @@ func judgeFromTheGroup(ctx context.Context, labelled, run Runner, spec CheckSpec
// A merge made for a check is a new commit each time (its time is in it); what it holds is the same for the same // A merge made for a check is a new commit each time (its time is in it); what it holds is the same for the same
// heads and bases, so the key names those and the trees the judge is built from. // heads and bases, so the key names those and the trees the judge is built from.
inputs = append(inputs, "member "+judged.Head+" onto "+judged.Trunk, "controller tree "+treeAt(ctx, run, source)) inputs = append(inputs, "member "+judged.Head+" onto "+judged.Trunk, "controller tree "+treeAt(ctx, run, source))
if validatorDir != "" {
inputs = append(inputs, "validator tree "+treeAt(ctx, run, validatorDir))
}
sum := sha256.Sum256([]byte(strings.Join(inputs, "\n"))) sum := sha256.Sum256([]byte(strings.Join(inputs, "\n")))
key := "siblings-" + hex.EncodeToString(sum[:16]) key := "siblings-" + hex.EncodeToString(sum[:16])
bin := filepath.Join(root, "bin", "judge-of-the-group") bin := filepath.Join(root, "bin", "judge-of-the-group")
@@ -1867,14 +1814,6 @@ func judgeFromTheGroup(ctx context.Context, labelled, run Runner, spec CheckSpec
if err := copyCheckout(ctx, run, source, src); err != nil { if err := copyCheckout(ctx, run, source, src); err != nil {
return "", "", true, fmt.Errorf("the controller that judges (%s) could not be copied: %w", from, err) return "", "", true, fmt.Errorf("the controller that judges (%s) could not be copied: %w", from, err)
} }
if validatorDir != "" {
vendored := filepath.Join(src, "vendor", "github.com", "novox", "mesh-host")
for _, pkg := range []string{"validate", filepath.Join("internal", "declaration")} {
if err := replaceGoFiles(filepath.Join(validatorDir, pkg), filepath.Join(vendored, pkg)); err != nil {
return "", "the validator (" + validator + ") could not be put in the judge: " + err.Error(), true, nil
}
}
}
if _, err := labelled(ctx, root, "docker", inToolchain(src, nil, if _, err := labelled(ctx, root, "docker", inToolchain(src, nil,
"go", "build", "-o", bin, "./cmd/mesh-controller")...); err != nil { "go", "build", "-o", bin, "./cmd/mesh-controller")...); err != nil {
return "", "the controller that judges (" + from + ") does not build with " + validator + ": " + firstLine(err.Error()), return "", "the controller that judges (" + from + ") does not build with " + validator + ": " + firstLine(err.Error()),
-21
View File
@@ -3,7 +3,6 @@ package builder
import ( import (
"encoding/json" "encoding/json"
"os" "os"
"path/filepath"
"strings" "strings"
"testing" "testing"
"time" "time"
@@ -82,26 +81,6 @@ func TestAScriptDeclaresItsToolchain(t *testing.T) {
} }
} }
// A node-engine change's validator is put in place of the one the judge vendors: its Go files, never its tests.
func TestTheChangesValidatorReplacesTheVendoredOne(t *testing.T) {
from, into := t.TempDir(), t.TempDir()
for name, body := range map[string]string{"v.go": "package validate // new", "v_test.go": "package validate"} {
if err := os.WriteFile(filepath.Join(from, name), []byte(body), 0o644); err != nil {
t.Fatal(err)
}
}
if err := os.WriteFile(filepath.Join(into, "old.go"), []byte("package validate // old"), 0o644); err != nil {
t.Fatal(err)
}
if err := replaceGoFiles(from, into); err != nil {
t.Fatal(err)
}
got, _ := filepath.Glob(filepath.Join(into, "*.go"))
if len(got) != 1 || filepath.Base(got[0]) != "v.go" {
t.Fatalf("the vendored validator holds %v", got)
}
}
// aJudge is a controller that judges as told: `merge-gate` answers a warning, `module check` refuses a // aJudge is a controller that judges as told: `merge-gate` answers a warning, `module check` refuses a
// manifest that says it is broken. What the gate layer is tested against without building the real one. // manifest that says it is broken. What the gate layer is tested against without building the real one.
var aJudge = map[string]string{ var aJudge = map[string]string{