The gate serves the seat mesh-delivery's verb source: each repository's trunk branch and head and the revision, read from source.main (milestone 47/G, source verb)
A node-engine reads the repositories here to call a reverted build's catch-up tool once per repository, never the gate's state. Rows in issue 888's test and the bus test, which call the verb over the runtime. Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ (Joined from b45fd0ab.) Builds: design 47 G §15 in part (GATE7) Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
@@ -137,6 +137,17 @@ func (s *fakeSource) Move(update func(prev *Source) (Source, bool)) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read is source.main as held, and its revision: the number of writes.
|
||||
func (s *fakeSource) Read() (*Source, uint64, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if s.main == nil {
|
||||
return nil, 0, nil
|
||||
}
|
||||
held := *s.main
|
||||
return &held, uint64(len(s.moves)), nil
|
||||
}
|
||||
|
||||
func sortStrings(s []string) { sort.Strings(s) }
|
||||
func itoa(n int) string { return strconv.Itoa(n) }
|
||||
|
||||
|
||||
@@ -631,6 +631,11 @@ func TestAPassingVerdictAsksTheApproveAndItsApproveMergesWithinSecondsOverARealB
|
||||
src, err := mdc.DecodeSource(e.Value())
|
||||
return err == nil && src.Group == "fix/x" && src.Heads["novox/app"] == strings.Repeat("b", 40)
|
||||
})
|
||||
// The seat's verb source answers it from the bucket, with its revision on the bus.
|
||||
said := strings.ReplaceAll(rt.call(t, Seat+".source", map[string]any{}), `\"`, `"`)
|
||||
if !strings.Contains(said, `{"head":"`+strings.Repeat("b", 40)+`","repository":"novox/app","trunk":"main"}`) || !strings.Contains(said, `"revision":`) {
|
||||
t.Fatalf("source answers %s", said)
|
||||
}
|
||||
// The delivery ends merged (ADR 0539 §17), and the gate asked the controller nothing (R6-C3).
|
||||
rt.until("the delivery to be merged", eventWithin, func() bool {
|
||||
e, err := b.kvs["deliveries"].Get(ctx, kvKey(IDOf("novox/app", head)))
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
mdc "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient"
|
||||
seat "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdelivery"
|
||||
)
|
||||
|
||||
// Issue 888: the checker judges on the trunk source.main names (mesh-delivery modules/mesh-delivery-checker main.go), and the
|
||||
@@ -38,6 +39,12 @@ func TestAMergeHeardOnlyFromTheForgeMovesSourceMain(t *testing.T) {
|
||||
if w.source.main == nil || w.source.main.Heads[i888App] != i888Merge1 || w.source.main.Heads[i888Other] != i888OtherH {
|
||||
t.Fatalf("source.main is %+v; wanted %s at %s and %s kept", w.source.main, i888App, i888Merge1, i888Other)
|
||||
}
|
||||
// The seat's verb source answers each repository's trunk branch and head, and the revision, from the same key.
|
||||
if a := w.h.SourceRevision(); a.Error != "" || a.Revision != 1 || len(a.Repositories) != 2 ||
|
||||
a.Repositories[0] != (seat.SourceHead{Repository: i888App, Trunk: MergeBranch, Head: i888Merge1}) ||
|
||||
a.Repositories[1].Repository != i888Other || a.Repositories[1].Head != i888OtherH {
|
||||
t.Fatalf("source answers %+v", a)
|
||||
}
|
||||
n := len(w.source.moves)
|
||||
w.settleAll()
|
||||
w.h.PullMerged(i888MergedAt(i888App, 3, i888Head1, i888Merge1, w.now.Add(-time.Minute))) // heard again
|
||||
|
||||
@@ -354,6 +354,12 @@ func tools(h *Holder, l *listening) []stdio.Tool {
|
||||
Input: map[string]any{"type": "object", "properties": map[string]any{
|
||||
"all": map[string]any{"type": "string", "enum": []string{"true", "false"}}}},
|
||||
Run: func(a map[string]any) (any, error) { return h.Groups(boolArg(a, "all")), nil }},
|
||||
{Name: seat + "source",
|
||||
Description: "The source revision the gate holds: every repository it names, its trunk branch and that trunk's head, the " +
|
||||
"group that moved it last and its revision. Read, never written: a node-engine reads the repositories here to call a " +
|
||||
"reverted build's catch-up tool once per repository, never the gate's state.",
|
||||
Input: map[string]any{"type": "object", "properties": map[string]any{}},
|
||||
Run: func(map[string]any) (any, error) { return h.SourceRevision(), nil }},
|
||||
{Name: seat + "checks",
|
||||
Description: "What the mesh's checks said of a pull request's head or of one commit: each of the commit's " +
|
||||
"statuses (mesh/check, mesh/delivery, …) with its state, description and when it " +
|
||||
|
||||
@@ -75,7 +75,7 @@ func TestItClaimsTheDeliverySeatAndFollowsWhatItHandles(t *testing.T) {
|
||||
}
|
||||
// The eight verbs the seat keeps (ADR 0539 §17): times, what-if, release, stop and retire-history went with the plans; and
|
||||
// settle, a person's ask to settle merges held without a passing check (ADR 0556 clause 5).
|
||||
if want := []string{"deliveries", "show", "groups", "checks", "table", "stalled", "recheck", "close", "settle"}; !reflect.DeepEqual(m.Claims[0].Serves, want) {
|
||||
if want := []string{"deliveries", "show", "groups", "source", "checks", "table", "stalled", "recheck", "close", "settle"}; !reflect.DeepEqual(m.Claims[0].Serves, want) {
|
||||
t.Fatalf("serves %v, wanted %v", m.Claims[0].Serves, want)
|
||||
}
|
||||
// The forge messages' layout is the bridge's published package forgemsg, taken by its series (ADR 0634 §3, design 32).
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"time"
|
||||
|
||||
mdc "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient"
|
||||
seat "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdelivery"
|
||||
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-forge-bridge/forgemsg"
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
)
|
||||
@@ -32,6 +33,8 @@ type SourceStore interface {
|
||||
// Move reads source.main and writes what update makes of it, unless update says nothing is to be written; a write
|
||||
// another writer won is read again and tried again.
|
||||
Move(update func(prev *Source) (Source, bool)) error
|
||||
// Read is source.main as held and its revision on the bus; nil and 0 before it is first written.
|
||||
Read() (*Source, uint64, error)
|
||||
}
|
||||
|
||||
// kvMerges is merges over the runtime.
|
||||
@@ -107,6 +110,40 @@ func (kvSource) Move(update func(prev *Source) (Source, bool)) error {
|
||||
return fmt.Errorf("source.main was written by another writer first %d times: %w", changeAttempts, stdio.ErrChanged)
|
||||
}
|
||||
|
||||
func (kvSource) Read() (*Source, uint64, error) {
|
||||
e, err := stdio.State("source").Get(mdc.SourceMainKey)
|
||||
if err != nil || e == nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
held, err := mdc.DecodeSource(e.Value)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("source.main is not readable: %w", err)
|
||||
}
|
||||
return &held, e.Revision, nil
|
||||
}
|
||||
|
||||
// SourceRevision is the seat's verb source: every repository source.main names, its trunk branch and head, the group that
|
||||
// moved it last and its revision, or why it cannot be read. source.main names each repository's main only (MergeBranch).
|
||||
// Outside the lock.
|
||||
func (h *Holder) SourceRevision() seat.SourceAnswer {
|
||||
if h.Source == nil {
|
||||
return seat.SourceAnswer{Error: "no source bucket"}
|
||||
}
|
||||
held, revision, err := h.Source.Read()
|
||||
if err != nil {
|
||||
return seat.SourceAnswer{Error: err.Error()}
|
||||
}
|
||||
if held == nil {
|
||||
return seat.SourceAnswer{}
|
||||
}
|
||||
out := seat.SourceAnswer{Group: held.Group, Revision: int64(revision)}
|
||||
for repository, head := range held.Heads {
|
||||
out.Repositories = append(out.Repositories, seat.SourceHead{Repository: repository, Trunk: MergeBranch, Head: head})
|
||||
}
|
||||
sort.Slice(out.Repositories, func(i, j int) bool { return out.Repositories[i].Repository < out.Repositories[j].Repository })
|
||||
return out
|
||||
}
|
||||
|
||||
// SourceMain is source.main as the bucket source holds it, read and never written: its group and each repository's trunk
|
||||
// head, or why it cannot be read. Outside the lock.
|
||||
func (h *Holder) SourceMain() map[string]any {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"deliveries",
|
||||
"show",
|
||||
"groups",
|
||||
"source",
|
||||
"checks",
|
||||
"table",
|
||||
"stalled",
|
||||
|
||||
Reference in New Issue
Block a user