Files
mesh-delivery/modules/mesh-delivery-builder/buildrun/toolchains.go
T
jschoubben 99b05bbb2e G findings4 buildrun: the checker's and the indexer's reads of the build modules' toolchains keep the gate over the assignments, which BL8 took out with the builder's
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
2026-11-11 22:17:09 +01:00

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
}