From d2eb3e5ecf67c71715d5c1146a0696f7b1a0f638 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 26 Oct 2026 11:35:16 +0100 Subject: [PATCH] =?UTF-8?q?checker:=20the=20line=20of=20a=20carried=20verd?= =?UTF-8?q?ict=20names=20the=20head=20on=20the=20new=20trunk,=20then=20whe?= =?UTF-8?q?re=20it=20was=20carried=20from;=20a=20verdict=20being=20carried?= =?UTF-8?q?=20is=20pending=20and=20says=20so=20(ADR=200548=20=C2=A76,=20?= =?UTF-8?q?=C2=A78;=20issue=20568=20D6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../internal/check/adr548_test.go | 34 +++++++++++++ .../internal/check/checker.go | 29 +++++++++-- .../internal/check/line.go | 49 ++++++++++++++++++- 3 files changed, 107 insertions(+), 5 deletions(-) diff --git a/modules/mesh-delivery-checker/internal/check/adr548_test.go b/modules/mesh-delivery-checker/internal/check/adr548_test.go index 7bd400c..7f2ff65 100644 --- a/modules/mesh-delivery-checker/internal/check/adr548_test.go +++ b/modules/mesh-delivery-checker/internal/check/adr548_test.go @@ -243,6 +243,27 @@ func TestOneCheckPerDelivery(t *testing.T) { t.Errorf("checks %v carried from %v; want a whole check, a carry, then a whole check after the conflict", kinds, from) } }, + // §8, §6: the line of a carried verdict names the head on the new trunk commit first, as the gate and the merge tool + // read a line, then where it was carried from and what the carry ran and kept; while it is carried it is pending and says so. + "the line of a carry says where it was carried from": func(t *testing.T) { + cs, p := lineCase(t, pullOf(repoA, 7, head1)) + v := passVerdict() + v.Trunk = map[string]string{repoA: trunkA} + v.Beside = map[string]string{catalogueRepository: trunkS2} + v.CarriedFrom = map[string]string{repoA: trunkA2, catalogueRepository: trunkS} + line := Line(v, cs, p, nil, nil, nil) + if !strings.HasPrefix(line, "pass 111111111111 on aaaaaaaaaaaa | carried from abababababab, mesh-catalog 555555555555 | unit 12 run") { + t.Errorf("the carried line = %q", line) + } + if head, trunk := checkWordsOf(line); head != head1[:12] || trunk != trunkA[:12] { + t.Errorf("the line names %s on %s first, want the head on the new trunk", head, trunk) + } + cs.CarriedFrom = map[string]string{repoA: trunkA2} + pending := Line(delivery.Verdict{State: delivery.VerdictRunning}, cs, p, nil, nil, nil) + if !strings.HasPrefix(pending, "being carried to 111111111111 on aaaaaaaaaaaa | from abababababab") { + t.Errorf("the pending line of a carry = %q", pending) + } + }, // §5: a carry that fails names the move it failed on. "a carry that fails names the move": func(t *testing.T) { j := &judgeFunc{f: func(_ context.Context, c *Case, call int) (Outcome, error) { @@ -268,3 +289,16 @@ func TestOneCheckPerDelivery(t *testing.T) { t.Run(name, run) } } + +// checkWordsOf is the head and trunk a line names first, as the gate reads it (mesh-delivery-gate onestatus.go checkLine). +func checkWordsOf(line string) (string, string) { + for _, part := range strings.Split(line, " | ") { + f := strings.Fields(part) + for i := 0; i+2 < len(f); i++ { + if f[i+1] == "on" && len(f[i]) == 12 && len(f[i+2]) == 12 { + return f[i], f[i+2] + } + } + } + return "", "" +} diff --git a/modules/mesh-delivery-checker/internal/check/checker.go b/modules/mesh-delivery-checker/internal/check/checker.go index 4e0f58d..3cfc970 100644 --- a/modules/mesh-delivery-checker/internal/check/checker.go +++ b/modules/mesh-delivery-checker/internal/check/checker.go @@ -525,8 +525,8 @@ func (ch *Checker) try(ctx context.Context, key string, cs *Case) { if ch.movedSince(cs) || besideMoved(v.Beside, ch.heads()) { // Judged on a trunk that moved while it ran: the verdict is kept as judged, and the status stays pending until the // check of the new trunk, which Reconsider starts, ends. - ch.writeOwed(ctx, cs, delivery.Verdict{State: delivery.VerdictRunning}, Outcome{}) - ch.logf("%s: %s on a trunk that moved while it ran; the status stays pending", cs.Key, v.State) + ch.owePending(ctx, cs, v) + ch.logf("%s: %s on a trunk that moved while it ran; the status stays pending until it is carried", cs.Key, v.State) return } ch.writeOwed(ctx, cs, v, out) @@ -666,14 +666,37 @@ func (ch *Checker) TrunkMoved(ctx context.Context) { if err != nil || !found { continue // never judged: no status to put out of date } - if v, err := delivery.DecodeVerdict(value); err == nil && !ch.stale(v, cs) { + v, err := delivery.DecodeVerdict(value) + if err == nil && !ch.stale(v, cs) { continue } + if err == nil && v.State == delivery.VerdictPass { + cs.CarriedFrom = judgedOn(v) // the status says it is being carried to the new commit (ADR 0548 §6) + } ch.writeOwed(ctx, cs, delivery.Verdict{State: delivery.VerdictRunning}, Outcome{}) } ch.Reconsider(ctx) } +// owePending owes mesh/check pending on the heads of a verdict judged on commits that moved while it ran: a pass says it is +// being carried to the trunk's head now (ADR 0548 §6), anything else that it is checked again. +func (ch *Checker) owePending(ctx context.Context, judged *Case, v delivery.Verdict) { + next := judged + ckey, err := delivery.CandidatesKey(v.Group) + ch.mu.Lock() + c, ok := ch.known[ckey] + ch.mu.Unlock() + if err == nil && ok { + if now := CaseOf(c, ch.heads()); now != nil && now.Key == judged.Key { + next = now + } + } + if v.State == delivery.VerdictPass { + next.CarriedFrom = judgedOn(v) + } + ch.writeOwed(ctx, next, delivery.Verdict{State: delivery.VerdictRunning}, Outcome{}) +} + // movedSince says whether a trunk the case was judged on is no longer the trunk's head. func (ch *Checker) movedSince(cs *Case) bool { now := ch.heads() diff --git a/modules/mesh-delivery-checker/internal/check/line.go b/modules/mesh-delivery-checker/internal/check/line.go index a105441..f09b839 100644 --- a/modules/mesh-delivery-checker/internal/check/line.go +++ b/modules/mesh-delivery-checker/internal/check/line.go @@ -85,18 +85,20 @@ type pieces struct { head, commits, counts, shortCounts, times, shortTimes, over, builds, composition, shortComposition, group string // judged names the program that judged a change to a build module (ADR 0535 §5); never dropped. judged string + // carried says, of a carried verdict, the commits it was carried from (ADR 0548 §8); never dropped. + carried string } // pullKey names a pull request of a case: `#`. func pullKey(p Pull) string { return fmt.Sprintf("%s#%d", p.Repository, p.Number) } func (p pieces) join(drop int) string { - parts := []string{p.head, p.commits, p.counts, p.judged} + parts := []string{p.head, p.commits, p.carried, p.counts, p.judged} switch { case drop < 3 && p.times != "": parts = append(parts, p.times) case drop == 3 && p.shortTimes != "": - parts = []string{p.head, p.commits, p.shortCounts, p.judged, p.shortTimes} + parts = []string{p.head, p.commits, p.carried, p.shortCounts, p.judged, p.shortTimes} } if p.over != "" { parts = append(parts, p.over) @@ -171,6 +173,39 @@ func buildWords(builds []BuildNote) string { return "builds: " + strings.Join(parts, ", ") } +// carriedWords say the commits a verdict was carried from, for the line of pull request p (ADR 0548 §8): its own trunk's +// commit as twelve characters, a sibling's with its name before it; only those that moved, sorted. Empty when none did. +func carriedWords(from map[string]string, v delivery.Verdict, cs *Case, p Pull) string { + var own string + var others []string + for r, old := range from { + now, ok := v.Trunk[r] + if cs != nil && !ok { + now, ok = cs.Trunk[r] + } + if !ok { + now = v.Beside[r] + } + if now == "" || now == old { + continue + } + if strings.EqualFold(r, p.Repository) { + own = short12(old) + continue + } + name := r + if i := strings.LastIndex(r, "/"); i >= 0 { + name = r[i+1:] + } + others = append(others, name+" "+short12(old)) + } + slices.Sort(others) + if own != "" { + others = append([]string{own}, others...) + } + return strings.Join(others, ", ") +} + // parts makes the pieces of the line of a pull request of a case. func parts(v delivery.Verdict, cs *Case, p Pull, builds []BuildNote, slowest []string, judged map[string]string) pieces { var pc pieces @@ -179,8 +214,18 @@ func parts(v delivery.Verdict, cs *Case, p Pull, builds []BuildNote, slowest []s switch v.State { case delivery.VerdictRunning: pc.head = "checking " + commits + if cs != nil && cs.CarriedFrom != nil { + // Out of date and being carried to the new commit (ADR 0548 §6): pending, and saying so. + pc.head = "being carried to " + commits + if w := carriedWords(cs.CarriedFrom, v, cs, p); w != "" { + pc.carried = "from " + w + } + } case delivery.VerdictPass: pc.head = "pass " + commits + if w := carriedWords(v.CarriedFrom, v, cs, p); w != "" { + pc.carried = "carried from " + w // the counts that follow say what the carry ran and kept + } default: pc.head = "failed: " + firstFailed(v) if n := failedCount(v); n > 1 {