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
129 lines
4.2 KiB
Go
129 lines
4.2 KiB
Go
package buildrun
|
|
|
|
import (
|
|
"sort"
|
|
"sync"
|
|
|
|
"git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
|
)
|
|
|
|
// ToolchainsHeardState is the builder's old state section of the toolchains it heard (design 47 G §604, §546): one key
|
|
// <node>.<language>, the toolchain as the build seat said it.
|
|
const (
|
|
ToolchainsHeardState = "toolchains-heard"
|
|
ToolchainsHeardBucket = "mesh-delivery-builder_" + ToolchainsHeardState
|
|
)
|
|
|
|
// ToolchainSet holds each node's toolchain of a language, as the builder heard it (design 47 G §604). Safe for use from
|
|
// the consumer and from a build at once.
|
|
type ToolchainSet struct {
|
|
// Seats finds the build module of a language: a report of a module that holds no build seat is left out (design 47 G A-82).
|
|
Seats *BuildSeats
|
|
mu sync.Mutex
|
|
by map[string]map[string]buildstep.Toolchain // language -> node -> entry
|
|
}
|
|
|
|
// Set takes a node's toolchain in and answers whether the language's entries changed (a node's version, recipe or
|
|
// digest, or a node appearing).
|
|
func (s *ToolchainSet) Set(node string, tc buildstep.Toolchain) bool {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
if s.by == nil {
|
|
s.by = map[string]map[string]buildstep.Toolchain{}
|
|
}
|
|
if s.by[tc.Language] == nil {
|
|
s.by[tc.Language] = map[string]buildstep.Toolchain{}
|
|
}
|
|
cur, had := s.by[tc.Language][node]
|
|
s.by[tc.Language][node] = tc
|
|
return !had || cur.Version != tc.Version || cur.Recipe != tc.Recipe || cur.Digest != tc.Digest
|
|
}
|
|
|
|
// Delete takes a node's toolchain of a language out and answers whether it was held.
|
|
func (s *ToolchainSet) Delete(node, language string) bool {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
if _, had := s.by[language][node]; !had {
|
|
return false
|
|
}
|
|
delete(s.by[language], node)
|
|
return true
|
|
}
|
|
|
|
// Entries are the nodes' entries of a language, in no particular order.
|
|
func (s *ToolchainSet) Entries(language string) []buildstep.NodeToolchain {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
var out []buildstep.NodeToolchain
|
|
for node, tc := range s.by[language] {
|
|
out = append(out, buildstep.NodeToolchain{Node: node, Toolchain: tc})
|
|
}
|
|
return out
|
|
}
|
|
|
|
// AgreedDigest is a digest of the agreed version and the nodes reporting it.
|
|
type AgreedDigest struct {
|
|
Digest string
|
|
Nodes []string
|
|
}
|
|
|
|
// Digests are the digests the nodes reporting the agreed toolchain's version report, sorted by digest, each with its
|
|
// nodes sorted (design 47 G §472, §574). A node that reports no digest is left out.
|
|
func Digests(entries []buildstep.NodeToolchain, agreed buildstep.Toolchain) []AgreedDigest {
|
|
by := map[string][]string{}
|
|
for _, e := range entries {
|
|
if e.Version == agreed.Version && e.Recipe == agreed.Recipe && e.Digest != "" {
|
|
by[e.Digest] = append(by[e.Digest], e.Node)
|
|
}
|
|
}
|
|
out := make([]AgreedDigest, 0, len(by))
|
|
for d, nodes := range by {
|
|
sort.Strings(nodes)
|
|
out = append(out, AgreedDigest{Digest: d, Nodes: nodes})
|
|
}
|
|
sort.Slice(out, func(i, j int) bool { return out[i].Digest < out[j].Digest })
|
|
return out
|
|
}
|
|
|
|
// The builder's tools over the toolchains it heard (design 47 G §604, §546).
|
|
const (
|
|
ToolchainsTool = "builder_toolchains"
|
|
ToolchainsDropTool = "builder_toolchains_drop"
|
|
)
|
|
|
|
// Put takes a change of a build module's toolchains bucket in: a put of a node's entry (key <node>.<language>) or its
|
|
// delete. It answers whether the language's entries changed (a node's version or recipe, or a node appearing or going).
|
|
// A key or a value that is not an entry of the module's language is left out.
|
|
func (s *ToolchainSet) Put(module, key string, value []byte, deleted bool) bool {
|
|
language, ok := s.Seats.LanguageOf(module)
|
|
if !ok {
|
|
return false
|
|
}
|
|
node, keyLanguage, err := buildstep.ParseToolchainKey(key)
|
|
if err != nil || keyLanguage != language {
|
|
return false
|
|
}
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
if deleted {
|
|
if _, had := s.by[language][node]; !had {
|
|
return false
|
|
}
|
|
delete(s.by[language], node)
|
|
return true
|
|
}
|
|
tc, err := buildstep.DecodeToolchain(value)
|
|
if err != nil || tc.Language != language {
|
|
return false
|
|
}
|
|
if s.by == nil {
|
|
s.by = map[string]map[string]buildstep.Toolchain{}
|
|
}
|
|
if s.by[language] == nil {
|
|
s.by[language] = map[string]buildstep.Toolchain{}
|
|
}
|
|
cur, had := s.by[language][node]
|
|
s.by[language][node] = tc
|
|
return !had || cur.Version != tc.Version || cur.Recipe != tc.Recipe || cur.Digest != tc.Digest
|
|
}
|