mesh-build-node runs its asks on the Delivery SDK's neutral build ask, step and answer forms instead of mesh-sdk's go/delivery; its state names are its seat's; the three build modules require the contracts they import (B5i; ADR 0634 §1, §6; issue 1966)

Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
2026-11-05 16:07:50 +01:00
parent aa29d8feb8
commit 941959198e
19 changed files with 114 additions and 100 deletions
+2
View File
@@ -3,6 +3,8 @@ module git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep
go 1.26.0
require (
git.novox.be/novox/mesh-delivery/contracts/go/seats/nodebuildgo v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-delivery/contracts/go/seats/nodebuildnode v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-delivery/contracts/go/seats/nodebuildimage v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-delivery/sdk v0.0.0-00010101000000-000000000000
+3
View File
@@ -3,6 +3,9 @@ module meshbuildgo
go 1.26.0
require (
git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-delivery/contracts/go/seats/nodebuildgo v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdeliverychecker v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-delivery/sdk v0.0.0-00010101000000-000000000000
+2
View File
@@ -3,6 +3,8 @@ module meshbuildimage
go 1.26.0
require (
git.novox.be/novox/mesh-delivery/contracts/go/seats/nodebuildimage v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-delivery/sdk v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep v0.0.0
@@ -6,9 +6,10 @@ import (
"time"
"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep"
seat "git.novox.be/novox/mesh-delivery/contracts/go/seats/nodebuildnode"
buildrun "git.novox.be/novox/mesh-delivery/sdk/buildrun"
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
stdio "git.novox.be/novox/mesh-sdk/go"
"git.novox.be/novox/mesh-sdk/go/delivery"
)
// **The module pulls its seat's asks** (novox/hq ADR 0525 §2-§6, ADR 0536 §4; issue 1887 A3node), as mesh-build-go
@@ -31,12 +32,12 @@ func (m *Module) askSteps(steps []StepTool) buildstep.AskSteps {
return buildstep.AskSteps{
Fetch: byName["build_node_fetch"], Build: byName["build_node_build"], Test: byName["build_node_test"],
Compare: buildstep.CompareStep(buildstep.GeneratedRootTS, func(ws string) string { return filepath.Join(m.Work, ws, "tree") }),
Toolchain: func(ctx context.Context) (delivery.ToolchainUsed, error) {
Toolchain: func(ctx context.Context) (sdkstep.ToolchainUsed, error) {
installed, err := m.Probe(ctx)
if err != nil {
return delivery.ToolchainUsed{}, err
return sdkstep.ToolchainUsed{}, err
}
return delivery.ToolchainUsed{Version: installed, Recipe: m.recipe().Version()}, nil
return sdkstep.ToolchainUsed{Version: installed, Recipe: m.recipe().Version()}, nil
},
Release: func(ws string) error { return m.spaces().Release(ws) },
Logf: m.Say,
@@ -46,7 +47,7 @@ func (m *Module) askSteps(steps []StepTool) buildstep.AskSteps {
// Puller is the module's pull of its seat's asks, on source (the runtime's seat pull when nil).
func (m *Module) Puller(steps []StepTool, source buildstep.AskSource) *buildstep.Puller {
if source == nil {
source = buildstep.SeatAsks{Language: delivery.AskNode}
source = buildstep.SeatAsks{Language: sdkstep.AskNode}
}
capacity := m.Capacity
// The capacity's own change wakes the puller (issue 1887): the runtime's watch of the config value as mesh-config
@@ -64,16 +65,16 @@ func (m *Module) Puller(steps []StepTool, source buildstep.AskSource) *buildstep
capacity = buildstep.NodeCapacity(m.Settings, m.Node, m.Say)
settings = m.Settings
watch = buildstep.WatchConfigServed
cores = buildstep.NodeCoresPerStep(m.Settings, buildstep.DefaultCoresPerStep[delivery.AskNode], m.Say)
cores = buildstep.NodeCoresPerStep(m.Settings, buildstep.DefaultCoresPerStep[sdkstep.AskNode], m.Say)
}
ask := m.askSteps(steps)
var load func(string) ([]byte, error)
if m.ReadPauses.Get != nil {
load = func(key string) ([]byte, error) { return m.ReadPauses.Get(key) }
}
return &buildstep.Puller{Source: source, Language: delivery.AskNode, Node: m.Node, Put: m.Steps, PausePut: m.Pauses, Load: load,
return &buildstep.Puller{Source: source, Language: sdkstep.AskNode, Node: m.Node, Put: m.Steps, PausePut: m.Pauses, Load: load,
Capacity: capacity, SettingsFile: settings, WatchConfig: watch, CoresPerStep: cores, BuildHours: hours, Logf: m.Say,
Run: func(ctx context.Context, a delivery.StepAsk) (any, error) {
Run: func(ctx context.Context, a sdkstep.StepAsk) (any, error) {
if err := m.Prepare(ctx); err != nil {
return nil, err
}
@@ -93,13 +94,13 @@ func (m *Module) SeatTools(p *buildstep.Puller) []stdio.Tool {
const StepEndTool = "build_node_step_end"
// endLeftRunning is the module's start sweep (issue 1890): it ends the steps of its node that a previous process left
// running (sdkstep.EndStaleAtStart; every step names its owner). main runs it once at the start, and the module's test
// running (buildrun.EndStaleAtStart; every step names its owner). main runs it once at the start, and the module's test
// runs it through the conformance kit's left-running case.
func (m *Module) endLeftRunning(ctx context.Context, scan sdkstep.Scan, booted time.Time, logf func(string, ...any)) {
sdkstep.EndStaleAtStart(ctx, scan, m.Node, booted, logf)
func (m *Module) endLeftRunning(ctx context.Context, scan buildrun.Scan, booted time.Time, logf func(string, ...any)) {
buildrun.EndStaleAtStart(ctx, scan, m.Node, booted, logf)
}
// stepScan is the module's steps state as buildstep reads it.
func (m *Module) stepScan() sdkstep.Scan {
return sdkstep.ScanOf(stdio.State(delivery.StepsState))
func (m *Module) stepScan() buildrun.Scan {
return buildrun.ScanOf(stdio.State(seat.StateSteps))
}
@@ -12,7 +12,6 @@ import (
"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep"
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
"git.novox.be/novox/mesh-sdk/go/delivery"
)
const askCommit = "0123456789abcdef0123456789abcdef01234567"
@@ -31,14 +30,14 @@ func fakeSteps(calls *[]string) []StepTool {
}
}
func nodeAsk(kind string) delivery.StepAsk {
a := delivery.StepAsk{ID: sdkstep.NewID(), Kind: kind, Language: delivery.AskNode, Asker: "mesh-delivery-checker", At: time.Now()}
fetch := map[string]any{"repository": "novox/mesh-catalog", "commit": askCommit}
if kind == delivery.AskDelivery {
func nodeAsk(kind string) sdkstep.StepAsk {
a := sdkstep.StepAsk{ID: sdkstep.NewID(), Kind: kind, Language: sdkstep.AskNode, Asker: "mesh-delivery-checker", At: time.Now()}
fetch := sdkstep.Fetch{Repository: "novox/mesh-catalog", Commit: askCommit}
if kind == sdkstep.AskDelivery {
a.Asker = "mesh-delivery-builder"
a.Build = &delivery.BuildJob{Module: "gitea", Path: "modules/gitea", Fetch: fetch, Artifacts: []delivery.AskArtifact{{Artifact: "code"}}}
a.Build = &sdkstep.BuildJob{Module: "gitea", Path: "modules/gitea", Fetch: &fetch, Artifacts: []sdkstep.AskArtifact{{Artifact: "code"}}}
} else {
a.Check = &delivery.CheckJob{Fetch: fetch, Levels: []string{delivery.LevelUnit}}
a.Check = &sdkstep.CheckJob{Fetch: fetch, Levels: []string{sdkstep.LevelUnit}}
}
return a
}
@@ -51,7 +50,7 @@ func TestAPulledNodeAskRunsItsJob(t *testing.T) {
m.Capacity = func() int { return 1 }
var calls []string
src := buildstep.NewFakeAsks()
build, check := nodeAsk(delivery.AskDelivery), nodeAsk(delivery.AskCheck)
build, check := nodeAsk(sdkstep.AskDelivery), nodeAsk(sdkstep.AskCheck)
_ = src.PublishAsk(check)
_ = src.PublishAsk(build)
p := m.Puller(fakeSteps(&calls), src)
@@ -70,7 +69,7 @@ func TestAPulledNodeAskRunsItsJob(t *testing.T) {
if got := strings.Join(calls, ","); got != "build_node_fetch,build_node_build,build_node_fetch,build_node_test" {
t.Errorf("calls = %s, want the delivery ask's job first, then the check's", got)
}
key, _ := delivery.AskStepKey("ace", build.ID)
key, _ := sdkstep.AskStepKey("ace", build.ID)
states.mu.Lock()
v := states.steps[key]
states.mu.Unlock()
@@ -92,10 +91,10 @@ func TestManifestTakesTheBuildCapacity(t *testing.T) {
}
_ = json.Unmarshal(raw, &doc)
// Issue 1806's follow-up: the cores a step needs is a setting of its own, in the settings file, with the SDK's default.
if cores := string(doc.Config[buildstep.CoresPerStepSetting].Default); cores != fmt.Sprint(buildstep.DefaultCoresPerStep[delivery.AskNode]) {
t.Errorf("the cores-per-step default = %s, want %g", cores, buildstep.DefaultCoresPerStep[delivery.AskNode])
if cores := string(doc.Config[buildstep.CoresPerStepSetting].Default); cores != fmt.Sprint(buildstep.DefaultCoresPerStep[sdkstep.AskNode]) {
t.Errorf("the cores-per-step default = %s, want %g", cores, buildstep.DefaultCoresPerStep[sdkstep.AskNode])
}
if got := buildstep.NodeCoresPerStep(moduleDir+"/files/settings.json", 0, nil)(); got != buildstep.DefaultCoresPerStep[delivery.AskNode] {
if got := buildstep.NodeCoresPerStep(moduleDir+"/files/settings.json", 0, nil)(); got != buildstep.DefaultCoresPerStep[sdkstep.AskNode] {
t.Errorf("the settings template holds cores-per-step %g", got)
}
// And the build hours (the operator, 2026-10-26): a setting of their own, empty by default (any hour), in the template.
@@ -18,9 +18,9 @@ import (
"strings"
"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep"
builds "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
"git.novox.be/novox/mesh-sdk/go/catalogue"
"git.novox.be/novox/mesh-sdk/go/delivery"
)
// Out is where a bundle's compiled files go in the module's directory (builder toolchain.go:118).
@@ -145,7 +145,7 @@ func (m *Module) build(ctx context.Context, args map[string]any) (any, error) {
// Published from this process once the build ended, a candidate's as a trunk build's (ADR 0498 §1 §2; CAT-BN3): the
// step answers the seat's reference, or fails not stored. The packed bytes are never written to the node (L9).
if err := m.publishOn(ctx, buildstep.ArtifactSeat, a.module+"/"+a.Name, "bundle", "", digest, archive); err != nil {
return nil, fmt.Errorf("%s%s: %v", delivery.NotStored, a.Name, err)
return nil, fmt.Errorf("%s%s: %v", builds.NotStored, a.Name, err)
}
files := []string{}
_ = filepath.WalkDir(bundleDir, func(p string, d os.DirEntry, err error) error {
@@ -6,6 +6,7 @@ import (
"time"
"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep/conformance"
buildrun "git.novox.be/novox/mesh-delivery/sdk/buildrun"
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
"git.novox.be/novox/mesh-sdk/go/forgekey"
)
@@ -25,7 +26,7 @@ func TestTheTestStepKeepsTheFailureContract(t *testing.T) {
// case: a step a previous process left running is ended at once and said, whatever its age, and a step of this process
// is kept.
func TestTheStartSweepEndsWhatAPreviousProcessLeft(t *testing.T) {
conformance.LeftRunning(t, func(ctx context.Context, scan sdkstep.Scan, node string, before time.Time, logf func(string, ...any)) {
conformance.LeftRunning(t, func(ctx context.Context, scan buildrun.Scan, node string, before time.Time, logf func(string, ...any)) {
(&Module{Node: node}).endLeftRunning(ctx, scan, before, logf)
})
}
@@ -15,9 +15,10 @@ import (
"strings"
"time"
seat "git.novox.be/novox/mesh-delivery/contracts/go/seats/nodebuildnode"
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
dforgekey "git.novox.be/novox/mesh-delivery/sdk/forgekey"
stdio "git.novox.be/novox/mesh-sdk/go"
"git.novox.be/novox/mesh-sdk/go/delivery"
)
// CacheDir is the cache directory, outside the account's home (ADR 0466 §1; module.json resource `cache`).
@@ -43,13 +44,13 @@ func main() {
Reload: func() (Recipe, error) { return ReadRecipe(paths.LockPath()) },
Probe: ProbeNode,
Toolchain: &Toolchain{Paths: paths, Run: dforgekey.ExecRunnerClean(0), Registry: registry},
Toolchains: stdio.State(delivery.ToolchainsState).Put,
Toolchains: stdio.State(seat.StateToolchains).Put,
Emit: func(t string, b any) error { return stdio.Emit(t, b) },
Steps: stdio.State(delivery.StepsState).Put,
ReadSteps: readOf(stdio.State(delivery.StepsState)),
Pauses: stdio.State(delivery.PausesState).Put,
ReadPauses: readOf(stdio.State(delivery.PausesState)),
ReadToolchainsState: readOf(stdio.State(delivery.ToolchainsState)),
Steps: stdio.State(seat.StateSteps).Put,
ReadSteps: readOf(stdio.State(seat.StateSteps)),
Pauses: stdio.State(sdkstep.PausesState).Put,
ReadPauses: readOf(stdio.State(sdkstep.PausesState)),
ReadToolchainsState: readOf(stdio.State(seat.StateToolchains)),
Work: WorkDir,
Run: dforgekey.ExecRunnerClean(0),
Ctx: ctx,
@@ -9,8 +9,8 @@ import (
"time"
"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep"
buildrun "git.novox.be/novox/mesh-delivery/sdk/buildrun"
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
"git.novox.be/novox/mesh-sdk/go/delivery"
"git.novox.be/novox/mesh-sdk/go/forgekey"
"git.novox.be/novox/mesh-sdk/go/health"
)
@@ -139,7 +139,7 @@ func (m *Module) Start(ctx context.Context) {
m.Say("the toolchain: %v", err)
}
m.CleanWorkspaces(m.now())
sdkstep.EndStaleRound(m.stepScan(), m.Node, m.Say) // asks past the step bound, on the module's own round (issue 1890)
buildrun.EndStaleRound(m.stepScan(), m.Node, m.Say) // asks past the step bound, on the module's own round (issue 1890)
select {
case <-ctx.Done():
return
@@ -190,8 +190,8 @@ func (m *Module) Long(tool string, run func(ctx context.Context, args map[string
if err != nil {
return nil, err
}
key, _ := delivery.StepKey(m.Node, id)
return map[string]any{"step": id, "key": key, "state": delivery.StepRunning}, nil
key, _ := sdkstep.StepKey(m.Node, id)
return map[string]any{"step": id, "key": key, "state": sdkstep.StepRunning}, nil
}
}
@@ -11,24 +11,25 @@ import (
"testing"
"time"
"git.novox.be/novox/mesh-sdk/go/delivery"
buildstep "git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep"
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
"git.novox.be/novox/mesh-sdk/go/health"
)
// keptStates stands in for the toolchains and the steps states.
type keptStates struct {
mu sync.Mutex
toolchains map[string]delivery.Toolchain
steps map[string][]delivery.Step
toolchains map[string]buildstep.Toolchain
steps map[string][]sdkstep.Step
ended chan string
}
func newStates() *keptStates {
return &keptStates{toolchains: map[string]delivery.Toolchain{}, steps: map[string][]delivery.Step{}, ended: make(chan string, 16)}
return &keptStates{toolchains: map[string]buildstep.Toolchain{}, steps: map[string][]sdkstep.Step{}, ended: make(chan string, 16)}
}
func (s *keptStates) putToolchain(key string, value any) (uint64, error) {
tc, err := delivery.DecodeToolchain(value.(json.RawMessage))
tc, err := buildstep.DecodeToolchain(value.(json.RawMessage))
if err != nil {
return 0, err
}
@@ -39,27 +40,27 @@ func (s *keptStates) putToolchain(key string, value any) (uint64, error) {
}
func (s *keptStates) putStep(key string, value any) (uint64, error) {
st, err := delivery.DecodeStep(value.(json.RawMessage))
st, err := sdkstep.DecodeStep(value.(json.RawMessage))
if err != nil {
return 0, err
}
s.mu.Lock()
s.steps[key] = append(s.steps[key], st)
s.mu.Unlock()
if st.State != delivery.StepRunning {
if st.State != sdkstep.StepRunning {
s.ended <- key
}
return 1, nil
}
func (s *keptStates) toolchain(key string) (delivery.Toolchain, bool) {
func (s *keptStates) toolchain(key string) (buildstep.Toolchain, bool) {
s.mu.Lock()
defer s.mu.Unlock()
tc, ok := s.toolchains[key]
return tc, ok
}
func (s *keptStates) waitEnd(t *testing.T) delivery.Step {
func (s *keptStates) waitEnd(t *testing.T) sdkstep.Step {
t.Helper()
select {
case key := <-s.ended:
@@ -69,7 +70,7 @@ func (s *keptStates) waitEnd(t *testing.T) delivery.Step {
return v[len(v)-1]
case <-time.After(3 * time.Second):
t.Fatal("the step never ended on its key")
return delivery.Step{}
return sdkstep.Step{}
}
}
@@ -87,7 +88,7 @@ func TestStartWritesTheToolchainAndHealthIsHealthy(t *testing.T) {
ctx, stop := context.WithCancel(context.Background())
defer stop()
go m.Start(ctx)
var tc delivery.Toolchain
var tc buildstep.Toolchain
for deadline := time.Now().Add(3 * time.Second); time.Now().Before(deadline); time.Sleep(5 * time.Millisecond) {
if got, ok := states.toolchain("ace.typescript"); ok {
tc = got
@@ -130,7 +131,7 @@ func TestANodeThatCannotBeRunRefusesEveryStepAndHealthIsUnhealthy(t *testing.T)
t.Fatalf("%s answered no step id: %v", tool.Name, out)
}
st := states.waitEnd(t)
if st.State != delivery.StepRefused {
if st.State != sdkstep.StepRefused {
t.Errorf("%s: the step ended %s, want refused", tool.Name, st.State)
}
reason = st.Reason
@@ -31,7 +31,7 @@ import (
"time"
"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep"
"git.novox.be/novox/mesh-sdk/go/delivery"
builds "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
"git.novox.be/novox/mesh-sdk/go/forgekey"
)
@@ -365,7 +365,7 @@ func (m *Module) placeSDK(ctx context.Context, dir, rel string, group *packed, t
if group != nil {
return fmt.Errorf("the lock of %s names %s, which neither the registry nor the group (%s@%s) holds", rel, want, group.Name, group.Version)
}
return fmt.Errorf("%s%s", delivery.WaitsFor, want)
return fmt.Errorf("%s%s", builds.WaitsFor, want)
}
if integrity != "" && integrityOf(got) != integrity {
return fmt.Errorf("the registry's %s is %s, and the lock of %s names %s", want, integrityOf(got), rel, integrity)
@@ -550,7 +550,7 @@ func (m *Module) buildPackage(ctx context.Context, workspace, module, root, dir
return res, nil
}
if err := m.publishOn(ctx, seat, p.Name, "package", p.Version, p.Digest, p.tgz); err != nil {
return nil, fmt.Errorf("%s%s: %v", delivery.NotStored, a.Name, err)
return nil, fmt.Errorf("%s%s: %v", builds.NotStored, a.Name, err)
}
res.Reference = p.Name + "@" + p.Version
return res, nil
@@ -14,7 +14,7 @@ import (
"sync/atomic"
"testing"
"git.novox.be/novox/mesh-sdk/go/delivery"
builds "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
)
// tgzOf is a packed npm package holding package/package.json of name@version and one more file.
@@ -103,8 +103,8 @@ func TestAMemberGetsTheSDKVersionItsLockNamesOrWaitsForIt(t *testing.T) {
}
dir = memberLocking(t, "0.1.16", "sha512-x")
err := m.placeSDK(context.Background(), dir, "modules/m", nil, nil)
if err == nil || err.Error() != delivery.WaitsFor+"@novox/mesh-sdk@0.1.16" {
t.Errorf("a version the registry lacks answered %v, want %s@novox/mesh-sdk@0.1.16", err, delivery.WaitsFor)
if err == nil || err.Error() != builds.WaitsFor+"@novox/mesh-sdk@0.1.16" {
t.Errorf("a version the registry lacks answered %v, want %s@novox/mesh-sdk@0.1.16", err, builds.WaitsFor)
}
dir = memberLocking(t, "0.1.15", "sha512-another")
if err := m.placeSDK(context.Background(), dir, "modules/m", nil, nil); err == nil || !strings.Contains(err.Error(), "sha512-another") {
@@ -134,7 +134,7 @@ func TestAMemberGetsTheSDKVersionItsLockNamesOrWaitsForIt(t *testing.T) {
}
dir = memberLocking(t, "0.1.15", integrityOf(held))
missing := &groupPackage{Name: sdkPackage, Version: "0.1.16-feat-x.9"}
if err := m.placeSDK(context.Background(), dir, "modules/m", nil, missing); err == nil || !strings.Contains(err.Error(), "0.1.16-feat-x.9") || strings.HasPrefix(err.Error(), delivery.WaitsFor) {
if err := m.placeSDK(context.Background(), dir, "modules/m", nil, missing); err == nil || !strings.Contains(err.Error(), "0.1.16-feat-x.9") || strings.HasPrefix(err.Error(), builds.WaitsFor) {
t.Errorf("a tagged version the registry lacks answered %v, want it named and no wait", err)
}
}
@@ -160,7 +160,7 @@ func TestTheGroupsPackageIsServedToAMemberLockingItsVersion(t *testing.T) {
t.Errorf("a member locking the registry's 0.1.14 in a group: %v, placed %q", err, placedVersion(dir))
}
dir = memberLocking(t, "0.1.13", "sha512-x")
if err := m.placeSDK(context.Background(), dir, "modules/m", group, nil); err == nil || !strings.Contains(err.Error(), "0.1.13") || !strings.Contains(err.Error(), "0.1.15") || strings.HasPrefix(err.Error(), delivery.WaitsFor) {
if err := m.placeSDK(context.Background(), dir, "modules/m", group, nil); err == nil || !strings.Contains(err.Error(), "0.1.13") || !strings.Contains(err.Error(), "0.1.15") || strings.HasPrefix(err.Error(), builds.WaitsFor) {
t.Errorf("a version neither holds answered %v, want both named and no wait", err)
}
before := asked.Load()
@@ -8,7 +8,7 @@ import (
"strings"
"testing"
"git.novox.be/novox/mesh-sdk/go/delivery"
builds "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
)
const packageManifest = `{"module":"mesh-sdk","version":"1","build":{"artifacts":[{"name":"lib","kind":"package","language":"typescript","paths":["src","package.json"]}]}}`
@@ -121,7 +121,7 @@ func TestATrunkPackageIsPublishedOnceAndAVersionTakenIsRefused(t *testing.T) {
m, _, _ = packageMachine(t, tgz)
withStore(t, m).fail = errors.New("refused: the builder did not number it")
if _, err = m.build(context.Background(), args); err == nil || !strings.HasPrefix(err.Error(), delivery.NotStored+"lib: ") {
if _, err = m.build(context.Background(), args); err == nil || !strings.HasPrefix(err.Error(), builds.NotStored+"lib: ") {
t.Errorf("a refused publish: %v", err)
}
}
@@ -14,9 +14,9 @@ import (
"testing"
registry "git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry"
"git.novox.be/novox/mesh-sdk/go/delivery"
"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep"
builds "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
)
// pushed are the blobs the fake holder took, by digest, for the tests that read an archive back.
@@ -127,7 +127,7 @@ func TestARefusedPushFailsNotStoredAndNoPackedFileStays(t *testing.T) {
}
}
_, err := m.build(context.Background(), buildArgs())
if c.words != "" && (err == nil || !strings.HasPrefix(err.Error(), delivery.NotStored+"tools: ") || !strings.Contains(err.Error(), c.words)) {
if c.words != "" && (err == nil || !strings.HasPrefix(err.Error(), builds.NotStored+"tools: ") || !strings.Contains(err.Error(), c.words)) {
t.Errorf("a refused publish answered %v", err)
}
var archives []string
@@ -14,8 +14,9 @@ import (
"sync"
"time"
buildstep "git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep"
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
stdio "git.novox.be/novox/mesh-sdk/go"
"git.novox.be/novox/mesh-sdk/go/delivery"
)
// The read tools, named as the module is: build_node_ and the verb.
@@ -98,26 +99,26 @@ func (m *Module) stepKey(id string) (string, error) {
}
id = rest
}
return delivery.StepKey(m.Node, id)
return sdkstep.StepKey(m.Node, id)
}
// ReadStep is the stored value of this node's step id (state, reason, result).
func (m *Module) ReadStep(id string) (delivery.Step, error) {
func (m *Module) ReadStep(id string) (sdkstep.Step, error) {
key, err := m.stepKey(id)
if err != nil {
return delivery.Step{}, err
return sdkstep.Step{}, err
}
if m.ReadSteps.Get == nil {
return delivery.Step{}, fmt.Errorf("the steps state cannot be read here")
return sdkstep.Step{}, fmt.Errorf("the steps state cannot be read here")
}
raw, err := m.ReadSteps.Get(key)
if err != nil {
return delivery.Step{}, err
return sdkstep.Step{}, err
}
if len(raw) == 0 {
return delivery.Step{}, fmt.Errorf("no step %s in the steps state", key)
return sdkstep.Step{}, fmt.Errorf("no step %s in the steps state", key)
}
return delivery.DecodeStep(raw)
return sdkstep.DecodeStep(raw)
}
// StepLine is a step without its result: the list answers these.
@@ -150,7 +151,7 @@ func (m *Module) RecentSteps(limit int) (lines []StepLine, cut bool, err error)
own := keys[:0:0]
for _, k := range keys {
if strings.HasPrefix(k, m.Node+".") {
if node, _, err := delivery.ParseStepKey(k); err == nil && node == m.Node {
if node, _, err := sdkstep.ParseStepKey(k); err == nil && node == m.Node {
own = append(own, k)
}
}
@@ -164,12 +165,12 @@ func (m *Module) RecentSteps(limit int) (lines []StepLine, cut bool, err error)
if err != nil {
return nil, cut, err
}
var steps []delivery.Step
var steps []sdkstep.Step
for _, raw := range raws {
if len(raw) == 0 {
continue
}
s, err := delivery.DecodeStep(raw)
s, err := sdkstep.DecodeStep(raw)
if err != nil {
continue // a value this module cannot read is not one of its own steps
}
@@ -191,7 +192,7 @@ func (m *Module) RecentSteps(limit int) (lines []StepLine, cut bool, err error)
}
// ReadToolchains is every key of the toolchains state (<node>.<language>) with its stored value.
func (m *Module) ReadToolchains() (map[string]delivery.Toolchain, error) {
func (m *Module) ReadToolchains() (map[string]buildstep.Toolchain, error) {
if m.ReadToolchainsState.Keys == nil || m.ReadToolchainsState.Get == nil {
return nil, fmt.Errorf("the toolchains state cannot be read here")
}
@@ -209,12 +210,12 @@ func (m *Module) ReadToolchains() (map[string]delivery.Toolchain, error) {
if err != nil {
return nil, err
}
out := map[string]delivery.Toolchain{}
out := map[string]buildstep.Toolchain{}
for i, raw := range raws {
if len(raw) == 0 {
continue
}
t, err := delivery.DecodeToolchain(raw)
t, err := buildstep.DecodeToolchain(raw)
if err != nil {
return nil, fmt.Errorf("%s: %w", keys[i], err)
}
@@ -10,7 +10,8 @@ import (
"testing"
"time"
"git.novox.be/novox/mesh-sdk/go/delivery"
buildstep "git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep"
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
)
// fakeState is a state as stored: key to raw value.
@@ -29,13 +30,13 @@ func (f fakeState) read() StateRead {
}
}
func putStep(t *testing.T, f fakeState, s delivery.Step) {
func putStep(t *testing.T, f fakeState, s sdkstep.Step) {
t.Helper()
b, err := delivery.EncodeStep(s)
b, err := sdkstep.EncodeStep(s)
if err != nil {
t.Fatal(err)
}
key, _ := delivery.StepKey(s.Node, s.ID)
key, _ := sdkstep.StepKey(s.Node, s.ID)
f[key] = b
}
@@ -60,22 +61,22 @@ func tool(t *testing.T, m *Module, name string) func(map[string]any) (any, error
func TestStepToolAnswersTheStoredStep(t *testing.T) {
steps := fakeState{}
t0 := time.Date(2026, 10, 21, 9, 0, 0, 0, time.UTC)
putStep(t, steps, delivery.Step{ID: "abc1", Tool: "build_node_fetch", Node: "ace", State: delivery.StepPassed, Started: t0, Ended: t0.Add(time.Second),
putStep(t, steps, sdkstep.Step{ID: "abc1", Tool: "build_node_fetch", Node: "ace", State: sdkstep.StepPassed, Started: t0, Ended: t0.Add(time.Second),
Result: json.RawMessage(`{"workspace":"w42"}`)})
putStep(t, steps, delivery.Step{ID: "abc2", Tool: "build_node_test", Node: "ace", State: delivery.StepRefused, Started: t0, Reason: "node cannot be run"})
putStep(t, steps, sdkstep.Step{ID: "abc2", Tool: "build_node_test", Node: "ace", State: sdkstep.StepRefused, Started: t0, Reason: "node cannot be run"})
m := readModule(steps, fakeState{})
for _, id := range []string{"abc1", "ace.abc1"} {
out, err := tool(t, m, StepReadTool)(map[string]any{"id": id})
if err != nil {
t.Fatal(err)
}
st := out.(delivery.Step)
if st.State != delivery.StepPassed || string(st.Result) != `{"workspace":"w42"}` || st.Tool != "build_node_fetch" {
st := out.(sdkstep.Step)
if st.State != sdkstep.StepPassed || string(st.Result) != `{"workspace":"w42"}` || st.Tool != "build_node_fetch" {
t.Errorf("id %s: step = %+v", id, st)
}
}
out, _ := tool(t, m, StepReadTool)(map[string]any{"id": "abc2"})
if st := out.(delivery.Step); st.State != delivery.StepRefused || st.Reason != "node cannot be run" {
if st := out.(sdkstep.Step); st.State != sdkstep.StepRefused || st.Reason != "node cannot be run" {
t.Errorf("refused step = %+v", st)
}
}
@@ -94,10 +95,10 @@ func TestStepToolRefusesAnUnknownOrForeignOrBadID(t *testing.T) {
func TestStepsToolListsRecentStepsOfThisNode(t *testing.T) {
steps := fakeState{}
t0 := time.Date(2026, 10, 21, 9, 0, 0, 0, time.UTC)
putStep(t, steps, delivery.Step{ID: "a", Tool: "build_node_fetch", Node: "ace", State: delivery.StepPassed, Started: t0, Ended: t0, Result: json.RawMessage(`1`)})
putStep(t, steps, delivery.Step{ID: "b", Tool: "build_node_test", Node: "ace", State: delivery.StepRunning, Started: t0.Add(time.Minute)})
putStep(t, steps, delivery.Step{ID: "c", Tool: "build_node_build", Node: "ace", State: delivery.StepFailed, Started: t0.Add(2 * time.Minute), Reason: "boom", What: "check ask, novox/x@abc"})
putStep(t, steps, delivery.Step{ID: "d", Tool: "build_node_build", Node: "g14", State: delivery.StepPassed, Started: t0.Add(time.Hour)})
putStep(t, steps, sdkstep.Step{ID: "a", Tool: "build_node_fetch", Node: "ace", State: sdkstep.StepPassed, Started: t0, Ended: t0, Result: json.RawMessage(`1`)})
putStep(t, steps, sdkstep.Step{ID: "b", Tool: "build_node_test", Node: "ace", State: sdkstep.StepRunning, Started: t0.Add(time.Minute)})
putStep(t, steps, sdkstep.Step{ID: "c", Tool: "build_node_build", Node: "ace", State: sdkstep.StepFailed, Started: t0.Add(2 * time.Minute), Reason: "boom", What: "check ask, novox/x@abc"})
putStep(t, steps, sdkstep.Step{ID: "d", Tool: "build_node_build", Node: "g14", State: sdkstep.StepPassed, Started: t0.Add(time.Hour)})
m := readModule(steps, fakeState{})
out, err := tool(t, m, StepsReadTool)(map[string]any{})
if err != nil {
@@ -111,7 +112,7 @@ func TestStepsToolListsRecentStepsOfThisNode(t *testing.T) {
if !slices.Equal(ids, []string{"c", "b", "a"}) {
t.Errorf("ids = %v, want c b a", ids)
}
if lines[0].Reason != "boom" || lines[0].What != "check ask, novox/x@abc" || lines[0].State != delivery.StepFailed {
if lines[0].Reason != "boom" || lines[0].What != "check ask, novox/x@abc" || lines[0].State != sdkstep.StepFailed {
t.Errorf("first = %+v", lines[0])
}
out, _ = tool(t, m, StepsReadTool)(map[string]any{"limit": float64(1)})
@@ -123,7 +124,7 @@ func TestStepsToolListsRecentStepsOfThisNode(t *testing.T) {
// 1562 (1483/AC5): build_node_toolchains answers each toolchains key as stored.
func TestToolchainsToolAnswersTheStoredKeys(t *testing.T) {
tcs := fakeState{}
b, err := delivery.EncodeToolchain(delivery.Toolchain{Language: "typescript", Version: "v22.1.0", Recipe: "1", At: time.Date(2026, 10, 21, 9, 0, 0, 0, time.UTC)})
b, err := buildstep.EncodeToolchain(buildstep.Toolchain{Language: "typescript", Version: "v22.1.0", Recipe: "1", At: time.Date(2026, 10, 21, 9, 0, 0, 0, time.UTC)})
if err != nil {
t.Fatal(err)
}
@@ -133,7 +134,7 @@ func TestToolchainsToolAnswersTheStoredKeys(t *testing.T) {
if err != nil {
t.Fatal(err)
}
got := out.(map[string]any)["toolchains"].(map[string]delivery.Toolchain)
got := out.(map[string]any)["toolchains"].(map[string]buildstep.Toolchain)
if got["ace.typescript"].Version != "v22.1.0" || got["ace.typescript"].Recipe != "1" {
t.Errorf("toolchains = %+v", got)
}
@@ -166,7 +167,7 @@ func TestRecentStepsReadsOnlyTheNodesKeysInParallelAndBounded(t *testing.T) {
stored := map[string][]byte{}
for _, n := range []string{"ace", "g14", "novox", "shanks"} {
for i := 0; i < row.perNode; i++ {
b, _ := delivery.EncodeStep(delivery.Step{ID: fmt.Sprintf("s%04d", i), Tool: "t", Node: n, State: delivery.StepPassed, Started: t0.Add(time.Duration(i) * time.Second)})
b, _ := sdkstep.EncodeStep(sdkstep.Step{ID: fmt.Sprintf("s%04d", i), Tool: "t", Node: n, State: sdkstep.StepPassed, Started: t0.Add(time.Duration(i) * time.Second)})
stored[n+".s"+fmt.Sprintf("%04d", i)] = b
}
}
@@ -9,7 +9,7 @@ import (
"time"
"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep"
"git.novox.be/novox/mesh-sdk/go/delivery"
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
"git.novox.be/novox/mesh-sdk/go/forgekey"
"git.novox.be/novox/mesh-sdk/go/health"
)
@@ -44,7 +44,7 @@ func TestUnderTheFloorEveryStepIsRefusedAndTheModuleIsUnhealthy(t *testing.T) {
if _, err := tool.Run(map[string]any{}); err != nil {
t.Fatal(err)
}
if st := states.waitEnd(t); st.State != delivery.StepRefused || !strings.HasPrefix(st.Reason, underTheFloor) {
if st := states.waitEnd(t); st.State != sdkstep.StepRefused || !strings.HasPrefix(st.Reason, underTheFloor) {
t.Errorf("the long step ended %s: %q, want refused: %q", st.State, st.Reason, underTheFloor)
}
case "short_probe":
@@ -21,8 +21,8 @@ import (
_ "github.com/distribution/distribution/v3/registry/storage/driver/inmemory"
registry "git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry"
builds "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
"git.novox.be/novox/mesh-sdk/go/catalogue"
"git.novox.be/novox/mesh-sdk/go/delivery"
)
// realStore starts the artifact store's own program (the distribution registry, modules/distribution) inside this
@@ -100,9 +100,9 @@ func TestADoneBundleResolvesInARealArtifactStore(t *testing.T) {
t.Fatal(err)
}
r := out.(BuildResult)
done := delivery.BuilderBuild{V: 1, State: delivery.BuildDone, Manifest: strings.Repeat("0", 64),
Artifacts: []delivery.BuiltArtifact{{Name: "tools", Kind: "bundle", Digest: r.Digest, Reference: r.Reference}}}
if _, err := delivery.EncodeBuilderBuild(done); err != nil {
done := builds.BuilderBuild{V: 1, State: builds.BuildDone, Manifest: strings.Repeat("0", 64),
Artifacts: []builds.BuiltArtifact{{Name: "tools", Kind: "bundle", Digest: r.Digest, Reference: r.Reference}}}
if _, err := builds.EncodeBuilderBuild(done); err != nil {
t.Errorf("the done key the builder would write is refused: %v", err)
}
if _, _, ok := catalogue.InRegistrySeat(r.Reference); !ok || !strings.HasSuffix(r.Reference, "@"+r.Digest) {
+2
View File
@@ -3,6 +3,8 @@ module meshbuildnode
go 1.26.0
require (
git.novox.be/novox/mesh-delivery/contracts/go/seats/nodebuildnode v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-delivery/sdk v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry v0.0.0-00010101000000-000000000000
git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep v0.0.0