Files
mesh-delivery/modules/mesh-delivery-builder/buildrun/buildseats.go
T
jschoubben 71a35a6376 MV1: move contracts and sdk under modules/, importers follow
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
2026-11-12 05:09:56 +01:00

167 lines
5.6 KiB
Go

package buildrun
import (
"fmt"
"maps"
"slices"
"strings"
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-sdk/buildstep"
"git.novox.be/novox/mesh-sdk/go/seatdef"
)
// **The builder names no build module** (design 47 G §7, §428, §704; A-82): a build module is the holder of the build seat
// whose acting rule `build` has an `on` matching an artifact's language or `kind:image`, found on the seat list (the seat
// definitions) and the catalog half (the manifest that claims the seat), as the indexer's seed finds it. Every task's module and
// every lane's key is that holder's name as the seat list and the catalog half give it.
// BuildModule is one build seat and the module that holds it.
type BuildModule struct {
Seat string // the seat whose acting rule builds
Module string // the module holding the seat, empty while the catalog half names none
// Toolchain is the language of the toolchain the rule's `on` names, empty for a rule on `kind:image` (no toolchain).
Toolchain string
// ByKind is whether the rule is on `kind:image`: its artifacts have no language and its nodes come from the node-engine's have.
ByKind bool
}
// AskLanguage is the language of the seat's asks (sdkstep.AskSubject): the seat is named node-build-<language>.
func (m BuildModule) AskLanguage() string { return strings.TrimPrefix(m.Seat, "node-build-") }
// BuildSeats finds the build modules from the seat definitions and the holders of seats, both read at each call.
type BuildSeats struct {
// Defs are the seat definitions of the seat list; Holder is a seat's holder on the catalog half.
defs func() map[string]seatdef.Definition
holder func(seat string) (string, bool)
}
// NewBuildSeats is the finder over defs (the seat list's definitions, nil before it is read) and holder.
func NewBuildSeats(defs func() map[string]seatdef.Definition, holder func(seat string) (string, bool)) *BuildSeats {
return &BuildSeats{defs: defs, holder: holder}
}
func (s *BuildSeats) all() []BuildModule {
if s == nil || s.defs == nil {
return nil
}
defs := s.defs()
var out []BuildModule
for _, seat := range slices.Sorted(maps.Keys(defs)) {
for _, r := range defs[seat].Acts {
if r.Step != seatdef.StepBuild || r.Acting() == seatdef.OnEvery {
continue
}
m := BuildModule{Seat: seat, ByKind: r.On == seatdef.OnKindImage}
if !m.ByKind {
m.Toolchain = r.Acting()
}
if s.holder != nil {
m.Module, _ = s.holder(seat)
}
out = append(out, m)
break
}
}
return out
}
// All are the build seats with the module holding each, by seat name; a seat no catalog entry claims has no module.
func (s *BuildSeats) All() []BuildModule { return s.all() }
// Modules are the holders of the build seats, sorted, none that is unnamed.
func (s *BuildSeats) Modules() []string {
var out []string
for _, m := range s.all() {
if m.Module != "" {
out = append(out, m.Module)
}
}
slices.Sort(out)
return slices.Compact(out)
}
// OfModule is the build seat held by module.
func (s *BuildSeats) OfModule(module string) (BuildModule, bool) {
for _, m := range s.all() {
if m.Module == module && module != "" {
return m, true
}
}
return BuildModule{}, false
}
// OfSeat is the build seat named seat.
func (s *BuildSeats) OfSeat(seat string) (BuildModule, bool) {
for _, m := range s.all() {
if m.Seat == seat {
return m, true
}
}
return BuildModule{}, false
}
// For is the build module of an artifact: the build seat whose rule's `on` is language, or `kind:image` when the artifact's
// kind is image. None, or more than one, or a seat no entry claims, is the error.
func (s *BuildSeats) For(language, kind string) (BuildModule, error) {
var found []BuildModule
for _, m := range s.all() {
if (kind == "image" && m.ByKind) || (!m.ByKind && language != "" && strings.EqualFold(m.Toolchain, strings.TrimSpace(language))) {
found = append(found, m)
}
}
switch len(found) {
case 0:
return BuildModule{}, fmt.Errorf("no seat builds an artifact of language %q and kind %q", language, kind)
case 1:
if found[0].Module == "" {
return BuildModule{}, fmt.Errorf("no module claims %s, which builds an artifact of language %q and kind %q", found[0].Seat, language, kind)
}
return found[0], nil
}
return BuildModule{}, fmt.Errorf("the seats %v each build an artifact of language %q and kind %q", seatsOf(found), language, kind)
}
func seatsOf(ms []BuildModule) []string {
var out []string
for _, m := range ms {
out = append(out, m.Seat)
}
return out
}
// LanguageOf is the language of the toolchain the build module builds with, false for a module that is no build seat's holder
// or builds with none.
func (s *BuildSeats) LanguageOf(module string) (string, bool) {
m, ok := s.OfModule(module)
return m.Toolchain, ok && m.Toolchain != ""
}
// Languages are the languages of the toolchains the build seats' rules name, sorted.
func (s *BuildSeats) Languages() []string {
var out []string
for _, m := range s.all() {
if m.Toolchain != "" {
out = append(out, m.Toolchain)
}
}
slices.Sort(out)
return slices.Compact(out)
}
// OfAskLanguage is the build module whose seat takes the asks of an ask language (go, node or image): the seat the sdk names
// for it (buildstep.BuildSeat), held by the module the catalog half names.
func (s *BuildSeats) OfAskLanguage(language string) (BuildModule, error) {
seat, err := buildstep.BuildSeat(language)
if err != nil {
return BuildModule{}, err
}
m, ok := s.OfSeat(seat)
if !ok {
return BuildModule{}, fmt.Errorf("the seat list holds no build seat %s", seat)
}
if m.Module == "" {
return BuildModule{}, fmt.Errorf("no module claims %s", seat)
}
return m, nil
}