BL8 (8255eb28) replaced buildrun's ToolchainGate, ToolchainSet.Put and ToolchainsBucket by PlacedNodes and ToolchainSet.Set for the builder (§546, §604); no design line removes the checker's and the indexer's toolchain reads, so the three come back beside them, and FollowAssignments feeds either. Builds: design 47 G §492, §604, §546, §472, §574 Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
130 lines
4.2 KiB
Go
130 lines
4.2 KiB
Go
package buildrun
|
|
|
|
import (
|
|
"sort"
|
|
"sync"
|
|
|
|
"git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
|
)
|
|
|
|
// LanguageOf is the language each build module's toolchain reports hold.
|
|
var LanguageOf = map[string]string{GoBuildModule: "go", NodeBuildModule: "typescript"}
|
|
|
|
// 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 {
|
|
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 := 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
|
|
}
|