The builder finds a build module as the indexer's seed does: the build seat whose acting rule `build` has an `on` matching the artifact's language or kind:image, and that seat's holder, read from the seat list's definitions and the claims of the catalog half's manifests. Task.Module, every lane key, the unpublished modules, the toolchain sources, the ends watched, the sayer and builder_stuck (through `<seat>.steps`) derive from it; the floor's sources are the holders of the seats git and mesh-catalog. buildrun.GoBuildModule, NodeBuildModule, LanguageOf, buildstep.BuildModuleOf use, build.ImageBuildModule and StuckBuildModules are gone from the builder (the checker keeps its own names in two small files until its part reads them from the list). Builds: design 47 G §7, §428, §704, §204, A-82 Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
138 lines
4.5 KiB
Go
138 lines
4.5 KiB
Go
package versions
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"slices"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
|
|
"git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient"
|
|
"git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog"
|
|
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
|
)
|
|
|
|
// Entries are the catalog graph entries of the halves the builder follows, one per module and watched branch (design 47 G §348,
|
|
// §614): where a package's name in its ecosystem is read from, each artifact's `package` (§676), never the indexer's state.
|
|
type Entries func() []meshcatalog.CatalogEntry
|
|
|
|
// artifactOf is the artifact name of an artifact reference `<module>:<artifact>` of module.
|
|
func artifactOf(module, ref string) (string, bool) {
|
|
m, a, ok := strings.Cut(ref, ":")
|
|
return a, ok && m == module && a != ""
|
|
}
|
|
|
|
// NamesFrom answers a package's name in its ecosystem from the entries (design 47 G §348): the name the entry of module gives the
|
|
// artifact; known is false when no entry names one for it.
|
|
func NamesFrom(all Entries) PackageNames {
|
|
return func(_ context.Context, module, artifact string) (string, bool, error) {
|
|
for _, e := range all() {
|
|
if e.Module != module {
|
|
continue
|
|
}
|
|
for _, a := range e.Artifacts {
|
|
if name, ok := artifactOf(module, a.Ref); ok && name == artifact && a.Package != nil && a.Package.Name != "" {
|
|
return a.Package.Name, true, nil
|
|
}
|
|
}
|
|
}
|
|
return "", false, nil
|
|
}
|
|
}
|
|
|
|
// CarriersFrom are the module artifacts a package's name in its ecosystem is the name of, as the entries hold them (design 47 G
|
|
// §572, §478), each distinct module and artifact once, sorted; a name no entry carries answers none.
|
|
func CarriersFrom(all Entries) func(ctx context.Context, name string) ([]Built, error) {
|
|
return func(_ context.Context, name string) ([]Built, error) {
|
|
seen := map[Built]bool{}
|
|
var out []Built
|
|
for _, e := range all() {
|
|
for _, a := range e.Artifacts {
|
|
artifact, ok := artifactOf(e.Module, a.Ref)
|
|
if !ok || a.Package == nil || a.Package.Name != name || seen[Built{e.Module, artifact}] {
|
|
continue
|
|
}
|
|
seen[Built{e.Module, artifact}] = true
|
|
out = append(out, Built{e.Module, artifact})
|
|
}
|
|
}
|
|
sort.Slice(out, func(i, j int) bool { return out[i].Module+"/"+out[i].Artifact < out[j].Module+"/"+out[j].Artifact })
|
|
return out, nil
|
|
}
|
|
}
|
|
|
|
// ModuleFrom is the module and artifact a package's name is built from, from the entries (CarriersFrom); more than one entry
|
|
// carrying it is an error naming them.
|
|
func ModuleFrom(all Entries) func(ctx context.Context, name string) (module, artifact string, found bool, err error) {
|
|
return moduleOfCarriers(CarriersFrom(all))
|
|
}
|
|
|
|
// EntriesOf are the entries of every half the branch follower holds, the followed branches in the order they came.
|
|
func EntriesOf(branches *meshcatalogclient.Branches) Entries {
|
|
return func() []meshcatalog.CatalogEntry {
|
|
var out []meshcatalog.CatalogEntry
|
|
for _, br := range branches.Followed() {
|
|
if cat, ok := branches.Catalog(br); ok {
|
|
if half, ok := cat.View(); ok {
|
|
for _, e := range half.Entries {
|
|
out = append(out, e)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
}
|
|
|
|
// HolderOf finds the module that holds a seat: the one whose entry in the first followed branch's half (the trunk's) has a
|
|
// manifest that claims it (design 47 G A-82; the seat list answers seat definitions alone, §527). More than one claimant, or
|
|
// none, is no holder.
|
|
func HolderOf(branches *meshcatalogclient.Branches) func(seat string) (string, bool) {
|
|
var mu sync.Mutex
|
|
claims := map[string][]string{} // manifest digest -> the seats it claims
|
|
claimed := func(e meshcatalog.CatalogEntry) []string {
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
if seats, ok := claims[e.ManifestDigest]; ok && e.ManifestDigest != "" {
|
|
return seats
|
|
}
|
|
var seats []string
|
|
if raw, err := json.Marshal(e.Manifest); err == nil {
|
|
if mf, err := catalogue.Decode(raw); err == nil {
|
|
for _, c := range mf.Claims {
|
|
seats = append(seats, c.Name)
|
|
}
|
|
}
|
|
}
|
|
if e.ManifestDigest != "" {
|
|
claims[e.ManifestDigest] = seats
|
|
}
|
|
return seats
|
|
}
|
|
return func(seat string) (string, bool) {
|
|
followed := branches.Followed()
|
|
if len(followed) == 0 {
|
|
return "", false
|
|
}
|
|
cat, ok := branches.Catalog(followed[0])
|
|
if !ok {
|
|
return "", false
|
|
}
|
|
half, ok := cat.View()
|
|
if !ok {
|
|
return "", false
|
|
}
|
|
var holders []string
|
|
for _, e := range half.Entries {
|
|
if slices.Contains(claimed(e), seat) {
|
|
holders = append(holders, e.Module)
|
|
}
|
|
}
|
|
if len(holders) != 1 {
|
|
return "", false
|
|
}
|
|
return holders[0], true
|
|
}
|
|
}
|