Forge bridge: the gate's merges and owed acts are the seat's types and the delivery client's, the version and the repository check the keys' and the trunk's; the holders and buckets it names are its own constants checked against its manifest (D-11; ADR 0634 §1, §2; issue 1966)
Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
@@ -17,8 +17,10 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"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/sdk/trunk"
|
||||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
||||||
|
|
||||||
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-forge-bridge/forgemsg"
|
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-forge-bridge/forgemsg"
|
||||||
|
|
||||||
@@ -26,6 +28,9 @@ import (
|
|||||||
"mesh-delivery-forge-bridge/internal/groups"
|
"mesh-delivery-forge-bridge/internal/groups"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// gateMergesState is the gate's state merges, which the tools read (reads: mesh-delivery-gate.merges in the manifest).
|
||||||
|
const gateMergesState = "mesh-delivery-gate.merges"
|
||||||
|
|
||||||
// Tool names.
|
// Tool names.
|
||||||
const (
|
const (
|
||||||
GroupsTool = "bridge_groups"
|
GroupsTool = "bridge_groups"
|
||||||
@@ -125,18 +130,18 @@ func MergedOf(raws map[string][]byte, repository, merge string, trees bridge.Tre
|
|||||||
return nil, fmt.Errorf("%q is not owner/name", repository)
|
return nil, fmt.Errorf("%q is not owner/name", repository)
|
||||||
}
|
}
|
||||||
for _, key := range slices.Sorted(maps.Keys(raws)) {
|
for _, key := range slices.Sorted(maps.Keys(raws)) {
|
||||||
m, err := delivery.DecodeMerge(raws[key])
|
m, err := meshdeliveryclient.DecodeMerge(raws[key])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
at := slices.IndexFunc(m.Pulls, func(p delivery.MergePull) bool {
|
at := slices.IndexFunc(m.Pulls, func(p seat.MergesByGroupValuePullsItem) bool {
|
||||||
return delivery.SameRepository(p.Repository, repository) && p.Commit == merge
|
return trunk.SameRepository(p.Repository, repository) && p.Commit == merge
|
||||||
})
|
})
|
||||||
if at < 0 {
|
if at < 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
p := m.Pulls[at]
|
p := m.Pulls[at]
|
||||||
msg, err := bridge.MergedMessage(owner, repo, p.Number, m.Group, p.Head.Head, merge, trees, files)
|
msg, err := bridge.MergedMessage(owner, repo, int(p.Number), m.Group, p.Head, merge, trees, files)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("the merged message of %s at %s cannot be built: %w", repository, merge, err)
|
return nil, fmt.Errorf("the merged message of %s at %s cannot be built: %w", repository, merge, err)
|
||||||
}
|
}
|
||||||
@@ -144,7 +149,7 @@ func MergedOf(raws map[string][]byte, repository, merge string, trees bridge.Tre
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if _, approved := m.Approved[delivery.ApprovedKey(f.Repository, f.Number)]; approved && len(m.Approved) >= 2 {
|
if _, approved := m.Approved[meshdeliveryclient.ApprovedKey(f.Repository, f.Number)]; approved && len(m.Approved) >= 2 {
|
||||||
f.Group, f.Members = m.Group, groups.MembersOf(m)
|
f.Group, f.Members = m.Group, groups.MembersOf(m)
|
||||||
}
|
}
|
||||||
out, err := forgemsg.EncodeForge(f)
|
out, err := forgemsg.EncodeForge(f)
|
||||||
@@ -253,7 +258,7 @@ func askCaller(_ context.Context, key string, body any) (json.RawMessage, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
merges, performed, asks := stdio.State(delivery.GateModule+".merges"), stdio.State(groups.PerformedState), stdio.State(groups.EndAsksState)
|
merges, performed, asks := stdio.State(gateMergesState), stdio.State(groups.PerformedState), stdio.State(groups.EndAsksState)
|
||||||
tools := Tools(askEnd(asks, 25*time.Second), time.Now,
|
tools := Tools(askEnd(asks, 25*time.Second), time.Now,
|
||||||
func() ([]groups.Row, []string, error) { return read(merges, performed, asks) },
|
func() ([]groups.Row, []string, error) { return read(merges, performed, asks) },
|
||||||
func(repository, merge string) (json.RawMessage, error) {
|
func(repository, merge string) (json.RawMessage, error) {
|
||||||
|
|||||||
+17
-16
@@ -8,8 +8,9 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"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"
|
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-forge-bridge/forgemsg"
|
||||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
||||||
|
|
||||||
"mesh-delivery-forge-bridge/internal/bridge"
|
"mesh-delivery-forge-bridge/internal/bridge"
|
||||||
|
|
||||||
@@ -22,19 +23,19 @@ import (
|
|||||||
func TestTheBridgeToolsReadTheGroupsAndAskTheEndAgain(t *testing.T) {
|
func TestTheBridgeToolsReadTheGroupsAndAskTheEndAgain(t *testing.T) {
|
||||||
h1, h2 := strings.Repeat("1", 40), strings.Repeat("2", 40)
|
h1, h2 := strings.Repeat("1", 40), strings.Repeat("2", 40)
|
||||||
c1, c2 := strings.Repeat("c", 40), strings.Repeat("d", 40)
|
c1, c2 := strings.Repeat("c", 40), strings.Repeat("d", 40)
|
||||||
enc := func(group string, state2 delivery.MergeState, c string) []byte {
|
enc := func(group string, state2 meshdeliveryclient.MergeState, c string) []byte {
|
||||||
v, err := delivery.EncodeMerge(delivery.Merge{Group: group, Ask: "ask", Approved: map[string]string{"novox/a@1": h1, "novox/b@2": h2},
|
v, err := meshdeliveryclient.EncodeMerge(seat.MergesByGroupValue{Group: group, Ask: "ask", Approved: map[string]string{"novox/a@1": h1, "novox/b@2": h2},
|
||||||
Pulls: []delivery.MergePull{{Head: delivery.Head{Repository: "novox/a", Number: 1, Head: h1}, State: delivery.MergeMerged, Commit: c1},
|
Pulls: []seat.MergesByGroupValuePullsItem{{Repository: "novox/a", Number: 1, Head: h1, State: string(meshdeliveryclient.MergeMerged), Commit: c1},
|
||||||
{Head: delivery.Head{Repository: "novox/b", Number: 2, Head: h2}, State: state2, Commit: c}}})
|
{Repository: "novox/b", Number: 2, Head: h2, State: string(state2), Commit: c}}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
lone, _ := delivery.EncodeMerge(delivery.Merge{Group: "solo", Ask: "ask", Approved: map[string]string{"novox/a@5": h1},
|
lone, _ := meshdeliveryclient.EncodeMerge(seat.MergesByGroupValue{Group: "solo", Ask: "ask", Approved: map[string]string{"novox/a@5": h1},
|
||||||
Pulls: []delivery.MergePull{{Head: delivery.Head{Repository: "novox/a", Number: 5, Head: h1}, State: delivery.MergeMerged, Commit: c1}}})
|
Pulls: []seat.MergesByGroupValuePullsItem{{Repository: "novox/a", Number: 5, Head: h1, State: string(meshdeliveryclient.MergeMerged), Commit: c1}}})
|
||||||
merges := map[string][]byte{"ended": enc("ended", delivery.MergeMerged, c2), "ready": enc("ready", delivery.MergeMerged, c2),
|
merges := map[string][]byte{"ended": enc("ended", meshdeliveryclient.MergeMerged, c2), "ready": enc("ready", meshdeliveryclient.MergeMerged, c2),
|
||||||
"gathered": enc("gathered", delivery.MergeMerging, ""), "solo": lone, "bad": []byte("{")}
|
"gathered": enc("gathered", meshdeliveryclient.MergeMerging, ""), "solo": lone, "bad": []byte("{")}
|
||||||
performed := map[string]bool{}
|
performed := map[string]bool{}
|
||||||
for _, c := range []string{c1, c2} {
|
for _, c := range []string{c1, c2} {
|
||||||
performed[groups.PublishedKey("novox/a", 1, c)], performed[groups.PublishedKey("novox/b", 2, c)] = true, true
|
performed[groups.PublishedKey("novox/a", 1, c)], performed[groups.PublishedKey("novox/b", 2, c)] = true, true
|
||||||
@@ -98,7 +99,7 @@ func TestTheBridgeToolsReadTheGroupsAndAskTheEndAgain(t *testing.T) {
|
|||||||
if a := all["ready"].LastEndAsk; a == nil || a.By != "lead@shanks" || a.Why != "lost" || !a.Written || a.Answer == "" || all["ended"].LastEndAsk != nil {
|
if a := all["ready"].LastEndAsk; a == nil || a.By != "lead@shanks" || a.Why != "lost" || !a.Written || a.Answer == "" || all["ended"].LastEndAsk != nil {
|
||||||
t.Errorf("ready's last end ask = %+v, ended's %+v: want ready's ask and its answer, none for ended", a, all["ended"].LastEndAsk)
|
t.Errorf("ready's last end ask = %+v, ended's %+v: want ready's ask and its answer, none for ended", a, all["ended"].LastEndAsk)
|
||||||
}
|
}
|
||||||
if r := all["gathered"]; !r.Open || !strings.Contains(r.Words, "gathered") || r.Members[1].State != string(delivery.MergeMerging) {
|
if r := all["gathered"]; !r.Open || !strings.Contains(r.Words, "gathered") || r.Members[1].State != string(meshdeliveryclient.MergeMerging) {
|
||||||
t.Errorf("gathered = %+v", r)
|
t.Errorf("gathered = %+v", r)
|
||||||
}
|
}
|
||||||
if open := rowsOf(map[string]any{"open": true}); len(open) != 2 || open["ended"].Group != "" {
|
if open := rowsOf(map[string]any{"open": true}); len(open) != 2 || open["ended"].Group != "" {
|
||||||
@@ -146,11 +147,11 @@ func TestTheBridgeToolsReadTheGroupsAndAskTheEndAgain(t *testing.T) {
|
|||||||
}
|
}
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
lone, _ := delivery.EncodeMerge(delivery.Merge{Group: "solo", Ask: "a", Approved: map[string]string{"novox/a@5": h1},
|
lone, _ := meshdeliveryclient.EncodeMerge(seat.MergesByGroupValue{Group: "solo", Ask: "a", Approved: map[string]string{"novox/a@5": h1},
|
||||||
Pulls: []delivery.MergePull{{Head: delivery.Head{Repository: "novox/a", Number: 5, Head: h1}, State: delivery.MergeMerged, Commit: c1}}})
|
Pulls: []seat.MergesByGroupValuePullsItem{{Repository: "novox/a", Number: 5, Head: h1, State: string(meshdeliveryclient.MergeMerged), Commit: c1}}})
|
||||||
group, _ := delivery.EncodeMerge(delivery.Merge{Group: "feat/g", Ask: "a", Approved: map[string]string{"novox/a@6": h1, "novox/b@7": h2},
|
group, _ := meshdeliveryclient.EncodeMerge(seat.MergesByGroupValue{Group: "feat/g", Ask: "a", Approved: map[string]string{"novox/a@6": h1, "novox/b@7": h2},
|
||||||
Pulls: []delivery.MergePull{{Head: delivery.Head{Repository: "novox/a", Number: 6, Head: h1}, State: delivery.MergeMerged, Commit: c1},
|
Pulls: []seat.MergesByGroupValuePullsItem{{Repository: "novox/a", Number: 6, Head: h1, State: string(meshdeliveryclient.MergeMerged), Commit: c1},
|
||||||
{Head: delivery.Head{Repository: "novox/b", Number: 7, Head: h2}, State: delivery.MergeMerging}}})
|
{Repository: "novox/b", Number: 7, Head: h2, State: string(meshdeliveryclient.MergeMerging)}}})
|
||||||
for _, c := range []struct {
|
for _, c := range []struct {
|
||||||
name string
|
name string
|
||||||
raws map[string][]byte
|
raws map[string][]byte
|
||||||
@@ -162,7 +163,7 @@ func TestTheBridgeToolsReadTheGroupsAndAskTheEndAgain(t *testing.T) {
|
|||||||
{name: "a lone pull request", raws: map[string][]byte{"solo": lone}, repository: "novox/a", merge: c1, want: published("a", 5, h1, "solo", c1)},
|
{name: "a lone pull request", raws: map[string][]byte{"solo": lone}, repository: "novox/a", merge: c1, want: published("a", 5, h1, "solo", c1)},
|
||||||
{name: "a member of a group", raws: map[string][]byte{"feat~g": group}, repository: "novox/a", merge: c1, want: func() forgemsg.Forge {
|
{name: "a member of a group", raws: map[string][]byte{"feat~g": group}, repository: "novox/a", merge: c1, want: func() forgemsg.Forge {
|
||||||
f := published("a", 6, h1, "feat/g", c1)
|
f := published("a", 6, h1, "feat/g", c1)
|
||||||
f.Group, f.Members = "feat/g", []delivery.Head{{Repository: "novox/a", Number: 6, Head: h1}, {Repository: "novox/b", Number: 7, Head: h2}}
|
f.Group, f.Members = "feat/g", []meshdeliveryclient.Head{{Repository: "novox/a", Number: 6, Head: h1}, {Repository: "novox/b", Number: 7, Head: h2}}
|
||||||
return f
|
return f
|
||||||
}()},
|
}()},
|
||||||
{name: "a merge the gate does not hold", raws: map[string][]byte{"solo": lone, "bad": []byte("{")}, repository: "novox/a", merge: c2, fail: "hold no merge"},
|
{name: "a merge the gate does not hold", raws: map[string][]byte{"solo": lone, "bad": []byte("{")}, repository: "novox/a", merge: c2, fail: "hold no merge"},
|
||||||
|
|||||||
@@ -9,15 +9,13 @@ import (
|
|||||||
|
|
||||||
"github.com/nats-io/nats.go/jetstream"
|
"github.com/nats-io/nats.go/jetstream"
|
||||||
|
|
||||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
||||||
|
|
||||||
"mesh-delivery-forge-bridge/internal/bridge"
|
"mesh-delivery-forge-bridge/internal/bridge"
|
||||||
)
|
)
|
||||||
|
|
||||||
// performedBucket is the module's state bucket performed, `<module>_<name>` as heads is: for each key of the gate's
|
// performedBucket is the module's state bucket performed, `<module>_<name>` as heads is: for each key of the gate's
|
||||||
// buckets the bridge has acted on, the revision it acted at (manifest state entry performed, novox/hq ADR 0437 §8).
|
// buckets the bridge has acted on, the revision it acted at (manifest state entry performed, novox/hq ADR 0437 §8).
|
||||||
// Its keys are `owed.<owed key>` and `merges.<merges key>`; its value is the revision in decimal.
|
// Its keys are `owed.<owed key>` and `merges.<merges key>`; its value is the revision in decimal.
|
||||||
const performedBucket = delivery.ForgeBridgeModule + "_performed"
|
const performedBucket = forgeBridgeModule + "_performed"
|
||||||
|
|
||||||
// kvRevisions is the bucket performed as the performer's revisions.
|
// kvRevisions is the bucket performed as the performer's revisions.
|
||||||
type kvRevisions struct{ kv jetstream.KeyValue }
|
type kvRevisions struct{ kv jetstream.KeyValue }
|
||||||
|
|||||||
@@ -9,8 +9,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"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"
|
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-forge-bridge/forgemsg"
|
||||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -52,13 +53,13 @@ func newPerformer(f *forgeFake) (*performer, *fakePub, memRevisions, *bytes.Buff
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The values are the SDK's own encoding (mesh-sdk go/delivery EncodeOwed, EncodeMerge), the keys its OwedKey.
|
// The values are the SDK's own encoding (mesh-sdk go/delivery EncodeOwed, EncodeMerge), the keys its OwedKey.
|
||||||
func owedKV(t *testing.T, repository string, number int, act string, o delivery.Owed) (string, []byte) {
|
func owedKV(t *testing.T, repository string, number int, act string, o seat.OwedByRepositoryNumberHeadActValue) (string, []byte) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
key, err := delivery.OwedKey(repository, number, headA, act)
|
key, err := meshdeliveryclient.OwedKey(repository, number, headA, act)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
v, err := delivery.EncodeOwed(o)
|
v, err := meshdeliveryclient.EncodeOwed(o)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -66,10 +67,11 @@ func owedKV(t *testing.T, repository string, number int, act string, o delivery.
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAnOwedStatusIsSetOnceForARevision(t *testing.T) { // 1341/AC1
|
func TestAnOwedStatusIsSetOnceForARevision(t *testing.T) { // 1341/AC1
|
||||||
|
t.Run("the names are the manifest's", theNamesAreTheManifests)
|
||||||
f := &forgeFake{}
|
f := &forgeFake{}
|
||||||
p, _, _, _ := newPerformer(f)
|
p, _, _, _ := newPerformer(f)
|
||||||
key, v := owedKV(t, "novox/mesh-delivery", 12, "status=mesh/merge-gate",
|
key, v := owedKV(t, "novox/mesh-delivery", 12, "status=mesh/merge-gate",
|
||||||
delivery.Owed{Act: delivery.OwedStatus, Context: "mesh/merge-gate", State: "success", Text: "all green", Target: "https://forge.example/p/12"})
|
seat.OwedByRepositoryNumberHeadActValue{Act: meshdeliveryclient.OwedStatus, Context: "mesh/merge-gate", State: "success", Text: "all green", Target: "https://forge.example/p/12"})
|
||||||
p.owed(context.Background(), key, 7, v)
|
p.owed(context.Background(), key, 7, v)
|
||||||
if len(f.calls) != 1 || f.calls[0].key != "gitea.gitea_commit_status" ||
|
if len(f.calls) != 1 || f.calls[0].key != "gitea.gitea_commit_status" ||
|
||||||
f.calls[0].body != `{"context":"mesh/merge-gate","description":"all green","owner":"novox","repo":"mesh-delivery","sha":"`+headA+`","state":"success","target_url":"https://forge.example/p/12"}` {
|
f.calls[0].body != `{"context":"mesh/merge-gate","description":"all green","owner":"novox","repo":"mesh-delivery","sha":"`+headA+`","state":"success","target_url":"https://forge.example/p/12"}` {
|
||||||
@@ -90,11 +92,11 @@ func TestViewNoteAndCommentEachCallTheirToolOnce(t *testing.T) { // 1341/AC2
|
|||||||
p, _, _, _ := newPerformer(f)
|
p, _, _, _ := newPerformer(f)
|
||||||
for _, c := range []struct {
|
for _, c := range []struct {
|
||||||
act string
|
act string
|
||||||
o delivery.Owed
|
o seat.OwedByRepositoryNumberHeadActValue
|
||||||
}{
|
}{
|
||||||
{"view", delivery.Owed{Act: delivery.OwedView, Text: "the view"}},
|
{"view", seat.OwedByRepositoryNumberHeadActValue{Act: meshdeliveryclient.OwedView, Text: "the view"}},
|
||||||
{"note", delivery.Owed{Act: delivery.OwedNote, Text: "a line", Ref: "mesh-plan"}},
|
{"note", seat.OwedByRepositoryNumberHeadActValue{Act: meshdeliveryclient.OwedNote, Text: "a line", Ref: "mesh-plan"}},
|
||||||
{"comment.1", delivery.Owed{Act: delivery.OwedComment, Text: "hello"}},
|
{"comment.1", seat.OwedByRepositoryNumberHeadActValue{Act: meshdeliveryclient.OwedComment, Text: "hello"}},
|
||||||
} {
|
} {
|
||||||
key, v := owedKV(t, "novox/mesh-delivery", 12, c.act, c.o)
|
key, v := owedKV(t, "novox/mesh-delivery", 12, c.act, c.o)
|
||||||
p.owed(context.Background(), key, 1, v)
|
p.owed(context.Background(), key, 1, v)
|
||||||
@@ -112,7 +114,7 @@ func TestViewNoteAndCommentEachCallTheirToolOnce(t *testing.T) { // 1341/AC2
|
|||||||
func TestARefusedActIsWrittenOnForgeOnceAndNotRetried(t *testing.T) { // 1341/AC3
|
func TestARefusedActIsWrittenOnForgeOnceAndNotRetried(t *testing.T) { // 1341/AC3
|
||||||
f := &forgeFake{refuse: map[string]string{"gitea.gitea_add_comment": "the pull request is locked"}}
|
f := &forgeFake{refuse: map[string]string{"gitea.gitea_add_comment": "the pull request is locked"}}
|
||||||
p, pub, _, _ := newPerformer(f)
|
p, pub, _, _ := newPerformer(f)
|
||||||
key, v := owedKV(t, "novox/mesh-delivery", 12, "comment.1", delivery.Owed{Act: delivery.OwedComment, Text: "hello"})
|
key, v := owedKV(t, "novox/mesh-delivery", 12, "comment.1", seat.OwedByRepositoryNumberHeadActValue{Act: meshdeliveryclient.OwedComment, Text: "hello"})
|
||||||
p.owed(context.Background(), key, 3, v)
|
p.owed(context.Background(), key, 3, v)
|
||||||
p.owed(context.Background(), key, 3, v)
|
p.owed(context.Background(), key, 3, v)
|
||||||
if len(f.calls) != 1 {
|
if len(f.calls) != 1 {
|
||||||
@@ -147,28 +149,28 @@ func TestAnOwedValueThatCannotBeReadIsLoggedAndNotRetried(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func mergeKV(t *testing.T, m delivery.Merge) []byte {
|
func mergeKV(t *testing.T, m seat.MergesByGroupValue) []byte {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
v, err := delivery.EncodeMerge(m)
|
v, err := meshdeliveryclient.EncodeMerge(m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func twoMembers(a, b delivery.MergeState) delivery.Merge {
|
func twoMembers(a, b meshdeliveryclient.MergeState) seat.MergesByGroupValue {
|
||||||
return delivery.Merge{Group: "g", Ask: "merge.g.0123456789ab",
|
return seat.MergesByGroupValue{Group: "g", Ask: "merge.g.0123456789ab",
|
||||||
Approved: map[string]string{delivery.ApprovedKey("novox/mesh-sdk", 4): headB, delivery.ApprovedKey("novox/mesh-delivery", 12): headA},
|
Approved: map[string]string{meshdeliveryclient.ApprovedKey("novox/mesh-sdk", 4): headB, meshdeliveryclient.ApprovedKey("novox/mesh-delivery", 12): headA},
|
||||||
Pulls: []delivery.MergePull{
|
Pulls: []seat.MergesByGroupValuePullsItem{
|
||||||
{Head: delivery.Head{Repository: "novox/mesh-delivery", Number: 12, Head: headA}, State: a, Commit: map[bool]string{true: "c1"}[a == delivery.MergeMerged]},
|
{Repository: "novox/mesh-delivery", Number: 12, Head: headA, State: string(a), Commit: map[bool]string{true: "c1"}[a == meshdeliveryclient.MergeMerged]},
|
||||||
{Head: delivery.Head{Repository: "novox/mesh-sdk", Number: 4, Head: headB}, State: b, Commit: map[bool]string{true: "c2"}[b == delivery.MergeMerged]},
|
{Repository: "novox/mesh-sdk", Number: 4, Head: headB, State: string(b), Commit: map[bool]string{true: "c2"}[b == meshdeliveryclient.MergeMerged]},
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAMergeRevisionMergesOnlyTheMemberHanded(t *testing.T) { // 1342/AC1
|
func TestAMergeRevisionMergesOnlyTheMemberHanded(t *testing.T) { // 1342/AC1
|
||||||
f := &forgeFake{}
|
f := &forgeFake{}
|
||||||
p, _, _, _ := newPerformer(f)
|
p, _, _, _ := newPerformer(f)
|
||||||
m := twoMembers(delivery.MergeMerging, delivery.MergeMerged)
|
m := twoMembers(meshdeliveryclient.MergeMerging, meshdeliveryclient.MergeMerged)
|
||||||
p.merges(context.Background(), "g", 5, mergeKV(t, m))
|
p.merges(context.Background(), "g", 5, mergeKV(t, m))
|
||||||
want := `{"approved_heads":["novox/mesh-delivery@` + headA + `","novox/mesh-sdk@` + headB + `"],"head":"` + headA + `","number":12,"owner":"novox","repo":"mesh-delivery"}`
|
want := `{"approved_heads":["novox/mesh-delivery@` + headA + `","novox/mesh-sdk@` + headB + `"],"head":"` + headA + `","number":12,"owner":"novox","repo":"mesh-delivery"}`
|
||||||
if len(f.calls) != 1 || f.calls[0].key != "gitea.gitea_merge_pull_request" || f.calls[0].body != want {
|
if len(f.calls) != 1 || f.calls[0].key != "gitea.gitea_merge_pull_request" || f.calls[0].body != want {
|
||||||
@@ -183,7 +185,7 @@ func TestAMergeRevisionMergesOnlyTheMemberHanded(t *testing.T) { // 1342/AC1
|
|||||||
func TestARefusedMergeIsWrittenOnForgeOnceAndNotRetried(t *testing.T) { // 1342/AC2
|
func TestARefusedMergeIsWrittenOnForgeOnceAndNotRetried(t *testing.T) { // 1342/AC2
|
||||||
f := &forgeFake{refuse: map[string]string{"gitea.gitea_merge_pull_request": "novox/mesh-delivery#12 is not merged: its head moved"}}
|
f := &forgeFake{refuse: map[string]string{"gitea.gitea_merge_pull_request": "novox/mesh-delivery#12 is not merged: its head moved"}}
|
||||||
p, pub, _, _ := newPerformer(f)
|
p, pub, _, _ := newPerformer(f)
|
||||||
v := mergeKV(t, twoMembers(delivery.MergeMerging, delivery.MergeMerged))
|
v := mergeKV(t, twoMembers(meshdeliveryclient.MergeMerging, meshdeliveryclient.MergeMerged))
|
||||||
p.merges(context.Background(), "g", 5, v)
|
p.merges(context.Background(), "g", 5, v)
|
||||||
p.merges(context.Background(), "g", 5, v)
|
p.merges(context.Background(), "g", 5, v)
|
||||||
if len(f.calls) != 1 || len(pub.data) != 1 {
|
if len(f.calls) != 1 || len(pub.data) != 1 {
|
||||||
@@ -204,7 +206,7 @@ func TestARefusedMergeIsWrittenOnForgeOnceAndNotRetried(t *testing.T) { // 1342/
|
|||||||
func TestAMergeWithTwoHandedStopsAtTheFirstRefusal(t *testing.T) { // ADR 0441 §3
|
func TestAMergeWithTwoHandedStopsAtTheFirstRefusal(t *testing.T) { // ADR 0441 §3
|
||||||
f := &forgeFake{refuse: map[string]string{"gitea.gitea_merge_pull_request": "no"}}
|
f := &forgeFake{refuse: map[string]string{"gitea.gitea_merge_pull_request": "no"}}
|
||||||
p, pub, _, _ := newPerformer(f)
|
p, pub, _, _ := newPerformer(f)
|
||||||
p.merges(context.Background(), "g", 1, mergeKV(t, twoMembers(delivery.MergeMerging, delivery.MergeMerging)))
|
p.merges(context.Background(), "g", 1, mergeKV(t, twoMembers(meshdeliveryclient.MergeMerging, meshdeliveryclient.MergeMerging)))
|
||||||
if len(f.calls) != 1 || len(pub.data) != 1 {
|
if len(f.calls) != 1 || len(pub.data) != 1 {
|
||||||
t.Errorf("calls %d messages %d, want the first refusal to stop the pass", len(f.calls), len(pub.data))
|
t.Errorf("calls %d messages %d, want the first refusal to stop the pass", len(f.calls), len(pub.data))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,10 @@ import (
|
|||||||
|
|
||||||
"github.com/nats-io/nats.go/jetstream"
|
"github.com/nats-io/nats.go/jetstream"
|
||||||
|
|
||||||
|
"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"
|
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-forge-bridge/forgemsg"
|
||||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
"git.novox.be/novox/mesh-delivery/sdk/trunk"
|
||||||
|
|
||||||
groupkeys "mesh-delivery-forge-bridge/internal/groups"
|
groupkeys "mesh-delivery-forge-bridge/internal/groups"
|
||||||
)
|
)
|
||||||
@@ -57,29 +59,29 @@ func kvMerges(kv jetstream.KeyValue) mergesRead {
|
|||||||
|
|
||||||
// groupOf is the gate's merge of the group a merged message's branch names, when the message's pull request is one of
|
// groupOf is the gate's merge of the group a merged message's branch names, when the message's pull request is one of
|
||||||
// its approved members and the group has more than one: a lone pull request is no group.
|
// its approved members and the group has more than one: a lone pull request is no group.
|
||||||
func (g *grouper) groupOf(ctx context.Context, f forgemsg.Forge) (delivery.Merge, uint64, bool) {
|
func (g *grouper) groupOf(ctx context.Context, f forgemsg.Forge) (seat.MergesByGroupValue, uint64, bool) {
|
||||||
if g == nil || g.merges == nil || f.Branch == "" {
|
if g == nil || g.merges == nil || f.Branch == "" {
|
||||||
return delivery.Merge{}, 0, false
|
return seat.MergesByGroupValue{}, 0, false
|
||||||
}
|
}
|
||||||
key, err := delivery.MergesKey(f.Branch)
|
key, err := meshdeliveryclient.MergesKey(f.Branch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return delivery.Merge{}, 0, false
|
return seat.MergesByGroupValue{}, 0, false
|
||||||
}
|
}
|
||||||
value, rev, found, err := g.merges(ctx, key)
|
value, rev, found, err := g.merges(ctx, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
g.logger.Printf("%s#%d: the gate's merges.%s cannot be read, so its merged message names no group: %v", f.Repository, f.Number, key, err)
|
g.logger.Printf("%s#%d: the gate's merges.%s cannot be read, so its merged message names no group: %v", f.Repository, f.Number, key, err)
|
||||||
return delivery.Merge{}, 0, false
|
return seat.MergesByGroupValue{}, 0, false
|
||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
return delivery.Merge{}, 0, false
|
return seat.MergesByGroupValue{}, 0, false
|
||||||
}
|
}
|
||||||
m, err := delivery.DecodeMerge(value)
|
m, err := meshdeliveryclient.DecodeMerge(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
g.logger.Printf("%s#%d: the gate's merges.%s is not a merge, so its merged message names no group: %v", f.Repository, f.Number, key, err)
|
g.logger.Printf("%s#%d: the gate's merges.%s is not a merge, so its merged message names no group: %v", f.Repository, f.Number, key, err)
|
||||||
return delivery.Merge{}, 0, false
|
return seat.MergesByGroupValue{}, 0, false
|
||||||
}
|
}
|
||||||
if _, ok := m.Approved[delivery.ApprovedKey(f.Repository, f.Number)]; !ok || len(m.Approved) < 2 {
|
if _, ok := m.Approved[meshdeliveryclient.ApprovedKey(f.Repository, f.Number)]; !ok || len(m.Approved) < 2 {
|
||||||
return delivery.Merge{}, 0, false
|
return seat.MergesByGroupValue{}, 0, false
|
||||||
}
|
}
|
||||||
return m, rev, true
|
return m, rev, true
|
||||||
}
|
}
|
||||||
@@ -110,7 +112,7 @@ func (g *grouper) published(ctx context.Context, f forgemsg.Forge) {
|
|||||||
g.logger.Printf("%s#%d: that its merged message is written is not kept, so the group's end waits for the next act: %v", f.Repository, f.Number, err)
|
g.logger.Printf("%s#%d: that its merged message is written is not kept, so the group's end waits for the next act: %v", f.Repository, f.Number, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
key, err := delivery.MergesKey(f.Branch)
|
key, err := meshdeliveryclient.MergesKey(f.Branch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -130,7 +132,7 @@ func (g *grouper) ended(ctx context.Context, key string, rev uint64, value []byt
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *grouper) end(ctx context.Context, key string, rev uint64, value []byte, again string) bool {
|
func (g *grouper) end(ctx context.Context, key string, rev uint64, value []byte, again string) bool {
|
||||||
m, err := delivery.DecodeMerge(value)
|
m, err := meshdeliveryclient.DecodeMerge(value)
|
||||||
if err != nil || len(m.Approved) < 2 {
|
if err != nil || len(m.Approved) < 2 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -141,8 +143,8 @@ func (g *grouper) end(ctx context.Context, key string, rev uint64, value []byte,
|
|||||||
}
|
}
|
||||||
end := forgemsg.ForgeGroupEnd{Group: m.Group}
|
end := forgemsg.ForgeGroupEnd{Group: m.Group}
|
||||||
for _, member := range groupkeys.MembersOf(m) {
|
for _, member := range groupkeys.MembersOf(m) {
|
||||||
at := slices.IndexFunc(m.Pulls, func(p delivery.MergePull) bool {
|
at := slices.IndexFunc(m.Pulls, func(p seat.MergesByGroupValuePullsItem) bool {
|
||||||
return delivery.SameRepository(p.Repository, member.Repository) && p.Number == member.Number
|
return trunk.SameRepository(p.Repository, member.Repository) && int(p.Number) == member.Number
|
||||||
})
|
})
|
||||||
if at < 0 || m.Pulls[at].Commit == "" {
|
if at < 0 || m.Pulls[at].Commit == "" {
|
||||||
return false // not every member is merged (a merge commit is written only for a merged pull): the group stays gathered
|
return false // not every member is merged (a merge commit is written only for a merged pull): the group stays gathered
|
||||||
@@ -154,8 +156,8 @@ func (g *grouper) end(ctx context.Context, key string, rev uint64, value []byte,
|
|||||||
}
|
}
|
||||||
// The members in the order the gate merged them, so the last commit names the end.
|
// The members in the order the gate merged them, so the last commit names the end.
|
||||||
slices.SortStableFunc(end.Members, func(a, b forgemsg.GroupMerge) int {
|
slices.SortStableFunc(end.Members, func(a, b forgemsg.GroupMerge) int {
|
||||||
return slices.IndexFunc(m.Pulls, func(p delivery.MergePull) bool { return p.Commit == a.Merge }) -
|
return slices.IndexFunc(m.Pulls, func(p seat.MergesByGroupValuePullsItem) bool { return p.Commit == a.Merge }) -
|
||||||
slices.IndexFunc(m.Pulls, func(p delivery.MergePull) bool { return p.Commit == b.Merge })
|
slices.IndexFunc(m.Pulls, func(p seat.MergesByGroupValuePullsItem) bool { return p.Commit == b.Merge })
|
||||||
})
|
})
|
||||||
data, err := forgemsg.EncodeForgeGroupEnd(end)
|
data, err := forgemsg.EncodeForgeGroupEnd(end)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"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"
|
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-forge-bridge/forgemsg"
|
||||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
||||||
|
|
||||||
groupkeys "mesh-delivery-forge-bridge/internal/groups"
|
groupkeys "mesh-delivery-forge-bridge/internal/groups"
|
||||||
)
|
)
|
||||||
@@ -29,19 +30,19 @@ func TestAGroupsMembersNameItAndItsEndIsSaidOnce(t *testing.T) { // 1585/T16, T1
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
h1, h2 := strings.Repeat("1", 40), strings.Repeat("2", 40)
|
h1, h2 := strings.Repeat("1", 40), strings.Repeat("2", 40)
|
||||||
c1, c2 := strings.Repeat("c", 40), strings.Repeat("d", 40)
|
c1, c2 := strings.Repeat("c", 40), strings.Repeat("d", 40)
|
||||||
merge := func(states ...delivery.MergePull) []byte {
|
merge := func(states ...seat.MergesByGroupValuePullsItem) []byte {
|
||||||
v, err := delivery.EncodeMerge(delivery.Merge{Group: "feat/take-in", Ask: "ask", Pulls: states,
|
v, err := meshdeliveryclient.EncodeMerge(seat.MergesByGroupValue{Group: "feat/take-in", Ask: "ask", Pulls: states,
|
||||||
Approved: map[string]string{"novox/mesh-sdk@37": h1, "novox/mesh-delivery@9": h2}})
|
Approved: map[string]string{"novox/mesh-sdk@37": h1, "novox/mesh-delivery@9": h2}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
sdk := func(state delivery.MergeState, commit, words string) delivery.MergePull {
|
sdk := func(state meshdeliveryclient.MergeState, commit, words string) seat.MergesByGroupValuePullsItem {
|
||||||
return delivery.MergePull{Head: delivery.Head{Repository: "novox/mesh-sdk", Number: 37, Head: h1}, State: state, Commit: commit, Words: words}
|
return seat.MergesByGroupValuePullsItem{Repository: "novox/mesh-sdk", Number: 37, Head: h1, State: string(state), Commit: commit, Words: words}
|
||||||
}
|
}
|
||||||
dlv := func(state delivery.MergeState, commit string) delivery.MergePull {
|
dlv := func(state meshdeliveryclient.MergeState, commit string) seat.MergesByGroupValuePullsItem {
|
||||||
return delivery.MergePull{Head: delivery.Head{Repository: "novox/mesh-delivery", Number: 9, Head: h2}, State: state, Commit: commit}
|
return seat.MergesByGroupValuePullsItem{Repository: "novox/mesh-delivery", Number: 9, Head: h2, State: string(state), Commit: commit}
|
||||||
}
|
}
|
||||||
gate := map[string][]byte{}
|
gate := map[string][]byte{}
|
||||||
var logs bytes.Buffer
|
var logs bytes.Buffer
|
||||||
@@ -63,17 +64,17 @@ func TestAGroupsMembersNameItAndItsEndIsSaidOnce(t *testing.T) { // 1585/T16, T1
|
|||||||
t.Errorf("a lone pull request names the group %q", f.Group)
|
t.Errorf("a lone pull request names the group %q", f.Group)
|
||||||
}
|
}
|
||||||
// The first member: its message names the group and both members; the group is not ended.
|
// The first member: its message names the group and both members; the group is not ended.
|
||||||
gate["feat/take-in"] = merge(sdk(delivery.MergeMerged, c1, ""), dlv(delivery.MergeMerging, ""))
|
gate["feat/take-in"] = merge(sdk(meshdeliveryclient.MergeMerged, c1, ""), dlv(meshdeliveryclient.MergeMerging, ""))
|
||||||
data, f, ok := g.member(ctx, mergedOf("novox/mesh-sdk", 37, h1, c1))
|
data, f, ok := g.member(ctx, mergedOf("novox/mesh-sdk", 37, h1, c1))
|
||||||
if back, err := forgemsg.DecodeForge(data); !ok || err != nil || back.Group != "feat/take-in" || len(back.Members) != 2 {
|
if back, err := forgemsg.DecodeForge(data); !ok || err != nil || back.Group != "feat/take-in" || len(back.Members) != 2 {
|
||||||
t.Fatalf("a member's message: %+v %v", back, err)
|
t.Fatalf("a member's message: %+v %v", back, err)
|
||||||
}
|
}
|
||||||
g.published(ctx, f)
|
g.published(ctx, f)
|
||||||
// A refusal of the other member is no end (ADR 0441 §3: the group merges on from it later).
|
// A refusal of the other member is no end (ADR 0441 §3: the group merges on from it later).
|
||||||
gate["feat/take-in"] = merge(sdk(delivery.MergeMerged, c1, ""), delivery.MergePull{Head: delivery.Head{Repository: "novox/mesh-delivery", Number: 9, Head: h2}, State: delivery.MergeRefused, Words: "405"})
|
gate["feat/take-in"] = merge(sdk(meshdeliveryclient.MergeMerged, c1, ""), seat.MergesByGroupValuePullsItem{Repository: "novox/mesh-delivery", Number: 9, Head: h2, State: string(meshdeliveryclient.MergeRefused), Words: "405"})
|
||||||
g.ended(ctx, "feat/take-in", 8, gate["feat/take-in"])
|
g.ended(ctx, "feat/take-in", 8, gate["feat/take-in"])
|
||||||
// Merged on at the gate before its merged message is written: still no end; then its message ends the group.
|
// Merged on at the gate before its merged message is written: still no end; then its message ends the group.
|
||||||
gate["feat/take-in"] = merge(sdk(delivery.MergeMerged, c1, ""), dlv(delivery.MergeMerged, c2))
|
gate["feat/take-in"] = merge(sdk(meshdeliveryclient.MergeMerged, c1, ""), dlv(meshdeliveryclient.MergeMerged, c2))
|
||||||
g.ended(ctx, "feat/take-in", 9, gate["feat/take-in"])
|
g.ended(ctx, "feat/take-in", 9, gate["feat/take-in"])
|
||||||
if len(pub.subjects) != 0 {
|
if len(pub.subjects) != 0 {
|
||||||
t.Fatalf("an end before every member's merged message is written: %v", pub.subjects)
|
t.Fatalf("an end before every member's merged message is written: %v", pub.subjects)
|
||||||
@@ -97,12 +98,12 @@ func TestAGroupsMembersNameItAndItsEndIsSaidOnce(t *testing.T) { // 1585/T16, T1
|
|||||||
}
|
}
|
||||||
// 1585/T19: an ask to say a group's end again is answered by the bridge from the gate's merges, with the ask's time on
|
// 1585/T19: an ask to say a group's end again is answered by the bridge from the gate's merges, with the ask's time on
|
||||||
// the message id; not written while a member is not merged, with the reason; not for a group the gate does not hold.
|
// the message id; not written while a member is not merged, with the reason; not for a group the gate does not hold.
|
||||||
pull := func(repo string, n int, head string, state delivery.MergeState, commit string) delivery.MergePull {
|
pull := func(repo string, n int, head string, state meshdeliveryclient.MergeState, commit string) seat.MergesByGroupValuePullsItem {
|
||||||
return delivery.MergePull{Head: delivery.Head{Repository: repo, Number: n, Head: head}, State: state, Commit: commit}
|
return seat.MergesByGroupValuePullsItem{Repository: repo, Number: int64(n), Head: head, State: string(state), Commit: commit}
|
||||||
}
|
}
|
||||||
askMerge := func(second delivery.MergeState, commit2 string) []byte {
|
askMerge := func(second meshdeliveryclient.MergeState, commit2 string) []byte {
|
||||||
v, err := delivery.EncodeMerge(delivery.Merge{Group: "feat/take-in", Ask: "ask", Approved: map[string]string{"novox/mesh-sdk@37": h1, "novox/mesh-delivery@9": h2},
|
v, err := meshdeliveryclient.EncodeMerge(seat.MergesByGroupValue{Group: "feat/take-in", Ask: "ask", Approved: map[string]string{"novox/mesh-sdk@37": h1, "novox/mesh-delivery@9": h2},
|
||||||
Pulls: []delivery.MergePull{pull("novox/mesh-sdk", 37, h1, delivery.MergeMerged, c1), pull("novox/mesh-delivery", 9, h2, second, commit2)}})
|
Pulls: []seat.MergesByGroupValuePullsItem{pull("novox/mesh-sdk", 37, h1, meshdeliveryclient.MergeMerged, c1), pull("novox/mesh-delivery", 9, h2, second, commit2)}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -116,9 +117,9 @@ func TestAGroupsMembersNameItAndItsEndIsSaidOnce(t *testing.T) { // 1585/T16, T1
|
|||||||
written bool
|
written bool
|
||||||
answer string
|
answer string
|
||||||
}{
|
}{
|
||||||
{"merged and published: written again", map[string][]byte{"feat/take-in": askMerge(delivery.MergeMerged, c2)}, true, true, "written again"},
|
{"merged and published: written again", map[string][]byte{"feat/take-in": askMerge(meshdeliveryclient.MergeMerged, c2)}, true, true, "written again"},
|
||||||
{"a member's message not written", map[string][]byte{"feat/take-in": askMerge(delivery.MergeMerged, c2)}, false, false, "not written"},
|
{"a member's message not written", map[string][]byte{"feat/take-in": askMerge(meshdeliveryclient.MergeMerged, c2)}, false, false, "not written"},
|
||||||
{"a member not merged", map[string][]byte{"feat/take-in": askMerge(delivery.MergeMerging, "")}, true, false, "not written"},
|
{"a member not merged", map[string][]byte{"feat/take-in": askMerge(meshdeliveryclient.MergeMerging, "")}, true, false, "not written"},
|
||||||
{"no merge at the gate", map[string][]byte{}, true, false, "holds no merge"},
|
{"no merge at the gate", map[string][]byte{}, true, false, "holds no merge"},
|
||||||
} {
|
} {
|
||||||
pub, done := &idPub{}, memRevisions{}
|
pub, done := &idPub{}, memRevisions{}
|
||||||
|
|||||||
@@ -5,13 +5,11 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/nats-io/nats.go/jetstream"
|
"github.com/nats-io/nats.go/jetstream"
|
||||||
|
|
||||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// headsBucket is the module's state bucket heads, `<module>_<name>` as the indexer's buckets are named: the head last
|
// headsBucket is the module's state bucket heads, `<module>_<name>` as the indexer's buckets are named: the head last
|
||||||
// written on FORGE for each pull request (manifest state entry heads, novox/hq ADR 0443 §5).
|
// written on FORGE for each pull request (manifest state entry heads, novox/hq ADR 0443 §5).
|
||||||
const headsBucket = delivery.ForgeBridgeModule + "_heads"
|
const headsBucket = forgeBridgeModule + "_heads"
|
||||||
|
|
||||||
// kvHeads is the bucket heads as the bridge's headStore.
|
// kvHeads is the bucket heads as the bridge's headStore.
|
||||||
type kvHeads struct{ kv jetstream.KeyValue }
|
type kvHeads struct{ kv jetstream.KeyValue }
|
||||||
|
|||||||
+17
-15
@@ -20,8 +20,10 @@ import (
|
|||||||
"github.com/nats-io/nats.go"
|
"github.com/nats-io/nats.go"
|
||||||
"github.com/nats-io/nats.go/jetstream"
|
"github.com/nats-io/nats.go/jetstream"
|
||||||
|
|
||||||
|
"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"
|
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-forge-bridge/forgemsg"
|
||||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
"git.novox.be/novox/mesh-sdk/go/keys"
|
||||||
"git.novox.be/novox/mesh-sdk/go/process"
|
"git.novox.be/novox/mesh-sdk/go/process"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -232,8 +234,8 @@ func startBed(t *testing.T, openReply string, prepare func(ctx context.Context,
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
// Its other state bucket, and the gate's buckets the bridge reads, made by the controller from the gate's manifest
|
// Its other state bucket, and the gate's buckets the bridge reads, made by the controller from the gate's manifest
|
||||||
// (mesh-sdk go/delivery MergesBucket, OwedBucket with their histories).
|
// (the gate's merges and owed with their histories).
|
||||||
for name, history := range map[string]uint8{performedBucket: 1, inputsBucket: 1, delivery.MergesBucket: delivery.MergesHistory, delivery.OwedBucket: delivery.OwedHistory} {
|
for name, history := range map[string]uint8{performedBucket: 1, inputsBucket: 1, mergesBucket: 10, owedBucket: 5} {
|
||||||
if _, err := js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: name, History: history}); err != nil {
|
if _, err := js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: name, History: history}); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -334,7 +336,7 @@ func TestBridgeWritesPullMessages(t *testing.T) { // 1235/AC2
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
want := forgemsg.Forge{V: delivery.Version, Kind: "pull", Repository: "novox/mesh-delivery", Branch: "feat/x", Number: 12, Head: head,
|
want := forgemsg.Forge{V: keys.Version, Kind: "pull", Repository: "novox/mesh-delivery", Branch: "feat/x", Number: 12, Head: head,
|
||||||
Action: "updated", Dirs: []string{"modules/a"}, DirsKnown: true, Manifests: map[string]string{"modules/a": "{}"}}
|
Action: "updated", Dirs: []string{"modules/a"}, DirsKnown: true, Manifests: map[string]string{"modules/a": "{}"}}
|
||||||
if !forgemsg.SameForge(f, want) {
|
if !forgemsg.SameForge(f, want) {
|
||||||
t.Errorf("message = %+v, want %+v", f, want)
|
t.Errorf("message = %+v, want %+v", f, want)
|
||||||
@@ -465,7 +467,7 @@ func TestBridgeWritesMergedMessage(t *testing.T) { // 1236/AC4, 1448/AC5
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
want := forgemsg.Forge{V: delivery.Version, Kind: "merged", Repository: "novox/mesh-delivery", Branch: "feat/x", Number: 12, Head: head,
|
want := forgemsg.Forge{V: keys.Version, Kind: "merged", Repository: "novox/mesh-delivery", Branch: "feat/x", Number: 12, Head: head,
|
||||||
Merge: merge, Root: "r0", Trees: map[string]string{"": "t0", "modules/a": "t1"},
|
Merge: merge, Root: "r0", Trees: map[string]string{"": "t0", "modules/a": "t1"},
|
||||||
Manifests: map[string]string{"": string(manifest), "modules/a": string(manifest)},
|
Manifests: map[string]string{"": string(manifest), "modules/a": string(manifest)},
|
||||||
// No mesh package: only the indexer computes what a fingerprint takes (ADR 0593 §2).
|
// No mesh package: only the indexer computes what a fingerprint takes (ADR 0593 §2).
|
||||||
@@ -690,15 +692,15 @@ func TestBridgeMergesAGroupOneMemberAtATime(t *testing.T) { // 1342/AC3
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
kv, err := b.js.KeyValue(b.ctx, delivery.MergesBucket)
|
kv, err := b.js.KeyValue(b.ctx, mergesBucket)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
approved := map[string]string{delivery.ApprovedKey("novox/mesh-delivery", 12): head, delivery.ApprovedKey("novox/mesh-sdk", 4): headB}
|
approved := map[string]string{meshdeliveryclient.ApprovedKey("novox/mesh-delivery", 12): head, meshdeliveryclient.ApprovedKey("novox/mesh-sdk", 4): headB}
|
||||||
pullA := delivery.MergePull{Head: delivery.Head{Repository: "novox/mesh-delivery", Number: 12, Head: head}, State: delivery.MergeMerging}
|
pullA := seat.MergesByGroupValuePullsItem{Repository: "novox/mesh-delivery", Number: 12, Head: head, State: string(meshdeliveryclient.MergeMerging)}
|
||||||
pullB := delivery.MergePull{Head: delivery.Head{Repository: "novox/mesh-sdk", Number: 4, Head: headB}, State: delivery.MergeMerging}
|
pullB := seat.MergesByGroupValuePullsItem{Repository: "novox/mesh-sdk", Number: 4, Head: headB, State: string(meshdeliveryclient.MergeMerging)}
|
||||||
put := func(pulls ...delivery.MergePull) {
|
put := func(pulls ...seat.MergesByGroupValuePullsItem) {
|
||||||
v, err := delivery.EncodeMerge(delivery.Merge{Group: "g", Ask: "merge.g.0123456789ab", Approved: approved, Pulls: pulls})
|
v, err := meshdeliveryclient.EncodeMerge(seat.MergesByGroupValue{Group: "g", Ask: "merge.g.0123456789ab", Approved: approved, Pulls: pulls})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -726,7 +728,7 @@ func TestBridgeMergesAGroupOneMemberAtATime(t *testing.T) { // 1342/AC3
|
|||||||
// The gate hands the next member only after the bridge reported the one before merged (ADR 0441 §5).
|
// The gate hands the next member only after the bridge reported the one before merged (ADR 0441 §5).
|
||||||
for deadline := time.Now().Add(10 * time.Second); time.Now().Before(deadline) && len(forgeMerged(t, b)) < 1; time.Sleep(50 * time.Millisecond) {
|
for deadline := time.Now().Add(10 * time.Second); time.Now().Before(deadline) && len(forgeMerged(t, b)) < 1; time.Sleep(50 * time.Millisecond) {
|
||||||
}
|
}
|
||||||
pullA.State, pullA.Commit = delivery.MergeMerged, mergeA
|
pullA.State, pullA.Commit = string(meshdeliveryclient.MergeMerged), mergeA
|
||||||
put(pullA, pullB)
|
put(pullA, pullB)
|
||||||
next("mesh-sdk")
|
next("mesh-sdk")
|
||||||
var got []string
|
var got []string
|
||||||
@@ -786,15 +788,15 @@ func TestBridgePerformsNoMeshCheckAct(t *testing.T) { // R2-1, ADR 0521
|
|||||||
if err := b.ctl.Flush(); err != nil {
|
if err := b.ctl.Flush(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
kv, err := b.js.CreateKeyValue(b.ctx, jetstream.KeyValueConfig{Bucket: delivery.CheckerOwedBucket, History: delivery.CheckerOwedHistory})
|
kv, err := b.js.CreateKeyValue(b.ctx, jetstream.KeyValueConfig{Bucket: "mesh-delivery-checker_owed", History: 5})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
key, err := delivery.CheckerOwedKey("novox/mesh-delivery", 12, head)
|
key, err := meshdeliveryclient.OwedKey("novox/mesh-delivery", 12, head, "status=mesh/check")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
v, err := delivery.EncodeCheckerOwed(delivery.Owed{Act: delivery.OwedStatus, Context: delivery.StatusContextCheck, State: "success", Text: "pass", Target: "https://view.example/checks/g.0123456789ab"})
|
v, err := meshdeliveryclient.EncodeOwed(seat.OwedByRepositoryNumberHeadActValue{Act: meshdeliveryclient.OwedStatus, Context: meshdeliveryclient.StatusContextCheck, State: "success", Text: "pass", Target: "https://view.example/checks/g.0123456789ab"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ import (
|
|||||||
|
|
||||||
"github.com/nats-io/nats.go/jetstream"
|
"github.com/nats-io/nats.go/jetstream"
|
||||||
|
|
||||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
||||||
|
|
||||||
"mesh-delivery-forge-bridge/internal/bridge"
|
"mesh-delivery-forge-bridge/internal/bridge"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -19,7 +17,7 @@ import (
|
|||||||
// directory, its manifest and lock-file hashes, by the directory's git tree hash, so a merge reads the forge only for the
|
// directory, its manifest and lock-file hashes, by the directory's git tree hash, so a merge reads the forge only for the
|
||||||
// directories it changed (issue 1126; internal/bridge kept.go). A tree hash names its bytes, so a value is never stale;
|
// directories it changed (issue 1126; internal/bridge kept.go). A tree hash names its bytes, so a value is never stale;
|
||||||
// a merge adds a value only for each directory it changed (a few kilobytes each).
|
// a merge adds a value only for each directory it changed (a few kilobytes each).
|
||||||
const inputsBucket = delivery.ForgeBridgeModule + "_inputs"
|
const inputsBucket = forgeBridgeModule + "_inputs"
|
||||||
|
|
||||||
// keptTimeout bounds one read or write of the bucket: the bucket is a saving, never a reason to fail a merge.
|
// keptTimeout bounds one read or write of the bucket: the bucket is a saving, never a reason to fail a merge.
|
||||||
const keptTimeout = 2 * time.Second
|
const keptTimeout = 2 * time.Second
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Command mesh-delivery-forge-bridge is delivery's forge bridge (novox/hq ADR 0437 §4, issue 1235): it hears the forge
|
// Command mesh-delivery-forge-bridge is delivery's forge bridge (novox/hq ADR 0437 §4, issue 1235): it hears the forge
|
||||||
// module's pull request events and writes each as a pull message on its stream FORGE. It runs beside today's
|
// module's pull request events and writes each as a pull message on its stream FORGE. It runs beside today's
|
||||||
// mesh-delivery and merges nothing. It serves no request subject and holds no timer: every reaction is an event
|
// mesh-delivery and merges nothing. It serves no request subject and holds no timer: every reaction is an event
|
||||||
// (ADR 0437 §8); a refused act is tried again only on the next event touching its delivery.
|
// (ADR 0437 §8); a refused act is tried again only on the next event touching its meshdeliveryclient.
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -22,7 +22,6 @@ import (
|
|||||||
"github.com/nats-io/nats.go/jetstream"
|
"github.com/nats-io/nats.go/jetstream"
|
||||||
|
|
||||||
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-forge-bridge/forgemsg"
|
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-forge-bridge/forgemsg"
|
||||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
||||||
|
|
||||||
"mesh-delivery-forge-bridge/internal/bridge"
|
"mesh-delivery-forge-bridge/internal/bridge"
|
||||||
groupkeys "mesh-delivery-forge-bridge/internal/groups"
|
groupkeys "mesh-delivery-forge-bridge/internal/groups"
|
||||||
@@ -35,7 +34,7 @@ const eventsStream = "EVENTS"
|
|||||||
// The bridge's own stream FORGE takes `mesh.stream.<module>.event.<event>` (mesh-control internal/broker
|
// The bridge's own stream FORGE takes `mesh.stream.<module>.event.<event>` (mesh-control internal/broker
|
||||||
// modstreams.go ModuleStreamSubject).
|
// modstreams.go ModuleStreamSubject).
|
||||||
func forgeSubject(event string) string {
|
func forgeSubject(event string) string {
|
||||||
return process.ModuleStreamSubject(delivery.ForgeBridgeModule, event)
|
return process.ModuleStreamSubject(forgeBridgeModule, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -158,7 +157,7 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
|
|||||||
for _, w := range []struct {
|
for _, w := range []struct {
|
||||||
bucket string
|
bucket string
|
||||||
react func(ctx context.Context, key string, rev uint64, value []byte)
|
react func(ctx context.Context, key string, rev uint64, value []byte)
|
||||||
}{{delivery.OwedBucket, perf.owed}, {delivery.MergesBucket, func(ctx context.Context, key string, rev uint64, value []byte) {
|
}{{owedBucket, perf.owed}, {mergesBucket, func(ctx context.Context, key string, rev uint64, value []byte) {
|
||||||
perf.merges(ctx, key, rev, value)
|
perf.merges(ctx, key, rev, value)
|
||||||
groups.ended(ctx, key, rev, value)
|
groups.ended(ctx, key, rev, value)
|
||||||
}}} {
|
}}} {
|
||||||
@@ -168,7 +167,7 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
|
|||||||
if kv == nil {
|
if kv == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if w.bucket == delivery.MergesBucket {
|
if w.bucket == mergesBucket {
|
||||||
groups.merges = kvMerges(kv)
|
groups.merges = kvMerges(kv)
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import groupkeys "mesh-delivery-forge-bridge/internal/groups"
|
||||||
|
|
||||||
|
// The holders and buckets the forge bridge names, taken from its own manifest (module.json: its name, its `reads`, its
|
||||||
|
// states); a unit row checks them against it (TestTheNamesAreTheManifests). No contract names a holder
|
||||||
|
// (novox/hq ADR 0625 §4, ADR 0634 §1).
|
||||||
|
const (
|
||||||
|
forgeBridgeModule = groupkeys.Module
|
||||||
|
gateModule = "mesh-delivery-gate"
|
||||||
|
|
||||||
|
// mergesBucket and owedBucket are the gate's states merges and owed, read by the bridge (reads: mesh-delivery-gate.merges,
|
||||||
|
// mesh-delivery-gate.owed).
|
||||||
|
mergesBucket = gateModule + "_merges"
|
||||||
|
owedBucket = gateModule + "_owed"
|
||||||
|
)
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
"slices"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// theNamesAreTheManifests (a subtest of TestAnOwedStatusIsSetOnceForARevision): the holders and buckets the bridge names
|
||||||
|
// are its manifest's (its module, its `reads`, its states), since no contract names a holder (novox/hq ADR 0634 §1).
|
||||||
|
func theNamesAreTheManifests(t *testing.T) {
|
||||||
|
raw, err := os.ReadFile("../../module.json")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var m struct {
|
||||||
|
Module string `json:"module"`
|
||||||
|
Reads []string `json:"reads"`
|
||||||
|
State []struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
} `json:"state"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(raw, &m); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if m.Module != forgeBridgeModule {
|
||||||
|
t.Errorf("the manifest's module is %q, the bridge names itself %q", m.Module, forgeBridgeModule)
|
||||||
|
}
|
||||||
|
for _, read := range []string{gateModule + ".merges", gateModule + ".owed"} {
|
||||||
|
if !slices.Contains(m.Reads, read) {
|
||||||
|
t.Errorf("the bridge reads %s, its manifest's reads are %v", read, m.Reads)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var own []string
|
||||||
|
for _, s := range m.State {
|
||||||
|
own = append(own, forgeBridgeModule+"_"+s.Name)
|
||||||
|
}
|
||||||
|
for _, b := range []string{headsBucket, performedBucket, inputsBucket} {
|
||||||
|
if !slices.Contains(own, b) {
|
||||||
|
t.Errorf("the bridge keeps %s, its manifest's states are %v", b, own)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,11 +11,13 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
"git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient"
|
||||||
|
"git.novox.be/novox/mesh-delivery/sdk/trunk"
|
||||||
|
"git.novox.be/novox/mesh-sdk/go/keys"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Version is the version of every value this package writes and the only one it reads: mesh-sdk's delivery values'.
|
// Version is the version of every value this package writes and the only one it reads: mesh-sdk's delivery values'.
|
||||||
const Version = delivery.Version
|
const Version = keys.Version
|
||||||
|
|
||||||
// ForgeStream is the forge bridge's stream as its manifest declares it; the broker derives the real names.
|
// ForgeStream is the forge bridge's stream as its manifest declares it; the broker derives the real names.
|
||||||
const ForgeStream = "forge"
|
const ForgeStream = "forge"
|
||||||
@@ -156,8 +158,8 @@ type Forge struct {
|
|||||||
// that group with its approved head, this one included (novox/hq ADR 0633 §4): the forge bridge sets both from the
|
// that group with its approved head, this one included (novox/hq ADR 0633 §4): the forge bridge sets both from the
|
||||||
// gate's merges state, and the indexer gathers the group's merges until the group's end (ForgeGroupEnd). A merge
|
// gate's merges state, and the indexer gathers the group's merges until the group's end (ForgeGroupEnd). A merge
|
||||||
// with no group carries neither, and an older bridge sends neither; an older reader ignores both.
|
// with no group carries neither, and an older bridge sends neither; an older reader ignores both.
|
||||||
Group string `json:"group,omitempty"`
|
Group string `json:"group,omitempty"`
|
||||||
Members []delivery.Head `json:"members,omitempty"`
|
Members []meshdeliveryclient.Head `json:"members,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkGroup refuses a group on anything but a merged message, a group without its members or members without a
|
// checkGroup refuses a group on anything but a merged message, a group without its members or members without a
|
||||||
@@ -187,7 +189,7 @@ func (f Forge) checkGroup() error {
|
|||||||
return fmt.Errorf("delivery: the group %q names its member %s twice", f.Group, k)
|
return fmt.Errorf("delivery: the group %q names its member %s twice", f.Group, k)
|
||||||
}
|
}
|
||||||
seen[k] = true
|
seen[k] = true
|
||||||
self = self || (delivery.SameRepository(m.Repository, f.Repository) && m.Number == f.Number)
|
self = self || (trunk.SameRepository(m.Repository, f.Repository) && m.Number == f.Number)
|
||||||
}
|
}
|
||||||
if !self {
|
if !self {
|
||||||
return fmt.Errorf("delivery: the members of the group %q leave out the merged pull request %s#%d", f.Group, f.Repository, f.Number)
|
return fmt.Errorf("delivery: the members of the group %q leave out the merged pull request %s#%d", f.Group, f.Repository, f.Number)
|
||||||
@@ -395,7 +397,7 @@ func checkName(kind, name string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// decode reads a value of Version, refusing another or none with delivery.ErrUnknownVersion, as mesh-sdk delivery's
|
// decode reads a value of Version, refusing another or none with keys.ErrUnknownVersion, as mesh-sdk delivery's
|
||||||
// values are read.
|
// values are read.
|
||||||
func decode(data []byte, into any) error {
|
func decode(data []byte, into any) error {
|
||||||
var probe struct {
|
var probe struct {
|
||||||
@@ -405,10 +407,10 @@ func decode(data []byte, into any) error {
|
|||||||
return fmt.Errorf("delivery: value is not JSON: %w", err)
|
return fmt.Errorf("delivery: value is not JSON: %w", err)
|
||||||
}
|
}
|
||||||
if probe.V == nil {
|
if probe.V == nil {
|
||||||
return fmt.Errorf("%w: no version in the value, this reader reads %d", delivery.ErrUnknownVersion, Version)
|
return fmt.Errorf("%w: no version in the value, this reader reads %d", keys.ErrUnknownVersion, Version)
|
||||||
}
|
}
|
||||||
if *probe.V != Version {
|
if *probe.V != Version {
|
||||||
return fmt.Errorf("%w: the value is version %d, this reader reads %d", delivery.ErrUnknownVersion, *probe.V, Version)
|
return fmt.Errorf("%w: the value is version %d, this reader reads %d", keys.ErrUnknownVersion, *probe.V, Version)
|
||||||
}
|
}
|
||||||
if err := json.Unmarshal(data, into); err != nil {
|
if err := json.Unmarshal(data, into); err != nil {
|
||||||
return fmt.Errorf("delivery: value does not fit its shape: %w", err)
|
return fmt.Errorf("delivery: value does not fit its shape: %w", err)
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
"git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient"
|
||||||
|
"git.novox.be/novox/mesh-sdk/go/keys"
|
||||||
)
|
)
|
||||||
|
|
||||||
const oddManifest = "{\n\t\"module\": \"a\",\n \"z\": 1, \"a\": [ \"x\",\"y\" ],\n \"why\": \"é — <b> & \\n\"\n}\n\n"
|
const oddManifest = "{\n\t\"module\": \"a\",\n \"z\": 1, \"a\": [ \"x\",\"y\" ],\n \"why\": \"é — <b> & \\n\"\n}\n\n"
|
||||||
@@ -20,7 +21,7 @@ func sampleForges() []Forge {
|
|||||||
{Kind: ForgeOpen, Open: []OpenPull{{Repository: "novox/mesh-sdk", Number: 36}, {Repository: "novox/mesh-control", Number: 7}}},
|
{Kind: ForgeOpen, Open: []OpenPull{{Repository: "novox/mesh-sdk", Number: 36}, {Repository: "novox/mesh-control", Number: 7}}},
|
||||||
// 1585/T1: a group member's merged message names its group and every approved member (ADR 0633 §4).
|
// 1585/T1: a group member's merged message names its group and every approved member (ADR 0633 §4).
|
||||||
{Kind: ForgeMerged, Repository: "novox/mesh-sdk", Number: 37, Head: "dddd", Merge: "eeee", Root: "t0",
|
{Kind: ForgeMerged, Repository: "novox/mesh-sdk", Number: 37, Head: "dddd", Merge: "eeee", Root: "t0",
|
||||||
Group: "feat/take-in", Members: []delivery.Head{{Repository: "novox/mesh-sdk", Number: 37, Head: "dddd"}, {Repository: "novox/mesh-delivery", Number: 9, Head: "ffff"}}},
|
Group: "feat/take-in", Members: []meshdeliveryclient.Head{{Repository: "novox/mesh-sdk", Number: 37, Head: "dddd"}, {Repository: "novox/mesh-delivery", Number: 9, Head: "ffff"}}},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,8 +108,8 @@ func TestForge(t *testing.T) {
|
|||||||
"group end": func(b []byte) error { _, err := DecodeForgeGroupEnd(b); return err },
|
"group end": func(b []byte) error { _, err := DecodeForgeGroupEnd(b); return err },
|
||||||
} {
|
} {
|
||||||
for _, in := range []string{`{"group":"g"}`, `{"v":2,"group":"g"}`} {
|
for _, in := range []string{`{"group":"g"}`, `{"v":2,"group":"g"}`} {
|
||||||
if err := dec([]byte(in)); !errors.Is(err, delivery.ErrUnknownVersion) {
|
if err := dec([]byte(in)); !errors.Is(err, keys.ErrUnknownVersion) {
|
||||||
t.Errorf("%s decode of %s = %v, want delivery.ErrUnknownVersion", name, in, err)
|
t.Errorf("%s decode of %s = %v, want keys.ErrUnknownVersion", name, in, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -158,7 +159,7 @@ func TestForge(t *testing.T) {
|
|||||||
{"open with locks", "only the open", Forge{Kind: ForgeOpen, Locks: map[string]map[string]string{"a": {"go.sum": "h"}}}},
|
{"open with locks", "only the open", Forge{Kind: ForgeOpen, Locks: map[string]map[string]string{"a": {"go.sum": "h"}}}},
|
||||||
// 1585/T1: a group only on a merged message, with its members, the message's own pull request among them.
|
// 1585/T1: a group only on a merged message, with its members, the message's own pull request among them.
|
||||||
{"pull with a group", "only a merged message names its delivery group", at(0, func(f *Forge) {
|
{"pull with a group", "only a merged message names its delivery group", at(0, func(f *Forge) {
|
||||||
f.Group, f.Members = "g", []delivery.Head{{Repository: "novox/mesh-sdk", Number: 36, Head: "aaaa"}}
|
f.Group, f.Members = "g", []meshdeliveryclient.Head{{Repository: "novox/mesh-sdk", Number: 36, Head: "aaaa"}}
|
||||||
})},
|
})},
|
||||||
{"merged with a group and no members", "together", at(4, func(f *Forge) { f.Members = nil })},
|
{"merged with a group and no members", "together", at(4, func(f *Forge) { f.Members = nil })},
|
||||||
{"merged with members and no group", "together", at(4, func(f *Forge) { f.Group = "" })},
|
{"merged with members and no group", "together", at(4, func(f *Forge) { f.Group = "" })},
|
||||||
|
|||||||
@@ -3,3 +3,8 @@ module git.novox.be/novox/mesh-delivery/modules/mesh-delivery-forge-bridge/forge
|
|||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104002338-5a38a1221659
|
require git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104002338-5a38a1221659
|
||||||
|
|
||||||
|
require (
|
||||||
|
git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient v0.0.0-00010101000000-000000000000
|
||||||
|
git.novox.be/novox/mesh-delivery/sdk v0.0.0-00010101000000-000000000000
|
||||||
|
)
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ module mesh-delivery-forge-bridge
|
|||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient v0.0.0-00010101000000-000000000000
|
||||||
|
git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdelivery v0.0.0-00010101000000-000000000000
|
||||||
|
git.novox.be/novox/mesh-delivery/sdk v0.0.0-00010101000000-000000000000
|
||||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104174359-05b3e2e1b94f
|
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104174359-05b3e2e1b94f
|
||||||
github.com/nats-io/nats-server/v2 v2.11.17
|
github.com/nats-io/nats-server/v2 v2.11.17
|
||||||
github.com/nats-io/nats.go v1.54.0
|
github.com/nats-io/nats.go v1.54.0
|
||||||
|
|||||||
@@ -5,8 +5,9 @@ import (
|
|||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"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"
|
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-forge-bridge/forgemsg"
|
||||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Call is a call of a forge tool: the tool by its manifest key and its arguments.
|
// Call is a call of a forge tool: the tool by its manifest key and its arguments.
|
||||||
@@ -26,7 +27,7 @@ type Act struct {
|
|||||||
Call Call
|
Call Call
|
||||||
}
|
}
|
||||||
|
|
||||||
// The forge's tools, by manifest key; their arguments are the ones of mesh-catalog modules/gitea: delivery.ts
|
// The forge's tools, by manifest key; their arguments are the ones of mesh-catalog modules/gitea: meshdeliveryclient.ts
|
||||||
// commitStatusTool (owner, repo, sha, context, state, description, target_url), tools/index.ts gitea_delivery_view
|
// commitStatusTool (owner, repo, sha, context, state, description, target_url), tools/index.ts gitea_delivery_view
|
||||||
// (owner, repo, number, body), gitea_note_append (owner, repo, sha, ref, line), gitea_add_comment (owner, repo,
|
// (owner, repo, number, body), gitea_note_append (owner, repo, sha, ref, line), gitea_add_comment (owner, repo,
|
||||||
// number, body), merge.ts gitea_merge_pull_request (owner, repo, number, head, approved_heads).
|
// number, body), merge.ts gitea_merge_pull_request (owner, repo, number, head, approved_heads).
|
||||||
@@ -50,15 +51,15 @@ func ownerRepo(repository string) (owner, repo string) {
|
|||||||
// it. A key that is no owed key, a value that is no owed act, a value whose act is not the key's, and a view or a comment
|
// it. A key that is no owed key, a value that is no owed act, a value whose act is not the key's, and a view or a comment
|
||||||
// on no pull request are errors, each saying why.
|
// on no pull request are errors, each saying why.
|
||||||
func OwedAct(key string, value []byte) (Act, error) {
|
func OwedAct(key string, value []byte) (Act, error) {
|
||||||
repository, number, head, name, err := delivery.ParseOwedKey(key)
|
repository, number, head, name, err := meshdeliveryclient.ParseOwedKey(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Act{}, err
|
return Act{}, err
|
||||||
}
|
}
|
||||||
o, err := delivery.DecodeOwed(value)
|
o, err := meshdeliveryclient.DecodeOwed(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Act{}, fmt.Errorf("the owed act %s: %w", key, err)
|
return Act{}, fmt.Errorf("the owed act %s: %w", key, err)
|
||||||
}
|
}
|
||||||
kind, context, err := delivery.ParseOwedAct(name)
|
kind, context, err := meshdeliveryclient.ParseOwedAct(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Act{}, err
|
return Act{}, err
|
||||||
}
|
}
|
||||||
@@ -74,26 +75,26 @@ func OwedAct(key string, value []byte) (Act, error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
switch kind {
|
switch kind {
|
||||||
case delivery.OwedStatus:
|
case meshdeliveryclient.OwedStatus:
|
||||||
if o.Context != context {
|
if o.Context != context {
|
||||||
return Act{}, fmt.Errorf("the owed status %s says the context %q, not the %q its key names", key, o.Context, context)
|
return Act{}, fmt.Errorf("the owed status %s says the context %q, not the %q its key names", key, o.Context, context)
|
||||||
}
|
}
|
||||||
if o.Context == delivery.StatusContextCheck {
|
if o.Context == meshdeliveryclient.StatusContextCheck {
|
||||||
// No tool writes mesh/check (R4e finding K1), and the bridge performs no act of it: the forge module writes it from
|
// No tool writes mesh/check (R4e finding K1), and the bridge performs no act of it: the forge module writes it from
|
||||||
// the checker's state mesh-delivery-checker.owed, read by a watch (ADR 0521, finding R2-1).
|
// the checker's state mesh-delivery-checker.owed, read by a watch (ADR 0521, finding R2-1).
|
||||||
return Act{}, fmt.Errorf("the owed status %s is %s, which the bridge does not perform: the forge module writes it from mesh-delivery-checker.owed",
|
return Act{}, fmt.Errorf("the owed status %s is %s, which the bridge does not perform: the forge module writes it from mesh-delivery-checker.owed",
|
||||||
key, delivery.StatusContextCheck)
|
key, meshdeliveryclient.StatusContextCheck)
|
||||||
}
|
}
|
||||||
a.Call = Call{Tool: commitStatusTool, Args: map[string]any{"owner": owner, "repo": repo, "sha": head, "context": o.Context,
|
a.Call = Call{Tool: commitStatusTool, Args: map[string]any{"owner": owner, "repo": repo, "sha": head, "context": o.Context,
|
||||||
"state": o.State, "description": o.Text, "target_url": o.Target}}
|
"state": o.State, "description": o.Text, "target_url": o.Target}}
|
||||||
case delivery.OwedView:
|
case meshdeliveryclient.OwedView:
|
||||||
if err := needsPull(); err != nil {
|
if err := needsPull(); err != nil {
|
||||||
return Act{}, err
|
return Act{}, err
|
||||||
}
|
}
|
||||||
a.Call = Call{Tool: deliveryViewTool, Args: map[string]any{"owner": owner, "repo": repo, "number": number, "body": o.Text}}
|
a.Call = Call{Tool: deliveryViewTool, Args: map[string]any{"owner": owner, "repo": repo, "number": number, "body": o.Text}}
|
||||||
case delivery.OwedNote:
|
case meshdeliveryclient.OwedNote:
|
||||||
a.Call = Call{Tool: noteAppendTool, Args: map[string]any{"owner": owner, "repo": repo, "sha": head, "ref": o.Ref, "line": o.Text}}
|
a.Call = Call{Tool: noteAppendTool, Args: map[string]any{"owner": owner, "repo": repo, "sha": head, "ref": o.Ref, "line": o.Text}}
|
||||||
case delivery.OwedComment:
|
case meshdeliveryclient.OwedComment:
|
||||||
if err := needsPull(); err != nil {
|
if err := needsPull(); err != nil {
|
||||||
return Act{}, err
|
return Act{}, err
|
||||||
}
|
}
|
||||||
@@ -102,19 +103,19 @@ func OwedAct(key string, value []byte) (Act, error) {
|
|||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MergeActs turns the value of a merges key (mesh-sdk go/delivery EncodeMerge) into the merges to perform: one for each
|
// MergeActs turns the value of a merges key (the delivery client's EncodeMerge) into the merges to perform: one for each
|
||||||
// pull request in the state merging, in the order the value names them, at the head it expects and with the approved
|
// pull request in the state merging, in the order the value names them, at the head it expects and with the approved
|
||||||
// heads of every member of the group, `<owner>/<repo>@<full sha>` sorted (the merge tool's approved_heads). A pull
|
// heads of every member of the group, `<owner>/<repo>@<full sha>` sorted (the merge tool's approved_heads). A pull
|
||||||
// request merged or refused already gets none. A value with no approved heads is an error: the forge's merge tool
|
// request merged or refused already gets none. A value with no approved heads is an error: the forge's merge tool
|
||||||
// needs them for a group.
|
// needs them for a group.
|
||||||
func MergeActs(value []byte) ([]Act, error) {
|
func MergeActs(value []byte) ([]Act, error) {
|
||||||
m, err := delivery.DecodeMerge(value)
|
m, err := meshdeliveryclient.DecodeMerge(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var handed []delivery.MergePull
|
var handed []seat.MergesByGroupValuePullsItem
|
||||||
for _, p := range m.Pulls {
|
for _, p := range m.Pulls {
|
||||||
if p.State == delivery.MergeMerging {
|
if meshdeliveryclient.MergeState(p.State) == meshdeliveryclient.MergeMerging {
|
||||||
handed = append(handed, p)
|
handed = append(handed, p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,8 +137,8 @@ func MergeActs(value []byte) ([]Act, error) {
|
|||||||
var acts []Act
|
var acts []Act
|
||||||
for _, p := range handed {
|
for _, p := range handed {
|
||||||
owner, repo := ownerRepo(p.Repository)
|
owner, repo := ownerRepo(p.Repository)
|
||||||
acts = append(acts, Act{Name: MergeAct, Repository: p.Repository, Number: p.Number, Head: p.Head.Head,
|
acts = append(acts, Act{Name: MergeAct, Repository: p.Repository, Number: int(p.Number), Head: p.Head,
|
||||||
Call: Call{Tool: mergePullTool, Args: map[string]any{"owner": owner, "repo": repo, "number": p.Number, "head": p.Head.Head,
|
Call: Call{Tool: mergePullTool, Args: map[string]any{"owner": owner, "repo": repo, "number": int(p.Number), "head": p.Head,
|
||||||
"approved_heads": approved}}})
|
"approved_heads": approved}}})
|
||||||
}
|
}
|
||||||
return acts, nil
|
return acts, nil
|
||||||
|
|||||||
@@ -5,8 +5,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"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"
|
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-forge-bridge/forgemsg"
|
||||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
"git.novox.be/novox/mesh-sdk/go/keys"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -15,13 +17,13 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// owedValue is an owed value as the SDK writes it (mesh-sdk go/delivery EncodeOwed), and its key as OwedKey spells it.
|
// owedValue is an owed value as the SDK writes it (mesh-sdk go/delivery EncodeOwed), and its key as OwedKey spells it.
|
||||||
func owedValue(t *testing.T, repository string, number int, act string, o delivery.Owed) (string, []byte) {
|
func owedValue(t *testing.T, repository string, number int, act string, o seat.OwedByRepositoryNumberHeadActValue) (string, []byte) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
key, err := delivery.OwedKey(repository, number, headA, act)
|
key, err := meshdeliveryclient.OwedKey(repository, number, headA, act)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
b, err := delivery.EncodeOwed(o)
|
b, err := meshdeliveryclient.EncodeOwed(o)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -30,7 +32,7 @@ func owedValue(t *testing.T, repository string, number int, act string, o delive
|
|||||||
|
|
||||||
func TestOwedStatusIsACommitStatusCall(t *testing.T) { // 1341/AC1, K1
|
func TestOwedStatusIsACommitStatusCall(t *testing.T) { // 1341/AC1, K1
|
||||||
key, v := owedValue(t, "novox/mesh-delivery", 12, "status=mesh/merge-gate",
|
key, v := owedValue(t, "novox/mesh-delivery", 12, "status=mesh/merge-gate",
|
||||||
delivery.Owed{Act: delivery.OwedStatus, Context: "mesh/merge-gate", State: "pending", Text: "waiting", Target: "https://forge.example/novox/mesh-delivery/pulls/12"})
|
seat.OwedByRepositoryNumberHeadActValue{Act: meshdeliveryclient.OwedStatus, Context: "mesh/merge-gate", State: "pending", Text: "waiting", Target: "https://forge.example/novox/mesh-delivery/pulls/12"})
|
||||||
a, err := OwedAct(key, v)
|
a, err := OwedAct(key, v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -46,26 +48,26 @@ func TestOwedStatusIsACommitStatusCall(t *testing.T) { // 1341/AC1, K1
|
|||||||
// R4e findings K1 and R2-1 (issue 1301 S4; ADR 0503 §3, ADR 0521): no tool writes mesh/check, since a tool is told no
|
// R4e findings K1 and R2-1 (issue 1301 S4; ADR 0503 §3, ADR 0521): no tool writes mesh/check, since a tool is told no
|
||||||
// caller and node-tools invokes every tool; the forge module writes it from the checker's owed state, and the bridge
|
// caller and node-tools invokes every tool; the forge module writes it from the checker's owed state, and the bridge
|
||||||
// performs no mesh/check act.
|
// performs no mesh/check act.
|
||||||
ck, cv := checkerKV(t, delivery.Owed{Act: delivery.OwedStatus, Context: delivery.StatusContextCheck, State: "success", Text: "pass", Target: "https://view.example/checks/g.1"})
|
ck, cv := checkerKV(t, seat.OwedByRepositoryNumberHeadActValue{Act: meshdeliveryclient.OwedStatus, Context: meshdeliveryclient.StatusContextCheck, State: "success", Text: "pass", Target: "https://view.example/checks/g.1"})
|
||||||
if a, err := OwedAct(ck, cv); err == nil || !strings.Contains(err.Error(), "mesh-delivery-checker.owed") {
|
if a, err := OwedAct(ck, cv); err == nil || !strings.Contains(err.Error(), "mesh-delivery-checker.owed") {
|
||||||
t.Fatalf("OwedAct(mesh/check) = %+v, %v; want refused, naming where the forge module reads it", a, err)
|
t.Fatalf("OwedAct(mesh/check) = %+v, %v; want refused, naming where the forge module reads it", a, err)
|
||||||
}
|
}
|
||||||
// Nor through the gate's own owed bucket: the context is refused whichever key carries it.
|
// Nor through the gate's own owed bucket: the context is refused whichever key carries it.
|
||||||
gk, gv := owedValue(t, "novox/mesh-delivery", 12, "status=mesh/check",
|
gk, gv := owedValue(t, "novox/mesh-delivery", 12, "status=mesh/check",
|
||||||
delivery.Owed{Act: delivery.OwedStatus, Context: delivery.StatusContextCheck, State: "success", Text: "pass", Target: "https://x.example"})
|
seat.OwedByRepositoryNumberHeadActValue{Act: meshdeliveryclient.OwedStatus, Context: meshdeliveryclient.StatusContextCheck, State: "success", Text: "pass", Target: "https://x.example"})
|
||||||
if a, err := OwedAct(gk, gv); err == nil || !strings.Contains(err.Error(), "mesh-delivery-checker.owed") {
|
if a, err := OwedAct(gk, gv); err == nil || !strings.Contains(err.Error(), "mesh-delivery-checker.owed") {
|
||||||
t.Fatalf("OwedAct(gate's mesh/check) = %+v, %v; want refused", a, err)
|
t.Fatalf("OwedAct(gate's mesh/check) = %+v, %v; want refused", a, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkerKV is the status the checker owes, as it writes it (mesh-sdk go/delivery CheckerOwedKey, EncodeCheckerOwed).
|
// checkerKV is the status the checker owes, as it writes it (the gate's owed key with the act status=mesh/check).
|
||||||
func checkerKV(t *testing.T, o delivery.Owed) (string, []byte) {
|
func checkerKV(t *testing.T, o seat.OwedByRepositoryNumberHeadActValue) (string, []byte) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
key, err := delivery.CheckerOwedKey("novox/mesh-delivery", 12, headA)
|
key, err := meshdeliveryclient.OwedKey("novox/mesh-delivery", 12, headA, "status=mesh/check")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
v, err := delivery.EncodeCheckerOwed(o)
|
v, err := meshdeliveryclient.EncodeOwed(o)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -75,14 +77,14 @@ func checkerKV(t *testing.T, o delivery.Owed) (string, []byte) {
|
|||||||
func TestOwedViewNoteAndCommentCalls(t *testing.T) { // 1341/AC2
|
func TestOwedViewNoteAndCommentCalls(t *testing.T) { // 1341/AC2
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
act string
|
act string
|
||||||
o delivery.Owed
|
o seat.OwedByRepositoryNumberHeadActValue
|
||||||
want Call
|
want Call
|
||||||
}{
|
}{
|
||||||
{"view", delivery.Owed{Act: delivery.OwedView, Text: "the view"},
|
{"view", seat.OwedByRepositoryNumberHeadActValue{Act: meshdeliveryclient.OwedView, Text: "the view"},
|
||||||
Call{Tool: "gitea.gitea_delivery_view", Args: map[string]any{"owner": "novox", "repo": "mesh-delivery", "number": 12, "body": "the view"}}},
|
Call{Tool: "gitea.gitea_delivery_view", Args: map[string]any{"owner": "novox", "repo": "mesh-delivery", "number": 12, "body": "the view"}}},
|
||||||
{"note", delivery.Owed{Act: delivery.OwedNote, Text: "a line", Ref: "mesh-plan"},
|
{"note", seat.OwedByRepositoryNumberHeadActValue{Act: meshdeliveryclient.OwedNote, Text: "a line", Ref: "mesh-plan"},
|
||||||
Call{Tool: "gitea.gitea_note_append", Args: map[string]any{"owner": "novox", "repo": "mesh-delivery", "sha": headA, "ref": "mesh-plan", "line": "a line"}}},
|
Call{Tool: "gitea.gitea_note_append", Args: map[string]any{"owner": "novox", "repo": "mesh-delivery", "sha": headA, "ref": "mesh-plan", "line": "a line"}}},
|
||||||
{"comment.2", delivery.Owed{Act: delivery.OwedComment, Text: "hello"},
|
{"comment.2", seat.OwedByRepositoryNumberHeadActValue{Act: meshdeliveryclient.OwedComment, Text: "hello"},
|
||||||
Call{Tool: "gitea.gitea_add_comment", Args: map[string]any{"owner": "novox", "repo": "mesh-delivery", "number": 12, "body": "hello"}}},
|
Call{Tool: "gitea.gitea_add_comment", Args: map[string]any{"owner": "novox", "repo": "mesh-delivery", "number": 12, "body": "hello"}}},
|
||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
@@ -98,11 +100,11 @@ func TestOwedViewNoteAndCommentCalls(t *testing.T) { // 1341/AC2
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestOwedActWithoutAPullRequestNeedsOneOnlyWhereTheToolDoes(t *testing.T) {
|
func TestOwedActWithoutAPullRequestNeedsOneOnlyWhereTheToolDoes(t *testing.T) {
|
||||||
key, v := owedValue(t, "novox/mesh-delivery", 0, "note", delivery.Owed{Act: delivery.OwedNote, Text: "l", Ref: "mesh-plan"})
|
key, v := owedValue(t, "novox/mesh-delivery", 0, "note", seat.OwedByRepositoryNumberHeadActValue{Act: meshdeliveryclient.OwedNote, Text: "l", Ref: "mesh-plan"})
|
||||||
if _, err := OwedAct(key, v); err != nil {
|
if _, err := OwedAct(key, v); err != nil {
|
||||||
t.Errorf("a note on a commit no pull request names: %v", err)
|
t.Errorf("a note on a commit no pull request names: %v", err)
|
||||||
}
|
}
|
||||||
key, v = owedValue(t, "novox/mesh-delivery", 0, "view", delivery.Owed{Act: delivery.OwedView, Text: "v"})
|
key, v = owedValue(t, "novox/mesh-delivery", 0, "view", seat.OwedByRepositoryNumberHeadActValue{Act: meshdeliveryclient.OwedView, Text: "v"})
|
||||||
if _, err := OwedAct(key, v); err == nil {
|
if _, err := OwedAct(key, v); err == nil {
|
||||||
t.Error("a view of no pull request was taken")
|
t.Error("a view of no pull request was taken")
|
||||||
}
|
}
|
||||||
@@ -110,12 +112,12 @@ func TestOwedActWithoutAPullRequestNeedsOneOnlyWhereTheToolDoes(t *testing.T) {
|
|||||||
|
|
||||||
func TestOwedActThatDisagreesWithItsKeyIsRefused(t *testing.T) {
|
func TestOwedActThatDisagreesWithItsKeyIsRefused(t *testing.T) {
|
||||||
// A dotted repository's key is unescaped, and its act names the repository whole.
|
// A dotted repository's key is unescaped, and its act names the repository whole.
|
||||||
dk, dv := owedValue(t, "novox/my.repo", 3, "comment.1", delivery.Owed{Act: delivery.OwedComment, Text: "x"})
|
dk, dv := owedValue(t, "novox/my.repo", 3, "comment.1", seat.OwedByRepositoryNumberHeadActValue{Act: meshdeliveryclient.OwedComment, Text: "x"})
|
||||||
if a, err := OwedAct(dk, dv); err != nil || a.Call.Args["repo"] != "my.repo" || a.Repository != "novox/my.repo" {
|
if a, err := OwedAct(dk, dv); err != nil || a.Call.Args["repo"] != "my.repo" || a.Repository != "novox/my.repo" {
|
||||||
t.Errorf("a dotted repository's act = %+v, %v", a, err)
|
t.Errorf("a dotted repository's act = %+v, %v", a, err)
|
||||||
}
|
}
|
||||||
key, _ := owedValue(t, "novox/mesh-delivery", 12, "view", delivery.Owed{Act: delivery.OwedView, Text: "v"})
|
key, _ := owedValue(t, "novox/mesh-delivery", 12, "view", seat.OwedByRepositoryNumberHeadActValue{Act: meshdeliveryclient.OwedView, Text: "v"})
|
||||||
v, _ := delivery.EncodeOwed(delivery.Owed{Act: delivery.OwedComment, Text: "v"})
|
v, _ := meshdeliveryclient.EncodeOwed(seat.OwedByRepositoryNumberHeadActValue{Act: meshdeliveryclient.OwedComment, Text: "v"})
|
||||||
if _, err := OwedAct(key, v); err == nil {
|
if _, err := OwedAct(key, v); err == nil {
|
||||||
t.Error("a comment under a view's key was taken")
|
t.Error("a comment under a view's key was taken")
|
||||||
}
|
}
|
||||||
@@ -127,9 +129,9 @@ func TestOwedActThatDisagreesWithItsKeyIsRefused(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func mergeValue(t *testing.T, m delivery.Merge) []byte {
|
func mergeValue(t *testing.T, m seat.MergesByGroupValue) []byte {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
b, err := delivery.EncodeMerge(m)
|
b, err := meshdeliveryclient.EncodeMerge(m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -137,10 +139,10 @@ func mergeValue(t *testing.T, m delivery.Merge) []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMergeActsHandOnlyTheMemberHandedWithEveryApprovedHead(t *testing.T) { // 1342/AC1
|
func TestMergeActsHandOnlyTheMemberHandedWithEveryApprovedHead(t *testing.T) { // 1342/AC1
|
||||||
approved := map[string]string{delivery.ApprovedKey("novox/mesh-sdk", 4): headB, delivery.ApprovedKey("novox/mesh-delivery", 12): headA}
|
approved := map[string]string{meshdeliveryclient.ApprovedKey("novox/mesh-sdk", 4): headB, meshdeliveryclient.ApprovedKey("novox/mesh-delivery", 12): headA}
|
||||||
v := mergeValue(t, delivery.Merge{Group: "g", Ask: "merge.g.0123456789ab", Approved: approved, Pulls: []delivery.MergePull{
|
v := mergeValue(t, seat.MergesByGroupValue{Group: "g", Ask: "merge.g.0123456789ab", Approved: approved, Pulls: []seat.MergesByGroupValuePullsItem{
|
||||||
{Head: delivery.Head{Repository: "novox/mesh-sdk", Number: 4, Head: headB}, State: delivery.MergeMerged, Commit: "c0"},
|
{Repository: "novox/mesh-sdk", Number: 4, Head: headB, State: string(meshdeliveryclient.MergeMerged), Commit: "c0"},
|
||||||
{Head: delivery.Head{Repository: "novox/mesh-delivery", Number: 12, Head: headA}, State: delivery.MergeMerging},
|
{Repository: "novox/mesh-delivery", Number: 12, Head: headA, State: string(meshdeliveryclient.MergeMerging)},
|
||||||
}})
|
}})
|
||||||
acts, err := MergeActs(v)
|
acts, err := MergeActs(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -158,8 +160,8 @@ func TestMergeActsHandOnlyTheMemberHandedWithEveryApprovedHead(t *testing.T) { /
|
|||||||
t.Errorf("act = %+v", acts[0])
|
t.Errorf("act = %+v", acts[0])
|
||||||
}
|
}
|
||||||
// A merge with no approved heads is refused: the forge's tool would refuse it with nothing to say which.
|
// A merge with no approved heads is refused: the forge's tool would refuse it with nothing to say which.
|
||||||
v = mergeValue(t, delivery.Merge{Group: "g", Ask: "a", Pulls: []delivery.MergePull{
|
v = mergeValue(t, seat.MergesByGroupValue{Group: "g", Ask: "a", Pulls: []seat.MergesByGroupValuePullsItem{
|
||||||
{Head: delivery.Head{Repository: "novox/mesh-delivery", Number: 12, Head: headA}, State: delivery.MergeMerging}}})
|
{Repository: "novox/mesh-delivery", Number: 12, Head: headA, State: string(meshdeliveryclient.MergeMerging)}}})
|
||||||
if _, err := MergeActs(v); err == nil {
|
if _, err := MergeActs(v); err == nil {
|
||||||
t.Error("a merge with no approved heads was taken")
|
t.Error("a merge with no approved heads was taken")
|
||||||
}
|
}
|
||||||
@@ -178,7 +180,7 @@ func TestRefusedMessageNamesTheActAndTheForgesWords(t *testing.T) { // 1341/AC3,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
want := forgemsg.Forge{V: delivery.Version, Kind: forgemsg.ForgeRefused, Repository: "novox/mesh-delivery", Number: 12, Head: headA,
|
want := forgemsg.Forge{V: keys.Version, Kind: forgemsg.ForgeRefused, Repository: "novox/mesh-delivery", Number: 12, Head: headA,
|
||||||
Act: "merge", Words: "novox/mesh-delivery#12 is not merged: its head moved"}
|
Act: "merge", Words: "novox/mesh-delivery#12 is not merged: its head moved"}
|
||||||
if !forgemsg.SameForge(f, want) {
|
if !forgemsg.SameForge(f, want) {
|
||||||
t.Errorf("message = %+v", f)
|
t.Errorf("message = %+v", f)
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import (
|
|||||||
|
|
||||||
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-forge-bridge/forgemsg"
|
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-forge-bridge/forgemsg"
|
||||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
"git.novox.be/novox/mesh-sdk/go/keys"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrUnreadable is wrapped by every error that says an event cannot be turned into a message; it is never
|
// ErrUnreadable is wrapped by every error that says an event cannot be turned into a message; it is never
|
||||||
@@ -118,7 +118,7 @@ func branchDeleted(subject string, data []byte) (Message, error) {
|
|||||||
case branch == Trunk:
|
case branch == Trunk:
|
||||||
return Message{}, fmt.Errorf("%w: %s of the trunk %s", ErrNotHandled, subject, Trunk)
|
return Message{}, fmt.Errorf("%w: %s of the trunk %s", ErrNotHandled, subject, Trunk)
|
||||||
}
|
}
|
||||||
body, err := json.Marshal(BranchDeleted{V: delivery.Version, Kind: ForgeBranchDeleted, Repository: d.Owner + "/" + d.Repo, Branch: branch, Head: d.SHA})
|
body, err := json.Marshal(BranchDeleted{V: keys.Version, Kind: ForgeBranchDeleted, Repository: d.Owner + "/" + d.Repo, Branch: branch, Head: d.SHA})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Message{}, unreadable(subject, "%v", err)
|
return Message{}, unreadable(subject, "%v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-forge-bridge/forgemsg"
|
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-forge-bridge/forgemsg"
|
||||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
"git.novox.be/novox/mesh-sdk/go/keys"
|
||||||
)
|
)
|
||||||
|
|
||||||
const head = "0123456789abcdef0123456789abcdef01234567"
|
const head = "0123456789abcdef0123456789abcdef01234567"
|
||||||
@@ -59,7 +59,7 @@ func TestUpdatedBecomesAPullMessage(t *testing.T) { // 1235/AC2 (the conversion;
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
want := forgemsg.Forge{V: delivery.Version, Kind: forgemsg.ForgePull, Repository: "novox/mesh-delivery", Branch: "feat/x", Number: 12,
|
want := forgemsg.Forge{V: keys.Version, Kind: forgemsg.ForgePull, Repository: "novox/mesh-delivery", Branch: "feat/x", Number: 12,
|
||||||
Head: head, Action: forgemsg.PullUpdated, Dirs: []string{"modules/a"}, DirsKnown: true, Manifests: map[string]string{"modules/a": "{}"}}
|
Head: head, Action: forgemsg.PullUpdated, Dirs: []string{"modules/a"}, DirsKnown: true, Manifests: map[string]string{"modules/a": "{}"}}
|
||||||
if !forgemsg.SameForge(f, want) {
|
if !forgemsg.SameForge(f, want) {
|
||||||
t.Errorf("message = %+v, want %+v", f, want)
|
t.Errorf("message = %+v, want %+v", f, want)
|
||||||
@@ -105,7 +105,7 @@ func TestClosedBecomesAClosedPullMessage(t *testing.T) { // 1235/AC4
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
want := forgemsg.Forge{V: delivery.Version, Kind: forgemsg.ForgePull, Repository: "novox/mesh-delivery", Branch: "feat/x", Number: 12,
|
want := forgemsg.Forge{V: keys.Version, Kind: forgemsg.ForgePull, Repository: "novox/mesh-delivery", Branch: "feat/x", Number: 12,
|
||||||
Head: head, Action: forgemsg.PullClosed}
|
Head: head, Action: forgemsg.PullClosed}
|
||||||
if !forgemsg.SameForge(f, want) {
|
if !forgemsg.SameForge(f, want) {
|
||||||
t.Errorf("message = %+v, want %+v", f, want)
|
t.Errorf("message = %+v, want %+v", f, want)
|
||||||
@@ -252,7 +252,7 @@ func TestMergeIntoMainBecomesAMergedMessage(t *testing.T) { // 1236/AC1
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
want := forgemsg.Forge{V: delivery.Version, Kind: forgemsg.ForgeMerged, Repository: "novox/mesh-delivery", Branch: "feat/x", Number: 12,
|
want := forgemsg.Forge{V: keys.Version, Kind: forgemsg.ForgeMerged, Repository: "novox/mesh-delivery", Branch: "feat/x", Number: 12,
|
||||||
Head: head, Merge: merge, Root: "r0", Trees: answer, Manifests: map[string]string{"modules/a": "{}", "modules/b": "{}"}}
|
Head: head, Merge: merge, Root: "r0", Trees: answer, Manifests: map[string]string{"modules/a": "{}", "modules/b": "{}"}}
|
||||||
if !forgemsg.SameForge(f, want) {
|
if !forgemsg.SameForge(f, want) {
|
||||||
t.Errorf("message = %+v, want %+v", f, want)
|
t.Errorf("message = %+v, want %+v", f, want)
|
||||||
@@ -272,7 +272,7 @@ func TestMergeIntoAnotherBranchIsAClosedPull(t *testing.T) { // 1236/AC2
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
want := forgemsg.Forge{V: delivery.Version, Kind: forgemsg.ForgePull, Repository: "novox/mesh-delivery", Branch: "feat/x", Number: 12,
|
want := forgemsg.Forge{V: keys.Version, Kind: forgemsg.ForgePull, Repository: "novox/mesh-delivery", Branch: "feat/x", Number: 12,
|
||||||
Head: head, Action: forgemsg.PullClosed}
|
Head: head, Action: forgemsg.PullClosed}
|
||||||
if msg.Event != "forge.novox.mesh-delivery.pull" || !forgemsg.SameForge(f, want) {
|
if msg.Event != "forge.novox.mesh-delivery.pull" || !forgemsg.SameForge(f, want) {
|
||||||
t.Errorf("event %q message %+v, want a closed pull message", msg.Event, f)
|
t.Errorf("event %q message %+v, want a closed pull message", msg.Event, f)
|
||||||
@@ -304,7 +304,7 @@ func TestNoModuleJSONAnywhereWritesAMergedMessageWithNoTrees(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
f, err := forgemsg.DecodeForge(msg.Data)
|
f, err := forgemsg.DecodeForge(msg.Data)
|
||||||
want := forgemsg.Forge{V: delivery.Version, Kind: forgemsg.ForgeMerged, Repository: "novox/mesh-delivery", Branch: "feat/x", Number: 12,
|
want := forgemsg.Forge{V: keys.Version, Kind: forgemsg.ForgeMerged, Repository: "novox/mesh-delivery", Branch: "feat/x", Number: 12,
|
||||||
Head: head, Merge: merge, Root: "r0"}
|
Head: head, Merge: merge, Root: "r0"}
|
||||||
if err != nil || msg.Event != "forge.novox.mesh-delivery.merged" || !forgemsg.SameForge(f, want) || !strings.Contains(msg.Said, "no module.json") {
|
if err != nil || msg.Event != "forge.novox.mesh-delivery.merged" || !forgemsg.SameForge(f, want) || !strings.Contains(msg.Said, "no module.json") {
|
||||||
t.Errorf("event %q message %+v said %q, %v; want a merged message with no trees", msg.Event, f, msg.Said, err)
|
t.Errorf("event %q message %+v said %q, %v; want a merged message with no trees", msg.Event, f, msg.Said, err)
|
||||||
@@ -330,8 +330,8 @@ func TestOpenPullsAreUpdatedPullMessagesWithUnknownDirs(t *testing.T) { // 1237/
|
|||||||
t.Fatalf("msgs %d skipped %v err %v", len(msgs), skipped, err)
|
t.Fatalf("msgs %d skipped %v err %v", len(msgs), skipped, err)
|
||||||
}
|
}
|
||||||
for i, want := range []forgemsg.Forge{
|
for i, want := range []forgemsg.Forge{
|
||||||
{V: delivery.Version, Kind: forgemsg.ForgePull, Repository: "novox/mesh-delivery", Branch: "feat/x", Number: 12, Head: head, Action: forgemsg.PullUpdated},
|
{V: keys.Version, Kind: forgemsg.ForgePull, Repository: "novox/mesh-delivery", Branch: "feat/x", Number: 12, Head: head, Action: forgemsg.PullUpdated},
|
||||||
{V: delivery.Version, Kind: forgemsg.ForgePull, Repository: "novox/mesh-catalog", Branch: "feat/x", Number: 300, Head: h2, Action: forgemsg.PullUpdated},
|
{V: keys.Version, Kind: forgemsg.ForgePull, Repository: "novox/mesh-catalog", Branch: "feat/x", Number: 300, Head: h2, Action: forgemsg.PullUpdated},
|
||||||
} {
|
} {
|
||||||
f, err := forgemsg.DecodeForge(msgs[i].Data)
|
f, err := forgemsg.DecodeForge(msgs[i].Data)
|
||||||
if err != nil || !forgemsg.SameForge(f, want) {
|
if err != nil || !forgemsg.SameForge(f, want) {
|
||||||
|
|||||||
@@ -16,17 +16,20 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
"git.novox.be/novox/mesh-delivery/contracts/go/clients/meshdeliveryclient"
|
||||||
|
seat "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdelivery"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The bridge's states the tools read and write.
|
// The bridge's states the tools read and write.
|
||||||
const (
|
const (
|
||||||
|
// Module is the forge bridge's own name (its manifest's module, checked by the bridge's names test).
|
||||||
|
Module = "mesh-delivery-forge-bridge"
|
||||||
// PerformedState is the bridge's state of what it performed and published.
|
// PerformedState is the bridge's state of what it performed and published.
|
||||||
PerformedState = "performed"
|
PerformedState = "performed"
|
||||||
// EndAsksState is the state of asks to say a group's end again, keyed by the gate's merges key of the group.
|
// EndAsksState is the state of asks to say a group's end again, keyed by the gate's merges key of the group.
|
||||||
EndAsksState = "end-asks"
|
EndAsksState = "end-asks"
|
||||||
// EndAsksBucket is its bucket on the bus.
|
// EndAsksBucket is its bucket on the bus.
|
||||||
EndAsksBucket = delivery.ForgeBridgeModule + "_" + EndAsksState
|
EndAsksBucket = Module + "_" + EndAsksState
|
||||||
)
|
)
|
||||||
|
|
||||||
// Key is the key of performed that says a group's end is written: `groups.<merges key>`, its value the revision of the
|
// Key is the key of performed that says a group's end is written: `groups.<merges key>`, its value the revision of the
|
||||||
@@ -75,7 +78,7 @@ func NewEndAsk(group, why, by string, at time.Time) (string, []byte, error) {
|
|||||||
if group == "" {
|
if group == "" {
|
||||||
return "", nil, errors.New("name the group whose end is said again (group)")
|
return "", nil, errors.New("name the group whose end is said again (group)")
|
||||||
}
|
}
|
||||||
key, err := delivery.MergesKey(group)
|
key, err := meshdeliveryclient.MergesKey(group)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
@@ -134,7 +137,7 @@ func Rows(merges map[string][]byte, performed map[string]bool, endedAt map[strin
|
|||||||
lastAsk[key] = &a
|
lastAsk[key] = &a
|
||||||
}
|
}
|
||||||
for key, raw := range merges {
|
for key, raw := range merges {
|
||||||
m, err := delivery.DecodeMerge(raw)
|
m, err := meshdeliveryclient.DecodeMerge(raw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
unread = append(unread, key)
|
unread = append(unread, key)
|
||||||
continue
|
continue
|
||||||
@@ -145,9 +148,9 @@ func Rows(merges map[string][]byte, performed map[string]bool, endedAt map[strin
|
|||||||
r := Row{Key: key, Group: m.Group}
|
r := Row{Key: key, Group: m.Group}
|
||||||
allMerged := true
|
allMerged := true
|
||||||
for _, p := range m.Pulls {
|
for _, p := range m.Pulls {
|
||||||
pub := p.Commit != "" && performed[PublishedKey(p.Repository, p.Number, p.Commit)]
|
pub := p.Commit != "" && performed[PublishedKey(p.Repository, int(p.Number), p.Commit)]
|
||||||
r.Members = append(r.Members, RowMember{Repository: p.Repository, Number: p.Number, State: string(p.State), Commit: p.Commit, Published: pub})
|
r.Members = append(r.Members, RowMember{Repository: p.Repository, Number: int(p.Number), State: p.State, Commit: p.Commit, Published: pub})
|
||||||
if p.State != delivery.MergeMerged || !pub {
|
if meshdeliveryclient.MergeState(p.State) != meshdeliveryclient.MergeMerged || !pub {
|
||||||
allMerged = false
|
allMerged = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -175,8 +178,8 @@ func Rows(merges map[string][]byte, performed map[string]bool, endedAt map[strin
|
|||||||
func sortRows(rows []Row) { sort.Slice(rows, func(i, j int) bool { return rows[i].Key < rows[j].Key }) }
|
func sortRows(rows []Row) { sort.Slice(rows, func(i, j int) bool { return rows[i].Key < rows[j].Key }) }
|
||||||
|
|
||||||
// MembersOf are a merge's approved members with their heads, in the order of their keys.
|
// MembersOf are a merge's approved members with their heads, in the order of their keys.
|
||||||
func MembersOf(m delivery.Merge) []delivery.Head {
|
func MembersOf(m seat.MergesByGroupValue) []meshdeliveryclient.Head {
|
||||||
var out []delivery.Head
|
var out []meshdeliveryclient.Head
|
||||||
for _, k := range slices.Sorted(func(yield func(string) bool) {
|
for _, k := range slices.Sorted(func(yield func(string) bool) {
|
||||||
for k := range m.Approved {
|
for k := range m.Approved {
|
||||||
if !yield(k) {
|
if !yield(k) {
|
||||||
@@ -192,7 +195,7 @@ func MembersOf(m delivery.Merge) []delivery.Head {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
out = append(out, delivery.Head{Repository: k[:at], Number: n, Head: m.Approved[k]})
|
out = append(out, meshdeliveryclient.Head{Repository: k[:at], Number: n, Head: m.Approved[k]})
|
||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user