Files
mesh-delivery/contracts/go/clients/meshdeliverycheckerclient/client.go
T
jschoubben d5d9f0c258 The domain SDK carries the run ask's rules and the checker's client asks a program's input (issue 1921, milestone 47/G D2)
B0's mesh-sdk go/delivery run lines move to their registry homes: the
run job, the one-job rule, the Go check stream alone, the input's bound,
digest and key in sdk/buildstep; AskWhat's run case in sdk/buildrun; the
ask of the verb input and the check of a verdict's judged-by in
meshdeliverycheckerclient. B0's tests come in as rows of the existing
tables.

Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
2026-11-07 02:35:31 +01:00

342 lines
13 KiB
Go

// Package meshdeliverycheckerclient holds the logic the holders and callers of the seat mesh-delivery-checker share over its
// contract (novox/hq ADR 0625 §4, ADR 0634 §2): the rules a verdict and a kept verdict must keep to be written or read. The
// checker writes them, and the view, the gate and the indexer read them; the contract's decoding is strict by schema only, so
// these rules are written once here and no caller copies them.
package meshdeliverycheckerclient
import (
"fmt"
pathpkg "path"
"strings"
checker "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdeliverychecker"
"git.novox.be/novox/mesh-sdk/go/keys"
)
// The states of a verdict, the levels of a check and the compositions' states.
const (
VerdictRunning = "running"
VerdictPass = "pass"
VerdictFail = "fail"
LevelUnit = "unit"
LevelIntegration = "integration"
CompositionNotRun = "not-run"
CompositionPass = "pass"
CompositionFail = "fail"
)
// MaxFailures is the most failures a verdict names; it names the count of the rest beside them.
const MaxFailures = 50
func checkLevel(level string) error {
if level != LevelUnit && level != LevelIntegration {
return fmt.Errorf("meshdeliverycheckerclient: the level %q is not %s or %s", level, LevelUnit, LevelIntegration)
}
return nil
}
func isHex12(s string) bool { return len(s) == 12 && strings.Trim(s, "0123456789abcdef") == "" }
// CheckRepository refuses a repository that is not <owner>/<name> with exactly one slash, each part a key part.
func CheckRepository(repository string) error {
owner, name, ok := strings.Cut(repository, "/")
if !ok || strings.Contains(name, "/") {
return fmt.Errorf("meshdeliverycheckerclient: the repository %q is not <owner>/<name>, with exactly one slash", repository)
}
if err := keys.CheckName("repository owner", owner); err != nil {
return fmt.Errorf("meshdeliverycheckerclient: the repository %q: %w", repository, err)
}
if err := keys.CheckName("repository name", name); err != nil {
return fmt.Errorf("meshdeliverycheckerclient: the repository %q: %w", repository, err)
}
return nil
}
func checkComposition(c *checker.VerdictsByGroupHeadsValueComposition) error {
if c == nil {
return nil
}
switch c.State {
case CompositionNotRun:
if c.Sdk != "" || len(c.Machines) > 0 {
return fmt.Errorf("meshdeliverycheckerclient: the verdict's composition is not run and names no mesh-sdk commit or machines")
}
case CompositionPass:
if c.Sdk == "" {
return fmt.Errorf("meshdeliverycheckerclient: the verdict's composition passed and names no mesh-sdk commit")
}
case CompositionFail:
if c.Words == "" {
return fmt.Errorf("meshdeliverycheckerclient: the verdict's composition failed and gives no words")
}
default:
return fmt.Errorf("meshdeliverycheckerclient: the verdict's composition state %q is not %s, %s or %s", c.State, CompositionNotRun, CompositionPass, CompositionFail)
}
return nil
}
// checkJudgedBy refuses a checking program named at no repository, full commit and package, kept under no program key,
// or over no head or a head at no full commit (novox/hq ADR 0644, ADR 0656; issue 1921).
func checkJudgedBy(j *checker.VerdictsByGroupHeadsValueJudgedBy) error {
const who = "meshdeliverycheckerclient: "
if j == nil {
return nil
}
if CheckRepository(j.Program.Repository) != nil || !isCommit(j.Program.Commit) || j.Program.Package == "" {
return fmt.Errorf(who+"the verdict is judged by a checking program built at no repository, full commit and package (%+v)", j.Program)
}
if !strings.HasPrefix(j.Key, "program-") {
return fmt.Errorf(who+"the verdict's checking program is kept under %q, which is no program key", j.Key)
}
if len(j.Heads) == 0 {
return fmt.Errorf(who + "the verdict's checking program read no head")
}
for _, h := range j.Heads {
if CheckRepository(h.Repository) != nil || !isCommit(h.Commit) {
return fmt.Errorf(who+"the verdict's checking program read %s at %q, which is no full commit", h.Repository, h.Commit)
}
}
return nil
}
func isCommit(s string) bool { return len(s) == 40 && strings.Trim(s, "0123456789abcdef") == "" }
// CheckVerdict refuses a verdict a reader could not place: no group, heads that are not 12 lowercase hex digits, a repository
// both merged and read beside, a carried-from that carries nothing, a series change from a series to itself or named twice, a
// level that is neither unit nor integration or counts a negative number, a time below zero, a pass that says nothing of a
// level, a fail that names neither a test nor a machine, and failures on a verdict that did not fail.
func CheckVerdict(v checker.VerdictsByGroupHeadsValue) error {
const who = "meshdeliverycheckerclient: "
if v.Group == "" {
return fmt.Errorf(who + "a verdict names no group")
}
if !isHex12(v.Heads) {
return fmt.Errorf(who+"the verdict's heads hash %q is not 12 lowercase hex digits", v.Heads)
}
for repository, head := range v.Trunk {
if head == "" {
return fmt.Errorf(who+"the verdict's trunk head of %s is empty", repository)
}
}
for repository, commit := range v.Beside {
if commit == "" {
return fmt.Errorf(who+"the verdict's sibling commit of %s is empty", repository)
}
if _, ok := v.Trunk[repository]; ok {
return fmt.Errorf(who+"the verdict names %s as a trunk it merged and as a sibling it read", repository)
}
}
if len(v.CarriedFrom) > 0 {
if v.State == VerdictRunning {
return fmt.Errorf(who + "a running verdict is carried from nothing: carried-from is written with its end")
}
moved := false
for repository, commit := range v.CarriedFrom {
now, inTrunk := v.Trunk[repository]
if !inTrunk {
now = v.Beside[repository]
}
if commit == "" || now == "" {
return fmt.Errorf(who+"the verdict's carried-from names %s, which it judged on no commit of", repository)
}
moved = moved || commit != now
}
if !moved {
return fmt.Errorf(who + "the verdict's carried-from names the commits it is judged on: nothing moved, so nothing was carried")
}
}
changed := map[string]bool{}
for _, c := range v.Series {
if c.Module == "" || c.Artifact == "" || c.From == "" || c.To == "" {
return fmt.Errorf(who+"the verdict's series change %+v names no module, artifact, from or to", c)
}
if c.From == c.To {
return fmt.Errorf(who+"the verdict's series change of %s/%s is from %s to the same series", c.Module, c.Artifact, c.From)
}
if changed[c.Module+"/"+c.Artifact] {
return fmt.Errorf(who+"the verdict names the series change of %s/%s twice", c.Module, c.Artifact)
}
changed[c.Module+"/"+c.Artifact] = true
}
for level, r := range v.Levels {
if err := checkLevel(level); err != nil {
return err
}
if r.Run < 0 || r.Kept < 0 || r.Skipped < 0 {
return fmt.Errorf(who+"the verdict's %s level counts a negative number of tests", level)
}
}
if err := checkComposition(v.Composition); err != nil {
return err
}
if err := checkJudgedBy(v.JudgedBy); err != nil {
return err
}
seen := map[string]bool{}
for _, level := range v.OverBound {
if err := checkLevel(level); err != nil {
return fmt.Errorf(who+"the verdict's over-bound names: %w", err)
}
if seen[level] {
return fmt.Errorf(who+"the verdict's over-bound names the level %s twice", level)
}
seen[level] = true
}
if t := v.Took; t != nil {
for part, ms := range map[string]int64{"queue": t.Queue, "clone": t.Clone, "unit": t.Unit, "lint": t.Lint,
"integration-setup": t.IntegrationSetup, "integration-run": t.IntegrationRun, "composition": t.Composition} {
if ms < 0 {
return fmt.Errorf(who+"the verdict's time of the part %s is negative", part)
}
}
}
named := v.Failed != nil && (v.Failed.Test != "" || v.Failed.Machine != "")
if len(v.Failures) > 0 && v.State != VerdictFail {
return fmt.Errorf(who+"a %s verdict names no failures", v.State)
}
if len(v.Failures) > MaxFailures+1 {
return fmt.Errorf(who+"the verdict names %d failures, more than %d and the count of the rest", len(v.Failures), MaxFailures)
}
switch v.State {
case VerdictRunning:
if v.Failed != nil && *v.Failed != (checker.VerdictsByGroupHeadsValueFailed{}) {
return fmt.Errorf(who + "a running verdict names no failure")
}
case VerdictPass:
if v.Failed != nil && *v.Failed != (checker.VerdictsByGroupHeadsValueFailed{}) {
return fmt.Errorf(who + "a pass names no failure")
}
for _, level := range []string{LevelUnit, LevelIntegration} {
if _, ok := v.Levels[level]; !ok {
return fmt.Errorf(who+"a pass says nothing of the %s level", level)
}
}
case VerdictFail:
if !named {
return fmt.Errorf(who + "a fail names neither a test nor a machine")
}
default:
return fmt.Errorf(who+"the verdict state %q is not running, pass or fail", v.State)
}
return nil
}
// CheckKept refuses a kept verdict that is not a pass of a repository's package at a level, with its toolchain and its time:
// a failure is never kept.
func CheckKept(k checker.KeptByLevelDigestValue) error {
const who = "meshdeliverycheckerclient: "
if k.State != VerdictPass {
return fmt.Errorf(who+"a kept verdict is a pass, never %q: a failure is never kept", k.State)
}
if err := CheckRepository(k.Repository); err != nil {
return err
}
if k.Package == "" {
return fmt.Errorf(who + "a kept verdict names no package")
}
if err := checkLevel(k.Level); err != nil {
return err
}
if k.Tests < 0 || k.Took < 0 {
return fmt.Errorf(who + "a kept verdict counts a negative number of tests or a negative time")
}
if k.Toolchain == "" {
return fmt.Errorf(who + "a kept verdict names no toolchain version")
}
if k.At == "" {
return fmt.Errorf(who + "a kept verdict says no time")
}
return nil
}
// CheckPreview refuses a preview a reader could not place (the checker writes it, the indexer reads it for the candidates'
// fingerprints): a repository, head or trunk commit that does not key, a head in conflict that names no file or has a merge
// commit or modules, a head that merged and names no merge commit, and a module with no directory, no tree, a manifest
// digest that is not 64 lowercase hex digits, a lock without path or hash, or a named path that is not clean and inside the
// module's directory or names no git object.
func CheckPreview(p checker.PreviewsByRepositoryHeadTrunkValue) error {
const who = "meshdeliverycheckerclient: "
if err := CheckRepository(p.Repository); err != nil {
return err
}
if err := keys.CheckName("head", p.Head); err != nil {
return fmt.Errorf(who+"the preview: %w", err)
}
if err := keys.CheckName("trunk commit", p.Trunk); err != nil {
return fmt.Errorf(who+"the preview: %w", err)
}
for _, f := range p.Conflicts {
if f == "" {
return fmt.Errorf(who + "a preview names a conflicting file with an empty path")
}
}
if len(p.Conflicts) > 0 {
if p.Merge != "" || len(p.Modules) > 0 {
return fmt.Errorf(who + "a preview of a head in conflict has no merge commit and no modules")
}
return nil
}
if p.Merge == "" {
return fmt.Errorf(who + "a preview names no merge commit")
}
for dir, m := range p.Modules {
if dir == "" {
return fmt.Errorf(who + "a preview names a module with an empty directory")
}
if m.Tree == "" {
return fmt.Errorf(who+"the preview's module %q has no tree", dir)
}
if !keys.IsHex64(m.Manifest) {
return fmt.Errorf(who+"the preview's module %q: %q is not a manifest digest, 64 lowercase hex digits", dir, m.Manifest)
}
for path, hash := range m.Locks {
if path == "" || hash == "" {
return fmt.Errorf(who+"the lock %q of the preview's module %q has an empty path or hash", path, dir)
}
}
for p, object := range m.Paths {
if p == "" || p != pathpkg.Clean(p) || strings.HasPrefix(p, "/") || p == ".." || strings.HasPrefix(p, "../") {
return fmt.Errorf(who+"the preview's module %q: the named path %q is not a clean path inside the module's directory", dir, p)
}
if object == "" {
return fmt.Errorf(who+"the preview's module %q: the named path %q names no git object", dir, p)
}
}
}
return nil
}
// VerdictKey is the key, in the state verdicts, of a group's check at its heads: `<escaped group>.<heads hash>`, the
// group escaped so a branch such as `fix/1.2` keys, the heads hash as the mesh-delivery client's HeadsHash gives it
// (12 lowercase hex digits).
func VerdictKey(group, headsHash string) (string, error) {
if group == "" {
return "", fmt.Errorf("meshdeliverycheckerclient: the group name is empty")
}
if !isHex12(headsHash) {
return "", fmt.Errorf("meshdeliverycheckerclient: %q is not a heads hash, 12 lowercase hex digits", headsHash)
}
return keys.EscapeName(group) + "." + headsHash, nil
}
// ParseVerdictKey is the inverse of VerdictKey.
func ParseVerdictKey(key string) (group, headsHash string, err error) {
i := strings.LastIndex(key, ".")
if i < 0 {
return "", "", fmt.Errorf("meshdeliverycheckerclient: %q is not a verdict key, <group>.<heads hash>", key)
}
part, headsHash := key[:i], key[i+1:]
if !isHex12(headsHash) {
return "", "", fmt.Errorf("meshdeliverycheckerclient: %q is not a verdict key: %q is not a heads hash, 12 lowercase hex digits", key, headsHash)
}
if part == "" || strings.Contains(part, ".") {
return "", "", fmt.Errorf("meshdeliverycheckerclient: %q is not a verdict key: the group part is empty or holds a dot", key)
}
if group, err = keys.UnescapeName(part); err != nil {
return "", "", fmt.Errorf("meshdeliverycheckerclient: %q is not a verdict key: %w", key, err)
}
return group, headsHash, nil
}