202 lines
5.3 KiB
Go
202 lines
5.3 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
|
|
sdk "git.novox.be/novox/mesh-sdk/go/delivery"
|
|
)
|
|
|
|
// fakeMerges is the gate's bucket merges with the forge bridge behind it. Every pull request the gate writes in the state
|
|
// merging is merged on the fake forge, as the bridge's merge does (mesh-delivery modules/mesh-delivery-forge-bridge
|
|
// internal/bridge acts.go MergeActs): the forge's merge tool is asked at the head with the approved heads of the whole
|
|
// group, `<repository>@<full sha>` sorted, and what the fake forge answers is written on FORGE as the bridge writes it, a
|
|
// merged message or a refused one with the forge's words. With manual set the bridge says nothing.
|
|
type fakeMerges struct {
|
|
mu sync.Mutex
|
|
forge *fakeForge
|
|
h *Holder
|
|
values map[string]sdk.Merge
|
|
puts []sdk.Merge
|
|
manual bool
|
|
failed error
|
|
// said are the messages the bridge wrote on FORGE, in order.
|
|
said []sdk.Forge
|
|
}
|
|
|
|
func newFakeMerges(f *fakeForge) *fakeMerges {
|
|
return &fakeMerges{forge: f, values: map[string]sdk.Merge{}}
|
|
}
|
|
|
|
func (m *fakeMerges) Get(group string) (*sdk.Merge, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
if v, ok := m.values[group]; ok {
|
|
return &v, nil
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func (m *fakeMerges) Put(group string, v sdk.Merge) error {
|
|
if _, err := sdk.EncodeMerge(v); err != nil {
|
|
return err
|
|
}
|
|
m.mu.Lock()
|
|
if m.failed != nil {
|
|
defer m.mu.Unlock()
|
|
return m.failed
|
|
}
|
|
m.values[group] = v
|
|
m.puts = append(m.puts, v)
|
|
manual := m.manual
|
|
m.mu.Unlock()
|
|
if manual {
|
|
return nil
|
|
}
|
|
for _, p := range v.Pulls {
|
|
if p.State != sdk.MergeMerging {
|
|
continue
|
|
}
|
|
owner, repo, _ := strings.Cut(p.Repository, "/")
|
|
var approved []string
|
|
for key, sha := range v.Approved {
|
|
repository, _, _ := strings.Cut(key, "@")
|
|
approved = append(approved, repository+"@"+sha)
|
|
}
|
|
sortStrings(approved)
|
|
f := m.forge
|
|
f.mu.Lock()
|
|
f.merges = append(f.merges, owner+"/"+repo+"#"+itoa(p.Number)+"@"+p.Head.Head)
|
|
f.approvedHeads = append(f.approvedHeads, approved)
|
|
err := f.refuseRepo[owner+"/"+repo]
|
|
if err == nil {
|
|
err = f.refuse
|
|
}
|
|
f.mu.Unlock()
|
|
msg := sdk.Forge{Repository: p.Repository, Number: p.Number, Head: p.Head.Head}
|
|
if err != nil {
|
|
msg.Kind, msg.Act, msg.Words = sdk.ForgeRefused, "merge", err.Error()
|
|
} else {
|
|
msg.Kind, msg.Merge, msg.Root = sdk.ForgeMerged, strings.Repeat("c", 40), "root"
|
|
msg.Trees = map[string]string{"app": "tree"}
|
|
}
|
|
m.say(msg)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// say writes a message on FORGE and has the gate hear it, as the runtime does.
|
|
func (m *fakeMerges) say(msg sdk.Forge) {
|
|
body, err := sdk.EncodeForge(msg)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
event, err := sdk.ForgeEvent(msg.Repository, msg.Kind)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
m.mu.Lock()
|
|
m.said = append(m.said, msg)
|
|
m.mu.Unlock()
|
|
m.h.Forged("mesh-delivery-forge-bridge."+event, body)
|
|
}
|
|
|
|
// last is the newest value written for a group.
|
|
func (m *fakeMerges) last(group string) sdk.Merge {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
return m.values[group]
|
|
}
|
|
|
|
// fakeSource is the gate's bucket source with its one key.
|
|
type fakeSource struct {
|
|
mu sync.Mutex
|
|
main *sdk.Source
|
|
moves []sdk.Source
|
|
}
|
|
|
|
func (s *fakeSource) Move(update func(prev *sdk.Source) (sdk.Source, bool)) error {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
next, write := update(s.main)
|
|
if !write {
|
|
return nil
|
|
}
|
|
if _, err := sdk.EncodeSource(next); err != nil {
|
|
return err
|
|
}
|
|
s.main = &next
|
|
s.moves = append(s.moves, next)
|
|
return nil
|
|
}
|
|
|
|
func sortStrings(s []string) { sort.Strings(s) }
|
|
func itoa(n int) string { return strconv.Itoa(n) }
|
|
|
|
// fakeOwed is the gate's bucket owed with the forge bridge behind it: every act written is performed on the fake forge, as the
|
|
// bridge performs it (mesh-delivery modules/mesh-delivery-forge-bridge internal/bridge acts.go OwedAct), once for each
|
|
// revision. With down set the bucket cannot be written (the bus is away); the forge's own refusals are the bridge's to say,
|
|
// not the gate's to retry.
|
|
type fakeOwed struct {
|
|
mu sync.Mutex
|
|
forge *fakeForge
|
|
down bool
|
|
// keys are the latest value of each key written, and writes every revision in order.
|
|
keys map[string]sdk.Owed
|
|
writes []string
|
|
}
|
|
|
|
func newFakeOwed(f *fakeForge) *fakeOwed { return &fakeOwed{forge: f, keys: map[string]sdk.Owed{}} }
|
|
|
|
func (o *fakeOwed) Put(key string, v sdk.Owed) error {
|
|
if _, err := sdk.EncodeOwed(v); err != nil {
|
|
return err
|
|
}
|
|
repository, number, head, act, err := sdk.ParseOwedKey(key)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
o.mu.Lock()
|
|
if o.down {
|
|
defer o.mu.Unlock()
|
|
return errors.New("the bus is away")
|
|
}
|
|
if kept, ok := o.keys[key]; ok && sdk.SameOwed(kept, v) {
|
|
o.mu.Unlock()
|
|
return nil
|
|
}
|
|
o.keys[key] = v
|
|
o.writes = append(o.writes, key)
|
|
o.mu.Unlock()
|
|
owner, repo, _ := strings.Cut(repository, "/")
|
|
f := o.forge
|
|
switch v.Act {
|
|
case sdk.OwedNote:
|
|
_ = f.Note(owner, repo, head, v.Text)
|
|
case sdk.OwedView:
|
|
_ = f.View(owner, repo, number, v.Text)
|
|
case sdk.OwedStatus:
|
|
_ = f.Status(owner, repo, head, v.Context, v.State, v.Text, v.Target)
|
|
case sdk.OwedComment:
|
|
_ = f.Comment(owner, repo, number, v.Text)
|
|
}
|
|
_ = act
|
|
return nil
|
|
}
|
|
|
|
// acts are the keys written whose act is of a kind, in order.
|
|
func (o *fakeOwed) acts(kind string) []string {
|
|
o.mu.Lock()
|
|
defer o.mu.Unlock()
|
|
var out []string
|
|
for _, k := range o.writes {
|
|
if _, _, _, act, err := sdk.ParseOwedKey(k); err == nil && actKind(act) == kind {
|
|
out = append(out, k)
|
|
}
|
|
}
|
|
return out
|
|
}
|