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
167 lines
5.6 KiB
Go
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
|
|
}
|