Say which validator judged a release-branch check, and prove clause 5
mesh/merge-gate head ea7d91bff190 merged into trunk 535c7641ac41 — pass: builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step; every …
mesh/repo-check head ea7d91bff190 merged into trunk 535c7641ac41 — pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
mesh/merge-gate head ea7d91bff190 merged into trunk 535c7641ac41 — pass: builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step; every …
mesh/repo-check head ea7d91bff190 merged into trunk 535c7641ac41 — pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
ADR 0358 version 4 asks the verdict to say whether mesh-host's release-branch validator or the vendored one judged, and checks that a release branch's own pull request into its trunk is judged as any trunk pull request (issue 704). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -331,7 +331,7 @@ func answer(ctx context.Context, publisher builder.Publisher, on, workspace stri
|
||||
result.Check = &link.CheckOutcome{Verdict: v.Verdict, Summary: v.Summary, Report: v.Report,
|
||||
Took: v.Took.Round(time.Second).String(), Gate: layerOf(v.Gate), RepoCheck: layerOf(v.Repo),
|
||||
Head: v.Head, Trunk: v.Trunk, Beside: v.Beside, Built: checkBuiltOf(v.Built),
|
||||
Release: v.Release, JudgedBy: v.JudgedBy, BesideAt: v.BesideAt}
|
||||
Release: v.Release, JudgedBy: v.JudgedBy, BesideAt: v.BesideAt, Validator: v.Validator}
|
||||
// What each of a group's heads was judged as (novox/hq ADR 0323 §6).
|
||||
for _, m := range v.Members {
|
||||
result.Check.Members = append(result.Check.Members, link.CheckedMember{Owner: m.Owner, Repo: m.Repo,
|
||||
|
||||
@@ -576,6 +576,7 @@ func checkedOf(result link.BuildResult) link.Checked {
|
||||
c.Trunk, c.Beside, c.Took = result.Check.Trunk, result.Check.Beside, result.Check.Took
|
||||
c.Built = result.Check.Built
|
||||
c.Release, c.JudgedBy, c.BesideAt = result.Check.Release, result.Check.JudgedBy, result.Check.BesideAt
|
||||
c.Validator = result.Check.Validator
|
||||
if c.Gate == nil {
|
||||
// A build seat from before the layers: its verdict is the gate's.
|
||||
c.Gate = &link.CheckLayer{Verdict: c.Verdict, Summary: c.Summary}
|
||||
|
||||
@@ -156,12 +156,40 @@ func TestAReleaseBranchVerdictNamesTheBranchTheJudgeAndWhatItRead(t *testing.T)
|
||||
c := checkedOf(link.BuildResult{ID: "b", Ref: "aaaaaaaaaaaa0000", Checked: asked, Check: &link.CheckOutcome{
|
||||
Verdict: "pass", Head: "aaaaaaaaaaaa", Trunk: "111111111111", Gate: &link.CheckLayer{Verdict: "pass"},
|
||||
RepoCheck: &link.CheckLayer{Verdict: "pass"}, Release: aRelease, JudgedBy: "fcdecb436117",
|
||||
Beside: map[string]string{"mesh-host": "222222222222", "mesh-lab": "333333333333"},
|
||||
BesideAt: map[string]string{"mesh-host": aRelease, "mesh-lab": "main"}}})
|
||||
Beside: map[string]string{"mesh-host": "222222222222", "mesh-lab": "333333333333"},
|
||||
BesideAt: map[string]string{"mesh-host": aRelease, "mesh-lab": "main"},
|
||||
Validator: "mesh-host's validator of " + aRelease + " at 222222222222"}})
|
||||
if c.Release != aRelease || c.JudgedBy != "fcdecb436117" {
|
||||
t.Fatalf("the verdict names release %q judged by %q", c.Release, c.JudgedBy)
|
||||
}
|
||||
if c.BesideAt["mesh-host"] != aRelease || c.BesideAt["mesh-lab"] != "main" || c.Beside["mesh-host"] != "222222222222" {
|
||||
if c.BesideAt["mesh-host"] != aRelease || c.BesideAt["mesh-lab"] != "main" || c.Beside["mesh-host"] != "222222222222" ||
|
||||
c.Validator != "mesh-host's validator of "+aRelease+" at 222222222222" {
|
||||
t.Fatalf("the verdict names what it read beside it as %v at %v", c.Beside, c.BesideAt)
|
||||
}
|
||||
}
|
||||
|
||||
// **Clause 5** (ADR 0358 §5): a release branch's own pull request into its trunk is no release-branch check: the forge
|
||||
// is asked nothing, the request names no release branch, and the repositories beside it are read as for any trunk
|
||||
// pull request. Its delivery group's composed check is asked as for any other (askGroupCheck, unchanged).
|
||||
func TestAReleaseBranchsPullRequestIntoItsTrunkIsCheckedAsAnyTrunkPullRequest(t *testing.T) {
|
||||
entries := theCore()
|
||||
asked := aForgeWithBranches(t, map[string][]string{"novox/mesh-controller": {"main", aRelease}}, nil)
|
||||
for _, repo := range []string{"mesh-catalog", "mesh-controller", "mesh-host"} {
|
||||
p := link.PullUpdated{Owner: "novox", Repo: repo, Number: 400, Head: aRelease, Base: "main", Commit: "abc",
|
||||
Paths: []string{"modules/gitea/index.ts"}}
|
||||
got, err := releaseBranchOf(t.Context(), p, pullScope(p, entries, nil, nil), entries)
|
||||
if err != nil || got != "" || *asked != 0 {
|
||||
t.Fatalf("%s's release branch into main read as release %q (%v) after %d question(s)", repo, got, err, *asked)
|
||||
}
|
||||
}
|
||||
clone := func(s inventory.Source) (string, error) { return "http://git/" + s.Repository + ".git", nil }
|
||||
beside, err := besideOf(entries, func(module string) string { return "running-" + module }, clone, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for dir, b := range beside {
|
||||
if b.Ref == aRelease || b.Else != "" {
|
||||
t.Fatalf("beside a trunk pull request %s is read at %+v", dir, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,6 +198,10 @@ type CheckVerdict struct {
|
||||
Release string
|
||||
JudgedBy string
|
||||
BesideAt map[string]string
|
||||
// Validator says which node-engine validator the judge of a release-branch check was built with (ADR 0358 §2):
|
||||
// mesh-host's of the release branch with its commit, the one the release branch's controller vendors, the
|
||||
// change's own, or the one the change's own controller vendors. Empty for a check of any other base.
|
||||
Validator string
|
||||
}
|
||||
|
||||
// Layer is one layer of a check, judged.
|
||||
@@ -384,7 +388,7 @@ func Check(ctx context.Context, run Runner, spec CheckSpec, workspace, registry
|
||||
}
|
||||
named := func(v CheckVerdict) CheckVerdict {
|
||||
v.Head, v.Trunk, v.Merge, v.Beside, v.Members = judged.Head, judged.Trunk, judged.Merge, judged.Beside, judged.Members
|
||||
v.Release, v.JudgedBy, v.BesideAt = spec.Release, judged.JudgedBy, judged.BesideAt
|
||||
v.Release, v.JudgedBy, v.BesideAt, v.Validator = spec.Release, judged.JudgedBy, judged.BesideAt, judged.Validator
|
||||
return v
|
||||
}
|
||||
// **What the merge would move is built from a checkout nothing of the check touches** (novox/hq issue 703): the
|
||||
@@ -594,8 +598,10 @@ func Check(ctx context.Context, run Runner, spec CheckSpec, workspace, registry
|
||||
judgedBy = tree
|
||||
}
|
||||
judged.JudgedBy = headAt(ctx, run, judgedBy)
|
||||
fmt.Fprintf(&out, "--- %s\n", releaseWords(spec.Release, judged.JudgedBy, judged.BesideAt, judged.Beside))
|
||||
say("check", "%s", releaseWords(spec.Release, judged.JudgedBy, judged.BesideAt, judged.Beside))
|
||||
judged.Validator = releaseValidator(spec, judged.BesideAt, judged.Beside)
|
||||
said := releaseWords(spec.Release, judged.JudgedBy, judged.Validator, judged.BesideAt, judged.Beside)
|
||||
fmt.Fprintf(&out, "--- %s\n", said)
|
||||
say("check", "%s", said)
|
||||
}
|
||||
verdictFile := filepath.Join(root, "verdict.json")
|
||||
env := []string{EnvFacts + "=" + factsFile, EnvGate + "=" + gate, EnvGateStore + "=" + storeURL,
|
||||
@@ -1209,13 +1215,27 @@ func fullHeadAt(ctx context.Context, run Runner, tree string) string {
|
||||
return strings.TrimSpace(out)
|
||||
}
|
||||
|
||||
// releaseValidator says which node-engine validator a release-branch check's judge is built with (novox/hq ADR 0358
|
||||
// §2), by the rule judgeFor builds it by.
|
||||
func releaseValidator(spec CheckSpec, at, commits map[string]string) string {
|
||||
switch {
|
||||
case spec.Judge == "self":
|
||||
return "the validator the change's own controller vendors"
|
||||
case spec.Judge == "validator":
|
||||
return "the change's own validator"
|
||||
case at["mesh-host"] == spec.Release:
|
||||
return fmt.Sprintf("mesh-host's validator of %s at %s", spec.Release, commits["mesh-host"])
|
||||
}
|
||||
return fmt.Sprintf("the validator %s's controller vendors (mesh-host has no branch %s)", spec.Release, spec.Release)
|
||||
}
|
||||
|
||||
// releaseWords is how a release-branch check says what judged it and what it read (novox/hq ADR 0358 §4).
|
||||
func releaseWords(release, judgedBy string, at, commits map[string]string) string {
|
||||
func releaseWords(release, judgedBy, validator string, at, commits map[string]string) string {
|
||||
var read []string
|
||||
for _, dir := range sortedKeys(at) {
|
||||
read = append(read, fmt.Sprintf("%s %s at %s", dir, refWords(at[dir]), commits[dir]))
|
||||
}
|
||||
said := fmt.Sprintf("judged for the release branch %s by its controller at %s", release, judgedBy)
|
||||
said := fmt.Sprintf("judged for the release branch %s by its controller at %s, with %s", release, judgedBy, validator)
|
||||
if len(read) > 0 {
|
||||
said += "; read beside it: " + strings.Join(read, ", ")
|
||||
}
|
||||
|
||||
@@ -239,6 +239,19 @@ func TestACataloguePullRequestIntoAReleaseBranchIsJudgedByThatBranchsController(
|
||||
if !strings.Contains(v.Report, "read beside it: ") || !strings.Contains(v.Report, "mesh-host "+releaseBranch+" at "+hostRelease[:12]) {
|
||||
t.Fatalf("the report does not say what was read beside it:\n%s", v.Report)
|
||||
}
|
||||
// It says which validator judged: mesh-host's of the release branch, with its commit.
|
||||
if want := "mesh-host's validator of " + releaseBranch + " at " + hostRelease[:12]; v.Validator != want ||
|
||||
!strings.Contains(v.Report, want) {
|
||||
t.Fatalf("the verdict says the validator was %q, not %q:\n%s", v.Validator, want, v.Report)
|
||||
}
|
||||
// Each repository read beside it is named with the branch and the commit read.
|
||||
for dir := range beside {
|
||||
if v.BesideAt[dir] == "" || v.Beside[dir] == "" ||
|
||||
!strings.Contains(v.Report, dir+" "+v.BesideAt[dir]+" at "+v.Beside[dir]) {
|
||||
t.Fatalf("%s is not named with its branch and commit: at %q, commit %q\n%s", dir, v.BesideAt[dir],
|
||||
v.Beside[dir], v.Report)
|
||||
}
|
||||
}
|
||||
if n := builds(t, built); n != 1 {
|
||||
t.Fatalf("the judge was built %d times for one check", n)
|
||||
}
|
||||
@@ -306,3 +319,65 @@ func TestAPullRequestIntoTheTrunkIsJudgedAsBefore(t *testing.T) {
|
||||
t.Fatalf("mesh-host was read at %s, not the commit asked %s", v.Beside["mesh-host"], hostMain[:12])
|
||||
}
|
||||
}
|
||||
|
||||
// **Without mesh-host's release branch, the release branch's controller keeps the validator it vendors** (ADR 0358
|
||||
// §2), and the verdict says so.
|
||||
func TestWithoutTheNodeEnginesReleaseBranchTheJudgeKeepsItsVendoredValidator(t *testing.T) {
|
||||
aRuntimeThatBuildsTheJudge(t)
|
||||
registry := aFactsRegistry(t)
|
||||
beside, controllerRelease, _, _ := theReleasedMesh(t)
|
||||
host, hostMain, _ := aRepositoryWithARelease(t, aHostSaying("main's validator"), nil)
|
||||
beside["mesh-host"] = Beside{Repository: host, Ref: releaseBranch, Else: "main"}
|
||||
catalogue, head := aPullRequestInto(t, map[string]string{"modules/gitea/module.json": `{"module":"gitea"}`,
|
||||
CheckScript: "true\n"}, releaseBranch, map[string]string{"modules/gitea/index.ts": "x"})
|
||||
v, err := Check(t.Context(), Command, CheckSpec{ID: "check-704-vendored", Repository: catalogue, Ref: head,
|
||||
Owner: "novox", Repo: "mesh-catalog", Number: 286, Base: releaseBranch, Release: releaseBranch,
|
||||
JudgeCache: t.TempDir(), Paths: []string{"modules/gitea/index.ts"}, Modules: []string{"gitea"}, Beside: beside,
|
||||
Toolchain: "golang"}, t.TempDir(), registry, GitCredential{}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if v.Gate == nil || !strings.HasSuffix(v.Gate.Summary, "the release branch's controller with the vendored validator") {
|
||||
t.Fatalf("without mesh-host's release branch the judge did not keep its vendored validator: %+v\n%s", v.Gate, v.Report)
|
||||
}
|
||||
if v.JudgedBy != controllerRelease[:12] || v.BesideAt["mesh-host"] != "main" || v.Beside["mesh-host"] != hostMain[:12] {
|
||||
t.Fatalf("judged by %q, mesh-host read at %q (%s)", v.JudgedBy, v.BesideAt["mesh-host"], v.Beside["mesh-host"])
|
||||
}
|
||||
if !strings.Contains(v.Validator, "controller vendors") || !strings.Contains(v.Report, v.Validator) {
|
||||
t.Fatalf("the verdict does not say the vendored validator judged: %q\n%s", v.Validator, v.Report)
|
||||
}
|
||||
}
|
||||
|
||||
// **Clause 5** (ADR 0358 §5): a release branch's own pull request into its trunk is checked as any pull request into a
|
||||
// trunk — by the controller beside it as the mesh runs it, the repositories at what a trunk check reads — and its
|
||||
// verdict names no release branch.
|
||||
func TestAReleaseBranchsPullRequestIntoItsTrunkIsJudgedByTheControllerTheMeshRuns(t *testing.T) {
|
||||
aRuntimeThatBuildsTheJudge(t)
|
||||
registry := aFactsRegistry(t)
|
||||
controller, controllerMain, _ := aRepositoryWithARelease(t, aJudgeOf("the controller the mesh runs"),
|
||||
aJudgeOf("the release branch's controller"))
|
||||
host, hostMain, _ := aRepositoryWithARelease(t, aHostSaying("main's validator"),
|
||||
aHostSaying("the release branch's validator"))
|
||||
catalogue, _, releaseHead := aRepositoryWithARelease(t, map[string]string{
|
||||
"modules/gitea/module.json": `{"module":"gitea"}`, CheckScript: "true\n"},
|
||||
map[string]string{"modules/gitea/index.ts": "released"})
|
||||
v, err := Check(t.Context(), Command, CheckSpec{ID: "check-704-release-into-trunk", Repository: catalogue,
|
||||
Ref: releaseHead, Owner: "novox", Repo: "mesh-catalog", Number: 300, Base: "main", JudgeCache: t.TempDir(),
|
||||
Paths: []string{"modules/gitea/index.ts"}, Modules: []string{"gitea"}, Toolchain: "golang",
|
||||
Beside: map[string]Beside{"mesh-controller": {Repository: controller, Ref: controllerMain},
|
||||
"mesh-host": {Repository: host, Ref: hostMain}}}, t.TempDir(), registry, GitCredential{}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if v.Gate == nil || v.Gate.Summary != "the controller the mesh runs with the vendored validator" {
|
||||
t.Fatalf("the release branch's pull request into its trunk was not judged by the controller the mesh runs: %+v\n%s",
|
||||
v.Gate, v.Report)
|
||||
}
|
||||
if v.Release != "" || v.JudgedBy != "" || v.BesideAt != nil || v.Validator != "" ||
|
||||
strings.Contains(v.Report, "release branch") || strings.Contains(v.Gate.Summary, "[release") {
|
||||
t.Fatalf("its verdict names a release branch: %q %q %v %q", v.Release, v.JudgedBy, v.BesideAt, v.Validator)
|
||||
}
|
||||
if v.Beside["mesh-host"] != hostMain[:12] || v.Beside["mesh-controller"] != controllerMain[:12] {
|
||||
t.Fatalf("beside it were read %v, not the trunk's %s and %s", v.Beside, controllerMain[:12], hostMain[:12])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,6 +297,8 @@ type CheckOutcome struct {
|
||||
Release string `json:"release,omitempty"`
|
||||
JudgedBy string `json:"judged-by,omitempty"`
|
||||
BesideAt map[string]string `json:"beside-at,omitempty"`
|
||||
// Validator says which node-engine validator the judge of a release-branch check was built with (ADR 0358 §2).
|
||||
Validator string `json:"validator,omitempty"`
|
||||
}
|
||||
|
||||
// SourceOnSeat names a repository by the seat whose holder serves it and its path there.
|
||||
|
||||
@@ -299,6 +299,8 @@ type Checked struct {
|
||||
Release string `json:"release,omitempty"`
|
||||
JudgedBy string `json:"judged-by,omitempty"`
|
||||
BesideAt map[string]string `json:"beside-at,omitempty"`
|
||||
// Validator says which node-engine validator the judge of a release-branch check was built with (ADR 0358 §2).
|
||||
Validator string `json:"validator,omitempty"`
|
||||
// Built are the builds the check made for its merge, each with its reuse key and digests, or why not
|
||||
// (novox/hq ADR 0350 §1): what the walk of the merge takes instead of building.
|
||||
Built []CheckBuilt `json:"built,omitempty"`
|
||||
|
||||
Reference in New Issue
Block a user