The delivery repository's contracts and SDK live under modules/ as mesh-delivery-contracts and mesh-delivery-sdk, their Go module paths renamed to match the folders (the TypeScript names already did), and every importer in mesh-delivery, mesh-catalog, mesh-host, mesh-tools and mesh-sdk follows in the same group: a design as it should be, one path per module, checked by the group's check building every importer. Builds: design 47 G §427, §88 Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
148 lines
6.2 KiB
Go
148 lines
6.2 KiB
Go
package bridge
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
|
|
git "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/seats/git"
|
|
seat "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/seats/meshdelivery"
|
|
checker "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/seats/meshdeliverychecker"
|
|
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-forge-bridge/forgemsg"
|
|
)
|
|
|
|
// OwedCall is one act owed to the forge as the seat git's verb that performs it: the seat that said it (Of), the act's
|
|
// id, and the verb with its input (novox/hq design 47 *What the gate owes the forge*). Every verb is safe to repeat at
|
|
// the forge: a status and the view replace what stands, a merge of a pull request merged at that head is answered
|
|
// performed, and a comment and a note line carry the id, which the seat's holder skips when it already holds it.
|
|
type OwedCall struct {
|
|
Of string
|
|
ID string
|
|
Act string
|
|
Repository string
|
|
Number int
|
|
Verb string
|
|
Input any
|
|
}
|
|
|
|
// ErrUndecodable is an owed event the bridge cannot read: it is said as owed.ended `undecodable`, never only logged.
|
|
var ErrUndecodable = errors.New("an owed act the bridge cannot read")
|
|
|
|
func undecodable(why string, args ...any) error {
|
|
return fmt.Errorf("%w: %s", ErrUndecodable, fmt.Sprintf(why, args...))
|
|
}
|
|
|
|
// GateOwed reads the seat mesh-delivery's event owed into the seat git's verb that performs it.
|
|
func GateOwed(data []byte) (OwedCall, error) {
|
|
var o seat.OwedEvent
|
|
if err := json.Unmarshal(data, &o); err != nil {
|
|
return OwedCall{}, undecodable("the gate's owed is not json: %v", err)
|
|
}
|
|
c := OwedCall{Of: seat.Seat, ID: o.ID, Act: o.Act, Repository: o.Repository, Number: int(o.Number)}
|
|
if err := c.common(o.Head); err != nil {
|
|
return c, err
|
|
}
|
|
commit := o.Commit
|
|
if commit == "" {
|
|
commit = o.Head
|
|
}
|
|
needsPull := func() error {
|
|
if o.Number <= 0 {
|
|
return undecodable("the owed %s %s names no pull request", o.Act, o.ID)
|
|
}
|
|
return nil
|
|
}
|
|
switch o.Act {
|
|
case seat.OwedActMerge:
|
|
// Only a switched act is a merge (§169): from G's delivery the gate owes none (§167), a session calls merge-pull
|
|
// itself; a merge of the old state, not yet performed by the old bridge, is not dropped.
|
|
if err := needsPull(); err != nil {
|
|
return c, err
|
|
}
|
|
c.Verb, c.Input = git.VerbMergePull, git.MergePullInput{ID: o.ID, Repository: o.Repository, Number: o.Number, Head: o.Head}
|
|
case seat.OwedActStatus:
|
|
if o.Context == "" || o.State == "" {
|
|
return c, undecodable("the owed status %s names no context or state", o.ID)
|
|
}
|
|
c.Verb, c.Input = git.VerbStatus, git.StatusInput{ID: o.ID, Repository: o.Repository, Commit: commit, Context: o.Context,
|
|
State: o.State, Description: o.Description, Target: o.Target}
|
|
case seat.OwedActView:
|
|
if err := needsPull(); err != nil {
|
|
return c, err
|
|
}
|
|
c.Verb, c.Input = git.VerbView, git.ViewInput{ID: o.ID, Repository: o.Repository, Number: o.Number, Text: o.Text}
|
|
case seat.OwedActComment:
|
|
if err := needsPull(); err != nil {
|
|
return c, err
|
|
}
|
|
c.Verb, c.Input = git.VerbComment, git.CommentInput{ID: o.ID, Repository: o.Repository, Number: o.Number, Text: o.Text}
|
|
case seat.OwedActNote:
|
|
if o.Line == "" {
|
|
return c, undecodable("the owed note line %s says no line", o.ID)
|
|
}
|
|
c.Verb, c.Input = git.VerbNote, git.NoteInput{ID: o.ID, Repository: o.Repository, Commit: commit, Line: o.Line}
|
|
default:
|
|
return c, undecodable("the owed %s is the act %q, none the seat git performs", o.ID, o.Act)
|
|
}
|
|
return c, nil
|
|
}
|
|
|
|
// CheckerOwed reads the seat mesh-delivery-checker's event owed (its mesh/check status) into the seat git's verb status.
|
|
func CheckerOwed(data []byte) (OwedCall, error) {
|
|
var o checker.OwedEvent
|
|
if err := json.Unmarshal(data, &o); err != nil {
|
|
return OwedCall{}, undecodable("the checker's owed is not json: %v", err)
|
|
}
|
|
c := OwedCall{Of: checker.Seat, ID: o.ID, Act: o.Act, Repository: o.Repository, Number: int(o.Number)}
|
|
if err := c.common(o.Head); err != nil {
|
|
return c, err
|
|
}
|
|
if o.Act != checker.OwedActStatus || o.Context == "" || o.State == "" {
|
|
return c, undecodable("the checker's owed %s is %q of the context %q in the state %q, not a status", o.ID, o.Act, o.Context, o.State)
|
|
}
|
|
c.Verb, c.Input = git.VerbStatus, git.StatusInput{ID: o.ID, Repository: o.Repository, Commit: o.Head, Context: o.Context,
|
|
State: o.State, Description: o.Description, Target: o.Target}
|
|
return c, nil
|
|
}
|
|
|
|
func (c OwedCall) common(head string) error {
|
|
switch {
|
|
case c.ID == "":
|
|
return undecodable("an owed %s of %s names no id", c.Act, c.Repository)
|
|
case c.Repository == "":
|
|
return undecodable("the owed %s names no repository", c.ID)
|
|
case !sha40.MatchString(head):
|
|
return undecodable("the owed %s names the head %q, not 40 hex digits", c.ID, head)
|
|
}
|
|
if _, _, err := splitRepository(c.Repository); err != nil {
|
|
return undecodable("the owed %s: %v", c.ID, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Outcome is what an act's call of the seat git came to, as owed.ended says it: performed when the forge holds the act
|
|
// (done now, or its id already held); refused with the forge's own words when the seat answers a refusal or an error
|
|
// of the kind refused; could not be performed with the error when the seat did not answer, answered an error of the
|
|
// kind failed, or answered what the contract cannot read.
|
|
func Outcome(raw json.RawMessage, callErr error) (outcome, words string) {
|
|
if callErr != nil {
|
|
return forgemsg.OwedCouldNotBePerformed, callErr.Error()
|
|
}
|
|
var a git.MergePullAnswer // every act verb's answer is this one's fields or fewer
|
|
if err := json.Unmarshal(raw, &a); err != nil {
|
|
return forgemsg.OwedCouldNotBePerformed, "the seat git's answer is not its contract's: " + err.Error()
|
|
}
|
|
switch {
|
|
case a.Refused != "":
|
|
return forgemsg.OwedRefused, a.Refused
|
|
case a.Error != "" && a.ErrorKind == git.ErrorKindRefused:
|
|
return forgemsg.OwedRefused, a.Error
|
|
case a.Error != "":
|
|
// failed, or a kind the contract does not name: the forge did not do it, and did not say no
|
|
return forgemsg.OwedCouldNotBePerformed, a.Error
|
|
case a.Performed || a.Held:
|
|
return forgemsg.OwedPerformed, ""
|
|
}
|
|
return forgemsg.OwedCouldNotBePerformed, "the seat git's answer says neither performed, held, refused nor an error: " + string(raw)
|
|
}
|