From b752b1f1df771f5fd7beeed4ea3d000453b4ec88 Mon Sep 17 00:00:00 2001 From: jochen Date: Sat, 17 Oct 2026 20:43:06 +0200 Subject: [PATCH] Remove the release-branch judge from the controller, the wire and the build seat (issue 977, part A, ADR 0401) --- .../{release_test.go => check_spec_test.go} | 12 +- cmd/mesh-builder/main.go | 7 +- cmd/mesh-controller/checks.go | 122 +------- cmd/mesh-controller/release_branch_test.go | 273 ----------------- cmd/mesh-controller/replay867_test.go | 102 ------- internal/builder/check.go | 225 ++------------ ...ase_branch_test.go => judge_build_test.go} | 274 +----------------- internal/link/build.go | 18 +- internal/link/events.go | 8 +- modules/build-agent/module.json | 2 +- 10 files changed, 43 insertions(+), 1000 deletions(-) rename cmd/mesh-builder/{release_test.go => check_spec_test.go} (64%) delete mode 100644 cmd/mesh-controller/release_branch_test.go rename internal/builder/{release_branch_test.go => judge_build_test.go} (63%) diff --git a/cmd/mesh-builder/release_test.go b/cmd/mesh-builder/check_spec_test.go similarity index 64% rename from cmd/mesh-builder/release_test.go rename to cmd/mesh-builder/check_spec_test.go index 17eef10f..56f40d21 100644 --- a/cmd/mesh-builder/release_test.go +++ b/cmd/mesh-builder/check_spec_test.go @@ -7,16 +7,8 @@ import ( "github.com/novox/mesh-controller/internal/link" ) -// **A pull request into a release branch reaches the check as one** (novox/hq ADR 0358): the build seat hands the -// check the release branch and, beside it, the trunk each repository is read at when it has no branch of that name; -// and it hands the check the judge cache the build agent declares (MESH_JUDGE_CACHE), never a path of its own. -func TestTheBuildSeatHandsTheCheckItsReleaseBranch(t *testing.T) { - spec := checkSpecOf(link.BuildRequest{ID: "b", Repository: "r", Ref: "abc", Check: &link.CheckRequest{ - Owner: "novox", Repo: "mesh-catalog", Base: "feat/57", Release: "feat/57", - Beside: map[string]link.CheckedOut{"mesh-host": {Repository: "h", Ref: "feat/57", Else: "main"}}}}) - if spec.Release != "feat/57" || spec.Beside["mesh-host"].Ref != "feat/57" || spec.Beside["mesh-host"].Else != "main" { - t.Fatalf("the check was handed release %q and mesh-host %+v", spec.Release, spec.Beside["mesh-host"]) - } +// The build seat hands the check the judge cache the build agent declares (MESH_JUDGE_CACHE), never a path of its own. +func TestTheBuildSeatHandsTheCheckTheJudgeCacheItDeclares(t *testing.T) { t.Setenv("MESH_JUDGE_CACHE", "/var/lib/mesh/build-agent/judge-cache") if got := judgeCache(); got != "/var/lib/mesh/build-agent/judge-cache" { t.Fatalf("the judge cache the manifest declares is read as %q", got) diff --git a/cmd/mesh-builder/main.go b/cmd/mesh-builder/main.go index 4216b495..468f2a4b 100644 --- a/cmd/mesh-builder/main.go +++ b/cmd/mesh-builder/main.go @@ -358,8 +358,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, Validator: v.Validator, - Siblings: siblingsOut(v.Siblings), SiblingsUnknown: v.SiblingsUnknown, JudgeFrom: v.JudgeFrom} + Validator: v.Validator, Siblings: siblingsOut(v.Siblings), SiblingsUnknown: v.SiblingsUnknown, JudgeFrom: v.JudgeFrom} // 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, @@ -498,13 +497,13 @@ func checkSpecOf(request link.BuildRequest) builder.CheckSpec { Owner: c.Owner, Repo: c.Repo, Number: c.Number, Paths: c.Paths, Beside: map[string]builder.Beside{}, Modules: c.Modules, New: c.New, Manifests: c.Manifests, Judge: c.Judge, Base: c.Base, Toolchain: builder.ToolchainOf(request.Held), Toolchains: builder.ToolchainsOf(request.Held), - Tests: c.Tests, StoreOnly: c.StoreOnly, Release: c.Release, SiblingsUnknown: c.SiblingsUnknown} + Tests: c.Tests, StoreOnly: c.StoreOnly, SiblingsUnknown: c.SiblingsUnknown} for _, s := range c.Siblings { spec.Siblings = append(spec.Siblings, builder.Sibling{Repository: s.Repository, Number: s.Number, Head: s.Head, Base: s.Base, Dir: s.Dir}) } for dir, b := range c.Beside { - spec.Beside[dir] = builder.Beside{Repository: b.Repository, Ref: b.Ref, Else: b.Else} + spec.Beside[dir] = builder.Beside{Repository: b.Repository, Ref: b.Ref} } for _, b := range c.Builds { spec.Builds = append(spec.Builds, builder.CheckBuildSpec{Module: b.Module, Repository: b.Repository, Path: b.Path, diff --git a/cmd/mesh-controller/checks.go b/cmd/mesh-controller/checks.go index e0104e34..d501d49a 100644 --- a/cmd/mesh-controller/checks.go +++ b/cmd/mesh-controller/checks.go @@ -3,7 +3,6 @@ package main import ( "context" "encoding/json" - "errors" "fmt" "path" "regexp" @@ -254,12 +253,7 @@ func checkRequestFor(ctx context.Context, open *stores, p link.PullUpdated, scop if err != nil { return link.BuildRequest{}, err } - // **A pull request into a release branch is judged by that branch** (novox/hq ADR 0358): asked of the forge once. - release, err := releaseBranchOf(ctx, p, scope, entries) - if err != nil { - return link.BuildRequest{}, err - } - beside, err := besideOf(entries, func(module string) string { return current[module].Commit }, clone, release) + beside, err := besideOf(entries, func(module string) string { return current[module].Commit }, clone) if err != nil { return link.BuildRequest{}, err } @@ -274,17 +268,14 @@ func checkRequestFor(ctx context.Context, open *stores, p link.PullUpdated, scop Check: &link.CheckRequest{Owner: p.Owner, Repo: p.Repo, Number: p.Number, Base: p.Base, Trunk: p.BaseSHA, Ready: p.Ready, Paths: p.Paths, Beside: beside, Modules: scope.Modules, Dependents: scope.Dependents, New: scope.New, Manifests: scope.Manifests, Judge: scope.Judge, Builds: builds, Named: namedForTheCheck(ctx, p, builds), - Release: release, Running: runningBy(ctx)}, + Running: runningBy(ctx)}, }, nil } // besideOf is what a check reads beside its own repository, by the directory it is found under: each core repository -// by the module the mesh builds from it, at besideRefs — or, for a pull request into a release branch (novox/hq ADR -// 0358 §3), at its branch of that name where it has one and at its trunk otherwise, the controller's main kept for -// its judge, and mesh-lab at its trunk whatever the base: its replays run with the container runtime's socket. running -// is the commit the mesh runs of a module. +// by the module the mesh builds from it, at besideRefs. running is the commit the mesh runs of a module. func besideOf(entries []inventory.Entry, running func(module string) string, - clone func(inventory.Source) (string, error), release string) (map[string]link.CheckedOut, error) { + clone func(inventory.Source) (string, error)) (map[string]link.CheckedOut, error) { beside := map[string]link.CheckedOut{} for _, e := range entries { dir, core := coreModules[e.Manifest.Module] @@ -296,34 +287,15 @@ func besideOf(entries []inventory.Entry, running func(module string) string, return nil, err } refs := besideRefs(dir, running(e.Manifest.Module)) - at := func(dir, trunk string) link.CheckedOut { - if release == "" { - return link.CheckedOut{Repository: url, Ref: refs[dir]} - } - if trunk == "" { - trunk = "main" - } - return link.CheckedOut{Repository: url, Ref: release, Else: trunk} - } - beside[dir] = at(dir, followedBranch(e.Source.Ref)) + beside[dir] = link.CheckedOut{Repository: url, Ref: refs[dir]} if dir == "mesh-controller" { // The controller's main stays its main: it judges only a check the running controller predates. beside["mesh-controller-main"] = link.CheckedOut{Repository: url, Ref: refs["mesh-controller-main"]} - if release != "" { - // mesh-controller has the release branch: that is what makes it one (ADR 0358 §1). - beside[dir] = link.CheckedOut{Repository: url, Ref: release} - } if e.Source.Seat != "" { for _, sibling := range []string{"mesh-lab", "mesh-sdk"} { if url, err := clone(inventory.Source{Seat: e.Source.Seat, Repository: siblingOf(e.Source.Repository, sibling)}); err == nil { - b := link.CheckedOut{Repository: url, Ref: refs[sibling]} - // **mesh-lab is read at its trunk, always** (ADR 0358): its replays run with the container - // runtime's socket, so they are only ever reviewed code — never a branch anyone may create. - if release != "" && sibling != "mesh-lab" { - b = link.CheckedOut{Repository: url, Ref: release, Else: "main"} - } - beside[sibling] = b + beside[sibling] = link.CheckedOut{Repository: url, Ref: refs[sibling]} } } } @@ -332,47 +304,6 @@ func besideOf(entries []inventory.Entry, running func(module string) string, return beside, nil } -// releaseBranchOf is the release branch a pull request merges into (novox/hq ADR 0358 §1): its base, when that is not -// its repository's trunk and mesh-controller has a branch of that name; empty otherwise, and the pull request is -// checked as before it. **A base that is a trunk asks nothing**: the branch a module built from the repository follows -// (main when it records none), or mesh-controller's own trunk. Any other base is asked of the forge once, and an -// answer it cannot give is an error, never read as "no release branch". -func releaseBranchOf(ctx context.Context, p link.PullUpdated, scope checkScope, entries []inventory.Entry) (string, error) { - base := p.Base - if base == "" { - return "", nil - } - if scope.From != nil && base == trunkOf(scope.From.Source) { - return "", nil - } - var controller *inventory.Entry - for i, e := range entries { - if e.Manifest.Module == "mesh-controller" && !e.Provided && e.Source.Repository != "" { - controller = &entries[i] - break - } - } - if controller == nil || base == trunkOf(controller.Source) { - return "", nil - } - if !tryRef.MatchString(base) || strings.Contains(base, "..") { - return "", nil // no branch the forge could hold: checked as before - } - owner, repo := sourceOwner(controller.Source.Repository), sourceName(controller.Source.Repository) - if owner == "" || repo == "" { - return "", nil - } - has, err := forgeHasBranch(ctx, owner, repo, base) - if err != nil { - return "", fmt.Errorf("whether %s/%s#%d merges into a release branch cannot be told — the forge did not say "+ - "whether %s/%s has a branch %s (novox/hq ADR 0358): %w", p.Owner, p.Repo, p.Number, owner, repo, base, err) - } - if !has { - return "", nil - } - return base, nil -} - // trunkOf is the branch a module's source follows: main when it records none, or records a commit. func trunkOf(s inventory.Source) string { if t := strings.TrimSpace(followedBranch(s.Ref)); t != "" { @@ -390,46 +321,6 @@ func sourceName(repository string) string { return parts[len(parts)-1] } -// forgeHasBranch asks the forge, through its module's tools on the bus, whether a repository has a branch. A -// variable so a test needs no forge. -var forgeHasBranch = func(ctx context.Context, owner, repo, branch string) (bool, error) { - js, err := aBus() - if err != nil { - return false, err - } - defer js.Close() - raw, _ := json.Marshal(map[string]any{"path": "/repos/" + owner + "/" + repo + "/branches/" + branch}) - answer, err := link.Ask(ctx, link.OverNATS{Conn: js.Conn()}, "gitea", "gitea_api", raw, 30*time.Second) - if err != nil { - return false, err - } - return branchAnswered(answer, branch) -} - -// branchAnswered reads the forge's answer about a branch: a 404 is "no such branch"; an answer naming the branch is -// "it has it"; anything else — another error, an unreadable answer, one naming no branch or another — is an error, -// never read as "no release branch". -func branchAnswered(answer link.Answer, branch string) (bool, error) { - if answer.Error != "" { - if strings.Contains(answer.Error, " 404 ") { - return false, nil - } - return false, errors.New(answer.Error) - } - var found struct { - Result struct { - Name string `json:"name"` - } `json:"result"` - } - if err := json.Unmarshal(answer.Result, &found); err != nil { - return false, fmt.Errorf("the forge's answer about branch %s cannot be read: %w", branch, err) - } - if found.Result.Name != branch { - return false, fmt.Errorf("the forge answered about branch %s naming %q", branch, found.Result.Name) - } - return true, nil -} - // namedForTheCheck are the builds earlier checks of the same pull request at the same head named, read once for the // check (novox/hq issue 703): asked again because only the trunk moved, the check keeps what they built of a key // still the same. Another pull request's build, or one of an earlier head, is never kept. None when they cannot be @@ -608,7 +499,6 @@ func checkedOf(result link.BuildResult) link.Checked { // repositories read beside it. 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 c.Siblings, c.SiblingsUnknown, c.JudgeFrom = result.Check.Siblings, result.Check.SiblingsUnknown, result.Check.JudgeFrom if c.Gate == nil { diff --git a/cmd/mesh-controller/release_branch_test.go b/cmd/mesh-controller/release_branch_test.go deleted file mode 100644 index 298b1588..00000000 --- a/cmd/mesh-controller/release_branch_test.go +++ /dev/null @@ -1,273 +0,0 @@ -package main - -import ( - "context" - "errors" - "reflect" - "strings" - "testing" - - "github.com/novox/mesh-controller/internal/inventory" - "github.com/novox/mesh-controller/internal/link" -) - -// **A pull request into a release branch is judged by that branch** (novox/hq ADR 0358, issue 704): a base that is -// not its repository's trunk, where mesh-controller has a branch of that name, is a release branch; the check then -// reads every repository beside it at its branch of that name where it has one, else at its trunk. Any other base is -// checked exactly as before. - -const aRelease = "feat/57-config-and-provisioning" - -// theCore is the mesh's core as a check reads it beside: the controller, the node-engine, the tool runner and the -// catalogue's bus, on the git seat. -func theCore() []inventory.Entry { - onSeat := func(module, repository string) inventory.Entry { - e := fromRepo(module, repository, "") - e.Source.Seat = "git" - return e - } - nats := onSeat("nats", "novox/mesh-catalog") - nats.Source.Path = "modules/nats" - return []inventory.Entry{onSeat("mesh-controller", "novox/mesh-controller"), onSeat("mesh-host", "novox/mesh-host"), - onSeat("node-tools", "novox/mesh-tools"), nats, onSeat("gitea", "novox/mesh-catalog")} -} - -// aForgeWithBranches stands in for the forge: which branches each repository has, and how often it was asked. -func aForgeWithBranches(t *testing.T, branches map[string][]string, failing error) *int { - t.Helper() - asked := 0 - was := forgeHasBranch - t.Cleanup(func() { forgeHasBranch = was }) - forgeHasBranch = func(_ context.Context, owner, repo, branch string) (bool, error) { - asked++ - if failing != nil { - return false, failing - } - for _, b := range branches[owner+"/"+repo] { - if b == branch { - return true, nil - } - } - return false, nil - } - return &asked -} - -func TestABaseIsAReleaseBranchWhenTheControllerHasABranchOfThatName(t *testing.T) { - entries := theCore() - scopeOf := func(p link.PullUpdated) checkScope { return pullScope(p, entries, nil, nil) } - pull := func(repo, base string) link.PullUpdated { - return link.PullUpdated{Owner: "novox", Repo: repo, Number: 286, Base: base, Commit: "abc", - Paths: []string{"modules/gitea/index.ts"}} - } - - asked := aForgeWithBranches(t, map[string][]string{"novox/mesh-controller": {"main", aRelease}}, nil) - for _, c := range []struct { - what string - p link.PullUpdated - release string - asks int - }{ - {"a pull request into the trunk asks nothing and is no release", pull("mesh-catalog", "main"), "", 0}, - {"a catalogue pull request into the release branch", pull("mesh-catalog", aRelease), aRelease, 1}, - {"a controller pull request into the release branch", pull("mesh-controller", aRelease), aRelease, 1}, - {"a base mesh-controller has no branch of is checked as today", pull("mesh-catalog", "feat/other"), "", 1}, - {"no base: as today", pull("mesh-catalog", ""), "", 0}, - } { - *asked = 0 - got, err := releaseBranchOf(t.Context(), c.p, scopeOf(c.p), entries) - if err != nil { - t.Fatalf("%s: %v", c.what, err) - } - if got != c.release || *asked != c.asks { - t.Errorf("%s: release %q after %d question(s) to the forge, not %q after %d", c.what, got, *asked, - c.release, c.asks) - } - } - - // The branch a module built from the repository follows is its trunk: never asked about. - photos := fromRepo("photos", "novox/photos", "") - photos.Source.Ref = "nox-mesh" - withPhotos := append(theCore(), photos) - p := link.PullUpdated{Owner: "novox", Repo: "photos", Base: "nox-mesh", Commit: "abc", Paths: []string{"x.js"}} - *asked = 0 - if got, err := releaseBranchOf(t.Context(), p, pullScope(p, withPhotos, nil, nil), withPhotos); err != nil || - got != "" || *asked != 0 { - t.Fatalf("a module's own trunk read as release %q (%v) after %d question(s)", got, err, *asked) - } - - // A forge that cannot answer is an error, never "no release branch". - aForgeWithBranches(t, nil, errors.New("nothing serves gitea.gitea_api")) - q := pull("mesh-catalog", aRelease) - if _, err := releaseBranchOf(t.Context(), q, scopeOf(q), entries); err == nil || - !strings.Contains(err.Error(), "cannot be told") { - t.Fatalf("an unanswered forge was read as an answer: %v", err) - } -} - -func TestBesideAReleaseBranchCheckEachRepositoryIsReadAtThatBranchElseItsTrunk(t *testing.T) { - entries := theCore() - clone := func(s inventory.Source) (string, error) { return "http://git/" + s.Repository + ".git", nil } - running := func(module string) string { return "running-" + module } - - beside, err := besideOf(entries, running, clone, aRelease) - if err != nil { - t.Fatal(err) - } - at := func(repo string, ref, orElse string) link.CheckedOut { - return link.CheckedOut{Repository: "http://git/novox/" + repo + ".git", Ref: ref, Else: orElse} - } - want := map[string]link.CheckedOut{ - // The judge: mesh-controller's release branch, which it has. - "mesh-controller": at("mesh-controller", aRelease, ""), - "mesh-controller-main": at("mesh-controller", "main", ""), - // A controller pull request into the release branch reads mesh-host at that branch where it has one. - "mesh-host": at("mesh-host", aRelease, "main"), - "mesh-tools": at("mesh-tools", aRelease, "main"), - "mesh-catalog": at("mesh-catalog", aRelease, "main"), - // mesh-lab at its trunk, always: its replays run with the container runtime's socket. - "mesh-lab": at("mesh-lab", "main", ""), - "mesh-sdk": at("mesh-sdk", aRelease, "main"), - } - if !reflect.DeepEqual(beside, want) { - t.Fatalf("beside a release-branch check:\n got %v\nwant %v", beside, want) - } - - // Any other base: exactly as before — what the mesh runs, the catalogue's and the lab's main. - beside, err = besideOf(entries, running, clone, "") - if err != nil { - t.Fatal(err) - } - want = map[string]link.CheckedOut{ - "mesh-controller": at("mesh-controller", "running-mesh-controller", ""), - "mesh-controller-main": at("mesh-controller", "main", ""), - "mesh-host": at("mesh-host", "running-mesh-host", ""), - "mesh-tools": at("mesh-tools", "running-node-tools", ""), - "mesh-catalog": at("mesh-catalog", "main", ""), - "mesh-lab": at("mesh-lab", "main", ""), - "mesh-sdk": at("mesh-sdk", sdkPinned(), ""), - } - if !reflect.DeepEqual(beside, want) { - t.Fatalf("beside a trunk check:\n got %v\nwant %v", beside, want) - } -} - -func TestAReleaseBranchVerdictNamesTheBranchTheJudgeAndWhatItRead(t *testing.T) { - asked := &link.CheckRequest{Owner: "novox", Repo: "mesh-catalog", Number: 286, Base: aRelease, Release: aRelease} - 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"}, - 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" || - 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) - } - } -} - -// **A forge answer that names no branch is an error**, never "no release branch"; a 404 is "no such branch". -func TestTheForgesAnswerAboutABranchIsReadStrictly(t *testing.T) { - for _, c := range []struct { - what string - answer link.Answer - has bool - fails bool - }{ - {"the branch named", link.Answer{Result: []byte(`{"result":{"name":"` + aRelease + `"}}`)}, true, false}, - {"not found", link.Answer{Error: "Gitea API /repos/novox/mesh-controller/branches/x: 404 {\"message\":\"not found\"}"}, - false, false}, - {"an empty branch name", link.Answer{Result: []byte(`{"result":{"name":""}}`)}, false, true}, - {"no result", link.Answer{Result: []byte(`{}`)}, false, true}, - {"another branch named", link.Answer{Result: []byte(`{"result":{"name":"main"}}`)}, false, true}, - {"unreadable", link.Answer{Result: []byte(`not json`)}, false, true}, - {"another error", link.Answer{Error: "Gitea API: 500 internal"}, false, true}, - } { - has, err := branchAnswered(c.answer, aRelease) - if has != c.has || (err != nil) != c.fails { - t.Errorf("%s: has %v, error %v", c.what, has, err) - } - } -} - -// **The trunk is the repository's own, not a name**: a repository whose trunk is not main, one that follows main, and -// a controller whose trunk is not main. -func TestATrunkIsTheBranchTheRepositoryFollows(t *testing.T) { - asked := aForgeWithBranches(t, map[string][]string{"novox/mesh-controller": {"main", aRelease, "master"}}, nil) - legacy := fromRepo("legacy", "novox/legacy", "") - legacy.Source.Ref = "master" - onMain := fromRepo("photos", "novox/photos", "") - onMain.Source.Ref = "main" - entries := append(theCore(), legacy, onMain) - for _, c := range []struct { - what, repo, base, release string - asks int - }{ - {"a repository whose trunk is master, into master", "legacy", "master", "", 0}, - {"a repository whose trunk is master, into the release branch", "legacy", aRelease, aRelease, 1}, - {"a repository whose module follows main, into main", "photos", "main", "", 0}, - {"a repository whose module follows main, into the release branch", "photos", aRelease, aRelease, 1}, - } { - *asked = 0 - p := link.PullUpdated{Owner: "novox", Repo: c.repo, Base: c.base, Commit: "abc", Paths: []string{"x"}} - got, err := releaseBranchOf(t.Context(), p, pullScope(p, entries, nil, nil), entries) - if err != nil || got != c.release || *asked != c.asks { - t.Errorf("%s: release %q (%v) after %d question(s), not %q after %d", c.what, got, err, *asked, c.release, c.asks) - } - } - - // A controller whose trunk is not main: main may be a release branch there, and its own trunk never is. - core := theCore() - core[0].Source.Ref = "trunk" - for _, c := range []struct { - base, release string - asks int - }{{"trunk", "", 0}, {"main", "main", 1}} { - *asked = 0 - p := link.PullUpdated{Owner: "novox", Repo: "hq", Base: c.base, Commit: "abc", Paths: []string{"x"}} - got, err := releaseBranchOf(t.Context(), p, pullScope(p, core, nil, nil), core) - if err != nil || got != c.release || *asked != c.asks { - t.Errorf("hq into %s, the controller following trunk: release %q (%v) after %d question(s)", c.base, got, err, - *asked) - } - } -} - -// trunkOf: the branch a source follows, and main when it records none or records a commit (issue 215's rule). -func TestATrunkIsMainWhenTheSourceRecordsNone(t *testing.T) { - for ref, want := range map[string]string{"": "main", "main": "main", "master": "master", "nox-mesh": "nox-mesh", - "0123456789abcdef0123456789abcdef01234567": "main", " ": "main"} { - if got := trunkOf(inventory.Source{Ref: ref}); got != want { - t.Errorf("a source recording %q follows %q, not %q", ref, got, want) - } - } -} diff --git a/cmd/mesh-controller/replay867_test.go b/cmd/mesh-controller/replay867_test.go index 5e8d67db..4aa03f3d 100644 --- a/cmd/mesh-controller/replay867_test.go +++ b/cmd/mesh-controller/replay867_test.go @@ -365,105 +365,3 @@ func TestTheRecheckVerbAsksMeshDeliveryOnABusOfItsOwn(t *testing.T) { t.Fatalf("mesh-delivery was asked %d times", asked) } } - -// **A delivery group's composed check is judged by the group's own controller** (novox/hq ADR 0239 §14), into a release -// branch too: the controller member and the mesh-host member of the pair are consistent only together (the controller -// calls what only the node-engine change brings), and both merge into a release branch. The release judge (the -// controller of the release branch with the validator of its own) cannot build it; the group's own controller, as -// merged, with the node-engine member's validator, can. It drives askGroupCheck and the build seat's Check for real. -func TestAGroupsCompositionIntoAReleaseBranchIsJudgedByItsOwnController(t *testing.T) { - groupIntoARelease(t, false) -} - -// The shape the live check had: the node-engine member is the group's primary, so the composed request is asked with -// Judge "validator" (a union of modules that includes mesh-host's) and the release branch set; the release judge then -// put this change's validator into the old controller and "judge-with-this-validator" did not build. -func TestAGroupsCompositionJudgedWithTheValidatorIntoAReleaseBranchIsJudgedByItsOwnController(t *testing.T) { - groupIntoARelease(t, true) -} - -func groupIntoARelease(t *testing.T, hostPrimary bool) { - if _, err := exec.LookPath("go"); err != nil { - t.Skip("no Go toolchain on this machine to build a judge with") - } - const release = "feat/r" - r := theGroupOfIssue867Into(t, release) - aForgeWithBranches(t, map[string][]string{"novox/mesh-controller": {release}}, nil) - // The pair renames what the validator says: the node-engine change drops Word for Word2, the controller change - // calls Word2. The controller of the release branch calls Word, so it does not build with the new validator; the - // controller change does not build with the validator it vendors. Only together are they consistent. - r867Git(t, r.hostRepo, "checkout", "--quiet", "-b", "swap", release) - r.hostHead = r867Commit(t, r.hostRepo, map[string]string{"validate/v.go": "package validate\n\nconst Word2 = \"the new validator\"\n"}) - r867Git(t, r.hostRepo, "checkout", "--quiet", "main") - r867Git(t, r.controllerRepo, "checkout", "--quiet", "-b", "swap", release) - swapped := strings.Replace(r867Controller("the controller", "")["cmd/mesh-controller/main.go"], "validate.Word)", "validate.Word2)", 1) - r.controllerHead = r867Commit(t, r.controllerRepo, map[string]string{"cmd/mesh-controller/main.go": swapped}) - r867Git(t, r.controllerRepo, "checkout", "--quiet", "main") - js, _ := aBuildQueue(t) - servingBus.Store(js) - t.Cleanup(func() { servingBus.Store(nil) }) - members := []orderMember{ - {ID: "novox/mesh-controller@" + r.controllerHead[:12], Repository: "novox/mesh-controller", Base: release, - Head: r.controllerHead, Number: 346, Paths: []string{"lock.txt", "cmd/mesh-controller/main.go"}, ModuleDirsSaid: true}, - {ID: "novox/mesh-host@" + r.hostHead[:12], Repository: "novox/mesh-host", Base: release, Head: r.hostHead, Number: 114, - Paths: []string{"validate/v.go"}, ModuleDirsSaid: true}} - if hostPrimary { - members[0], members[1] = members[1], members[0] - } - if _, err := askGroupCheck(t.Context(), r.open, "feat/867", members); err != nil { - t.Fatal(err) - } - q, err := link.ReadQueue(t.Context(), js, link.TheBuildMachine) - if err != nil || len(q.Asks) == 0 { - t.Fatalf("nothing was asked of the build seat: %v", err) - } - request := q.Asks[len(q.Asks)-1].Request - if request.Check.Release != release || request.Check.Group != "feat/867" || len(request.Check.Members) != 1 || len(request.Check.Modules) == 0 { - t.Fatalf("the composed check is asked as release %q, group %q, %d other head(s)", request.Check.Release, - request.Check.Group, len(request.Check.Members)) - } - if hostPrimary { - if request.Check.Repo != "mesh-host" { - t.Fatalf("the primary is %s, not the node-engine", request.Check.Repo) - } - // As the live ask had it (the mesh-host module among the union's): the node-engine's validator judges. - request.Check.Judge = link.JudgeValidator - } - // What the build seat makes of the ask. - var wire map[string]json.RawMessage - body, _ := json.Marshal(request.Check) - _ = json.Unmarshal(body, &wire) - delete(wire, "group") - delete(wire, "members") - body, _ = json.Marshal(wire) - var spec builder.CheckSpec - if err := json.Unmarshal(body, &spec); err != nil { - t.Fatal(err) - } - for _, m := range request.Check.Members { - spec.Group = append(spec.Group, builder.GroupHead{Owner: m.Owner, Repo: m.Repo, Repository: m.Repository, Ref: m.Ref, - Paths: m.Paths, Base: m.Base}) - } - spec.ID, spec.Repository, spec.Ref = request.ID, request.Repository, request.Ref - spec.Toolchain, spec.JudgeCache = "golang", r.judgeCache - v, err := builder.Check(t.Context(), builder.Command, spec, t.TempDir(), r.registry, builder.GitCredential{}, nil) - if err != nil { - t.Fatal(err) - } - if v.Gate == nil || v.Gate.Verdict != "pass" { - t.Fatalf("the group's composition into %s was not judged by its own controller: %+v\n%s", release, v.Gate, v.Report) - } - if mark := "[release " + release + ", judged by the group's controller " + v.JudgedBy + "] "; !strings.HasPrefix(v.Gate.Summary, mark) || - !strings.Contains(v.Report, "by the group's controller at "+v.JudgedBy) { - t.Fatalf("the statuses and the report do not say the group's controller judged:\n%s\n%s", v.Gate.Summary, v.Report) - } - if !strings.Contains(v.Gate.Summary, "the new validator") { - t.Fatalf("the gate was judged by %q, not by the group's controller with the node-engine member's validator", v.Gate.Summary) - } - if !strings.HasPrefix(v.JudgeFrom, "the group's own controller") || !strings.Contains(v.Validator, "the group's mesh-host merge") { - t.Fatalf("the verdict does not say where the judge and the validator came from: %q / %q", v.JudgeFrom, v.Validator) - } - if v.Release != release || v.JudgedBy == "" { - t.Fatalf("the verdict names release %q judged by %q", v.Release, v.JudgedBy) - } -} diff --git a/internal/builder/check.go b/internal/builder/check.go index 3f272fcd..d25d2687 100644 --- a/internal/builder/check.go +++ b/internal/builder/check.go @@ -91,16 +91,10 @@ type CheckSpec struct { // Judge is who judges the gate: "" the controller the mesh runs, "self" the change's own, "validator" // the running one with the change's validator. Judge string - // Release is the release branch the pull request merges into (novox/hq ADR 0358), empty for any other base. The - // gate is then judged by the controller beside it as mesh-controller — mesh-controller's release branch, never - // the controller the mesh runs nor its main — with mesh-host's validator from beside it when mesh-host was read at - // the release branch too; a change to the controller still judges itself and a change to the node-engine brings - // its own validator. The verdict names the branch, the judge's commit and the branch each beside was read at. - Release string // JudgeCache is the judge cache (novox/hq ADR 0358 §6): a directory of the build agent's own, outside the // workspace, that only judge builds mount — never a check's containers, so no pull request's script can write it. - // It holds the Go build cache every judge is built with (judges stay warm), the release-branch judges kept by the - // commits they are built from (judges/, one per release-branch head), and each judge build's fresh home (homes/, + // It holds the Go build cache every judge is built with (judges stay warm), the group's judges kept by the + // commits they are built from (judges/), and each judge build's fresh home (homes/, // removed after the build). Empty: each judge is built cold under the check's own root, which every check empties // before it clones, and nothing is kept. JudgeCache string @@ -206,12 +200,10 @@ func ToolchainsOf(held map[string]string) map[string]string { return out } -// Beside is one repository cloned next to the one checked: at Ref, or at Else when Else is said and the repository -// has no branch Ref (novox/hq ADR 0358 §3). +// Beside is one repository cloned next to the one checked, at Ref. type Beside struct { Repository string Ref string - Else string } // CheckVerdict is what came of one check. Verdict and Summary are the gate's; Gate and Repo each layer. @@ -234,19 +226,12 @@ type CheckVerdict struct { Members []JudgedHead // Built are the builds it made for its merge (novox/hq ADR 0350 §1), each with its key, or why not. Built []CheckBuilt - // Release is the release branch it was judged for (novox/hq ADR 0358 §4), JudgedBy the commit of the controller - // that judged its gate (twelve characters), and BesideAt the branch each repository beside it was read at, by - // directory. Empty for a check of any other base. - 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 says which node-engine validator the group's judge was built with (ADR 0382 §3), empty for any other + // judge. Validator string // Siblings are the pull requests of the delivery group the check read beside it, each merged onto its base commit // (novox/hq ADR 0382 §4); SiblingsUnknown is why it could not learn them; JudgeFrom says where the judge came from - // when the group made it (§3), the validator's source being in Validator. + // when the group made it (§3). Siblings []JudgedSibling SiblingsUnknown string JudgeFrom string @@ -393,22 +378,15 @@ func Check(ctx context.Context, run Runner, spec CheckSpec, workspace, registry if replay.base != "" { defer os.RemoveAll(replay.base) } - // cloneAt clones a repository and checks out ref — or, when the repository has no branch ref and orElse is said, - // orElse (novox/hq ADR 0358 §3); readAt keeps which, by directory. + // clone clones a repository and checks out ref; readAt keeps which, by directory. readAt := map[string]string{} - cloneAt := func(repository, ref, orElse, dir string) error { + clone := func(repository, ref, dir string) error { if _, err := run(ctx, root, "git", cloneWith(credentials, "clone", "--quiet", repository, dir)...); err != nil { return fmt.Errorf("cannot clone %s: %s", redact.redact(repository), redact.redact(err.Error())) } if err := recordedWithoutUserinfo(ctx, run, filepath.Join(root, dir), repository); err != nil { return err } - if orElse != "" && ref != "" { - if _, err := run(ctx, filepath.Join(root, dir), "git", "rev-parse", "--verify", "--quiet", - "refs/remotes/origin/"+ref); err != nil { - ref = orElse - } - } readAt[dir] = ref if ref != "" { if _, err := run(ctx, filepath.Join(root, dir), "git", "checkout", "--quiet", ref); err != nil { @@ -417,9 +395,6 @@ func Check(ctx context.Context, run Runner, spec CheckSpec, workspace, registry } return nil } - clone := func(repository, ref, dir string) error { - return cloneAt(repository, ref, "", dir) - } name := spec.Repo if name == "" || !safeName.MatchString(name) { name = "checked" @@ -455,8 +430,8 @@ 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, v.Validator = spec.Release, judged.JudgedBy, judged.BesideAt, judged.Validator v.Siblings, v.SiblingsUnknown, v.JudgeFrom = judged.Siblings, spec.SiblingsUnknown, judged.JudgeFrom + v.Validator = judged.Validator return v } // **What the merge would move is built from a checkout nothing of the check touches** (novox/hq issue 703): the @@ -500,22 +475,10 @@ func Check(ctx context.Context, run Runner, spec CheckSpec, workspace, registry if dir == name || !safeName.MatchString(dir) { continue } - if dir == "mesh-lab" { - // **mesh-lab is never read at a release branch** (novox/hq ADR 0358): its replays run with the container - // runtime's socket, so a branch of a release's name — which anyone may create — is never what runs there, - // whether it is asked with a fallback or without. - if b.Else != "" || (spec.Release != "" && b.Ref == spec.Release) { - b.Ref = b.Else - if b.Ref == "" { - b.Ref = "main" - } - } - b.Else = "" - } if sibling := siblingIn(spec, dir); sibling != nil && dir != "mesh-lab" { // **A sibling's repository is read at the sibling's merge** (novox/hq ADR 0382 §2): its head merged onto its // base's head, as its own check merges it — in place of the commit the mesh runs. - if err := cloneAt(b.Repository, "", "", dir); err != nil { + if err := clone(b.Repository, "", dir); err != nil { return CheckVerdict{}, fmt.Errorf("beside it, %w", err) } m, err := mergeIntoTrunk(ctx, run, filepath.Join(root, dir), sibling.Base, sibling.Head) @@ -532,7 +495,7 @@ func Check(ctx context.Context, run Runner, spec CheckSpec, workspace, registry Gate: &Layer{Verdict: "fail", Summary: said}, Repo: &Layer{Verdict: "fail", Summary: said}}), nil } readAt[dir] = sibling.Base - } else if err := cloneAt(b.Repository, b.Ref, b.Else, dir); err != nil { + } else if err := clone(b.Repository, b.Ref, dir); err != nil { return CheckVerdict{}, fmt.Errorf("beside it, %w", err) } if dir == "mesh-lab" { @@ -542,13 +505,6 @@ func Check(ctx context.Context, run Runner, spec CheckSpec, workspace, registry return CheckVerdict{}, fmt.Errorf("mesh-lab could not be copied for its replays: %w", err) } } - if spec.Release != "" { - // The branch each was read at is named with its commit (ADR 0358 §4). - if judged.BesideAt == nil { - judged.BesideAt = map[string]string{} - } - judged.BesideAt[dir] = readAt[dir] - } // The commit each one read is named in the verdict (ADR 0323 §3): its move is issue 568's question. if at := headAt(ctx, run, filepath.Join(root, dir)); at != "" { if judged.Beside == nil { @@ -756,32 +712,12 @@ func Check(ctx context.Context, run Runner, spec CheckSpec, workspace, registry // The judge. Its failing to build is the change's fault when the change is the judge or its validator, // and the check's when it is the controller the mesh runs. var siblingJudge siblingJudgement - gate, judgeFault, err := judgeFor(ctx, labelled, run, spec, root, tree, judged.BesideAt, judged, &siblingJudge, inJudgeBuild, say) + gate, judgeFault, err := judgeFor(ctx, labelled, run, spec, root, tree, judged, &siblingJudge, inJudgeBuild, say) if err != nil { return CheckVerdict{}, err } judged.JudgeFrom = siblingJudge.From - if siblingJudge.Validator != "" { - judged.Validator = siblingJudge.Validator - } - if spec.Release != "" { - // **A release-branch judgement says so** (novox/hq ADR 0358 §4): the branch, the controller's commit that - // judged, and each repository read beside it at its branch and commit. - judgedBy := filepath.Join(root, "mesh-controller") - if spec.Judge == "self" { - judgedBy = tree - } - if siblingJudge.Dir != "" { - judgedBy = siblingJudge.Dir - } - judged.JudgedBy = headAt(ctx, run, judgedBy) - if siblingJudge.Validator == "" { - judged.Validator = releaseValidator(spec, judged.BesideAt, judged.Beside) - } - said := releaseWords(spec.Release, whoJudges(siblingJudge), judged.JudgedBy, judged.Validator, judged.BesideAt, judged.Beside) - fmt.Fprintf(&out, "--- %s\n", said) - say("check", "%s", said) - } + judged.Validator = siblingJudge.Validator verdictFile := filepath.Join(root, "verdict.json") env := []string{EnvFacts + "=" + factsFile, EnvGate + "=" + gate, EnvGateStore + "=" + storeURL, EnvTestStore + "=" + storeURL, EnvTestBus + "=nats://" + bus, EnvRepository + "=" + spec.Owner + "/" + spec.Repo, @@ -847,11 +783,6 @@ func Check(ctx context.Context, run Runner, spec CheckSpec, workspace, registry } } - if spec.Release != "" { - // The statuses name the release branch and the judge first, so the forge's short description keeps them. - mark := releaseMark(spec.Release, whoJudges(siblingJudge), judged.JudgedBy) - v.Gate.Summary, v.Repo.Summary = mark+v.Gate.Summary, mark+v.Repo.Summary - } // What the check printed travels in the verdict to the forge and the controller: redacted as the log is. v.Gate.Summary, v.Repo.Summary, v.Repo.Failed = redact.redact(v.Gate.Summary), redact.redact(v.Repo.Summary), redact.redact(v.Repo.Failed) @@ -1234,12 +1165,7 @@ func gateLayer(ctx context.Context, spec CheckSpec, tree, root, gate, verdictFil // the mesh runs. It answers the judge's path; or, when the change makes its own judge unbuildable, why — // the change's fault, a failing gate; or an error when the check cannot build a judge at all. A check // whose gate does not run builds a judge only to hand its scripts one, and goes on without when it cannot. -// -// **A pull request into a release branch is judged by that branch's controller** (novox/hq ADR 0358 §2): the one -// beside it as mesh-controller, read at the release branch — with mesh-host's validator from beside it when mesh-host -// has that branch too, or with the change's own validator for a change to the node-engine — never the controller the -// mesh runs. besideAt is the branch each repository beside it was read at. -func judgeFor(ctx context.Context, labelled, run Runner, spec CheckSpec, root, tree string, besideAt map[string]string, +func judgeFor(ctx context.Context, labelled, run Runner, spec CheckSpec, root, tree string, judged CheckVerdict, siblings *siblingJudgement, inToolchain func(string, []string, ...string) []string, say func(step, format string, args ...any)) (string, string, error) { gated := spec.Gated() @@ -1256,14 +1182,7 @@ func judgeFor(ctx context.Context, labelled, run Runner, spec CheckSpec, root, t say("check", "judged by the change's own controller") return bin, "", nil } - var bin, judgeTree string - var err error - if spec.Release != "" { - withHost := spec.Judge != "validator" && besideAt["mesh-host"] == spec.Release - bin, judgeTree, err = releaseJudge(ctx, labelled, run, spec, root, withHost, inToolchain, say) - } else { - bin, judgeTree, err = judge(ctx, labelled, run, root, inToolchain, say) - } + bin, judgeTree, err := judge(ctx, labelled, run, root, inToolchain, say) if err != nil { if !gated { say("check", "no judge for its script: %v", err) @@ -1284,69 +1203,12 @@ func judgeFor(ctx context.Context, labelled, run Runner, spec CheckSpec, root, t withValidator := filepath.Join(root, "bin", "judge-with-this-validator") if _, err := labelled(ctx, root, "docker", inToolchain(filepath.Join(root, judgeTree), nil, "go", "build", "-o", withValidator, "./cmd/mesh-controller")...); err != nil { - if spec.Release != "" { - return "", spec.Release + "'s controller does not build with this change's validator: " + - firstLine(err.Error()), nil - } return "", "the controller the mesh runs does not build with this change's validator: " + firstLine(err.Error()), nil } - if spec.Release != "" { - say("check", "judged by %s's controller, with this change's validator", spec.Release) - return withValidator, "", nil - } say("check", "judged by the controller the mesh runs, with this change's validator") return withValidator, "", nil } -// releaseJudge builds the controller that judges a pull request into a release branch (novox/hq ADR 0358 §2): the -// one beside the check as mesh-controller, read at that branch — with mesh-host's validator from beside it in place -// of the one it vendors when withHost. **Built once per release-branch head**: kept in the spec's judge cache by the -// commits it is built from and taken from there while they stand. It answers the binary and the directory it was -// built from. -func releaseJudge(ctx context.Context, run, plain Runner, spec CheckSpec, root string, withHost bool, - inToolchain func(string, []string, ...string) []string, say func(step, format string, args ...any)) (string, string, error) { - const dir = "mesh-controller" - if _, err := os.Stat(filepath.Join(root, dir)); err != nil { - return "", "", fmt.Errorf("no controller of %s beside the check to judge it with", spec.Release) - } - key := fullHeadAt(ctx, plain, filepath.Join(root, dir)) - if key == "" { - return "", "", fmt.Errorf("the controller of %s beside the check names no commit", spec.Release) - } - with := "" - if withHost { - host := fullHeadAt(ctx, plain, filepath.Join(root, "mesh-host")) - if host == "" { - return "", "", fmt.Errorf("mesh-host of %s beside the check names no commit", spec.Release) - } - key += "-with-mesh-host-" + host - with = ", with mesh-host's validator of " + spec.Release + " at " + twelve(host) - } - bin := filepath.Join(root, "bin", "mesh-controller") - if took := judgeKept(keptJudges(spec.JudgeCache), key, bin); took { - say("check", "judged by %s's controller at %s%s, built for an earlier check of that head", spec.Release, - twelve(key), with) - return bin, dir, nil - } - if withHost { - vendored := filepath.Join(root, dir, "vendor", "github.com", "novox", "mesh-host") - for _, pkg := range []string{"validate", filepath.Join("internal", "declaration")} { - if err := replaceGoFiles(filepath.Join(root, "mesh-host", pkg), filepath.Join(vendored, pkg)); err != nil { - return "", "", fmt.Errorf("mesh-host's validator of %s could not be put in its controller: %w", spec.Release, err) - } - } - } - // Built where no pull request reaches (JudgeBuildEnv): a judge kept for this head is the head's, nothing else. - if _, err := run(ctx, root, "docker", inToolchain(filepath.Join(root, dir), nil, - "go", "build", "-o", bin, "./cmd/mesh-controller")...); err != nil { - return "", "", fmt.Errorf("the controller of %s does not build%s: %w", spec.Release, with, err) - } - // Kept before anything of the change runs: nothing a pull request's script writes can reach the cache. - keepJudge(keptJudges(spec.JudgeCache), key, bin, say) - say("check", "judged by %s's controller at %s%s", spec.Release, twelve(key), with) - return bin, dir, nil -} - // judgeBuildEnv is JudgeBuildEnv, a seam only a test replaces: to show what the plain environment would let a planted // workspace do to a judge's build. var judgeBuildEnv = JudgeBuildEnv @@ -1492,7 +1354,7 @@ func copyCheckout(ctx context.Context, run Runner, from, dir string) error { return err } -// keptJudges is where a judge cache keeps the release-branch judges, empty when there is no judge cache. +// keptJudges is where a judge cache keeps the group's judges, empty when there is no judge cache. func keptJudges(cache string) string { if cache == "" { return "" @@ -1500,7 +1362,7 @@ func keptJudges(cache string) string { return filepath.Join(cache, "judges") } -// judgesKept is how many release-branch judges a judge cache keeps: a head moved past is not built from again. +// judgesKept is how many judges a judge cache keeps: a head moved past is not built from again. const judgesKept = 4 // judgeKept puts the judge kept under key in the cache at bin, and says whether there was one. @@ -1574,47 +1436,6 @@ 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, controller, 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 %s at %s, with %s", release, controller, judgedBy, validator) - if len(read) > 0 { - said += "; read beside it: " + strings.Join(read, ", ") - } - return said -} - -// whoJudges says which controller judged: the release branch's own ("its controller"), or — when a delivery group's -// member in mesh-controller judged (novox/hq ADR 0239 §14) — the group's. -func whoJudges(j siblingJudgement) string { - if j.Dir != "" { - return "the group's controller" - } - return "its controller" -} - -// releaseMark opens each layer's summary of a release-branch check, so a status says it first. -func releaseMark(release, controller, judgedBy string) string { - return fmt.Sprintf("[release %s, judged by %s %s] ", release, controller, judgedBy) -} - // refWords is a ref as a line says it: a commit by its first eight characters, a branch whole. func refWords(ref string) string { if commitLike.MatchString(ref) { @@ -1626,15 +1447,6 @@ func refWords(ref string) string { // commitLike is a ref that names a commit. var commitLike = regexp.MustCompile(`^[0-9a-f]{7,40}$`) -func sortedKeys(m map[string]string) []string { - keys := make([]string, 0, len(m)) - for k := range m { - keys = append(keys, k) - } - sort.Strings(keys) - return keys -} - // 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")) @@ -2004,8 +1816,7 @@ func judgeFromTheGroup(ctx context.Context, labelled, run Runner, spec CheckSpec switch { 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 - // base it merges into — a release branch included (ADR 0358 §2 changes the judge of one pull request, not of a - // group): 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 of the group's mesh-host member's // merge when it has one, else the one that controller vendors. source, validatorDir = groupDir(spec, root, tree, "mesh-controller"), groupDir(spec, root, tree, "mesh-host") from = "the group's own controller, merge " + headAt(ctx, run, source) diff --git a/internal/builder/release_branch_test.go b/internal/builder/judge_build_test.go similarity index 63% rename from internal/builder/release_branch_test.go rename to internal/builder/judge_build_test.go index a0adb5ae..f4998c2e 100644 --- a/internal/builder/release_branch_test.go +++ b/internal/builder/judge_build_test.go @@ -10,10 +10,7 @@ import ( "testing" ) -// **A pull request into a release branch is judged by the controller of that branch** (novox/hq ADR 0358, issue -// 704): the build seat's check builds the judge from mesh-controller read at the release branch — with mesh-host's -// validator of that branch when mesh-host has one — reads every repository beside it at that branch where it has one -// and at its trunk otherwise, and names the branch, the judge's commit and each repository read with its commit. +// Helpers of the judge-build tests: repositories with a branch, a fake container runtime, and the planted workspace. const releaseBranch = "feat/57-config-and-provisioning" @@ -193,10 +190,10 @@ func theReleasedMesh(t *testing.T) (map[string]Beside, string, string, string) { aHostSaying("the release branch's validator")) lab, labMain, _ := aRepositoryWithARelease(t, map[string]string{"README": "the lab"}, nil) return map[string]Beside{ - "mesh-controller": {Repository: controller, Ref: releaseBranch}, + "mesh-controller": {Repository: controller, Ref: "main"}, "mesh-controller-main": {Repository: controller, Ref: "main"}, - "mesh-host": {Repository: host, Ref: releaseBranch, Else: "main"}, - "mesh-lab": {Repository: lab, Ref: releaseBranch, Else: "main"}, + "mesh-host": {Repository: host, Ref: "main"}, + "mesh-lab": {Repository: lab, Ref: "main"}, }, controllerRelease, hostRelease, labMain } @@ -210,230 +207,6 @@ func aPullRequestInto(t *testing.T, files map[string]string, base string, change return repo, head } -func TestACataloguePullRequestIntoAReleaseBranchIsJudgedByThatBranchsController(t *testing.T) { - built := aRuntimeThatBuildsTheJudge(t) - registry := aFactsRegistry(t) - beside, controllerRelease, hostRelease, labMain := theReleasedMesh(t) - 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"}) - cache := t.TempDir() - check := func(id string) CheckVerdict { - t.Helper() - v, err := Check(t.Context(), Command, CheckSpec{ID: id, Repository: catalogue, Ref: head, Owner: "novox", - Repo: "mesh-catalog", Number: 286, Base: releaseBranch, Release: releaseBranch, JudgeCache: cache, - Paths: []string{"modules/gitea/index.ts"}, Modules: []string{"gitea"}, Beside: beside, Toolchain: "golang"}, - t.TempDir(), registry, GitCredential{}, nil) - if err != nil { - t.Fatal(err) - } - return v - } - v := check("check-704-catalogue") - // The release branch's controller judged, with mesh-host's validator of the release branch. - if v.Gate == nil || v.Gate.Verdict != "pass" || - !strings.HasSuffix(v.Gate.Summary, "the release branch's controller with the release branch's validator") { - t.Fatalf("the gate was not judged by the release branch's controller with its node-engine's validator: %+v\n%s", - v.Gate, v.Report) - } - // It names the release branch, the judge's commit, and each repository read beside it at its branch and commit. - mark := "[release " + releaseBranch + ", judged by its controller " + controllerRelease[:12] + "] " - if !strings.HasPrefix(v.Gate.Summary, mark) || !strings.HasPrefix(v.Repo.Summary, mark) { - t.Fatalf("the statuses do not name the release branch and its judge first:\n%s\n%s", v.Gate.Summary, v.Repo.Summary) - } - if v.Release != releaseBranch || v.JudgedBy != controllerRelease[:12] { - t.Fatalf("the verdict names release %q judged by %q, not %s by %s", v.Release, v.JudgedBy, releaseBranch, - controllerRelease[:12]) - } - if v.BesideAt["mesh-host"] != releaseBranch || v.Beside["mesh-host"] != hostRelease[:12] { - t.Fatalf("mesh-host was read at %q (%s), not its release branch at %s", v.BesideAt["mesh-host"], - v.Beside["mesh-host"], hostRelease[:12]) - } - // A repository with no release branch is read at its trunk, and says so. - if v.BesideAt["mesh-lab"] != "main" || v.Beside["mesh-lab"] != labMain[:12] { - t.Fatalf("the lab, with no release branch, was read at %q (%s), not main at %s", v.BesideAt["mesh-lab"], - v.Beside["mesh-lab"], labMain[:12]) - } - 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) - } - // **The judge is built with the judge cache's Go cache** (ADR 0358 §6), which no check's container mounts, and no - // Go environment file. - said, _ := os.ReadFile(built) - if !strings.Contains(string(said), "GOCACHE="+filepath.Join(cache, "go-cache")+" GOENV=off go build") { - t.Fatalf("the release judge was not built with the judge cache's Go cache:\n%s", said) - } - // **Clause 5**: the head was merged onto the release branch's head, which the verdict names as its trunk commit, - // so the release branch's move puts it out of date as a trunk's move does (ADR 0323 §4, the forge's holder). - if releaseHead := gitInRepo(t, catalogue, "rev-parse", releaseBranch); v.Trunk != releaseHead[:12] { - t.Fatalf("the verdict names trunk %q, not the release branch's head %s", v.Trunk, releaseHead[:12]) - } - - // **Built once per release-branch head**: a second check while that head stands takes the judge kept. - v = check("check-704-catalogue-again") - if !strings.HasSuffix(v.Gate.Summary, "the release branch's controller with the release branch's validator") { - t.Fatalf("the kept judge did not judge: %+v\n%s", v.Gate, v.Report) - } - if n := builds(t, built); n != 1 { - t.Fatalf("the judge of the same release-branch head was built again: %d builds", n) - } -} - -func TestAControllerPullRequestIntoAReleaseBranchReadsTheNodeEngineAtThatBranch(t *testing.T) { - aRuntimeThatBuildsTheJudge(t) - registry := aFactsRegistry(t) - beside, _, hostRelease, _ := theReleasedMesh(t) - delete(beside, "mesh-controller") - controller, head := aPullRequestInto(t, aJudgeOf("the change's controller"), releaseBranch, - map[string]string{"cmd/x.txt": "x"}) - v, err := Check(t.Context(), Command, CheckSpec{ID: "check-704-controller", Repository: controller, Ref: head, - Owner: "novox", Repo: "mesh-controller", Number: 315, Base: releaseBranch, Release: releaseBranch, - JudgeCache: t.TempDir(), Paths: []string{"cmd/x.txt"}, Modules: []string{"mesh-controller"}, Judge: "self", - 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 change's controller with the vendored validator") { - t.Fatalf("a controller change into a release branch did not judge itself: %+v\n%s", v.Gate, v.Report) - } - if v.BesideAt["mesh-host"] != releaseBranch || v.Beside["mesh-host"] != hostRelease[:12] { - t.Fatalf("mesh-host was read beside it at %q (%s), not at the release branch's %s", v.BesideAt["mesh-host"], - v.Beside["mesh-host"], hostRelease[:12]) - } - if v.Release != releaseBranch || v.JudgedBy == "" || v.JudgedBy != v.Merge[:12] { - t.Fatalf("the verdict names release %q judged by %q, not its own merge %s", v.Release, v.JudgedBy, v.Merge) - } -} - -func TestAPullRequestIntoTheTrunkIsJudgedAsBefore(t *testing.T) { - aRuntimeThatBuildsTheJudge(t) - registry := aFactsRegistry(t) - controller, controllerMain, _ := aRepositoryWithARelease(t, aJudgeOf("main's controller"), - aJudgeOf("the release branch's controller")) - host, hostMain, _ := aRepositoryWithARelease(t, aHostSaying("main's validator"), - aHostSaying("the release branch's validator")) - catalogue, head := aPullRequestInto(t, map[string]string{"modules/gitea/module.json": `{"module":"gitea"}`, - CheckScript: "true\n"}, "main", map[string]string{"modules/gitea/index.ts": "x"}) - v, err := Check(t.Context(), Command, CheckSpec{ID: "check-704-trunk", Repository: catalogue, Ref: head, - Owner: "novox", Repo: "mesh-catalog", Number: 1, 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 != "main's controller with the vendored validator" { - t.Fatalf("a trunk pull request was not judged by the controller beside it as before: %+v\n%s", v.Gate, v.Report) - } - if v.Release != "" || v.JudgedBy != "" || v.BesideAt != nil || strings.Contains(v.Report, "release branch") { - t.Fatalf("a trunk pull request's verdict speaks of a release branch: %q %q %v", v.Release, v.JudgedBy, v.BesideAt) - } - if v.Beside["mesh-host"] != hostMain[:12] { - 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 7** (ADR 0358 §7): 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]) - } -} - -// **mesh-lab is read at its trunk, always** (ADR 0358): its replays run with the container runtime's socket, so a -// branch of the release's name in mesh-lab — which anyone may create — is never what a check reads. -func TestAReleaseBranchCheckReadsMeshLabAtItsTrunkEvenWhenItHasThatBranch(t *testing.T) { - aRuntimeThatBuildsTheJudge(t) - registry := aFactsRegistry(t) - beside, _, _, _ := theReleasedMesh(t) - lab, labMain, labRelease := aRepositoryWithARelease(t, map[string]string{"README": "the lab"}, - map[string]string{"README": "a branch anyone could push"}) - beside["mesh-lab"] = Beside{Repository: lab, 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-lab", 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.BesideAt["mesh-lab"] != "main" || v.Beside["mesh-lab"] != labMain[:12] || v.Beside["mesh-lab"] == labRelease[:12] { - t.Fatalf("mesh-lab was read at %q (%s), not its trunk main at %s", v.BesideAt["mesh-lab"], v.Beside["mesh-lab"], - labMain[:12]) - } -} - // aPlantedWorkspace is a workspace a pull request's own script has written into, as it can: a git configuration in its // HOME whose fsmonitor hook says each directory git runs in, a git configuration under XDG_CONFIG_HOME doing the same, // and a Go environment file that would hand the judge's build a tool of its own. It answers the workspace and the @@ -475,8 +248,8 @@ func judgeRanTheHook(t *testing.T, ran string) string { // **Every judge is built where no pull request reaches** (issue 482, ADR 0358 review): a git configuration with a // hook, and a Go environment file, planted in the workspace by an earlier pull request's script, do not run during -// the release judge's build — while the pull request's own script, in the workspace, still meets them. -func TestAHookPlantedInTheWorkspaceDoesNotRunDuringTheReleaseJudgesBuild(t *testing.T) { +// the judge's build — while the pull request's own script, in the workspace, still meets them. +func TestAHookPlantedInTheWorkspaceDoesNotRunDuringTheJudgesBuild(t *testing.T) { aRuntimeThatBuildsTheJudge(t) homes := filepath.Join(t.TempDir(), "homes") t.Setenv("JUDGE_HOMES", homes) @@ -486,7 +259,7 @@ func TestAHookPlantedInTheWorkspaceDoesNotRunDuringTheReleaseJudgesBuild(t *test catalogue, head := aPullRequestInto(t, map[string]string{"modules/gitea/module.json": `{"module":"gitea"}`, CheckScript: "git status >/dev/null 2>&1; true\n"}, releaseBranch, map[string]string{"modules/gitea/index.ts": "x"}) v, err := Check(t.Context(), Command, CheckSpec{ID: "check-704-planted", Repository: catalogue, Ref: head, - Owner: "novox", Repo: "mesh-catalog", Number: 286, Base: releaseBranch, Release: releaseBranch, + Owner: "novox", Repo: "mesh-catalog", Number: 286, Base: releaseBranch, JudgeCache: t.TempDir(), Paths: []string{"modules/gitea/index.ts"}, Modules: []string{"gitea"}, Beside: beside, Toolchain: "golang"}, workspace, registry, GitCredential{}, nil) if err != nil { @@ -506,7 +279,7 @@ func TestAHookPlantedInTheWorkspaceDoesNotRunDuringTheReleaseJudgesBuild(t *test } } -// The same for the judge built with a node-engine change's own validator, into a release branch (ADR 0358 §2). +// The same for the judge built with a node-engine change's own validator. func TestAHookPlantedInTheWorkspaceDoesNotRunDuringTheValidatorJudgesBuild(t *testing.T) { aRuntimeThatBuildsTheJudge(t) homes := filepath.Join(t.TempDir(), "homes") @@ -520,14 +293,14 @@ func TestAHookPlantedInTheWorkspaceDoesNotRunDuringTheValidatorJudgesBuild(t *te files[CheckScript] = "git status >/dev/null 2>&1; true\n" host, head := aPullRequestInto(t, files, releaseBranch, aHostSaying("this change's validator")) v, err := Check(t.Context(), Command, CheckSpec{ID: "check-704-planted-validator", Repository: host, Ref: head, - Owner: "novox", Repo: "mesh-host", Number: 111, Base: releaseBranch, Release: releaseBranch, Judge: "validator", + Owner: "novox", Repo: "mesh-host", Number: 111, Base: releaseBranch, Judge: "validator", JudgeCache: t.TempDir(), Paths: []string{"validate/v.go"}, Modules: []string{"mesh-host"}, Beside: beside, Toolchain: "golang"}, workspace, registry, GitCredential{}, nil) if err != nil { t.Fatal(err) } - if v.Gate == nil || !strings.HasSuffix(v.Gate.Summary, "the release branch's controller with this change's validator") { - t.Fatalf("a node-engine change into a release branch was not judged by that branch's controller with its own "+ + if v.Gate == nil || !strings.HasSuffix(v.Gate.Summary, "controller with this change's validator") { + t.Fatalf("a node-engine change was not judged by the controller with its own "+ "validator: %+v\n%s", v.Gate, v.Report) } if body, _ := os.ReadFile(ran); !strings.Contains(string(body), "/check/mesh-host") { @@ -541,31 +314,6 @@ func TestAHookPlantedInTheWorkspaceDoesNotRunDuringTheValidatorJudgesBuild(t *te } } -// **mesh-lab is never read at a release branch**, asked with a fallback or without (ADR 0358). -func TestTheBuildSeatNeverReadsMeshLabAtAReleaseBranch(t *testing.T) { - aRuntimeThatBuildsTheJudge(t) - registry := aFactsRegistry(t) - lab, labMain, _ := aRepositoryWithARelease(t, map[string]string{"README": "the lab"}, - map[string]string{"README": "a branch anyone could push"}) - for _, asked := range []Beside{{Repository: lab, Ref: releaseBranch}, {Repository: lab, Ref: releaseBranch, Else: "main"}} { - beside, _, _, _ := theReleasedMesh(t) - beside["mesh-lab"] = asked - 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-lab-asked", 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.BesideAt["mesh-lab"] != "main" || v.Beside["mesh-lab"] != labMain[:12] { - t.Fatalf("mesh-lab asked as %+v was read at %q (%s), not main at %s", asked, v.BesideAt["mesh-lab"], - v.Beside["mesh-lab"], labMain[:12]) - } - } -} - // **Judges are built warm and where no pull request reaches** (ADR 0358 §6): every judge build uses the judge cache's // one Go cache, kept between checks; its home is made fresh under the judge cache and removed after; nothing is left in // a temporary directory of the host; and no container a check runs for the pull request mounts the judge cache. diff --git a/internal/link/build.go b/internal/link/build.go index e698f9da..6ba3231f 100644 --- a/internal/link/build.go +++ b/internal/link/build.go @@ -173,12 +173,6 @@ type CheckRequest struct { // the controller, judged by itself; JudgeValidator for a change to the node-engine, judged by the // running controller built with the change's validator in place of the one it vendors. Judge string `json:"judge,omitempty"` - // Release is the release branch the pull request merges into (novox/hq ADR 0358): its base, when that is not its - // repository's trunk and mesh-controller has a branch of that name. The gate is then judged by the controller - // built from mesh-controller's release branch — a change to the controller still by itself, a change to the - // node-engine with its own validator — and each repository beside it is read at its branch of that name where it - // has one (CheckedOut.Else otherwise). Empty for every other base, checked as before it. - Release string `json:"release,omitempty"` // Siblings are the other open pull requests of this one's delivery group the check reads beside it (novox/hq ADR // 0382 §1): each repository found under Dir is read at the sibling's head merged onto the sibling's base, in // place of the commit the mesh runs. SiblingsUnknown says mesh-delivery could not be asked, or answered without @@ -299,10 +293,6 @@ const ( type CheckedOut struct { Repository string `json:"repository"` Ref string `json:"ref,omitempty"` - // Else is the ref read when the repository has no branch Ref (novox/hq ADR 0358 §3): beside a check of a pull - // request into a release branch, Ref is that branch and Else the repository's trunk. Empty: Ref is read, or the - // check cannot run. - Else string `json:"else,omitempty"` } // CheckOutcome is a merge check's verdict. @@ -327,13 +317,7 @@ type CheckOutcome struct { Members []CheckedMember `json:"members,omitempty"` // Built are the builds it made for its merge (novox/hq ADR 0350 §1). Built []CheckBuilt `json:"built,omitempty"` - // Release is the release branch the check was judged for (novox/hq ADR 0358 §4), JudgedBy the commit of the - // controller that judged its gate, as twelve characters, and BesideAt the branch each repository beside it was - // read at, by directory. Empty from a check of any other base, and from a build seat from before it. - 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 says which node-engine validator the group's judge was built with (ADR 0382 §3). Validator string `json:"validator,omitempty"` // Siblings are the pull requests of its delivery group it read beside it, each as its head merged onto its base // commit (ADR 0382 §4), SiblingsUnknown why it could not learn them, and JudgeFrom where the judge came from when diff --git a/internal/link/events.go b/internal/link/events.go index e545802d..8ef08c94 100644 --- a/internal/link/events.go +++ b/internal/link/events.go @@ -307,13 +307,7 @@ type Checked struct { Trunk string `json:"trunk,omitempty"` Beside map[string]string `json:"beside,omitempty"` Took string `json:"took,omitempty"` - // Release is the release branch the check judged for (novox/hq ADR 0358 §4), JudgedBy the commit of the - // controller that judged its gate (twelve characters), and BesideAt the branch each repository beside it was - // read at, by directory: so a reader sees a release-branch judgement apart from a trunk one. Empty otherwise. - 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 says which node-engine validator the group's judge was built with (ADR 0382 §3). Validator string `json:"validator,omitempty"` // Siblings are the other pull requests of the delivery group the check read beside it (novox/hq ADR 0382 §4), each // with its head and the base commit it was merged onto; SiblingsUnknown is why it could not learn them, set only diff --git a/modules/build-agent/module.json b/modules/build-agent/module.json index f9b789af..4d74764d 100644 --- a/modules/build-agent/module.json +++ b/modules/build-agent/module.json @@ -47,7 +47,7 @@ "id": "judge-cache", "path": "${dir:judge-cache}", "class": "cache", - "why": "the Go cache every merge check's judge is built with and the release-branch judges kept per head; mounted into judge builds only, never into a check's containers (novox/hq ADR 0358 §6)" + "why": "the Go cache every merge check's judge is built with and the group judges kept per head; mounted into judge builds only, never into a check's containers (novox/hq ADR 0358 §6)" } ] },