diff --git a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/record_test.go b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/record_test.go index 59285a28e..2b51ba7de 100644 --- a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/record_test.go +++ b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/record_test.go @@ -715,6 +715,33 @@ func TestTheRecordsToolsReadThemAndEndOneByHand(t *testing.T) { t.Fatal("a recovery with no why was taken") } }) + t.Run("recovery-merged names the branch the merge moved and the module its record's catalog half names (design 47 G A-161, A-172)", func(t *testing.T) { + var body meshdelivery.RecoveryMergedEvent + rs := &Records{Ledger: newLedgerFake(t), Now: func() time.Time { return at }, + Say: func(subject string, b any, id string) error { + if subject == meshdelivery.SubjectRecoveryMerged { + body = b.(meshdelivery.RecoveryMergedEvent) + } + return nil + }} + commit := strings.Repeat("2", 40) + r, _, err := rs.Open(recordMilestone("feat/x", 4), map[string]string{"novox/app@222222222222": commit}, false) + if err != nil { + t.Fatal(err) + } + if err := rs.AddEntry(r, RecordEntry{Module: "mesh-y", Fingerprint: "src2:f2"}); err != nil { + t.Fatal(err) + } + if err := rs.AddEntry(r, RecordEntry{Module: "mesh-x", Fingerprint: "src2:f1", Node: "ace"}); err != nil { + t.Fatal(err) + } + if _, err := rs.Recovery("novox/app", commit, "why", "jochen"); err != nil { + t.Fatal(err) + } + if body.Branch != "feat/x" || body.Module != "mesh-y" { + t.Fatalf("said %+v", body) + } + }) t.Run("a removal asks the runtime's ledger removal of the instance, and one said twice changes nothing", func(t *testing.T) { was := ledgerCall t.Cleanup(func() { ledgerCall = was }) diff --git a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/recovery.go b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/recovery.go index 3cc8944a5..3259ce5a1 100644 --- a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/recovery.go +++ b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/recovery.go @@ -3,9 +3,11 @@ package main import ( "errors" "fmt" + "sort" "strings" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/seats/meshdelivery" + "git.novox.be/novox/mesh-sdk/go/keys" ) // Recovery names a merge a recovery (design 47 G ยง81, A-23): the delivery record that holds the commit is marked through its @@ -36,7 +38,8 @@ func (rs *Records) Recovery(repository, commit, why, by string) (meshdelivery.Re r.Recovery, r.Sequence = &want, seq now := rs.now() if err := rs.say(meshdelivery.SubjectRecoveryMerged, meshdelivery.RecoveryMergedEvent{At: &now, By: by, Commit: head, - Repository: repository, V: 1, Why: why}, "recovery-merged."+r.Instance+"."+head); err != nil { + Repository: repository, V: 1, Why: why, + Branch: branchOf(r), Module: moduleOf(r)}, "recovery-merged."+r.Instance+"."+head); err != nil { return meshdelivery.RecoveryAnswer{}, fmt.Errorf("the record %s is marked but recovery-merged is not said: %w", r.Instance, err) } return meshdelivery.RecoveryAnswer{By: by, Commit: head, Recovery: true, Repository: repository}, nil @@ -44,3 +47,32 @@ func (rs *Records) Recovery(repository, commit, why, by string) (meshdelivery.Re } return meshdelivery.RecoveryAnswer{}, fmt.Errorf("no delivery record holds %s at %s: the merge is taken in first", repository, commit) } + +// branchOf is the watched branch a record's merge moved: the record of a taken source revision is keyed `.` +// (recordMilestone); a milestone of another form names no branch. +func branchOf(r *Record) string { + part, _, ok := strings.Cut(r.Milestone, ".") + if !ok { + return "" + } + branch, err := keys.UnescapeName(part) + if err != nil { + return "" + } + return branch +} + +// moduleOf is the module the merge changed, from the entries of the record's catalog half (those that name no node), the +// modules in order and comma-separated when the merge changed several. +func moduleOf(r *Record) string { + seen := map[string]bool{} + var modules []string + for _, e := range r.Entries { + if e.Node == "" && e.Module != "" && !seen[e.Module] { + seen[e.Module] = true + modules = append(modules, e.Module) + } + } + sort.Strings(modules) + return strings.Join(modules, ", ") +}