Files
mesh-catalog/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/owed.go
T

118 lines
4.2 KiB
Go

package main
import (
"encoding/json"
"errors"
"fmt"
"strconv"
"strings"
stdio "git.novox.be/novox/mesh-sdk/go"
sdk "git.novox.be/novox/mesh-sdk/go/delivery"
)
// The gate writes nothing to the forge: what a transition owes it — a status, the view on the pull request, a line of the
// commit's note, a comment — is one key of the gate's bucket owed, `<repository>.<number>.<head>.<act>` (mesh-sdk go/delivery
// OwedKey), which the forge bridge performs once for each revision (novox/hq ADR 0437 §8, ADR 0450 §4). The number is 0 for
// a commit no pull request names; a comment's act carries a sequence number, so two comments on one head are two acts.
// What the gate keeps is the effect owed until its key is written (effects.go); the forge's own refusal is the bridge's
// to say on FORGE, and the gate has no retry for it.
// noPage is the target of a status whose delivery has no page to point at.
const noPage = "none"
// notesRef is the notes ref the commit's note is kept under.
const notesRef = "mesh-plan"
// OwedStore is the gate's bucket owed.
type OwedStore interface {
// Put writes the act under its key, unless that key holds the same act already (a revision is an act to perform).
Put(key string, o sdk.Owed) error
}
// kvOwed is owed over the runtime.
type kvOwed struct{}
func (kvOwed) Put(key string, o sdk.Owed) error {
raw, err := sdk.EncodeOwed(o)
if err != nil {
return err
}
if e, err := stdio.State("owed").Get(key); err == nil && e != nil {
if kept, err := sdk.DecodeOwed(e.Value); err == nil && sdk.SameOwed(kept, o) {
return nil
}
}
_, err = stdio.State("owed").Put(key, json.RawMessage(raw))
return err
}
// owe writes one owed act as its key. An act that has no key (a name the key refuses, a status with no page to point at) is
// said and dropped, since writing it again would fail the same.
func (h *Holder) owe(repository string, number int, head, act string, o sdk.Owed) error {
if h.Owed == nil {
return errors.New("no owed bucket")
}
key, err := sdk.OwedKey(repository, number, head, act)
if err == nil {
o.Act = actKind(act)
_, err = sdk.EncodeOwed(o)
}
if err != nil {
h.Refuse(fmt.Errorf("the act %q on %s#%d at %s cannot be owed and is dropped: %v", act, repository, number, shortOf(head, 12), err))
return nil
}
return h.Owed.Put(key, o)
}
// actKind is the kind of an owed act: `status=<context>` is a status, `comment.<seq>` a comment.
func actKind(act string) string {
kind, _, _ := sdk.ParseOwedAct(act)
return kind
}
// statusAct is the act of a status of a context.
func statusAct(context string) string { return sdk.OwedStatus + "=" + context }
// commentAct is the act of the seq-th comment on a head.
func commentAct(seq int) string {
act, _ := sdk.OwedCommentAct(seq)
return act
}
// owedOf is an effect as the act its key names and its value: the repository and number are the delivery's (or, for a
// group's status, the member's the effect names in Last), the body the view's.
func owedOf(e Effect, repository string, number int, head, body, page string) (act string, value sdk.Owed, num int, hd string) {
num, hd = number, head
switch e.Kind {
case EffectNote:
hd = e.Commit
if e.Commit != head {
num = 0 // the merge commit is named by no pull request
}
return sdk.OwedNote, sdk.Owed{Text: e.Line, Ref: notesRef}, num, hd
case EffectView:
return sdk.OwedView, sdk.Owed{Text: body}, num, hd
case EffectStatus:
// The owed status must name where it points; a delivery with no pull request page points at none, which the forge's
// tool leaves out (it links only an http or https address).
if page == "" {
page = noPage
}
return statusAct(e.Context), sdk.Owed{Context: e.Context, State: e.State, Text: e.Line, Target: page}, num, e.Commit
case EffectComment:
seq, _ := strconv.Atoi(e.Context)
return commentAct(seq), sdk.Owed{Text: e.Line}, num, hd
}
return "", sdk.Owed{}, 0, ""
}
// memberOfLast reads the repository, pull request number and page a group's status effect names in Last
// (`owner/repo#N page`).
func memberOfLast(last string) (repository string, number int, page string) {
where, page, _ := strings.Cut(last, " ")
repository, n, _ := strings.Cut(where, "#")
number, _ = strconv.Atoi(n)
return repository, number, page
}