From 93c0ae19dd2a65b3ae27c367b4b100cafc453e06 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 28 Oct 2026 10:50:39 +0100 Subject: [PATCH] Forge bridge: write a head pushed to a pull request into another base as closed, and say so (issue 1422) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The checker judges pull requests into main only and source.main names main's head only (ADR 0484 §11), but a live update into another base (de-spiegel#6 into nox-mesh) became a candidate, unlike at the start (OpenPulls) and at its merge. Once the gate seeds a repository's trunk head, such a candidate would be judged against main's head: a verdict that lies. --- .../cmd/mesh-delivery-forge-bridge/main.go | 3 +++ .../internal/bridge/bridge.go | 13 ++++++++++++- .../internal/bridge/bridge_test.go | 15 +++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/modules/mesh-delivery-forge-bridge/cmd/mesh-delivery-forge-bridge/main.go b/modules/mesh-delivery-forge-bridge/cmd/mesh-delivery-forge-bridge/main.go index 9bfb202..a11626c 100644 --- a/modules/mesh-delivery-forge-bridge/cmd/mesh-delivery-forge-bridge/main.go +++ b/modules/mesh-delivery-forge-bridge/cmd/mesh-delivery-forge-bridge/main.go @@ -96,6 +96,9 @@ func handle(ctx context.Context, m delivered, pub publisher, trees bridge.Trees, logger.Printf("%s: %s could not be written on FORGE, left for redelivery: %v", m.Subject(), msg.Event, err) return } + if msg.Said != "" { + logger.Printf("%s: %s", m.Subject(), msg.Said) + } _ = m.Ack() } diff --git a/modules/mesh-delivery-forge-bridge/internal/bridge/bridge.go b/modules/mesh-delivery-forge-bridge/internal/bridge/bridge.go index 45f4334..80e9b24 100644 --- a/modules/mesh-delivery-forge-bridge/internal/bridge/bridge.go +++ b/modules/mesh-delivery-forge-bridge/internal/bridge/bridge.go @@ -54,6 +54,8 @@ var LockFiles = []string{"go.sum", "package-lock.json"} type Message struct { Event string Data []byte + // Said is what the bridge did that the log says, beside writing the message; empty for none. + Said string } // The subjects of the forge module's events the bridge writes pull messages for (mesh-catalog modules/gitea, @@ -148,6 +150,15 @@ func ConvertWith(subject string, data []byte, trees Trees, files Files, list Lis if subject == MergedSubject && p.Base == Trunk { return merged(subject, p, trees, files, list) } + said := "" + if action == delivery.PullUpdated && p.Base != "" && p.Base != Trunk { + // The checker judges pull requests into main only, and source.main names main's head only (novox/hq ADR 0484 §11): a + // pull request into another base is no candidate, as it is none at the start (OpenPulls) and at its merge. It is + // written closed, so a candidate an older head made is taken out, and said (issue 1422). + action = delivery.PullClosed + said = fmt.Sprintf("%s/%s#%d at %s is into %s, not %s: written closed, so it is no candidate; the checker judges pull requests into %s only (novox/hq ADR 0484 §11)", + p.Owner, p.Repo, p.Number, p.HeadSHA[:12], p.Base, Trunk, Trunk) + } f := delivery.Forge{Kind: delivery.ForgePull, Repository: p.Owner + "/" + p.Repo, Branch: p.Head, Number: p.Number, Head: p.HeadSHA, Action: action} // The directories are the forge's word only when it said it could read them (module_dirs_said). @@ -173,7 +184,7 @@ func ConvertWith(subject string, data []byte, trees Trees, files Files, list Lis if err != nil { return Message{}, unreadable(subject, "%v", err) } - return Message{Event: event, Data: body}, nil + return Message{Event: event, Data: body, Said: said}, nil } // MaxReadsInFlight is how many files of the forge inputsOf asks for at once. A merge into a repository of many diff --git a/modules/mesh-delivery-forge-bridge/internal/bridge/bridge_test.go b/modules/mesh-delivery-forge-bridge/internal/bridge/bridge_test.go index c2424a5..9536738 100644 --- a/modules/mesh-delivery-forge-bridge/internal/bridge/bridge_test.go +++ b/modules/mesh-delivery-forge-bridge/internal/bridge/bridge_test.go @@ -110,6 +110,21 @@ func TestClosedBecomesAClosedPullMessage(t *testing.T) { // 1235/AC4 if !delivery.SameForge(f, want) { t.Errorf("message = %+v, want %+v", f, want) } + // Issue 1422, ADR 0484 §11: a head pushed to a pull request into another base is written closed, read nothing for, and said; + // one into main is no closed message, and an event naming no base is written as before. + msg, err = Convert(updatedSubject, updated(map[string]any{"base": "nox-mesh"}), nil, nil) + if err != nil { + t.Fatal(err) + } + if f, err = delivery.DecodeForge(msg.Data); err != nil || !delivery.SameForge(f, want) || !strings.Contains(msg.Said, "is into nox-mesh, not main") { + t.Errorf("an update into nox-mesh is %+v (%v), said %q; want it closed and said", f, err, msg.Said) + } + for _, base := range []any{"main", nil} { + msg, err = Convert(updatedSubject, updated(map[string]any{"base": base}), nil, anyFiles) + if f, _ = delivery.DecodeForge(msg.Data); err != nil || f.Action != delivery.PullUpdated || msg.Said != "" { + t.Errorf("an update with base %v is %+v (%v), said %q; want it updated", base, f, err, msg.Said) + } + } } func TestUnreadableEventsWriteNothing(t *testing.T) { // 1235/AC5