Files
mesh-delivery/modules/mesh-delivery-builder/internal/versions/names.go
T
jschoubben 5524cfa8d8 BL6: the builder follows the catalog half per watched branch through the shared branch follower
The builder adopts meshcatalogclient's branch follower on its views reader (A-5, A-15): the modules, their manifests (kept from the
entry that holds them, by its digest), their packages and each package's name in its ecosystem are read from the catalog half's
entries, never the indexer's modules, manifests, candidates and packages views; an entry of a branch other than the module's first
builds its fingerprint and supersedes only its own branch's older one; the start's sweep runs once every followed half's first view
is in; the builder's tools bundle reads package names through the same follower. A run's candidate is no input of the builder
(A-84): its read of the indexer's candidates view goes, and the entry point for a run's build ask is not built (no line gives the
ask's form). The seats' events the builder's own handler takes are named per seat so the follower's events have one taker.

Builds: design 47 G §348, §479, §614, A-5, A-15, A-84
Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-11 23:27:01 +01:00

145 lines
5.0 KiB
Go

package versions
import (
"context"
"errors"
"fmt"
"sort"
"strings"
"git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog"
)
// PackagesState is the indexer's packages state as the builder reads it (its manifest's reads grant): its keys, and one
// key's value (found false when it holds none).
type PackagesState interface {
Keys(ctx context.Context) ([]string, error)
Get(ctx context.Context, key string) (value []byte, found bool, err error)
}
// NamesIn are a package's names in its ecosystem as the indexer's packages state holds them (design 32): the name of the
// series key that names module and artifact as what it is built from. A series key is named `<name>.<series>`; a key
// with no dot is a mesh Go module's published packages, not a series. A value that does not decode is passed over.
func NamesIn(s PackagesState) PackageNames {
return func(ctx context.Context, module, artifact string) (string, bool, error) {
keys, err := s.Keys(ctx)
if err != nil {
return "", false, err
}
sort.Strings(keys)
built := NameOf(module, artifact)
for _, key := range keys {
if !isSeriesKey(key) {
continue
}
value, found, err := s.Get(ctx, key)
if err != nil {
return "", false, err
}
if !found {
continue
}
v, err := meshcatalog.DecodePackagesByPackageSeriesValue(value) // the indexer's value of a series key (mesh-sdk's contract, J4)
if err != nil || v.Package != built {
continue
}
return v.Name, true, nil
}
return "", false, nil
}
}
// NamedModule is the module a value of the indexer's packages state names a package's name for (design 32), and that
// name: a series key whose value names a name and the <module>/<artifact> it is built from. The builder looks at that module again when
// the value is heard, so a done build that left the package unnumbered (its name not known at its claim) is numbered then.
func NamedModule(key string, value []byte) (module, name string, ok bool) {
if !isSeriesKey(key) {
return "", "", false
}
v, err := meshcatalog.DecodePackagesByPackageSeriesValue(value)
if err != nil || v.Name == "" {
return "", "", false
}
module, artifact, cut := strings.Cut(v.Package, "/")
if !cut || module == "" || artifact == "" {
return "", "", false
}
return module, v.Name, true
}
// isSeriesKey says whether a key of the indexer's packages state is a series key (`<package>.<series>`), not a per-module one.
func isSeriesKey(key string) bool {
_, _, ok := meshcatalog.ParsePackagesByPackageSeriesKey(key)
return ok
}
// Built is a module's artifact, which a package's name is the name of.
type Built struct{ Module, Artifact string }
// Carriers are the module artifacts a package's name in its ecosystem is the name of, as the catalog half's packages view
// holds them (the inverse of NamesIn; design 47 G §572, §478): each distinct module and artifact of a series key whose value
// names that name, sorted. A name no entry carries answers none.
func Carriers(s PackagesState) func(ctx context.Context, name string) ([]Built, error) {
return func(ctx context.Context, name string) ([]Built, error) {
keys, err := s.Keys(ctx)
if err != nil {
return nil, err
}
sort.Strings(keys)
seen := map[Built]bool{}
var out []Built
for _, key := range keys {
if !isSeriesKey(key) {
continue
}
value, found, err := s.Get(ctx, key)
if err != nil {
return nil, err
}
if !found {
continue
}
v, err := meshcatalog.DecodePackagesByPackageSeriesValue(value)
if err != nil || v.Name != name {
continue
}
if module, artifact, cut := strings.Cut(v.Package, "/"); cut && module != "" && artifact != "" && !seen[Built{module, artifact}] {
seen[Built{module, artifact}] = true
out = append(out, Built{module, artifact})
}
}
return out, nil
}
}
// ModuleOf is the module and artifact a package's name is built from (Carriers), found false for a name no entry carries; more
// than one entry carrying it is an error naming them.
func ModuleOf(s PackagesState) func(ctx context.Context, name string) (module, artifact string, found bool, err error) {
return moduleOfCarriers(Carriers(s))
}
// moduleOfCarriers is ModuleOf over one way of finding the carriers.
func moduleOfCarriers(carriers func(ctx context.Context, name string) ([]Built, error)) func(ctx context.Context, name string) (module, artifact string, found bool, err error) {
return func(ctx context.Context, name string) (string, string, bool, error) {
got, err := carriers(ctx, name)
switch {
case err != nil:
return "", "", false, err
case len(got) == 0:
return "", "", false, nil
case len(got) > 1:
return "", "", false, errors.New(MoreThanOne(name, got))
}
return got[0].Module, got[0].Artifact, true, nil
}
}
// MoreThanOne is the words of a name that more than one entry carries.
func MoreThanOne(name string, got []Built) string {
var refs []string
for _, b := range got {
refs = append(refs, b.Module+"/"+b.Artifact)
}
return fmt.Sprintf("the name %s is carried by more than one entry: %s", name, strings.Join(refs, ", "))
}