Merge mesh-catalog main (559939aa) into the registry branch: gate reads forgemsg, messenger narrows asks (K-0; issue 1966)
Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-forge-bridge/forgemsg"
|
||||
sdk "git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
@@ -24,7 +25,7 @@ type fakeMerges struct {
|
||||
manual bool
|
||||
failed error
|
||||
// said are the messages the bridge wrote on FORGE, in order.
|
||||
said []sdk.Forge
|
||||
said []forgemsg.Forge
|
||||
}
|
||||
|
||||
func newFakeMerges(f *fakeForge) *fakeMerges {
|
||||
@@ -76,11 +77,11 @@ func (m *fakeMerges) Put(group string, v sdk.Merge) error {
|
||||
err = f.refuse
|
||||
}
|
||||
f.mu.Unlock()
|
||||
msg := sdk.Forge{Repository: p.Repository, Number: p.Number, Head: p.Head.Head}
|
||||
msg := forgemsg.Forge{Repository: p.Repository, Number: p.Number, Head: p.Head.Head}
|
||||
if err != nil {
|
||||
msg.Kind, msg.Act, msg.Words = sdk.ForgeRefused, "merge", err.Error()
|
||||
msg.Kind, msg.Act, msg.Words = forgemsg.ForgeRefused, "merge", err.Error()
|
||||
} else {
|
||||
msg.Kind, msg.Merge, msg.Root = sdk.ForgeMerged, strings.Repeat("c", 40), "root"
|
||||
msg.Kind, msg.Merge, msg.Root = forgemsg.ForgeMerged, strings.Repeat("c", 40), "root"
|
||||
msg.Trees = map[string]string{"app": "tree"}
|
||||
}
|
||||
m.say(msg)
|
||||
@@ -89,12 +90,12 @@ func (m *fakeMerges) Put(group string, v sdk.Merge) error {
|
||||
}
|
||||
|
||||
// say writes a message on FORGE and has the gate hear it, as the runtime does.
|
||||
func (m *fakeMerges) say(msg sdk.Forge) {
|
||||
body, err := sdk.EncodeForge(msg)
|
||||
func (m *fakeMerges) say(msg forgemsg.Forge) {
|
||||
body, err := forgemsg.EncodeForge(msg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
event, err := sdk.ForgeEvent(msg.Repository, msg.Kind)
|
||||
event, err := forgemsg.ForgeEvent(msg.Repository, msg.Kind)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"time"
|
||||
|
||||
oc "git.novox.be/novox/mesh-contracts/go/seats/operatorchannel"
|
||||
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-forge-bridge/forgemsg"
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
sdk "git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"github.com/nats-io/nats-server/v2/server"
|
||||
@@ -585,7 +586,7 @@ func TestAPassingVerdictAsksTheApproveAndItsApproveMergesWithinSecondsOverARealB
|
||||
t.Fatalf("the gate asked the forge to merge %d times: the bridge merges", n)
|
||||
}
|
||||
// The bridge says it merged; the mesh's source then holds the merge, in one write.
|
||||
b.forgeSays(t, sdk.Forge{Kind: sdk.ForgeMerged, Repository: "novox/app", Number: 7, Head: head, Merge: strings.Repeat("b", 40),
|
||||
b.forgeSays(t, forgemsg.Forge{Kind: forgemsg.ForgeMerged, Repository: "novox/app", Number: 7, Head: head, Merge: strings.Repeat("b", 40),
|
||||
Root: "r", Trees: map[string]string{"app": "t"}})
|
||||
// The forge's own word that the pull request merged follows the bridge's (its webhook): the delivery ends merged.
|
||||
b.publish(t, "mesh.mod.gitea.event.pull.merged", merged("novox/app", 7, head, strings.Repeat("b", 40)))
|
||||
@@ -684,13 +685,13 @@ func (b *testBus) answer(t *testing.T, key, option string) time.Time {
|
||||
}
|
||||
|
||||
// forgeSays is the forge bridge's message on FORGE, as its stream carries it.
|
||||
func (b *testBus) forgeSays(t *testing.T, f sdk.Forge) {
|
||||
func (b *testBus) forgeSays(t *testing.T, f forgemsg.Forge) {
|
||||
t.Helper()
|
||||
raw, err := sdk.EncodeForge(f)
|
||||
raw, err := forgemsg.EncodeForge(f)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
event, err := sdk.ForgeEvent(f.Repository, f.Kind)
|
||||
event, err := forgemsg.ForgeEvent(f.Repository, f.Kind)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -749,7 +750,7 @@ func TestAGroupOfTwoWithGreenChecksGetsOneAskAndOnItsApproveBothMergeOneAfterThe
|
||||
t.Fatalf("the second member was handed before the first merged: %+v", again.Pulls)
|
||||
}
|
||||
one := first.Pulls[0]
|
||||
b.forgeSays(t, sdk.Forge{Kind: sdk.ForgeMerged, Repository: one.Repository, Number: one.Number, Head: one.Head.Head,
|
||||
b.forgeSays(t, forgemsg.Forge{Kind: forgemsg.ForgeMerged, Repository: one.Repository, Number: one.Number, Head: one.Head.Head,
|
||||
Merge: strings.Repeat("a", 40), Root: "r", Trees: map[string]string{"x": "t"}})
|
||||
var both sdk.Merge
|
||||
rt.until("the second member to be handed", eventWithin, func() bool { both = mergeAt(b, "feat/x"); return len(both.Pulls) == 2 })
|
||||
@@ -757,7 +758,7 @@ func TestAGroupOfTwoWithGreenChecksGetsOneAskAndOnItsApproveBothMergeOneAfterThe
|
||||
t.Fatalf("the second merge handed is %+v", both.Pulls)
|
||||
}
|
||||
two := both.Pulls[1]
|
||||
b.forgeSays(t, sdk.Forge{Kind: sdk.ForgeMerged, Repository: two.Repository, Number: two.Number, Head: two.Head.Head,
|
||||
b.forgeSays(t, forgemsg.Forge{Kind: forgemsg.ForgeMerged, Repository: two.Repository, Number: two.Number, Head: two.Head.Head,
|
||||
Merge: strings.Repeat("b", 40), Root: "r", Trees: map[string]string{"x": "t"}})
|
||||
rt.until("source.main to hold both merges", eventWithin, func() bool {
|
||||
e, err := b.kvs["source"].Get(ctx, sdk.SourceMainKey)
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
sdk "git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-forge-bridge/forgemsg"
|
||||
)
|
||||
|
||||
// The holder, end to end over fakes (novox/hq ADR 0239 *How it is checked*, cut by ADR 0539 §17): a pull request's head
|
||||
@@ -241,9 +241,9 @@ func TestAMergedGroupEndsMergedWithSourceMainMoved(t *testing.T) {
|
||||
t.Fatalf("source.main is %+v, wanted both merges", w.source.main)
|
||||
}
|
||||
// A merge that leaves no module.json carries no trees (issue 1367): the gate reads it, and refuses nothing.
|
||||
msg := sdk.Forge{Kind: sdk.ForgeMerged, Repository: "novox/a", Number: 9, Head: strings.Repeat("e", 40),
|
||||
msg := forgemsg.Forge{Kind: forgemsg.ForgeMerged, Repository: "novox/a", Number: 9, Head: strings.Repeat("e", 40),
|
||||
Merge: strings.Repeat("f", 40), Root: "r"}
|
||||
body, err := sdk.EncodeForge(msg)
|
||||
body, err := forgemsg.EncodeForge(msg)
|
||||
if err != nil {
|
||||
t.Fatalf("a merged message with no trees is refused: %v", err)
|
||||
}
|
||||
@@ -251,6 +251,17 @@ func TestAMergedGroupEndsMergedWithSourceMainMoved(t *testing.T) {
|
||||
if len(w.h.refusals) != 0 {
|
||||
t.Errorf("the gate refused a merged message with no trees: %v", w.h.refusals)
|
||||
}
|
||||
// A group's end is the indexer's: the gate ignores it before decoding, and says no refusal (1585/T25).
|
||||
w.h.Forged("mesh-delivery-forge-bridge.forge.group.merged", []byte(`{"v":1,"group":"feat/y"}`))
|
||||
if len(w.h.refusals) != 0 {
|
||||
t.Errorf("the gate refused a group's end: %v", w.h.refusals)
|
||||
}
|
||||
// The repository forge/group's merged message ends the same way: it is decoded as a forge message, and this one,
|
||||
// which is none, is refused.
|
||||
w.h.Forged("mesh-delivery-forge-bridge.forge.forge.group.merged", []byte(`{"v":1,"group":"feat/y"}`))
|
||||
if len(w.h.refusals) != 1 {
|
||||
t.Errorf("forge/group's merged message was passed by as a group's end: refusals %v", w.h.refusals)
|
||||
}
|
||||
}
|
||||
|
||||
// ADR 0419 §2, issue 978: a description's `after:` line is not read, and says so once on its pull request. The
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
occ "git.novox.be/novox/mesh-contracts/go/clients/operatorchannelclient"
|
||||
oc "git.novox.be/novox/mesh-contracts/go/seats/operatorchannel"
|
||||
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-forge-bridge/forgemsg"
|
||||
sdk "git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
@@ -766,6 +767,6 @@ func allGreenFor(w *world, ms ...member) {
|
||||
|
||||
// bridgeMerged is the forge bridge's word that it merged a member.
|
||||
func bridgeMerged(w *world, repository string, number int, head, merge string) {
|
||||
w.merges.say(sdk.Forge{Kind: sdk.ForgeMerged, Repository: repository, Number: number, Head: head, Merge: merge, Root: "root",
|
||||
w.merges.say(forgemsg.Forge{Kind: forgemsg.ForgeMerged, Repository: repository, Number: number, Head: head, Merge: merge, Root: "root",
|
||||
Trees: map[string]string{"app": "tree"}})
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
occ "git.novox.be/novox/mesh-contracts/go/clients/operatorchannelclient"
|
||||
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-forge-bridge/forgemsg"
|
||||
sdk "git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
@@ -101,7 +102,7 @@ func TestAnAnswerHandsTheFirstMemberOnlyAndTheBridgesMergedHandsTheNext(t *testi
|
||||
if len(w.merges.puts) != 1 {
|
||||
t.Fatalf("the next member was handed before the bridge said the first merged: %d merges written", len(w.merges.puts))
|
||||
}
|
||||
w.merges.say(sdk.Forge{Kind: sdk.ForgeMerged, Repository: gCat, Number: 2, Head: gHk, Merge: strings.Repeat("a", 40), Root: "r", Trees: map[string]string{"x": "t"}})
|
||||
w.merges.say(forgemsg.Forge{Kind: forgemsg.ForgeMerged, Repository: gCat, Number: 2, Head: gHk, Merge: strings.Repeat("a", 40), Root: "r", Trees: map[string]string{"x": "t"}})
|
||||
w.settleAll()
|
||||
if len(w.merges.puts) != 2 {
|
||||
t.Fatalf("the second member was not handed after the first merged: %d merges written", len(w.merges.puts))
|
||||
@@ -116,7 +117,7 @@ func TestAnAnswerHandsTheFirstMemberOnlyAndTheBridgesMergedHandsTheNext(t *testi
|
||||
// AC2: the bridge's refused hands no member, and the group is said urgent, naming the member and the forge's words.
|
||||
func TestTheBridgesRefusalHandsNoMemberAndIsSaidUrgent(t *testing.T) {
|
||||
w := answeredFirst(t)
|
||||
w.merges.say(sdk.Forge{Kind: sdk.ForgeRefused, Repository: gCat, Number: 2, Head: gHk, Act: "merge",
|
||||
w.merges.say(forgemsg.Forge{Kind: forgemsg.ForgeRefused, Repository: gCat, Number: 2, Head: gHk, Act: "merge",
|
||||
Words: "the forge refused gitea_merge_pull_request: mesh/repo-check (missing)"})
|
||||
w.later(time30s)
|
||||
w.settleAll()
|
||||
@@ -138,7 +139,7 @@ func TestTheBridgesRefusalHandsNoMemberAndIsSaidUrgent(t *testing.T) {
|
||||
// the group, even after the first member merged.
|
||||
func TestTheNextMemberIsNotHandedWhileARequiredStatusOfTheGroupIsNotASuccess(t *testing.T) {
|
||||
w := answeredFirst(t)
|
||||
w.merges.say(sdk.Forge{Kind: sdk.ForgeMerged, Repository: gCat, Number: 2, Head: gHk, Merge: strings.Repeat("a", 40), Root: "r", Trees: map[string]string{"x": "t"}})
|
||||
w.merges.say(forgemsg.Forge{Kind: forgemsg.ForgeMerged, Repository: gCat, Number: 2, Head: gHk, Merge: strings.Repeat("a", 40), Root: "r", Trees: map[string]string{"x": "t"}})
|
||||
setChecks(w, gCtl, gHc, "success", "") // the second member's repository check went missing meanwhile
|
||||
w.settleAll()
|
||||
if len(w.merges.puts) != 1 {
|
||||
@@ -194,7 +195,7 @@ func TestARefusalThatRepeatsWithNoTrunkMoveIsNotAskedAgainWithoutEnd(t *testing.
|
||||
w := answeredFirst(t)
|
||||
d := w.h.deliveries[IDOf(gCat, gHk)]
|
||||
refuse := func(took string) {
|
||||
w.h.ForgeRefused(sdk.Forge{Kind: sdk.ForgeRefused, Repository: gCat, Number: 2, Head: gHk, Act: "merge",
|
||||
w.h.ForgeRefused(forgemsg.Forge{Kind: forgemsg.ForgeRefused, Repository: gCat, Number: 2, Head: gHk, Act: "merge",
|
||||
Words: "novox/mesh-catalog#2 is not merged: its verdict names no trunk commit it was judged against, and main is now f70a99dbf485. " +
|
||||
"The check of head d2d2d2d2d2d2 merged into trunk f70a99dbf485 was asked; the last check took " + took + ". Ask again once both statuses pass"})
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-forge-bridge/forgemsg"
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
sdk "git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
@@ -312,23 +313,33 @@ func (h *Holder) mergeValue(name, key string, approved map[string]string, target
|
||||
return m, len(m.Pulls) > 0 && state == sdk.MergeMerged
|
||||
}
|
||||
|
||||
// forgeGroupEnd is the whole event of a group's end on FORGE, the forge bridge's event name under its module: compared
|
||||
// exactly, so the merged message of a repository named forge/group, which ends the same way, is read as one.
|
||||
const forgeGroupEnd = sdk.ForgeBridgeModule + "." + forgemsg.ForgeGroupEndEvent
|
||||
|
||||
// Forged takes a message of the forge bridge on FORGE: the gate follows a merge and the refusal of a merge, and nothing else.
|
||||
//
|
||||
// The end of a group's merges (`forge.group.merged`, a body this gate has no use for: the gate wrote the merges itself) is
|
||||
// the indexer's, and is ignored before it is decoded, so no refusal is said for it (issue 1585, ADR 0633 clause 4).
|
||||
func (h *Holder) Forged(event string, body []byte) {
|
||||
f, err := sdk.DecodeForge(body)
|
||||
if event == forgeGroupEnd {
|
||||
return
|
||||
}
|
||||
f, err := forgemsg.DecodeForge(body)
|
||||
if err != nil {
|
||||
h.Refuse(fmt.Errorf("a message of the forge bridge is not readable: %v", err))
|
||||
return
|
||||
}
|
||||
switch {
|
||||
case f.Kind == sdk.ForgeMerged:
|
||||
case f.Kind == forgemsg.ForgeMerged:
|
||||
h.ForgeMerged(f)
|
||||
case f.Kind == sdk.ForgeRefused && f.Act == "merge":
|
||||
case f.Kind == forgemsg.ForgeRefused && f.Act == "merge":
|
||||
h.ForgeRefused(f)
|
||||
}
|
||||
}
|
||||
|
||||
// memberOfForge is the delivery a forge message is about: at the head it names.
|
||||
func (h *Holder) memberOfForge(f sdk.Forge) *Delivery {
|
||||
func (h *Holder) memberOfForge(f forgemsg.Forge) *Delivery {
|
||||
if d := h.deliveries[IDOf(f.Repository, f.Head)]; d != nil && d.Number == f.Number {
|
||||
return d
|
||||
}
|
||||
@@ -337,7 +348,7 @@ func (h *Holder) memberOfForge(f sdk.Forge) *Delivery {
|
||||
|
||||
// ForgeMerged takes the bridge's word that a pull request the gate handed to it merged: the merge is announced once,
|
||||
// with the ask, its asker the teller and the heads (issue 1025), and the next member's turn comes at the next look.
|
||||
func (h *Holder) ForgeMerged(f sdk.Forge) {
|
||||
func (h *Holder) ForgeMerged(f forgemsg.Forge) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
d := h.memberOfForge(f)
|
||||
@@ -354,7 +365,7 @@ func (h *Holder) ForgeMerged(f sdk.Forge) {
|
||||
// ForgeRefused takes the bridge's word that the forge refused a merge: nothing after it is merged, and the refusal is said
|
||||
// as a stalled line (ADR 0441 §3, §4). The merge is handed again only at the next event that decides whether it may
|
||||
// merge (mergeAgainNow).
|
||||
func (h *Holder) ForgeRefused(f sdk.Forge) {
|
||||
func (h *Holder) ForgeRefused(f forgemsg.Forge) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
d := h.memberOfForge(f)
|
||||
|
||||
Reference in New Issue
Block a user