mesh-build-go runs its asks on the Delivery SDK's neutral build ask, step and answer forms instead of mesh-sdk's go/delivery: the Puller takes buildstep.StepAsk (the read bridge is the form's own AskAs), the toolchains value is the build runner's own, the stale-step part is buildrun's, keys and values come from the seats and clients (B5i; ADR 0634 §1, §6; issue 1966)
The part build-step lacks the check job's toolchain and types the check answer's fetch as the fetch arguments; both are kept in askrun_gaps.go until the part has them. Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
@@ -4,13 +4,13 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
trunk "git.novox.be/novox/mesh-delivery/sdk/trunk"
|
||||
"maps"
|
||||
"path"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// **One ask, one whole job** (novox/hq ADR 0525 §3). A build module runs a pulled ask with its own step tools' bodies,
|
||||
@@ -35,21 +35,21 @@ type AskSteps struct {
|
||||
Compare StepFunc
|
||||
// Toolchain is the toolchain the module builds with now; a build ask naming another is declined (Declining). Nil checks nothing
|
||||
// (an image or an archive names none).
|
||||
Toolchain func(ctx context.Context) (delivery.ToolchainUsed, error)
|
||||
// Kept looks up a kept verdict by level and digest (the checker's kept bucket, delivery.KeptKey): whether one is
|
||||
Toolchain func(ctx context.Context) (sdkstep.ToolchainUsed, error)
|
||||
// Kept looks up a kept verdict by level and digest (the checker's kept bucket, the checker seat's kept key): whether one is
|
||||
// kept and how many tests it counted. Nil takes none.
|
||||
Kept func(ctx context.Context, level, digest string) (tests int, found bool, err error)
|
||||
// Release removes a workspace once the ask's steps on it ended (the module's release tool's body); nil leaves it to
|
||||
// the module's sweep.
|
||||
Release func(workspace string) error
|
||||
Logf func(format string, args ...any)
|
||||
// Has says whether this node has a need a package's tests declare (delivery.AskNeeds, issue 890); a package needing
|
||||
// Has says whether this node has a need a package's tests declare (sdkstep.AskNeeds, issue 890); a package needing
|
||||
// what it lacks is not tested here but named in the level's Unmet, for the asker to place. Nil has nothing.
|
||||
Has func(need string) bool
|
||||
}
|
||||
|
||||
// RunAsk runs ask's job with steps and answers a delivery.BuildAnswer or a delivery.CheckAnswer.
|
||||
func RunAsk(ctx context.Context, steps AskSteps, ask delivery.StepAsk) (any, error) {
|
||||
// RunAsk runs ask's job with steps and answers a sdkstep.BuildAnswer or a CheckAnswer.
|
||||
func RunAsk(ctx context.Context, steps AskSteps, ask sdkstep.StepAsk) (any, error) {
|
||||
if ask.Build != nil {
|
||||
job := *ask.Build
|
||||
job.Fetch = withPackages(job.Fetch, ask.Packages)
|
||||
@@ -57,7 +57,7 @@ func RunAsk(ctx context.Context, steps AskSteps, ask delivery.StepAsk) (any, err
|
||||
}
|
||||
if ask.Check != nil {
|
||||
job := *ask.Check
|
||||
job.Fetch = withPackages(job.Fetch, ask.Packages)
|
||||
job.Fetch = *withPackages(&job.Fetch, ask.Packages)
|
||||
return steps.check(ctx, job)
|
||||
}
|
||||
return nil, Refuse("the ask %s holds no job", ask.ID)
|
||||
@@ -66,13 +66,28 @@ func RunAsk(ctx context.Context, steps AskSteps, ask delivery.StepAsk) (any, err
|
||||
// withPackages is the fetch's arguments with the ask's package versions (novox/hq ADR 0573 §2), which the build module
|
||||
// writes into the workspace it fetches; a copy, so the ask is left as it was. An ask with none, or a copy alone that
|
||||
// fetches nothing, is left as it is.
|
||||
func withPackages(fetch map[string]any, packages delivery.PackageVersions) map[string]any {
|
||||
func withPackages(fetch *sdkstep.Fetch, packages sdkstep.PackageVersions) *sdkstep.Fetch {
|
||||
if len(packages) == 0 || fetch == nil {
|
||||
return fetch
|
||||
}
|
||||
out := maps.Clone(fetch)
|
||||
out["packages"] = maps.Clone(packages)
|
||||
return out
|
||||
out := *fetch
|
||||
out.Packages = maps.Clone(packages)
|
||||
return &out
|
||||
}
|
||||
|
||||
// FetchArgs is a fetch as the fetch tool takes it (sdkstep.FetchRequest's Args and the build module's own, beside): the
|
||||
// ask's fetch through the one JSON form, without the layout version, which no tool takes.
|
||||
func FetchArgs(f sdkstep.Fetch) (map[string]any, error) {
|
||||
b, err := json.Marshal(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
args := map[string]any{}
|
||||
if err := json.Unmarshal(b, &args); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
delete(args, "v")
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func raw(v any) (json.RawMessage, error) {
|
||||
@@ -91,11 +106,18 @@ type fetched struct {
|
||||
Modules []string `json:"modules"`
|
||||
}
|
||||
|
||||
func (s AskSteps) fetch(ctx context.Context, args map[string]any) (json.RawMessage, fetched, error) {
|
||||
func (s AskSteps) fetch(ctx context.Context, fetch *sdkstep.Fetch) (json.RawMessage, fetched, error) {
|
||||
if s.Fetch == nil {
|
||||
return nil, fetched{}, Refuse("this build module fetches nothing")
|
||||
}
|
||||
if _, merges := args["base"]; merges {
|
||||
if fetch == nil {
|
||||
return nil, fetched{}, Refuse("the ask names no fetch")
|
||||
}
|
||||
args, err := FetchArgs(*fetch)
|
||||
if err != nil {
|
||||
return nil, fetched{}, err
|
||||
}
|
||||
if fetch.Base != "" {
|
||||
Phase(ctx, "fetch and merge")
|
||||
} else {
|
||||
Phase(ctx, "fetch")
|
||||
@@ -118,7 +140,7 @@ func (s AskSteps) fetch(ctx context.Context, args map[string]any) (json.RawMessa
|
||||
// onToolchain is the toolchain this node builds with when the ask names one and this module reports its own, nil when
|
||||
// either is absent. A node on another toolchain than the ask names answers a Declining: it is not on the agreed one and
|
||||
// sits out, for a node that is to take the ask (novox/hq ADR 0584 clause 2).
|
||||
func (s AskSteps) onToolchain(ctx context.Context, want *delivery.ToolchainUsed) (*delivery.ToolchainUsed, error) {
|
||||
func (s AskSteps) onToolchain(ctx context.Context, want *sdkstep.ToolchainUsed) (*sdkstep.ToolchainUsed, error) {
|
||||
if want == nil || s.Toolchain == nil {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -134,8 +156,8 @@ func (s AskSteps) onToolchain(ctx context.Context, want *delivery.ToolchainUsed)
|
||||
return &tc, nil
|
||||
}
|
||||
|
||||
func (s AskSteps) build(ctx context.Context, job delivery.BuildJob) (any, error) {
|
||||
answer := delivery.BuildAnswer{}
|
||||
func (s AskSteps) build(ctx context.Context, job sdkstep.BuildJob) (any, error) {
|
||||
answer := sdkstep.BuildAnswer{}
|
||||
tc, err := s.onToolchain(ctx, job.Toolchain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -149,7 +171,7 @@ func (s AskSteps) build(ctx context.Context, job delivery.BuildJob) (any, error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, candidate := job.Fetch["base"]; candidate {
|
||||
if job.Fetch.Base != "" {
|
||||
// A candidate is built from its head merged onto the trunk; a head that does not merge leaves the tree at the
|
||||
// trunk, and building that would store the trunk's build under the candidate's fingerprint.
|
||||
if len(got.Conflicts) > 0 {
|
||||
@@ -225,7 +247,7 @@ func (s AskSteps) build(ctx context.Context, job delivery.BuildJob) (any, error)
|
||||
if err := json.Unmarshal(r, &built); err != nil || built.Digest == "" {
|
||||
return nil, fmt.Errorf("the build of %s answered no digest: %s", a.Artifact, r)
|
||||
}
|
||||
answer.Artifacts = append(answer.Artifacts, delivery.AskBuilt{Artifact: a.Artifact, Digest: built.Digest, Reference: built.Reference})
|
||||
answer.Artifacts = append(answer.Artifacts, sdkstep.AskBuilt{Artifact: a.Artifact, Digest: built.Digest, Reference: built.Reference})
|
||||
}
|
||||
if s.Imports != nil && f.Workspace != "" {
|
||||
out, err := s.Imports(ctx, map[string]any{"workspace": f.Workspace, "module": job.Path})
|
||||
@@ -242,7 +264,7 @@ func (s AskSteps) build(ctx context.Context, job delivery.BuildJob) (any, error)
|
||||
if err := json.Unmarshal(r, &read); err != nil {
|
||||
return nil, fmt.Errorf("the imports of %s answered what is not a list: %s", job.Module, r)
|
||||
}
|
||||
if err := delivery.CheckImports(read.Imports); err != nil {
|
||||
if err := trunk.CheckImports(read.Imports); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
answer.Imports = read.Imports
|
||||
@@ -250,16 +272,16 @@ func (s AskSteps) build(ctx context.Context, job delivery.BuildJob) (any, error)
|
||||
return answer, nil
|
||||
}
|
||||
|
||||
func (s AskSteps) check(ctx context.Context, job delivery.CheckJob) (any, error) {
|
||||
if _, err := s.onToolchain(ctx, job.Toolchain); err != nil {
|
||||
func (s AskSteps) check(ctx context.Context, job sdkstep.CheckJob) (any, error) {
|
||||
if _, err := s.onToolchain(ctx, checkToolchain(ctx)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r, f, err := s.fetch(ctx, job.Fetch)
|
||||
r, f, err := s.fetch(ctx, &job.Fetch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer s.release(f.Workspace)
|
||||
answer := delivery.CheckAnswer{Fetch: r}
|
||||
answer := CheckAnswer{Fetch: r}
|
||||
if len(f.Conflicts) > 0 {
|
||||
return answer, nil // the asker reads the conflict from the fetch's answer: a verdict, not an error
|
||||
}
|
||||
@@ -273,7 +295,7 @@ func (s AskSteps) check(ctx context.Context, job delivery.CheckJob) (any, error)
|
||||
// Every level runs, also after one that did not pass: one check names every failure, not only the first (the operator,
|
||||
// 2026-11-02: "A failed check shows only its first fault").
|
||||
for _, level := range job.Levels {
|
||||
la := delivery.LevelAnswer{Level: level}
|
||||
la := sdkstep.LevelAnswer{Level: level}
|
||||
_, err := s.level(ctx, job, level, f, runner, &la)
|
||||
answer.Levels = append(answer.Levels, la)
|
||||
if err != nil {
|
||||
@@ -284,13 +306,13 @@ func (s AskSteps) check(ctx context.Context, job delivery.CheckJob) (any, error)
|
||||
}
|
||||
|
||||
// level runs one level of a check ask and says whether it passed.
|
||||
func (s AskSteps) level(ctx context.Context, job delivery.CheckJob, level string, f fetched, runner string, la *delivery.LevelAnswer) (bool, error) {
|
||||
func (s AskSteps) level(ctx context.Context, job sdkstep.CheckJob, level string, f fetched, runner string, la *sdkstep.LevelAnswer) (bool, error) {
|
||||
withRunner := func(args map[string]any) map[string]any {
|
||||
if runner != "" {
|
||||
args["runner"] = runner
|
||||
}
|
||||
if len(job.NodeNames) > 0 {
|
||||
args[delivery.NodeNamesArg] = job.NodeNames // the names check's (ADR 0567 §12)
|
||||
args[sdkstep.NodeNamesArg] = job.NodeNames // the names check's (ADR 0567 §12)
|
||||
}
|
||||
return args
|
||||
}
|
||||
@@ -390,7 +412,7 @@ func (s AskSteps) release(workspace string) {
|
||||
// is digested for keeping (ADR 0535 §4); when the installed module answers it (it does not take the runner), the level is
|
||||
// planned as any other and the runner is dropped. The level runs whole and takes nothing when the digest names no package
|
||||
// or does not cover the siblings the fetch placed (ADR 0444, L5).
|
||||
func (s AskSteps) plan(ctx context.Context, job delivery.CheckJob, level string, f fetched, runner string, withRunner func(map[string]any) map[string]any, la *delivery.LevelAnswer) ([]string, bool, bool) {
|
||||
func (s AskSteps) plan(ctx context.Context, job sdkstep.CheckJob, level string, f fetched, runner string, withRunner func(map[string]any) map[string]any, la *sdkstep.LevelAnswer) ([]string, bool, bool) {
|
||||
if job.Run != "" {
|
||||
return nil, true, true
|
||||
}
|
||||
@@ -406,7 +428,7 @@ func (s AskSteps) plan(ctx context.Context, job delivery.CheckJob, level string,
|
||||
if err != nil {
|
||||
return nil, true, true
|
||||
}
|
||||
var d delivery.LevelDigest // packages kept as a sorted list, never keyed by a package path (issue 1930)
|
||||
var d sdkstep.LevelDigest // packages kept as a sorted list, never keyed by a package path (issue 1930)
|
||||
if err := json.Unmarshal(r, &d); err != nil {
|
||||
return nil, true, true
|
||||
}
|
||||
@@ -437,16 +459,16 @@ func (s AskSteps) plan(ctx context.Context, job delivery.CheckJob, level string,
|
||||
s.Logf("the kept verdict of %s at %s cannot be read, so it runs: %v", p, level, err)
|
||||
}
|
||||
if err == nil && found {
|
||||
la.Kept = append(la.Kept, delivery.KeptTaken{Package: p, Digest: pd.Digest, Tests: tests})
|
||||
la.Kept = append(la.Kept, sdkstep.KeptTaken{Package: p, Digest: pd.Digest, Tests: tests})
|
||||
continue
|
||||
}
|
||||
}
|
||||
if needs := d.NeedsOf(p); len(delivery.Lacks(needs, s.Has)) > 0 {
|
||||
if needs := d.NeedsOf(p); len(sdkstep.Lacks(needs, s.Has)) > 0 {
|
||||
// Its tests would only skip here (issue 890): it is left for a node that has what they need.
|
||||
la.Unmet = append(la.Unmet, delivery.PackageNeeds{Package: p, Needs: needs})
|
||||
la.Unmet = append(la.Unmet, sdkstep.PackageNeeds{Package: p, Needs: needs})
|
||||
if s.Logf != nil {
|
||||
s.Logf("the %s level of %s is left to a node with %s: this node lacks %s", level, p,
|
||||
strings.Join(needs, ", "), strings.Join(delivery.Lacks(needs, s.Has), ", "))
|
||||
strings.Join(needs, ", "), strings.Join(sdkstep.Lacks(needs, s.Has), ", "))
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -456,18 +478,9 @@ func (s AskSteps) plan(ctx context.Context, job delivery.CheckJob, level string,
|
||||
}
|
||||
|
||||
// besideOf are the siblings a fetch's arguments place, as a digest echoes them: `<owner/repo> <commit>`, sorted.
|
||||
func besideOf(fetch map[string]any) []string {
|
||||
raw, err := json.Marshal(fetch["beside"])
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
var refs []struct {
|
||||
Repository string `json:"repository"`
|
||||
Commit string `json:"commit"`
|
||||
}
|
||||
_ = json.Unmarshal(raw, &refs)
|
||||
func besideOf(fetch sdkstep.Fetch) []string {
|
||||
var out []string
|
||||
for _, r := range refs {
|
||||
for _, r := range fetch.Beside {
|
||||
out = append(out, r.Repository+" "+r.Commit)
|
||||
}
|
||||
sort.Strings(out)
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package buildstep
|
||||
|
||||
// Two layouts the part build-step (mesh-delivery-sdk buildstep, as the three node-build seats generate it) lacks, which a
|
||||
// check ask still carries; both are kept here and nowhere else, so that deleting this file is the whole change once the
|
||||
// part has them (B5i finding 1 and 2, issue 1966):
|
||||
//
|
||||
// - the check job's toolchain (novox/hq ADR 0584 clause 2): the part's CheckJob has no `toolchain`, so a node on
|
||||
// another toolchain than the checker agreed would take the ask and record a pass it did not run. The Puller reads
|
||||
// the field from the ask's own bytes and the run takes it from its context.
|
||||
// - the check answer's fetch: the part types the answer's `fetch` as Fetch (the arguments), but the fetch tool's answer
|
||||
// (workspace, merge, conflicts, changed, modules) is what the checker reads a conflict from.
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
)
|
||||
|
||||
type checkToolchainKey struct{}
|
||||
|
||||
// checkToolchainOf reads the toolchain a check ask names, from the ask's bytes; nil when it names none or is no check.
|
||||
func checkToolchainOf(data []byte) *sdkstep.ToolchainUsed {
|
||||
var a struct {
|
||||
Check *struct {
|
||||
Toolchain *sdkstep.ToolchainUsed `json:"toolchain"`
|
||||
} `json:"check"`
|
||||
}
|
||||
if json.Unmarshal(data, &a) != nil || a.Check == nil {
|
||||
return nil
|
||||
}
|
||||
return a.Check.Toolchain
|
||||
}
|
||||
|
||||
// WithCheckToolchain is ctx carrying the toolchain the ask in data names for its check.
|
||||
func WithCheckToolchain(ctx context.Context, data []byte) context.Context {
|
||||
if tc := checkToolchainOf(data); tc != nil {
|
||||
return context.WithValue(ctx, checkToolchainKey{}, tc)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
func checkToolchain(ctx context.Context) *sdkstep.ToolchainUsed {
|
||||
tc, _ := ctx.Value(checkToolchainKey{}).(*sdkstep.ToolchainUsed)
|
||||
return tc
|
||||
}
|
||||
|
||||
// CheckAnswer is a check ask's answer with the fetch tool's own answer as it is.
|
||||
type CheckAnswer struct {
|
||||
Fetch json.RawMessage `json:"fetch"`
|
||||
Levels []sdkstep.LevelAnswer `json:"levels,omitempty"`
|
||||
}
|
||||
@@ -10,8 +10,6 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
const runCommit = "0123456789abcdef0123456789abcdef01234567"
|
||||
@@ -22,7 +20,7 @@ type recorder struct {
|
||||
fetch map[string]any
|
||||
tests map[string]bool // package or module directory -> passed
|
||||
digests map[string]string
|
||||
needs []delivery.PackageNeeds
|
||||
needs []sdkstep.PackageNeeds
|
||||
}
|
||||
|
||||
func (r *recorder) steps() AskSteps {
|
||||
@@ -58,12 +56,12 @@ func (r *recorder) steps() AskSteps {
|
||||
}
|
||||
}
|
||||
|
||||
func ask(job any) delivery.StepAsk {
|
||||
a := delivery.StepAsk{ID: sdkstep.NewID(), Kind: delivery.AskCheck, Language: delivery.AskGo, Asker: "t", At: time.Now()}
|
||||
func ask(job any) sdkstep.StepAsk {
|
||||
a := sdkstep.StepAsk{ID: sdkstep.NewID(), Kind: sdkstep.AskCheck, Language: sdkstep.AskGo, Asker: "t", At: time.Now()}
|
||||
switch j := job.(type) {
|
||||
case delivery.BuildJob:
|
||||
case sdkstep.BuildJob:
|
||||
a.Build = &j
|
||||
case delivery.CheckJob:
|
||||
case sdkstep.CheckJob:
|
||||
a.Check = &j
|
||||
}
|
||||
return a
|
||||
@@ -84,15 +82,15 @@ func TestRunAskBuilds(t *testing.T) {
|
||||
return map[string]any{"imports": []string{"git.novox.be/novox/mesh-sdk/go/announce", "git.novox.be/novox/mesh-sdk/go/delivery"}}, nil
|
||||
}
|
||||
// Issue 1585: the ask's package versions reach the fetch, and the ask's own fetch arguments stay as they were.
|
||||
a := ask(delivery.BuildJob{Module: "m", Path: "modules/m", Publish: true,
|
||||
Fetch: map[string]any{"repository": "novox/x", "commit": runCommit},
|
||||
Artifacts: []delivery.AskArtifact{{Artifact: "base", From: "registry/b@sha256:00"}, {Artifact: "bundle"}, {Artifact: "lib", Seat: "go-package-registry", Version: "v0.1.15"}}})
|
||||
a.Packages = delivery.PackageVersions{"git.novox.be/novox/mesh-sdk/go": runCommit}
|
||||
a := ask(sdkstep.BuildJob{Module: "m", Path: "modules/m", Publish: true,
|
||||
Fetch: &sdkstep.Fetch{Repository: "novox/x", Commit: runCommit},
|
||||
Artifacts: []sdkstep.AskArtifact{{Artifact: "base", From: "registry/b@sha256:00"}, {Artifact: "bundle"}, {Artifact: "lib", Seat: "go-package-registry", Version: "v0.1.15"}}})
|
||||
a.Packages = sdkstep.PackageVersions{"git.novox.be/novox/mesh-sdk/go": runCommit}
|
||||
out, err := RunAsk(context.Background(), steps, a)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, put := a.Build.Fetch["packages"]; put {
|
||||
if len(a.Build.Fetch.Packages) != 0 {
|
||||
t.Error("the ask's fetch arguments were changed")
|
||||
}
|
||||
got := strings.Join(r.calls, "\n")
|
||||
@@ -107,7 +105,7 @@ imports {"module":"modules/m","workspace":"/w/1"}`
|
||||
if len(released) != 1 || released[0] != "/w/1" {
|
||||
t.Errorf("released %v, want the one workspace", released)
|
||||
}
|
||||
answer := out.(delivery.BuildAnswer)
|
||||
answer := out.(sdkstep.BuildAnswer)
|
||||
if len(answer.Artifacts) != 3 || answer.Artifacts[1].Reference != "artifact-store://bundle" {
|
||||
t.Fatalf("answer %+v", answer)
|
||||
}
|
||||
@@ -120,18 +118,24 @@ imports {"module":"modules/m","workspace":"/w/1"}`
|
||||
func TestRunAskRefusesAnotherToolchain(t *testing.T) {
|
||||
r := &recorder{fetch: map[string]any{"workspace": "/w"}}
|
||||
s := r.steps()
|
||||
s.Toolchain = func(context.Context) (delivery.ToolchainUsed, error) {
|
||||
return delivery.ToolchainUsed{Version: "go1.27", Recipe: "3"}, nil
|
||||
s.Toolchain = func(context.Context) (sdkstep.ToolchainUsed, error) {
|
||||
return sdkstep.ToolchainUsed{Version: "go1.27", Recipe: "3"}, nil
|
||||
}
|
||||
_, err := RunAsk(context.Background(), s, ask(delivery.BuildJob{Module: "m", Path: ".", Fetch: map[string]any{"repository": "novox/x", "commit": runCommit},
|
||||
Artifacts: []delivery.AskArtifact{{Artifact: "bundle"}}, Toolchain: &delivery.ToolchainUsed{Version: "go1.26", Recipe: "3"}}))
|
||||
_, err := RunAsk(context.Background(), s, ask(sdkstep.BuildJob{Module: "m", Path: ".", Fetch: &sdkstep.Fetch{Repository: "novox/x", Commit: runCommit},
|
||||
Artifacts: []sdkstep.AskArtifact{{Artifact: "bundle"}}, Toolchain: &sdkstep.ToolchainUsed{Version: "go1.26", Recipe: "3"}}))
|
||||
var dec *Declining
|
||||
if !errors.As(err, &dec) || len(dec.Lacks) != 1 || len(r.calls) != 0 {
|
||||
t.Fatalf("err %v, calls %v", err, r.calls)
|
||||
}
|
||||
// A check ask names the toolchain too (ADR 0584), and a node on another declines it before it fetches.
|
||||
_, err = RunAsk(context.Background(), s, ask(delivery.CheckJob{Fetch: map[string]any{"repository": "novox/x", "commit": runCommit}, Levels: []string{delivery.LevelUnit},
|
||||
Toolchain: &delivery.ToolchainUsed{Version: "go1.26", Recipe: "3"}}))
|
||||
// A check ask names the toolchain too (ADR 0584), and a node on another declines it before it fetches. The part's
|
||||
// CheckJob has no field for it, so the ask's own bytes carry it (askrun_gaps.go).
|
||||
check := ask(sdkstep.CheckJob{Fetch: sdkstep.Fetch{Repository: "novox/x", Commit: runCommit}, Levels: []string{sdkstep.LevelUnit}})
|
||||
raw, _ := json.Marshal(check)
|
||||
var doc map[string]any
|
||||
_ = json.Unmarshal(raw, &doc)
|
||||
doc["check"].(map[string]any)["toolchain"] = map[string]any{"version": "go1.26", "recipe": "3"}
|
||||
raw, _ = json.Marshal(doc)
|
||||
_, err = RunAsk(WithCheckToolchain(context.Background(), raw), s, check)
|
||||
if !errors.As(err, &dec) || len(r.calls) != 0 {
|
||||
t.Fatalf("a check ask: err %v, calls %v", err, r.calls)
|
||||
}
|
||||
@@ -146,8 +150,8 @@ func TestRunAskChecksTheLevelsOfOneCheck(t *testing.T) {
|
||||
digests: map[string]string{"x/pkg/a": "d-a", "x/pkg/b": "d-b", "x/pkg/k": "d-kept"},
|
||||
tests: map[string]bool{"x/pkg/a": true, "x/pkg/b": false},
|
||||
}
|
||||
out, err := RunAsk(context.Background(), r.steps(), ask(delivery.CheckJob{Levels: []string{delivery.LevelUnit, delivery.LevelIntegration}, Kept: true,
|
||||
Runner: "workspace", RunnerWhen: "modules/mesh-build-go/", Fetch: map[string]any{"repository": "novox/x", "commit": runCommit, "base": runCommit}}))
|
||||
out, err := RunAsk(context.Background(), r.steps(), ask(sdkstep.CheckJob{Levels: []string{sdkstep.LevelUnit, sdkstep.LevelIntegration}, Kept: true,
|
||||
Runner: "workspace", RunnerWhen: "modules/mesh-build-go/", Fetch: sdkstep.Fetch{Repository: "novox/x", Commit: runCommit, Base: runCommit}}))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -162,20 +166,20 @@ test {"level":"integration","packages":["x/pkg/a"],"workspace":"/w"}`
|
||||
if got != want {
|
||||
t.Fatalf("calls:\n%s\nwant:\n%s", got, want)
|
||||
}
|
||||
a := out.(delivery.CheckAnswer)
|
||||
a := out.(CheckAnswer)
|
||||
if len(a.Levels) != 2 {
|
||||
t.Fatalf("levels %+v, want both levels", a.Levels)
|
||||
}
|
||||
u := a.Levels[0]
|
||||
if u.Level != delivery.LevelUnit || len(u.Kept) != 1 || u.Kept[0].Package != "x/pkg/k" || u.Kept[0].Tests != 7 || len(u.Tests) != 2 || u.Digest == nil {
|
||||
if u.Level != sdkstep.LevelUnit || len(u.Kept) != 1 || u.Kept[0].Package != "x/pkg/k" || u.Kept[0].Tests != 7 || len(u.Tests) != 2 || u.Digest == nil {
|
||||
t.Fatalf("the unit level %+v", u)
|
||||
}
|
||||
|
||||
// ADR 0567 §12: a check's node names reach the digest and test tools, for the names check (a kept verdict misses when
|
||||
// they change).
|
||||
r.calls = nil
|
||||
if _, err := RunAsk(context.Background(), r.steps(), ask(delivery.CheckJob{Levels: []string{delivery.LevelUnit}, NodeNames: []string{"anchor"},
|
||||
Fetch: map[string]any{"repository": "novox/x", "commit": runCommit}})); err != nil || !strings.Contains(strings.Join(r.calls, "\n"), `digest {"level":"unit","node-names":["anchor"]`) ||
|
||||
if _, err := RunAsk(context.Background(), r.steps(), ask(sdkstep.CheckJob{Levels: []string{sdkstep.LevelUnit}, NodeNames: []string{"anchor"},
|
||||
Fetch: sdkstep.Fetch{Repository: "novox/x", Commit: runCommit}})); err != nil || !strings.Contains(strings.Join(r.calls, "\n"), `digest {"level":"unit","node-names":["anchor"]`) ||
|
||||
!strings.Contains(strings.Join(r.calls, "\n"), `test {"level":"unit","node-names":["anchor"]`) {
|
||||
t.Fatalf("the node names are not handed to the digest and test tools: %v, %v", r.calls, err)
|
||||
}
|
||||
@@ -184,19 +188,19 @@ test {"level":"integration","packages":["x/pkg/a"],"workspace":"/w"}`
|
||||
// tested on a node that has it.
|
||||
for _, has := range []bool{false, true} {
|
||||
r.calls, r.tests["x/pkg/b"] = nil, true
|
||||
r.needs = []delivery.PackageNeeds{{Package: "x/pkg/a", Needs: []string{delivery.NeedNodeBuildNode}}}
|
||||
r.needs = []sdkstep.PackageNeeds{{Package: "x/pkg/a", Needs: []string{sdkstep.NeedNodeBuildNode}}}
|
||||
steps := r.steps()
|
||||
if has {
|
||||
steps.Has = func(n string) bool { return n == delivery.NeedNodeBuildNode }
|
||||
steps.Has = func(n string) bool { return n == sdkstep.NeedNodeBuildNode }
|
||||
}
|
||||
out, err := RunAsk(context.Background(), steps, ask(delivery.CheckJob{Levels: []string{delivery.LevelUnit}, Kept: true,
|
||||
Fetch: map[string]any{"repository": "novox/x", "commit": runCommit}}))
|
||||
out, err := RunAsk(context.Background(), steps, ask(sdkstep.CheckJob{Levels: []string{sdkstep.LevelUnit}, Kept: true,
|
||||
Fetch: sdkstep.Fetch{Repository: "novox/x", Commit: runCommit}}))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
u := out.(delivery.CheckAnswer).Levels[0]
|
||||
u := out.(CheckAnswer).Levels[0]
|
||||
tested := strings.Contains(strings.Join(r.calls, "\n"), `"x/pkg/a"`)
|
||||
if tested != has || (len(u.Unmet) == 0) == !has || (!has && (u.Unmet[0].Package != "x/pkg/a" || delivery.UnmetNeeds([]delivery.LevelAnswer{u})[0] != delivery.NeedNodeBuildNode)) {
|
||||
if tested != has || (len(u.Unmet) == 0) == !has || (!has && (u.Unmet[0].Package != "x/pkg/a" || sdkstep.UnmetNeeds([]sdkstep.LevelAnswer{u})[0] != sdkstep.NeedNodeBuildNode)) {
|
||||
t.Fatalf("has %v: tested x/pkg/a %v, unmet %+v, calls %v", has, tested, u.Unmet, r.calls)
|
||||
}
|
||||
}
|
||||
@@ -206,12 +210,12 @@ test {"level":"integration","packages":["x/pkg/a"],"workspace":"/w"}`
|
||||
r.calls, r.digests = nil, digests
|
||||
steps := r.steps()
|
||||
steps.Has = func(string) bool { return true }
|
||||
out, err := RunAsk(context.Background(), steps, ask(delivery.CheckJob{Levels: []string{delivery.LevelUnit}, Kept: true, Packages: []string{"x/pkg/a"},
|
||||
Fetch: map[string]any{"repository": "novox/x", "commit": runCommit}}))
|
||||
out, err := RunAsk(context.Background(), steps, ask(sdkstep.CheckJob{Levels: []string{sdkstep.LevelUnit}, Kept: true, Packages: []string{"x/pkg/a"},
|
||||
Fetch: sdkstep.Fetch{Repository: "novox/x", Commit: runCommit}}))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
u := out.(delivery.CheckAnswer).Levels[0]
|
||||
u := out.(CheckAnswer).Levels[0]
|
||||
if last := r.calls[len(r.calls)-1]; !strings.HasSuffix(last, `"packages":["x/pkg/a"],"workspace":"/w"}`) || len(u.Kept) != 0 || len(u.Tests) != 1 {
|
||||
t.Fatalf("digests %v: calls %v, kept %+v: want x/pkg/a alone tested, nothing taken kept", digests != nil, r.calls, u.Kept)
|
||||
}
|
||||
@@ -235,15 +239,15 @@ test {"level":"unit","runner":"workspace","workspace":"/w"}`},
|
||||
_, _ = digest(ctx, args)
|
||||
return map[string]any{"level": "unit", "packages": r.digests, "judged_by": tc.judged}, nil
|
||||
}
|
||||
out, err := RunAsk(context.Background(), steps, ask(delivery.CheckJob{Levels: []string{delivery.LevelUnit}, Kept: true,
|
||||
Runner: "workspace", RunnerWhen: "modules/mesh-build-go/", Fetch: map[string]any{"repository": "novox/x", "commit": runCommit}}))
|
||||
out, err := RunAsk(context.Background(), steps, ask(sdkstep.CheckJob{Levels: []string{sdkstep.LevelUnit}, Kept: true,
|
||||
Runner: "workspace", RunnerWhen: "modules/mesh-build-go/", Fetch: sdkstep.Fetch{Repository: "novox/x", Commit: runCommit}}))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := strings.Join(r.calls[1:], "\n"); got != tc.want {
|
||||
t.Errorf("judged by %s: calls\n%s\nwant\n%s", tc.judged, got, tc.want)
|
||||
}
|
||||
l := out.(delivery.CheckAnswer).Levels[0]
|
||||
l := out.(CheckAnswer).Levels[0]
|
||||
if tc.judged == "installed" && (len(l.Kept) != 1 || l.Judged != "") {
|
||||
t.Errorf("an installed module's level %+v, want the kept verdict taken", l)
|
||||
}
|
||||
@@ -252,12 +256,12 @@ test {"level":"unit","runner":"workspace","workspace":"/w"}`},
|
||||
}
|
||||
}
|
||||
r := &recorder{fetch: map[string]any{"workspace": "/w"}, digests: map[string]string{"x/pkg/a": "d-kept"}, tests: map[string]bool{}}
|
||||
out, err := RunAsk(context.Background(), r.steps(), ask(delivery.CheckJob{Levels: []string{delivery.LevelUnit}, Kept: true,
|
||||
Fetch: map[string]any{"repository": "novox/x", "commit": runCommit, "beside": []any{map[string]any{"repository": "novox/mesh-sdk", "commit": runCommit}}}}))
|
||||
out, err := RunAsk(context.Background(), r.steps(), ask(sdkstep.CheckJob{Levels: []string{sdkstep.LevelUnit}, Kept: true,
|
||||
Fetch: sdkstep.Fetch{Repository: "novox/x", Commit: runCommit, Beside: []sdkstep.FetchBeside{{Repository: "novox/mesh-sdk", Commit: runCommit}}}}))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if l := out.(delivery.CheckAnswer).Levels[0]; len(l.Kept) != 0 || l.Digest != nil {
|
||||
if l := out.(CheckAnswer).Levels[0]; len(l.Kept) != 0 || l.Digest != nil {
|
||||
t.Fatalf("a digest without the siblings kept %+v", l)
|
||||
}
|
||||
}
|
||||
@@ -269,15 +273,15 @@ func TestRunAskChecksNodeModulesAndConflicts(t *testing.T) {
|
||||
tests: map[string]bool{"a": true, "b": true}}
|
||||
s := r.steps()
|
||||
s.Digest = nil
|
||||
if _, err := RunAsk(context.Background(), s, ask(delivery.CheckJob{Levels: []string{delivery.LevelUnit}, Fetch: map[string]any{"repository": "novox/x", "commit": runCommit}})); err != nil {
|
||||
if _, err := RunAsk(context.Background(), s, ask(sdkstep.CheckJob{Levels: []string{sdkstep.LevelUnit}, Fetch: sdkstep.Fetch{Repository: "novox/x", Commit: runCommit}})); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if fmt.Sprint(r.calls[1:]) != `[test {"level":"unit","module":"b","workspace":"/w"} test {"level":"unit","module":"a","workspace":"/w"}]` {
|
||||
t.Fatalf("calls %v", r.calls)
|
||||
}
|
||||
r = &recorder{fetch: map[string]any{"workspace": "/w", "conflicts": []string{"go.mod"}}}
|
||||
out, err := RunAsk(context.Background(), r.steps(), ask(delivery.CheckJob{Levels: []string{delivery.LevelUnit}, Fetch: map[string]any{"repository": "novox/x", "commit": runCommit}}))
|
||||
if err != nil || len(r.calls) != 1 || len(out.(delivery.CheckAnswer).Levels) != 0 {
|
||||
out, err := RunAsk(context.Background(), r.steps(), ask(sdkstep.CheckJob{Levels: []string{sdkstep.LevelUnit}, Fetch: sdkstep.Fetch{Repository: "novox/x", Commit: runCommit}}))
|
||||
if err != nil || len(r.calls) != 1 || len(out.(CheckAnswer).Levels) != 0 {
|
||||
t.Fatalf("a conflict: %v %v", err, r.calls)
|
||||
}
|
||||
}
|
||||
@@ -301,8 +305,8 @@ func TestRunAskRunsEveryPackageAndLevelAfterAFailure(t *testing.T) {
|
||||
if _, ok := row.tests["x/pkg/c"]; ok {
|
||||
r.digests["x/pkg/c"] = "d-c"
|
||||
}
|
||||
out, err := RunAsk(context.Background(), r.steps(), ask(delivery.CheckJob{Levels: []string{delivery.LevelUnit, delivery.LevelIntegration},
|
||||
Fetch: map[string]any{"repository": "novox/x", "commit": runCommit}}))
|
||||
out, err := RunAsk(context.Background(), r.steps(), ask(sdkstep.CheckJob{Levels: []string{sdkstep.LevelUnit, sdkstep.LevelIntegration},
|
||||
Fetch: sdkstep.Fetch{Repository: "novox/x", Commit: runCommit}}))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -317,7 +321,7 @@ func TestRunAskRunsEveryPackageAndLevelAfterAFailure(t *testing.T) {
|
||||
if want := append(slices.Clone(row.want), row.want...); fmt.Sprint(got) != fmt.Sprint(want) {
|
||||
t.Errorf("%s: tested %v, want %v", row.name, got, want)
|
||||
}
|
||||
if a := out.(delivery.CheckAnswer); len(a.Levels) != 2 || len(a.Levels[0].Tests) != len(row.want) {
|
||||
if a := out.(CheckAnswer); len(a.Levels) != 2 || len(a.Levels[0].Tests) != len(row.want) {
|
||||
t.Errorf("%s: levels %+v, want both, each with every test result", row.name, a.Levels)
|
||||
}
|
||||
}
|
||||
@@ -326,12 +330,12 @@ func TestRunAskRunsEveryPackageAndLevelAfterAFailure(t *testing.T) {
|
||||
tests: map[string]bool{"a": true, "b": false}}
|
||||
s := r.steps()
|
||||
s.Digest = nil
|
||||
out, err := RunAsk(context.Background(), s, ask(delivery.CheckJob{Levels: []string{delivery.LevelUnit, delivery.LevelIntegration},
|
||||
Fetch: map[string]any{"repository": "novox/x", "commit": runCommit}}))
|
||||
out, err := RunAsk(context.Background(), s, ask(sdkstep.CheckJob{Levels: []string{sdkstep.LevelUnit, sdkstep.LevelIntegration},
|
||||
Fetch: sdkstep.Fetch{Repository: "novox/x", Commit: runCommit}}))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if a := out.(delivery.CheckAnswer); len(r.calls) != 5 || len(a.Levels) != 2 || len(a.Levels[0].Tests) != 2 {
|
||||
if a := out.(CheckAnswer); len(r.calls) != 5 || len(a.Levels) != 2 || len(a.Levels[0].Tests) != 2 {
|
||||
t.Fatalf("calls %v, levels %+v: want b then a at each of the two levels", r.calls, a.Levels)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package buildstep
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"math"
|
||||
"os"
|
||||
"runtime"
|
||||
@@ -10,7 +11,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep/buildcpu"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// **A build seat's capacity follows the CPU its node gives builds** (issue 1806's follow-up; issue 1890, 2026-10-26
|
||||
@@ -30,7 +30,7 @@ const CoresPerStepSetting = "cores-per-step"
|
||||
// check on mesh-build-go takes what it is given (three at once took 965 % of the workstation's 1200 % slice, about 4 cores each,
|
||||
// and ran past the 30-minute step bound), so a Go step is given 6, half again what starved; a Node step (tsc, esbuild, a
|
||||
// test runner: median 15 s) and an image build (buildah, its RUN lines mostly fetches) need 2.
|
||||
var DefaultCoresPerStep = map[string]float64{delivery.AskGo: 6, delivery.AskNode: 2, delivery.AskImage: 2}
|
||||
var DefaultCoresPerStep = map[string]float64{sdkstep.AskGo: 6, sdkstep.AskNode: 2, sdkstep.AskImage: 2}
|
||||
|
||||
// Quota is the build slice's CPU quota as this process's cgroup holds it, in percent of one processor (1200 is 12
|
||||
// cores), and where it was read: the cgroup's cpu.max (buildcpu.ReadInForce, the tighter of its unit's and its slice's),
|
||||
@@ -54,11 +54,11 @@ func Quota() (percent int, from string) {
|
||||
// one processor, 0 when none is known) and the cores one of its steps needs. Without a quota it is the configured one;
|
||||
// with one, min(configured, floor(quota cores / coresPerStep)), and one at least while the configured one is above
|
||||
// zero. A coresPerStep of zero or less is taken as 1.
|
||||
func EffectiveCapacity(configured, quotaPercent int, quotaFrom string, coresPerStep float64) delivery.Capacity {
|
||||
func EffectiveCapacity(configured, quotaPercent int, quotaFrom string, coresPerStep float64) sdkstep.Capacity {
|
||||
if coresPerStep <= 0 || math.IsNaN(coresPerStep) || math.IsInf(coresPerStep, 0) {
|
||||
coresPerStep = 1
|
||||
}
|
||||
c := delivery.Capacity{Configured: configured, Effective: configured, QuotaFrom: quotaFrom, CoresPerStep: coresPerStep}
|
||||
c := sdkstep.Capacity{Configured: configured, Effective: configured, QuotaFrom: quotaFrom, CoresPerStep: coresPerStep}
|
||||
switch {
|
||||
case configured <= 0:
|
||||
c.Effective = 0
|
||||
|
||||
@@ -3,6 +3,7 @@ package conformance
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
buildrun "git.novox.be/novox/mesh-delivery/sdk/buildrun"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -25,14 +26,14 @@ func TestTheFailureContract(t *testing.T) {
|
||||
// 1890/AC7: the SDK's start sweep keeps the left-running case, and the kit refuses a sweep that ends nothing or ends
|
||||
// everything.
|
||||
t.Run("the left-running case", func(t *testing.T) {
|
||||
LeftRunning(t, func(ctx context.Context, scan sdkstep.Scan, node string, before time.Time, logf func(string, ...any)) {
|
||||
sdkstep.EndStaleAtStart(ctx, scan, node, before, logf)
|
||||
LeftRunning(t, func(ctx context.Context, scan buildrun.Scan, node string, before time.Time, logf func(string, ...any)) {
|
||||
buildrun.EndStaleAtStart(ctx, scan, node, before, logf)
|
||||
})
|
||||
if p := JudgeLeftRunning(func(context.Context, sdkstep.Scan, string, time.Time, func(string, ...any)) {}); len(p) != 11 {
|
||||
if p := JudgeLeftRunning(func(context.Context, buildrun.Scan, string, time.Time, func(string, ...any)) {}); len(p) != 11 {
|
||||
t.Errorf("a sweep that ends nothing: %d problems, want 11 (three steps not ended and unsaid, one owner unnamed, two asks not deleted and unsaid):\n%s", len(p), strings.Join(p, "\n"))
|
||||
}
|
||||
wrongNode := func(ctx context.Context, scan sdkstep.Scan, _ string, before time.Time, logf func(string, ...any)) {
|
||||
sdkstep.EndStaleAtStart(ctx, scan, "kitother", before, logf)
|
||||
wrongNode := func(ctx context.Context, scan buildrun.Scan, _ string, before time.Time, logf func(string, ...any)) {
|
||||
buildrun.EndStaleAtStart(ctx, scan, "kitother", before, logf)
|
||||
}
|
||||
if p := JudgeLeftRunning(wrongNode); len(p) == 0 {
|
||||
t.Error("a sweep of another node's steps is not refused")
|
||||
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
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"
|
||||
)
|
||||
|
||||
// **A step left running by a previous process** (novox/hq issue 1890, note 21): when a delivery replaced mesh-build-go
|
||||
@@ -23,9 +23,9 @@ import (
|
||||
// catalogue check (mesh-catalog checks/failurekit) refuses a build module whose tests do not call LeftRunning.
|
||||
|
||||
// Sweep is a build module's own start sweep, as its main runs it once at its start: it ends node's steps in scan that
|
||||
// no process of the module runs any more (buildstep.EndStaleAtStart, here with the runtime already serving), before
|
||||
// no process of the module runs any more (buildrun.EndStaleAtStart, here with the runtime already serving), before
|
||||
// being the start of the process, saying each end with logf.
|
||||
type Sweep func(ctx context.Context, scan buildstep.Scan, node string, before time.Time, logf func(string, ...any))
|
||||
type Sweep func(ctx context.Context, scan buildrun.Scan, node string, before time.Time, logf func(string, ...any))
|
||||
|
||||
// LeftNode is the node the case's steps are of.
|
||||
const LeftNode = "kitnode"
|
||||
@@ -47,12 +47,12 @@ func leftCases() []leftCase {
|
||||
{id: "mvpwlkzi7e6ca8", tool: "check ask", owner: gone, age: 5 * time.Minute, deleted: true}, // note 21's ghost
|
||||
{id: "mvpwlkzi7e6ca9", tool: "delivery ask", owner: gone, age: -time.Second, deleted: true},
|
||||
{id: "gonehand1", tool: "build_x_test", owner: gone, age: time.Minute, ended: true},
|
||||
{id: "oldhand1", tool: "build_x_fetch", age: time.Hour, ended: true}, // no owner: an older build's
|
||||
{id: "mvpwlkzi7e6caa", tool: "check ask", age: time.Hour, ended: true}, // no owner, past the bound
|
||||
{id: "mvpwlkzi7e6cab", tool: "check ask", age: 5 * time.Minute}, // no owner, the stream's still
|
||||
{id: "mvpwlkzi7e6cac", tool: "check ask", owner: buildstep.Owner, age: time.Minute}, // this process's
|
||||
{id: "minehand1", tool: "build_x_test", owner: buildstep.Owner, age: -time.Second},
|
||||
{id: "donehand1", tool: "build_x_test", owner: gone, age: time.Hour, state: delivery.StepPassed},
|
||||
{id: "oldhand1", tool: "build_x_fetch", age: time.Hour, ended: true}, // no owner: an older build's
|
||||
{id: "mvpwlkzi7e6caa", tool: "check ask", age: time.Hour, ended: true}, // no owner, past the bound
|
||||
{id: "mvpwlkzi7e6cab", tool: "check ask", age: 5 * time.Minute}, // no owner, the stream's still
|
||||
{id: "mvpwlkzi7e6cac", tool: "check ask", owner: buildrun.Owner, age: time.Minute}, // this process's
|
||||
{id: "minehand1", tool: "build_x_test", owner: buildrun.Owner, age: -time.Second},
|
||||
{id: "donehand1", tool: "build_x_test", owner: gone, age: time.Hour, state: sdkstep.StepPassed},
|
||||
{id: "otherhand1", tool: "build_x_test", owner: gone, age: time.Hour, node: "kitother"},
|
||||
}
|
||||
}
|
||||
@@ -76,10 +76,10 @@ func JudgeLeftRunning(sweep Sweep) []string {
|
||||
node = LeftNode
|
||||
}
|
||||
if state == "" {
|
||||
state = delivery.StepRunning
|
||||
state = sdkstep.StepRunning
|
||||
}
|
||||
key, _ := delivery.StepKey(node, c.id)
|
||||
raw, err := delivery.EncodeStep(delivery.Step{ID: c.id, Tool: c.tool, Node: node, State: state, Started: before.Add(-c.age).UTC(), Owner: c.owner})
|
||||
key, _ := sdkstep.StepKey(node, c.id)
|
||||
raw, err := sdkstep.EncodeStep(sdkstep.Step{ID: c.id, Tool: c.tool, Node: node, State: state, Started: before.Add(-c.age).UTC(), Owner: c.owner})
|
||||
if err != nil {
|
||||
return []string{"the kit's step " + c.id + ": " + err.Error()}
|
||||
}
|
||||
@@ -87,9 +87,9 @@ func JudgeLeftRunning(sweep Sweep) []string {
|
||||
}
|
||||
served := make(chan struct{})
|
||||
close(served)
|
||||
was := buildstep.StaleServed
|
||||
buildstep.StaleServed = func() <-chan struct{} { return served }
|
||||
defer func() { buildstep.StaleServed = was }()
|
||||
was := buildrun.StaleServed
|
||||
buildrun.StaleServed = func() <-chan struct{} { return served }
|
||||
defer func() { buildrun.StaleServed = was }()
|
||||
var mu sync.Mutex
|
||||
var said []string
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
@@ -108,7 +108,7 @@ func JudgeLeftRunning(sweep Sweep) []string {
|
||||
if node == "" {
|
||||
node = LeftNode
|
||||
}
|
||||
key, _ := delivery.StepKey(node, c.id)
|
||||
key, _ := sdkstep.StepKey(node, c.id)
|
||||
raw := st.get(key)
|
||||
if c.deleted {
|
||||
if raw != nil {
|
||||
@@ -124,17 +124,17 @@ func JudgeLeftRunning(sweep Sweep) []string {
|
||||
problems = append(problems, "left running: the step "+key+" is gone after the sweep, want it kept or ended")
|
||||
continue
|
||||
}
|
||||
s, err := delivery.DecodeStep(raw)
|
||||
s, err := sdkstep.DecodeStep(raw)
|
||||
if err != nil {
|
||||
problems = append(problems, key+": "+err.Error())
|
||||
continue
|
||||
}
|
||||
want := c.state
|
||||
if want == "" {
|
||||
want = delivery.StepRunning
|
||||
want = sdkstep.StepRunning
|
||||
}
|
||||
if c.ended {
|
||||
want = delivery.StepFailed
|
||||
want = sdkstep.StepFailed
|
||||
}
|
||||
if s.State != want {
|
||||
problems = append(problems, fmt.Sprintf("left running: the step %s (%s, owner %q, %s before the start) is %s after the sweep, want %s", key, c.tool, c.owner, c.age, s.State, want))
|
||||
@@ -169,8 +169,8 @@ func (s *steps) get(key string) []byte {
|
||||
return s.vals[key]
|
||||
}
|
||||
|
||||
func (s *steps) scan() buildstep.Scan {
|
||||
return buildstep.Scan{
|
||||
func (s *steps) scan() buildrun.Scan {
|
||||
return buildrun.Scan{
|
||||
Keys: func() ([]string, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
@@ -5,13 +5,12 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// credentialField is the node runtime's rule for a state value (mesh-tools node-tools/internal/bus/state.go, novox/hq
|
||||
@@ -59,11 +58,11 @@ func credentialField(value json.RawMessage) string {
|
||||
// runtimeState is a steps state that refuses a value as the node runtime does, and keeps every value it took.
|
||||
type runtimeState struct {
|
||||
mu sync.Mutex
|
||||
took map[string][]delivery.Step
|
||||
took map[string][]sdkstep.Step
|
||||
refused int
|
||||
}
|
||||
|
||||
func newRuntimeState() *runtimeState { return &runtimeState{took: map[string][]delivery.Step{}} }
|
||||
func newRuntimeState() *runtimeState { return &runtimeState{took: map[string][]sdkstep.Step{}} }
|
||||
|
||||
func (s *runtimeState) put(key string, value any) (uint64, error) {
|
||||
raw, _ := value.(json.RawMessage)
|
||||
@@ -73,7 +72,7 @@ func (s *runtimeState) put(key string, value any) (uint64, error) {
|
||||
s.mu.Unlock()
|
||||
return 0, fmt.Errorf("steps.%s carries a field %q, which names a credential", key, f)
|
||||
}
|
||||
st, err := delivery.DecodeStep(raw)
|
||||
st, err := sdkstep.DecodeStep(raw)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -83,12 +82,12 @@ func (s *runtimeState) put(key string, value any) (uint64, error) {
|
||||
return uint64(len(s.took[key])), nil
|
||||
}
|
||||
|
||||
func (s *runtimeState) last(key string) (delivery.Step, bool) {
|
||||
func (s *runtimeState) last(key string) (sdkstep.Step, bool) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
t := s.took[key]
|
||||
if len(t) == 0 {
|
||||
return delivery.Step{}, false
|
||||
return sdkstep.Step{}, false
|
||||
}
|
||||
return t[len(t)-1], true
|
||||
}
|
||||
@@ -109,8 +108,8 @@ func TestADigestOfAPackageNamedTokenIsStoredAsAList(t *testing.T) {
|
||||
digests: map[string]string{"x/internal/token": "d-token", "x/pkg/a": "d-a"},
|
||||
tests: map[string]bool{"x/internal/token": true, "x/pkg/a": true},
|
||||
}
|
||||
out, err := RunAsk(context.Background(), r.steps(), ask(delivery.CheckJob{Levels: []string{delivery.LevelUnit},
|
||||
Fetch: map[string]any{"repository": "novox/x", "commit": runCommit}}))
|
||||
out, err := RunAsk(context.Background(), r.steps(), ask(sdkstep.CheckJob{Levels: []string{sdkstep.LevelUnit},
|
||||
Fetch: sdkstep.Fetch{Repository: "novox/x", Commit: runCommit}}))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -119,21 +118,21 @@ func TestADigestOfAPackageNamedTokenIsStoredAsAList(t *testing.T) {
|
||||
t.Fatalf("the rule does not refuse the form before issue 1930 (%s): the test proves nothing", old)
|
||||
}
|
||||
st := newRuntimeState()
|
||||
if err := finish(st.put, "shanks.a1", delivery.Step{ID: "a1", Tool: "check ask", Node: "shanks", State: delivery.StepRunning, Started: time.Now()}, out, nil, t.Logf); err != nil {
|
||||
if err := finish(st.put, "shanks.a1", sdkstep.Step{ID: "a1", Tool: "check ask", Node: "shanks", State: sdkstep.StepRunning, Started: time.Now()}, out, nil, t.Logf); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s, ok := st.last("shanks.a1")
|
||||
if !ok || s.State != delivery.StepPassed || st.refused != 0 {
|
||||
if !ok || s.State != sdkstep.StepPassed || st.refused != 0 {
|
||||
t.Fatalf("the end %+v (written %v, refused %d), want it passed and taken at once", s, ok, st.refused)
|
||||
}
|
||||
var a delivery.CheckAnswer
|
||||
var a CheckAnswer
|
||||
if err := json.Unmarshal(s.Result, &a); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(a.Levels) != 1 || a.Levels[0].Digest == nil {
|
||||
t.Fatalf("levels %+v, want the unit level with its digest", a.Levels)
|
||||
}
|
||||
want := []delivery.PackageDigest{{Package: "x/internal/token", Digest: "d-token"}, {Package: "x/pkg/a", Digest: "d-a"}}
|
||||
want := []sdkstep.PackageDigest{{Package: "x/internal/token", Digest: "d-token"}, {Package: "x/pkg/a", Digest: "d-a"}}
|
||||
if got := a.Levels[0].Digest.Packages; fmt.Sprint(got) != fmt.Sprint(want) {
|
||||
t.Errorf("packages %v, want %v", got, want)
|
||||
}
|
||||
@@ -148,9 +147,9 @@ func TestARefusedEndIsSaidAndEndsTheStepFailed(t *testing.T) {
|
||||
fastEnds(t)
|
||||
big := errors.New("the value carries a field \"x/internal/token\", which names a credential")
|
||||
var refused int
|
||||
var stored []delivery.Step
|
||||
var stored []sdkstep.Step
|
||||
put := func(key string, value any) (uint64, error) {
|
||||
st, _ := delivery.DecodeStep(value.(json.RawMessage))
|
||||
st, _ := sdkstep.DecodeStep(value.(json.RawMessage))
|
||||
if len(st.Result) > 0 {
|
||||
refused++
|
||||
return 0, big
|
||||
@@ -160,7 +159,7 @@ func TestARefusedEndIsSaidAndEndsTheStepFailed(t *testing.T) {
|
||||
}
|
||||
var logged []string
|
||||
logf := func(format string, args ...any) { logged = append(logged, fmt.Sprintf(format, args...)) }
|
||||
err := finish(put, "shanks.a2", delivery.Step{ID: "a2", Tool: "check ask", Node: "shanks", State: delivery.StepRunning, Started: time.Now()}, map[string]any{"levels": 1}, nil, logf)
|
||||
err := finish(put, "shanks.a2", sdkstep.Step{ID: "a2", Tool: "check ask", Node: "shanks", State: sdkstep.StepRunning, Started: time.Now()}, map[string]any{"levels": 1}, nil, logf)
|
||||
if err != nil {
|
||||
t.Fatalf("finish answered %v, want the small end written", err)
|
||||
}
|
||||
@@ -168,7 +167,7 @@ func TestARefusedEndIsSaidAndEndsTheStepFailed(t *testing.T) {
|
||||
t.Fatalf("refused %d, stored %d, want %d refusals and one small end", refused, len(stored), EndAttempts)
|
||||
}
|
||||
s := stored[0]
|
||||
if s.State != delivery.StepFailed || len(s.Result) != 0 || !strings.Contains(s.Reason, "could not be written") ||
|
||||
if s.State != sdkstep.StepFailed || len(s.Result) != 0 || !strings.Contains(s.Reason, "could not be written") ||
|
||||
!strings.Contains(s.Reason, big.Error()) || !strings.Contains(s.Reason, "passed") {
|
||||
t.Errorf("the small end %+v, want failed, no result, naming the write's error and that it passed", s)
|
||||
}
|
||||
@@ -176,7 +175,7 @@ func TestARefusedEndIsSaidAndEndsTheStepFailed(t *testing.T) {
|
||||
t.Errorf("logged %q, want the refusal said", logged)
|
||||
}
|
||||
none := func(string, any) (uint64, error) { return 0, errors.New("the bus is gone") }
|
||||
if err := finish(none, "shanks.a3", delivery.Step{ID: "a3", Tool: "check ask", Node: "shanks", Started: time.Now()}, nil, nil, logf); err == nil || !strings.Contains(err.Error(), "the bus is gone") {
|
||||
if err := finish(none, "shanks.a3", sdkstep.Step{ID: "a3", Tool: "check ask", Node: "shanks", Started: time.Now()}, nil, nil, logf); err == nil || !strings.Contains(err.Error(), "the bus is gone") {
|
||||
t.Errorf("finish with no write possible answered %v, want the error", err)
|
||||
}
|
||||
}
|
||||
@@ -192,16 +191,16 @@ func TestAnAskWhoseEndCannotBeWrittenIsNotAcked(t *testing.T) {
|
||||
}{{"the small end written", true, true}, {"no end written", false, false}} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
src := NewFakeAsks()
|
||||
a := testAsk(delivery.AskCheck)
|
||||
a := testAsk(sdkstep.AskCheck)
|
||||
_ = src.PublishAsk(a)
|
||||
var mu sync.Mutex
|
||||
var ends []delivery.Step
|
||||
var ends []sdkstep.Step
|
||||
put := func(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
|
||||
}
|
||||
if st.State != delivery.StepRunning && (len(st.Result) > 0 || !tc.smallOK) {
|
||||
if st.State != sdkstep.StepRunning && (len(st.Result) > 0 || !tc.smallOK) {
|
||||
return 0, errors.New("refused")
|
||||
}
|
||||
mu.Lock()
|
||||
@@ -209,9 +208,9 @@ func TestAnAskWhoseEndCannotBeWrittenIsNotAcked(t *testing.T) {
|
||||
mu.Unlock()
|
||||
return 1, nil
|
||||
}
|
||||
p := &Puller{Source: src, Language: delivery.AskGo, Node: "shanks", Put: put, Capacity: func() int { return 1 },
|
||||
p := &Puller{Source: src, Language: sdkstep.AskGo, Node: "shanks", Put: put, Capacity: func() int { return 1 },
|
||||
PullWait: 10 * time.Millisecond, Logf: t.Logf,
|
||||
Run: func(ctx context.Context, a delivery.StepAsk) (any, error) { return map[string]any{"ran": true}, nil }}
|
||||
Run: func(ctx context.Context, a sdkstep.StepAsk) (any, error) { return map[string]any{"ran": true}, nil }}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
go p.Pull(ctx)
|
||||
@@ -225,7 +224,7 @@ func TestAnAskWhoseEndCannotBeWrittenIsNotAcked(t *testing.T) {
|
||||
defer mu.Unlock()
|
||||
if tc.smallOK {
|
||||
last := ends[len(ends)-1]
|
||||
if last.State != delivery.StepFailed || !strings.Contains(last.Reason, "refused") {
|
||||
if last.State != sdkstep.StepFailed || !strings.Contains(last.Reason, "refused") {
|
||||
t.Errorf("the end %+v, want the small failed end", last)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ package buildstep
|
||||
// it was built with (its version is this package's requirement, so a new generator is a new build module and a new
|
||||
// fingerprint of what it builds) and names each file out of step: one that differs, one missing, one under a generated
|
||||
// root the generator did not write. The test step fails on any; a trunk build's ask answers them in its out-of-step
|
||||
// (delivery.BuildAnswer.OutOfStep), so the builder's build end raises generated-types-out-of-step even where no test
|
||||
// (sdkstep.BuildAnswer.OutOfStep), so the builder's build end raises generated-types-out-of-step even where no test
|
||||
// step runs. Each build module compares its own roots: mesh-build-go `go`, mesh-build-node `ts`.
|
||||
|
||||
import (
|
||||
|
||||
@@ -3,6 +3,7 @@ package buildstep
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
@@ -11,7 +12,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep/seatgen"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// contracts writes a contracts module of one seat, its generated files as the generator writes them, and answers its
|
||||
@@ -113,16 +113,16 @@ func TestAContractsModulesGeneratedFilesAreComparedWithTheGenerator(t *testing.T
|
||||
}
|
||||
return map[string]any{"digest": "sha256:aa"}, nil
|
||||
}}
|
||||
job := delivery.BuildJob{Module: "c", Path: filepath.Base(dir), Fetch: map[string]any{"repository": "novox/c"},
|
||||
Artifacts: []delivery.AskArtifact{{Artifact: "go"}}}
|
||||
job := sdkstep.BuildJob{Module: "c", Path: filepath.Base(dir), Fetch: &sdkstep.Fetch{Repository: "novox/c", Commit: "0123456789abcdef0123456789abcdef01234567"},
|
||||
Artifacts: []sdkstep.AskArtifact{{Artifact: "go"}}}
|
||||
out, err := RunAsk(context.Background(), steps, ask(job))
|
||||
if a, ok := out.(delivery.BuildAnswer); err != nil || !ok || !reflect.DeepEqual(a.OutOfStep, sorted(goFile, "go/extra.go")) || len(a.Artifacts) != 1 {
|
||||
if a, ok := out.(sdkstep.BuildAnswer); err != nil || !ok || !reflect.DeepEqual(a.OutOfStep, sorted(goFile, "go/extra.go")) || len(a.Artifacts) != 1 {
|
||||
t.Errorf("the build ask's out-of-step: %+v %v", out, err)
|
||||
}
|
||||
job.Artifacts = append(job.Artifacts, delivery.AskArtifact{Artifact: "go-other"})
|
||||
job.Artifacts = append(job.Artifacts, sdkstep.AskArtifact{Artifact: "go-other"})
|
||||
built = 0
|
||||
out, err = RunAsk(context.Background(), steps, ask(job))
|
||||
if a, ok := out.(delivery.BuildAnswer); err == nil || !ok || len(a.OutOfStep) != 2 {
|
||||
if a, ok := out.(sdkstep.BuildAnswer); err == nil || !ok || len(a.OutOfStep) != 2 {
|
||||
t.Errorf("a failed build answered no out-of-step: %+v %v", out, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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/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
|
||||
git.novox.be/novox/mesh-delivery/contracts/go/clients/containerimageregistryclient v0.0.0-00010101000000-000000000000
|
||||
git.novox.be/novox/mesh-delivery/contracts/go/clients/gopackageregistryclient v0.0.0-00010101000000-000000000000
|
||||
|
||||
@@ -4,15 +4,13 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// Guard is a build module's check that the toolchain installed on its node is the one its recipe builds with
|
||||
// (novox/hq ADR 0469 §1; issue 1473). The same guard serves ADR 0470 §4's postgres major for the integration
|
||||
// level, with its own Probe and Want.
|
||||
type Guard struct {
|
||||
// Language is the toolchain's key part: go or typescript (delivery.ToolchainKey).
|
||||
// Language is the toolchain's key part: go or typescript (ToolchainKey).
|
||||
Language string
|
||||
// Want is the toolchain version the recipe builds with, and Recipe the recipe's own version. The indexer
|
||||
// compares both texts as they are written here.
|
||||
@@ -20,7 +18,7 @@ type Guard struct {
|
||||
// Probe reads the installed version (go version, node --version).
|
||||
Probe func(ctx context.Context) (installed string, err error)
|
||||
Node string
|
||||
// Put is the toolchains state's Put (stdio.State(delivery.ToolchainsState).Put).
|
||||
// Put is the toolchains state's Put (stdio.State(the seat's toolchains state).Put).
|
||||
Put Put
|
||||
}
|
||||
|
||||
@@ -33,11 +31,11 @@ func (g Guard) Check(ctx context.Context) error {
|
||||
if err != nil {
|
||||
return &Refusal{Text: fmt.Sprintf("the installed %s version could not be read: %v", g.Language, err), Want: g.Want}
|
||||
}
|
||||
key, err := delivery.ToolchainKey(g.Node, g.Language)
|
||||
key, err := ToolchainKey(g.Node, g.Language)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
raw, err := delivery.EncodeToolchain(delivery.Toolchain{Language: g.Language, Version: installed, Recipe: g.Recipe, At: now().UTC()})
|
||||
raw, err := EncodeToolchain(Toolchain{Language: g.Language, Version: installed, Recipe: g.Recipe, At: now().UTC()})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -6,14 +6,12 @@ import (
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
type toolchainKeys map[string]delivery.Toolchain
|
||||
type toolchainKeys map[string]Toolchain
|
||||
|
||||
func (k toolchainKeys) put(key string, value any) (uint64, error) {
|
||||
tc, err := delivery.DecodeToolchain(value.(json.RawMessage))
|
||||
tc, err := DecodeToolchain(value.(json.RawMessage))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
builds "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
|
||||
"io"
|
||||
"log"
|
||||
"slices"
|
||||
@@ -21,11 +22,10 @@ import (
|
||||
reg "git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry"
|
||||
"git.novox.be/novox/mesh-delivery/sdk/parts"
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// NumberedPattern is the event type a build module consumes for the late publish, as its manifest's `consumes` names it.
|
||||
const NumberedPattern = delivery.BuilderModule + "." + delivery.VersionNumberedEvent
|
||||
const NumberedPattern = builds.BuilderModule + "." + builds.VersionNumberedEvent
|
||||
|
||||
// errNotHere is a held part's Open: its bytes did not stay on the node.
|
||||
var errNotHere = errors.New("the built bytes did not stay on the node; the holder must hold the digest")
|
||||
@@ -35,7 +35,7 @@ var errNotHere = errors.New("the built bytes did not stay on the node; the holde
|
||||
// kind, another seat (an event naming none is the artifact seat's, as the builder wrote it before images), or a version
|
||||
// numbered with no digest.
|
||||
func LatePublish(ctx context.Context, call SeatCall, body []byte, seat string, kinds ...string) (bool, error) {
|
||||
n, err := delivery.DecodeVersionNumbered(body)
|
||||
n, err := builds.DecodeVersionNumbered(body)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
@@ -5,13 +5,13 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
reg "git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// CAT-NR1: a build's publish on a seat says asked, waiting on the seat (on every tick of NarrateEvery while the holder
|
||||
@@ -52,9 +52,9 @@ func TestABuildNarratesItsPublishOnASeat(t *testing.T) {
|
||||
|
||||
var said []string
|
||||
wrote := make(chan struct{}, 16)
|
||||
step := delivery.Step{ID: "a1", Tool: "build", Node: "ace", State: delivery.StepRunning, Started: clock}
|
||||
step := sdkstep.Step{ID: "a1", Tool: "build", Node: "ace", State: sdkstep.StepRunning, Started: clock}
|
||||
ph := &phases{key: "ace.a1", step: step, put: func(key string, value any) (uint64, error) {
|
||||
var s delivery.Step
|
||||
var s sdkstep.Step
|
||||
_ = json.Unmarshal(value.(json.RawMessage), &s)
|
||||
phasesMu.Lock()
|
||||
said = append(said, s.Phase)
|
||||
|
||||
@@ -2,18 +2,17 @@ package buildstep
|
||||
|
||||
import (
|
||||
"context"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// **A running ask says its phases** (issue 1890, 2026-10-26 19:09: a mesh-catalog try's step on the workstation ran 30 minutes
|
||||
// and its module's journal held no line naming its ask, so nobody could tell whether it fetched, linted or tested). The
|
||||
// puller hands every ask's run a phase reporter in its context; RunAsk and a module's own step bodies call Phase as they
|
||||
// move (fetch and merge, a level's digest, lint, a level's tests), and each phase is logged with the ask's id and its
|
||||
// time since the start, and written to the ask's running step key (delivery.Step.Phase), so its asker and the delivery
|
||||
// time since the start, and written to the ask's running step key (sdkstep.Step.Phase), so its asker and the delivery
|
||||
// view read where it stands from the bus.
|
||||
|
||||
type phaseKey struct{}
|
||||
@@ -30,7 +29,7 @@ type phases struct {
|
||||
mu sync.Mutex
|
||||
put Put
|
||||
key string
|
||||
step delivery.Step
|
||||
step sdkstep.Step
|
||||
ended bool
|
||||
logf func(format string, args ...any)
|
||||
prefix string
|
||||
@@ -59,7 +58,7 @@ func (r *phases) enter(phase string) {
|
||||
}
|
||||
|
||||
// end stops the writes (the step's end is written after it) and answers the step with its last phase, for the end.
|
||||
func (r *phases) end() delivery.Step {
|
||||
func (r *phases) end() sdkstep.Step {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
r.ended = true
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
buildrun "git.novox.be/novox/mesh-delivery/sdk/buildrun"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -12,7 +13,6 @@ import (
|
||||
"time"
|
||||
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// **The pull loop the build modules share** (novox/hq ADR 0525 §4-§6, ADR 0536 §4; issue 1887). A build module on a node
|
||||
@@ -55,23 +55,23 @@ var (
|
||||
// Puller pulls a build seat's asks and runs them.
|
||||
type Puller struct {
|
||||
Source AskSource
|
||||
Language string // the seat's language: delivery.AskGo, AskNode or AskImage
|
||||
Language string // the seat's language: sdkstep.AskGo, AskNode or AskImage
|
||||
Node string // the node this holder runs on, the first part of its step keys
|
||||
Put Put // writes the module's steps state
|
||||
// Capacity is how many asks the node runs at once; read before every pull, so a change takes effect at the next
|
||||
// one. 0 pulls nothing.
|
||||
Capacity func() int
|
||||
// Run runs one ask's job; a *Refusal ends its step refused. Its result is the step's result.
|
||||
Run func(ctx context.Context, ask delivery.StepAsk) (any, error)
|
||||
Run func(ctx context.Context, ask sdkstep.StepAsk) (any, error)
|
||||
// PullWait and Heartbeat default to DefaultPullWait and DefaultHeartbeat.
|
||||
PullWait time.Duration
|
||||
Heartbeat time.Duration
|
||||
Logf func(format string, args ...any)
|
||||
// Load reads one key of the state the node's pause is kept in (nil and no error when it is absent). When set, Pull
|
||||
// reads the node's pause key (delivery.PauseKey) before it pulls, and Pause and Resume write it with PausePut, so a
|
||||
// reads the node's pause key (sdkstep.PauseKey) before it pulls, and Pause and Resume write it with PausePut, so a
|
||||
// pause outlives a restart.
|
||||
Load func(key string) ([]byte, error)
|
||||
// PausePut writes the node's pause key: the module's pauses state (delivery.PausesState), which has no age, so a
|
||||
// PausePut writes the node's pause key: the module's pauses state (sdkstep.PausesState), which has no age, so a
|
||||
// pause never ends by itself while the steps state drops its old keys. Put when nil.
|
||||
PausePut Put
|
||||
// WatchConfig, when set, follows the module's config value `capacity` (CapacitySetting) as mesh-config resolves it
|
||||
@@ -94,8 +94,8 @@ type Puller struct {
|
||||
// taken, and the asks go to a node that can run them instead of failing here. The module calls Wake when its answer
|
||||
// changes. Nil is always eligible.
|
||||
Eligible func() string
|
||||
// Has says whether this node has a need an ask names (delivery.AskNeeds, issue 890). An ask needing what it lacks is
|
||||
// declined: its step ends refused as declined (delivery.StepDeclined), the message is dropped for its asker to put
|
||||
// Has says whether this node has a need an ask names (sdkstep.AskNeeds, issue 890). An ask needing what it lacks is
|
||||
// declined: its step ends refused as declined (sdkstep.StepDeclined), the message is dropped for its asker to put
|
||||
// again, and the worker it came from is not looked at again until the next pull's wait, so a node lacking the need
|
||||
// does not take the asker's next copy at once. Nil has nothing.
|
||||
Has func(need string) bool
|
||||
@@ -129,7 +129,7 @@ type Puller struct {
|
||||
hoursFault string
|
||||
hasHours bool
|
||||
// judged is the capacity said last (log and capacity.<node>), so a change is said once.
|
||||
judged delivery.Capacity
|
||||
judged sdkstep.Capacity
|
||||
hasJudged bool
|
||||
capWritten bool
|
||||
capFault string
|
||||
@@ -166,11 +166,11 @@ func (p *Puller) Resume(by string) error { return p.setPaused(false, by) }
|
||||
|
||||
func (p *Puller) setPaused(paused bool, by string) error {
|
||||
if p.Load != nil {
|
||||
key, err := delivery.PauseKey(p.Node)
|
||||
key, err := sdkstep.PauseKey(p.Node)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
raw, err := delivery.EncodePause(delivery.Pause{Paused: paused, By: by, At: now()})
|
||||
raw, err := sdkstep.EncodePause(sdkstep.Pause{Paused: paused, By: by, At: now()})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -194,7 +194,7 @@ func (p *Puller) restore() error {
|
||||
if p.Load == nil {
|
||||
return nil
|
||||
}
|
||||
key, err := delivery.PauseKey(p.Node)
|
||||
key, err := sdkstep.PauseKey(p.Node)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -205,7 +205,7 @@ func (p *Puller) restore() error {
|
||||
if raw == nil {
|
||||
return nil
|
||||
}
|
||||
v, err := delivery.DecodePause(raw)
|
||||
v, err := sdkstep.DecodePause(raw)
|
||||
if err != nil {
|
||||
return fmt.Errorf("buildstep: the pause of %s: %w", p.Node, err)
|
||||
}
|
||||
@@ -287,7 +287,7 @@ func (p *Puller) CapacityNow() int {
|
||||
// else Capacity), bounded by the node's build slice when CoresPerStep is set (EffectiveCapacity), its quota read now. A
|
||||
// change from what it said last is logged and written to `capacity.<node>` of the pauses state (PausePut), so the
|
||||
// delivery view says it too.
|
||||
func (p *Puller) CapacityDetail() delivery.Capacity {
|
||||
func (p *Puller) CapacityDetail() sdkstep.Capacity {
|
||||
p.mu.Lock()
|
||||
watched, has := p.watched, p.hasWatched
|
||||
p.mu.Unlock()
|
||||
@@ -307,9 +307,9 @@ func (p *Puller) CapacityDetail() delivery.Capacity {
|
||||
case p.Capacity != nil:
|
||||
configured = p.Capacity()
|
||||
}
|
||||
var c delivery.Capacity
|
||||
var c sdkstep.Capacity
|
||||
if p.CoresPerStep == nil {
|
||||
c = delivery.Capacity{Configured: configured, Effective: configured, QuotaFrom: "not judged: the module names no cores a step needs",
|
||||
c = sdkstep.Capacity{Configured: configured, Effective: configured, QuotaFrom: "not judged: the module names no cores a step needs",
|
||||
Why: fmt.Sprintf("its configured capacity %d", configured)}
|
||||
} else {
|
||||
quota := p.Quota
|
||||
@@ -375,7 +375,7 @@ func (p *Puller) untilEdge() (time.Duration, bool) {
|
||||
|
||||
// sayCapacity logs c when it differs from what was said last, and writes it to the node's capacity key until a write
|
||||
// lands (a failed write is tried again at the next judgement, its error said once).
|
||||
func (p *Puller) sayCapacity(c delivery.Capacity) {
|
||||
func (p *Puller) sayCapacity(c sdkstep.Capacity) {
|
||||
p.mu.Lock()
|
||||
same := p.hasJudged && p.judged.Effective == c.Effective && p.judged.Configured == c.Configured && p.judged.ByCPU == c.ByCPU &&
|
||||
p.judged.CoresPerStep == c.CoresPerStep && p.judged.QuotaCores == c.QuotaCores && p.judged.OutsideHours == c.OutsideHours &&
|
||||
@@ -392,12 +392,12 @@ func (p *Puller) sayCapacity(c delivery.Capacity) {
|
||||
if !write {
|
||||
return
|
||||
}
|
||||
key, err := delivery.CapacityKey(p.Node)
|
||||
key, err := sdkstep.CapacityKey(p.Node)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.At = now()
|
||||
raw, err := delivery.EncodeCapacity(c)
|
||||
raw, err := sdkstep.EncodeCapacity(c)
|
||||
if err == nil {
|
||||
_, err = p.PausePut(key, json.RawMessage(raw))
|
||||
}
|
||||
@@ -493,7 +493,7 @@ func (p *Puller) next(ctx context.Context, wait time.Duration) (*Pulled, error)
|
||||
p.mu.Lock()
|
||||
declined := p.declined
|
||||
p.mu.Unlock()
|
||||
for _, kind := range delivery.AskKinds {
|
||||
for _, kind := range sdkstep.AskKinds {
|
||||
if declined[kind] {
|
||||
continue
|
||||
}
|
||||
@@ -502,24 +502,24 @@ func (p *Puller) next(ctx context.Context, wait time.Duration) (*Pulled, error)
|
||||
return withKind(msg, kind), err
|
||||
}
|
||||
}
|
||||
msg, err := p.Source.Next(ctx, delivery.AskDelivery, wait)
|
||||
msg, err := p.Source.Next(ctx, sdkstep.AskDelivery, wait)
|
||||
p.mu.Lock()
|
||||
p.declined = nil
|
||||
p.mu.Unlock()
|
||||
return withKind(msg, delivery.AskDelivery), err
|
||||
return withKind(msg, sdkstep.AskDelivery), err
|
||||
}
|
||||
|
||||
// decline ends the step of an ask this node lacks a need of, refused as declined with what it lacks, and drops the
|
||||
// message: the asker puts the ask again under a new id (issue 890). A message whose decline cannot be written is handed
|
||||
// back, as one whose start cannot be.
|
||||
func (p *Puller) decline(msg *Pulled, ask delivery.StepAsk, lacks []string) {
|
||||
step, key, err := begin(p.Put, p.Node, ask.Kind+" ask", ask.ID, sdkstep.AskWhat(askRead(ask)))
|
||||
func (p *Puller) decline(msg *Pulled, ask sdkstep.StepAsk, lacks []string) {
|
||||
step, key, err := begin(p.Put, p.Node, ask.Kind+" ask", ask.ID, buildrun.AskWhat(askRead(ask)))
|
||||
if err == nil {
|
||||
why := Refuse("the ask %s needs %s, and %s lacks %s: it is left to a node that has it", ask.ID,
|
||||
strings.Join(ask.Needs, ", "), p.Node, strings.Join(lacks, ", "))
|
||||
step.Ended = now().UTC()
|
||||
step.State, step.Reason = delivery.StepRefused, why.Error()
|
||||
step.Result, err = json.Marshal(delivery.StepDeclined{Declined: true, Needs: ask.Needs, Lacks: lacks})
|
||||
step.State, step.Reason = sdkstep.StepRefused, why.Error()
|
||||
step.Result, err = json.Marshal(sdkstep.StepDeclined{Declined: true, Needs: ask.Needs, Lacks: lacks})
|
||||
if err == nil {
|
||||
err = writeEnd(p.Put, key, step)
|
||||
}
|
||||
@@ -542,10 +542,10 @@ func (p *Puller) decline(msg *Pulled, ask delivery.StepAsk, lacks []string) {
|
||||
// declineRun ends the step of an ask that began and then found this node should not run it (Declining), as decline does
|
||||
// before it begins: refused as declined, the message dropped for its asker to put again, and the worker it came from left
|
||||
// alone until the next pull's wait.
|
||||
func (p *Puller) declineRun(msg *Pulled, ask delivery.StepAsk, key string, step delivery.Step, d *Declining) {
|
||||
result, _ := json.Marshal(delivery.StepDeclined{Declined: true, Needs: ask.Needs, Lacks: d.Lacks})
|
||||
func (p *Puller) declineRun(msg *Pulled, ask sdkstep.StepAsk, key string, step sdkstep.Step, d *Declining) {
|
||||
result, _ := json.Marshal(sdkstep.StepDeclined{Declined: true, Needs: ask.Needs, Lacks: d.Lacks})
|
||||
step.Ended = now().UTC()
|
||||
step.State, step.Reason, step.Result = delivery.StepRefused, d.Text, result
|
||||
step.State, step.Reason, step.Result = sdkstep.StepRefused, d.Text, result
|
||||
if err := writeEnd(p.Put, key, step); err != nil {
|
||||
p.logf("the ask %s cannot be declined on %s, so it is given back: %v", ask.ID, p.Node, err)
|
||||
_ = msg.Nak()
|
||||
@@ -571,7 +571,7 @@ func withKind(msg *Pulled, kind string) *Pulled {
|
||||
// take starts one ask: it decodes it, refuses one that is not of this seat, and runs it as a step under its own id.
|
||||
func (p *Puller) take(ctx context.Context, msg *Pulled) {
|
||||
kind := msg.Kind
|
||||
ask, err := delivery.DecodeStepAsk(msg.Data)
|
||||
ask, err := sdkstep.DecodeStepAsk(msg.Data)
|
||||
if err == nil && (ask.Language != p.Language || (kind != "" && ask.Kind != kind)) {
|
||||
err = fmt.Errorf("the ask %s is a %s ask of %s, pulled from the %s %s worker", ask.ID, ask.Kind, ask.Language, p.Language, kind)
|
||||
}
|
||||
@@ -581,17 +581,17 @@ func (p *Puller) take(ctx context.Context, msg *Pulled) {
|
||||
_ = msg.Term()
|
||||
return
|
||||
}
|
||||
if lacks := delivery.Lacks(ask.Needs, p.Has); len(lacks) > 0 {
|
||||
if lacks := sdkstep.Lacks(ask.Needs, p.Has); len(lacks) > 0 {
|
||||
p.decline(msg, ask, lacks)
|
||||
return
|
||||
}
|
||||
step, key, err := begin(p.Put, p.Node, ask.Kind+" ask", ask.ID, sdkstep.AskWhat(askRead(ask)))
|
||||
step, key, err := begin(p.Put, p.Node, ask.Kind+" ask", ask.ID, buildrun.AskWhat(askRead(ask)))
|
||||
if err != nil {
|
||||
p.logf("the ask %s cannot start on %s: %v", ask.ID, p.Node, err)
|
||||
_ = msg.Nak()
|
||||
return
|
||||
}
|
||||
if msg.Delivered > delivery.AskRuns {
|
||||
if msg.Delivered > sdkstep.AskRuns {
|
||||
// Run AskRuns times and never ended: each run took its node down or died with it. It is run no more, and said
|
||||
// (issue 1907): the asker's key fails with why.
|
||||
why := fmt.Errorf("the ask was taken %d times and no run of it ended: each took its build node down or died with it, so it is run no more (poison)", msg.Delivered-1)
|
||||
@@ -615,7 +615,7 @@ func (p *Puller) take(ctx context.Context, msg *Pulled) {
|
||||
go p.run(ctx, msg, ask, key, step)
|
||||
}
|
||||
|
||||
func (p *Puller) run(ctx context.Context, msg *Pulled, ask delivery.StepAsk, key string, step delivery.Step) {
|
||||
func (p *Puller) run(ctx context.Context, msg *Pulled, ask sdkstep.StepAsk, key string, step sdkstep.Step) {
|
||||
defer func() {
|
||||
p.mu.Lock()
|
||||
p.inHand--
|
||||
@@ -644,7 +644,7 @@ func (p *Puller) run(ctx context.Context, msg *Pulled, ask delivery.StepAsk, key
|
||||
}()
|
||||
ph := &phases{put: p.Put, key: key, step: step, logf: p.Logf, prefix: fmt.Sprintf("the %s ask %s on %s", ask.Kind, ask.ID, p.Node)}
|
||||
result, err := protect(ctx, func(ctx context.Context) (any, error) {
|
||||
return p.Run(context.WithValue(ctx, phaseKey{}, ph), ask)
|
||||
return p.Run(WithCheckToolchain(context.WithValue(ctx, phaseKey{}, ph), msg.Data), ask)
|
||||
})
|
||||
step = ph.end() // its end keeps the phase it ended in
|
||||
close(done)
|
||||
@@ -798,12 +798,9 @@ func (p *Puller) capacityFrom(c stdio.ProvisionChange) (int, string) {
|
||||
return n, ""
|
||||
}
|
||||
|
||||
// askRead is the part of an ask that mesh-delivery-sdk's AskWhat reads, taken from the ask's own layout (the part
|
||||
// build-step's StepAsk is the same JSON).
|
||||
func askRead(ask delivery.StepAsk) sdkstep.Ask {
|
||||
var out sdkstep.Ask
|
||||
if raw, err := json.Marshal(ask); err == nil {
|
||||
_ = json.Unmarshal(raw, &out)
|
||||
}
|
||||
// askRead is the part of an ask that buildrun.AskWhat reads, taken from the ask's own layout by the one conversion the
|
||||
// neutral form has (sdkstep.AskAs); an ask that does not convert reads as empty and is described by its kind alone.
|
||||
func askRead(ask sdkstep.StepAsk) sdkstep.Ask {
|
||||
out, _ := sdkstep.AskAs[sdkstep.Ask](ask)
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -3,16 +3,15 @@ package buildstep
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/nats-io/nats.go/jetstream"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// JetStreamAsks is a build seat's workers on the bus: the shared pull consumer of each kind on the seat's stream
|
||||
// (delivery.AskStream, AskWorker), which the broker makes when the seat is declared; a holder only binds them.
|
||||
// (sdkstep.AskStream, AskWorker), which the broker makes when the seat is declared; a holder only binds them.
|
||||
type JetStreamAsks struct {
|
||||
JS jetstream.JetStream
|
||||
Language string
|
||||
@@ -27,11 +26,11 @@ func (s *JetStreamAsks) consumer(ctx context.Context, kind string) (jetstream.Co
|
||||
if c, ok := s.cons[kind]; ok {
|
||||
return c, nil
|
||||
}
|
||||
stream, err := delivery.AskStream(s.Language)
|
||||
stream, err := sdkstep.AskStream(s.Language)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
worker, err := delivery.AskWorker(s.Language, kind)
|
||||
worker, err := sdkstep.AskWorker(s.Language, kind)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -81,18 +80,18 @@ func (s *JetStreamAsks) Next(ctx context.Context, kind string, wait time.Duratio
|
||||
// modules' integration tests start one). On the mesh the broker declares them from the seat, as it declares every
|
||||
// seat's stream (mesh-controller internal/broker SeatStreams); a holder never does.
|
||||
func DeclareAskWorkers(ctx context.Context, js jetstream.JetStream, language string, ackWait time.Duration) error {
|
||||
stream, err := delivery.AskStream(language)
|
||||
stream, err := sdkstep.AskStream(language)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
seat, _ := delivery.BuildSeat(language)
|
||||
seat, _ := sdkstep.BuildSeat(language)
|
||||
if _, err := js.CreateOrUpdateStream(ctx, jetstream.StreamConfig{Name: stream, Subjects: []string{"mesh.seat." + seat + ".accept.>"},
|
||||
Retention: jetstream.WorkQueuePolicy}); err != nil {
|
||||
return fmt.Errorf("the stream %s: %w", stream, err)
|
||||
}
|
||||
for _, kind := range delivery.AskKinds {
|
||||
worker, _ := delivery.AskWorker(language, kind)
|
||||
subject, _ := delivery.AskSubject(language, kind)
|
||||
for _, kind := range sdkstep.AskKinds {
|
||||
worker, _ := sdkstep.AskWorker(language, kind)
|
||||
subject, _ := sdkstep.AskSubject(language, kind)
|
||||
if _, err := js.CreateOrUpdateConsumer(ctx, stream, jetstream.ConsumerConfig{Durable: worker, FilterSubject: subject,
|
||||
AckPolicy: jetstream.AckExplicitPolicy, AckWait: ackWait, MaxDeliver: 5}); err != nil {
|
||||
return fmt.Errorf("the worker %s: %w", worker, err)
|
||||
|
||||
@@ -2,10 +2,9 @@ package buildstep
|
||||
|
||||
import (
|
||||
"context"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// FakeAsks is a test double of a seat's workers for the build modules' unit tests: Publish puts an ask on a kind's
|
||||
@@ -38,8 +37,8 @@ func (f *FakeAsks) Publish(kind string, data []byte) {
|
||||
}
|
||||
|
||||
// PublishAsk encodes a and puts it on its kind's worker.
|
||||
func (f *FakeAsks) PublishAsk(a delivery.StepAsk) error {
|
||||
raw, err := delivery.EncodeStepAsk(a)
|
||||
func (f *FakeAsks) PublishAsk(a sdkstep.StepAsk) error {
|
||||
raw, err := sdkstep.EncodeStepAsk(a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -93,7 +92,7 @@ func (f *FakeAsks) Next(ctx context.Context, kind string, wait time.Duration) (*
|
||||
|
||||
func (f *FakeAsks) pulled(data []byte) *Pulled {
|
||||
id := string(data)
|
||||
if a, err := delivery.DecodeStepAsk(data); err == nil {
|
||||
if a, err := sdkstep.DecodeStepAsk(data); err == nil {
|
||||
id = a.ID
|
||||
}
|
||||
fate := func(list *[]string) func() error {
|
||||
|
||||
@@ -4,6 +4,7 @@ package buildstep
|
||||
|
||||
import (
|
||||
"context"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
@@ -12,7 +13,6 @@ import (
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/nats-io/nats.go/jetstream"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/testbus"
|
||||
)
|
||||
|
||||
@@ -27,19 +27,19 @@ func askBus(t *testing.T, ackWait time.Duration) jetstream.JetStream {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := DeclareAskWorkers(context.Background(), js, delivery.AskGo, ackWait); err != nil {
|
||||
if err := DeclareAskWorkers(context.Background(), js, sdkstep.AskGo, ackWait); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return js
|
||||
}
|
||||
|
||||
func publishAsk(t *testing.T, js jetstream.JetStream, a delivery.StepAsk) {
|
||||
func publishAsk(t *testing.T, js jetstream.JetStream, a sdkstep.StepAsk) {
|
||||
t.Helper()
|
||||
raw, err := delivery.EncodeStepAsk(a)
|
||||
raw, err := sdkstep.EncodeStepAsk(a)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
subject, _ := delivery.AskSubject(a.Language, a.Kind)
|
||||
subject, _ := sdkstep.AskSubject(a.Language, a.Kind)
|
||||
if _, err := js.Publish(context.Background(), subject, raw); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -49,7 +49,7 @@ func publishAsk(t *testing.T, js jetstream.JetStream, a delivery.StepAsk) {
|
||||
// with capacity 2 and three asks two run, the third waits in the stream and starts when one ends.
|
||||
func TestPullOrderAndCapacityOnTheBus(t *testing.T) {
|
||||
js := askBus(t, 30*time.Second)
|
||||
check, first, second := testAsk(delivery.AskCheck), testAsk(delivery.AskDelivery), testAsk(delivery.AskDelivery)
|
||||
check, first, second := testAsk(sdkstep.AskCheck), testAsk(sdkstep.AskDelivery), testAsk(sdkstep.AskDelivery)
|
||||
publishAsk(t, js, check)
|
||||
publishAsk(t, js, first)
|
||||
publishAsk(t, js, second)
|
||||
@@ -57,9 +57,9 @@ func TestPullOrderAndCapacityOnTheBus(t *testing.T) {
|
||||
var mu sync.Mutex
|
||||
var order []string
|
||||
st := newSteps()
|
||||
p := &Puller{Source: &JetStreamAsks{JS: js, Language: delivery.AskGo}, Language: delivery.AskGo, Node: "shanks", Put: st.put,
|
||||
p := &Puller{Source: &JetStreamAsks{JS: js, Language: sdkstep.AskGo}, Language: sdkstep.AskGo, Node: "shanks", Put: st.put,
|
||||
Capacity: func() int { return 2 }, PullWait: 200 * time.Millisecond,
|
||||
Run: func(ctx context.Context, a delivery.StepAsk) (any, error) {
|
||||
Run: func(ctx context.Context, a sdkstep.StepAsk) (any, error) {
|
||||
mu.Lock()
|
||||
order = append(order, a.ID)
|
||||
mu.Unlock()
|
||||
@@ -80,9 +80,9 @@ func TestPullOrderAndCapacityOnTheBus(t *testing.T) {
|
||||
waitFor(t, "the check taken", func() bool { mu.Lock(); defer mu.Unlock(); return len(order) == 3 })
|
||||
close(release)
|
||||
waitFor(t, "every step ended", func() bool {
|
||||
for _, a := range []delivery.StepAsk{check, first, second} {
|
||||
key, _ := delivery.AskStepKey("shanks", a.ID)
|
||||
if s, ok := st.get(key); !ok || s.State != delivery.StepPassed {
|
||||
for _, a := range []sdkstep.StepAsk{check, first, second} {
|
||||
key, _ := sdkstep.AskStepKey("shanks", a.ID)
|
||||
if s, ok := st.get(key); !ok || s.State != sdkstep.StepPassed {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -102,13 +102,13 @@ func TestPullOrderAndCapacityOnTheBus(t *testing.T) {
|
||||
// whose step key names the reference.
|
||||
func TestPullRedeliversAKilledPullersAsk(t *testing.T) {
|
||||
js := askBus(t, 2*time.Second)
|
||||
a := testAsk(delivery.AskDelivery)
|
||||
a := testAsk(sdkstep.AskDelivery)
|
||||
publishAsk(t, js, a)
|
||||
st := newSteps()
|
||||
taken := make(chan struct{})
|
||||
dying := &Puller{Source: &JetStreamAsks{JS: js, Language: delivery.AskGo}, Language: delivery.AskGo, Node: "ace", Put: st.put,
|
||||
dying := &Puller{Source: &JetStreamAsks{JS: js, Language: sdkstep.AskGo}, Language: sdkstep.AskGo, Node: "ace", Put: st.put,
|
||||
Capacity: func() int { return 1 }, PullWait: 200 * time.Millisecond, Heartbeat: time.Hour,
|
||||
Run: func(ctx context.Context, a delivery.StepAsk) (any, error) {
|
||||
Run: func(ctx context.Context, a sdkstep.StepAsk) (any, error) {
|
||||
close(taken)
|
||||
<-ctx.Done()
|
||||
return nil, ctx.Err()
|
||||
@@ -117,18 +117,18 @@ func TestPullRedeliversAKilledPullersAsk(t *testing.T) {
|
||||
go dying.Pull(kill)
|
||||
<-taken
|
||||
killed()
|
||||
other := &Puller{Source: &JetStreamAsks{JS: js, Language: delivery.AskGo}, Language: delivery.AskGo, Node: "shanks", Put: st.put,
|
||||
other := &Puller{Source: &JetStreamAsks{JS: js, Language: sdkstep.AskGo}, Language: sdkstep.AskGo, Node: "shanks", Put: st.put,
|
||||
Capacity: func() int { return 1 }, PullWait: 200 * time.Millisecond,
|
||||
Run: func(ctx context.Context, a delivery.StepAsk) (any, error) {
|
||||
return delivery.BuildAnswer{Artifacts: []delivery.AskBuilt{{Artifact: "bundle", Digest: "sha256:00", Reference: "artifact-store://x"}}}, nil
|
||||
Run: func(ctx context.Context, a sdkstep.StepAsk) (any, error) {
|
||||
return sdkstep.BuildAnswer{Artifacts: []sdkstep.AskBuilt{{Artifact: "bundle", Digest: "sha256:00", Reference: "artifact-store://x"}}}, nil
|
||||
}}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
go other.Pull(ctx)
|
||||
key, _ := delivery.AskStepKey("shanks", a.ID)
|
||||
key, _ := sdkstep.AskStepKey("shanks", a.ID)
|
||||
deadline := time.Now().Add(10 * time.Second)
|
||||
for {
|
||||
if s, ok := st.get(key); ok && s.State == delivery.StepPassed {
|
||||
if s, ok := st.get(key); ok && s.State == sdkstep.StepPassed {
|
||||
if want := `artifact-store://x`; !contains(string(s.Result), want) {
|
||||
t.Fatalf("the step's result %s names no %s", s.Result, want)
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@ package buildstep
|
||||
|
||||
import (
|
||||
"context"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"time"
|
||||
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// SeatAsks is a build seat's workers as a tools bundle reaches them: through its runtime's seat pull and settle
|
||||
@@ -27,7 +27,7 @@ func (s SeatAsks) Next(ctx context.Context, kind string, wait time.Duration) (*P
|
||||
if settle == nil {
|
||||
settle = stdio.SeatSettle
|
||||
}
|
||||
worker, err := delivery.AskWorker(s.Language, kind)
|
||||
worker, err := sdkstep.AskWorker(s.Language, kind)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -2,18 +2,18 @@ package buildstep
|
||||
|
||||
import (
|
||||
"context"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// 1887/A11: a bundle's source pulls the kind's worker by its seat's name and settles each ask by the runtime's id.
|
||||
func TestSeatAsksPullsTheKindsWorkerAndSettlesByID(t *testing.T) {
|
||||
var pulled []string
|
||||
var settled []string
|
||||
src := SeatAsks{Language: delivery.AskGo,
|
||||
src := SeatAsks{Language: sdkstep.AskGo,
|
||||
Pull: func(worker string, wait time.Duration) (*stdio.Pulled, error) {
|
||||
pulled = append(pulled, worker)
|
||||
if worker != "SEAT_NODE_BUILD_GO_CHECK_worker" {
|
||||
@@ -22,10 +22,10 @@ func TestSeatAsksPullsTheKindsWorkerAndSettlesByID(t *testing.T) {
|
||||
return &stdio.Pulled{ID: "p9", Worker: worker, Body: []byte(`{"v":1}`), Delivered: 2}, nil
|
||||
},
|
||||
Settle: func(id, fate string) error { settled = append(settled, id+" "+fate); return nil }}
|
||||
if p, err := src.Next(context.Background(), delivery.AskDelivery, 0); err != nil || p != nil {
|
||||
if p, err := src.Next(context.Background(), sdkstep.AskDelivery, 0); err != nil || p != nil {
|
||||
t.Fatalf("delivery = %+v, %v", p, err)
|
||||
}
|
||||
p, err := src.Next(context.Background(), delivery.AskCheck, time.Second)
|
||||
p, err := src.Next(context.Background(), sdkstep.AskCheck, time.Second)
|
||||
if err != nil || p == nil || string(p.Data) != `{"v":1}` || p.Delivered != 2 {
|
||||
t.Fatalf("check = %+v, %v", p, err)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
buildrun "git.novox.be/novox/mesh-delivery/sdk/buildrun"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -15,20 +16,19 @@ import (
|
||||
"time"
|
||||
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// steps records a steps state's puts: the last value of each key.
|
||||
type steps struct {
|
||||
mu sync.Mutex
|
||||
keys map[string]delivery.Step
|
||||
keys map[string]sdkstep.Step
|
||||
}
|
||||
|
||||
func newSteps() *steps { return &steps{keys: map[string]delivery.Step{}} }
|
||||
func newSteps() *steps { return &steps{keys: map[string]sdkstep.Step{}} }
|
||||
|
||||
func (s *steps) put(key string, value any) (uint64, error) {
|
||||
raw, _ := value.(json.RawMessage)
|
||||
st, err := delivery.DecodeStep(raw)
|
||||
st, err := sdkstep.DecodeStep(raw)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -38,28 +38,28 @@ func (s *steps) put(key string, value any) (uint64, error) {
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
func (s *steps) get(key string) (delivery.Step, bool) {
|
||||
func (s *steps) get(key string) (sdkstep.Step, bool) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
st, ok := s.keys[key]
|
||||
return st, ok
|
||||
}
|
||||
|
||||
func testAsk(kind string) delivery.StepAsk {
|
||||
func testAsk(kind string) sdkstep.StepAsk {
|
||||
id := sdkstep.NewID()
|
||||
a := delivery.StepAsk{ID: id, Kind: kind, Language: delivery.AskGo, Asker: "mesh-delivery-builder", At: time.Now()}
|
||||
fetch := map[string]any{"repository": "novox/x", "commit": "0123456789abcdef0123456789abcdef01234567"}
|
||||
if kind == delivery.AskDelivery {
|
||||
a := sdkstep.StepAsk{ID: id, Kind: kind, Language: sdkstep.AskGo, Asker: "mesh-delivery-builder", At: time.Now()}
|
||||
fetch := sdkstep.Fetch{Repository: "novox/x", Commit: "0123456789abcdef0123456789abcdef01234567"}
|
||||
if kind == sdkstep.AskDelivery {
|
||||
b := buildJobFixture
|
||||
b.Fetch = fetch
|
||||
b.Fetch = &fetch
|
||||
a.Build = &b
|
||||
} else {
|
||||
a.Check = &delivery.CheckJob{Fetch: fetch, Levels: []string{delivery.LevelUnit}}
|
||||
a.Check = &sdkstep.CheckJob{Fetch: fetch, Levels: []string{sdkstep.LevelUnit}}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
var buildJobFixture = delivery.BuildJob{Module: "m", Path: ".", Artifacts: []delivery.AskArtifact{{Artifact: "bundle"}}}
|
||||
var buildJobFixture = sdkstep.BuildJob{Module: "m", Path: ".", Artifacts: []sdkstep.AskArtifact{{Artifact: "bundle"}}}
|
||||
|
||||
func waitFor(t *testing.T, what string, cond func() bool) {
|
||||
t.Helper()
|
||||
@@ -75,16 +75,16 @@ func waitFor(t *testing.T, what string, cond func() bool) {
|
||||
// 1887/A2a: given a check ask published before a delivery ask and one slot, the delivery ask runs first.
|
||||
func TestPullTakesDeliveryBeforeCheck(t *testing.T) {
|
||||
src, st := NewFakeAsks(), newSteps()
|
||||
check, recheck, deliver := testAsk(delivery.AskCheck), testAsk(delivery.AskRecheck), testAsk(delivery.AskDelivery)
|
||||
for _, a := range []delivery.StepAsk{recheck, check, deliver} {
|
||||
check, recheck, deliver := testAsk(sdkstep.AskCheck), testAsk(sdkstep.AskRecheck), testAsk(sdkstep.AskDelivery)
|
||||
for _, a := range []sdkstep.StepAsk{recheck, check, deliver} {
|
||||
if err := src.PublishAsk(a); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
var mu sync.Mutex
|
||||
var order []string
|
||||
p := &Puller{Source: src, Language: delivery.AskGo, Node: "shanks", Put: st.put, Capacity: func() int { return 1 },
|
||||
PullWait: 10 * time.Millisecond, Run: func(ctx context.Context, a delivery.StepAsk) (any, error) {
|
||||
p := &Puller{Source: src, Language: sdkstep.AskGo, Node: "shanks", Put: st.put, Capacity: func() int { return 1 },
|
||||
PullWait: 10 * time.Millisecond, Run: func(ctx context.Context, a sdkstep.StepAsk) (any, error) {
|
||||
mu.Lock()
|
||||
order = append(order, a.Kind)
|
||||
mu.Unlock()
|
||||
@@ -96,12 +96,12 @@ func TestPullTakesDeliveryBeforeCheck(t *testing.T) {
|
||||
waitFor(t, "three asks acked", func() bool { a, _, _ := src.Fates(); return len(a) == 3 })
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if want := []string{delivery.AskDelivery, delivery.AskCheck, delivery.AskRecheck}; len(order) != 3 || order[0] != want[0] || order[1] != want[1] || order[2] != want[2] {
|
||||
if want := []string{sdkstep.AskDelivery, sdkstep.AskCheck, sdkstep.AskRecheck}; len(order) != 3 || order[0] != want[0] || order[1] != want[1] || order[2] != want[2] {
|
||||
t.Fatalf("order = %v, want %v", order, want)
|
||||
}
|
||||
key, _ := delivery.AskStepKey("shanks", deliver.ID)
|
||||
key, _ := sdkstep.AskStepKey("shanks", deliver.ID)
|
||||
s, ok := st.get(key)
|
||||
if !ok || s.State != delivery.StepPassed || string(s.Result) != `{"ran":"`+deliver.ID+`"}` {
|
||||
if !ok || s.State != sdkstep.StepPassed || string(s.Result) != `{"ran":"`+deliver.ID+`"}` {
|
||||
t.Fatalf("the step %s = %+v (%v)", key, s, ok)
|
||||
}
|
||||
}
|
||||
@@ -112,9 +112,9 @@ func TestPullTakesDeliveryBeforeCheck(t *testing.T) {
|
||||
// its ask id, and its end keeps the last phase.
|
||||
func TestPullHoldsAtMostItsCapacity(t *testing.T) {
|
||||
src, st := NewFakeAsks(), newSteps()
|
||||
asks := []delivery.StepAsk{}
|
||||
asks := []sdkstep.StepAsk{}
|
||||
for i := 0; i < 3; i++ {
|
||||
a := testAsk(delivery.AskDelivery)
|
||||
a := testAsk(sdkstep.AskDelivery)
|
||||
asks = append(asks, a)
|
||||
_ = src.PublishAsk(a)
|
||||
}
|
||||
@@ -124,7 +124,7 @@ func TestPullHoldsAtMostItsCapacity(t *testing.T) {
|
||||
var said []string
|
||||
capacities := map[string][]byte{}
|
||||
quota := 1200
|
||||
p := &Puller{Source: src, Language: delivery.AskGo, Node: "shanks", Put: st.put, Capacity: func() int { return 3 },
|
||||
p := &Puller{Source: src, Language: sdkstep.AskGo, Node: "shanks", Put: st.put, Capacity: func() int { return 3 },
|
||||
CoresPerStep: func() float64 { return 6 },
|
||||
Quota: func() (int, string) { mu.Lock(); defer mu.Unlock(); return quota, "a test's slice" },
|
||||
PausePut: func(key string, v any) (uint64, error) {
|
||||
@@ -134,7 +134,7 @@ func TestPullHoldsAtMostItsCapacity(t *testing.T) {
|
||||
return 1, nil
|
||||
},
|
||||
Logf: func(f string, a ...any) { mu.Lock(); said = append(said, fmt.Sprintf(f, a...)); mu.Unlock() },
|
||||
PullWait: 10 * time.Millisecond, Run: func(ctx context.Context, a delivery.StepAsk) (any, error) {
|
||||
PullWait: 10 * time.Millisecond, Run: func(ctx context.Context, a sdkstep.StepAsk) (any, error) {
|
||||
mu.Lock()
|
||||
started++
|
||||
mu.Unlock()
|
||||
@@ -147,8 +147,8 @@ func TestPullHoldsAtMostItsCapacity(t *testing.T) {
|
||||
go p.Pull(ctx)
|
||||
waitFor(t, "two in hand", func() bool { return p.InHand() == 2 })
|
||||
time.Sleep(30 * time.Millisecond) // three pull waits: the third must still wait
|
||||
if p.InHand() != 2 || src.Waiting(delivery.AskDelivery) != 1 {
|
||||
t.Fatalf("in hand %d, waiting %d; want 2 and 1", p.InHand(), src.Waiting(delivery.AskDelivery))
|
||||
if p.InHand() != 2 || src.Waiting(sdkstep.AskDelivery) != 1 {
|
||||
t.Fatalf("in hand %d, waiting %d; want 2 and 1", p.InHand(), src.Waiting(sdkstep.AskDelivery))
|
||||
}
|
||||
c := p.CapacityDetail()
|
||||
if c.Effective != 2 || c.Configured != 3 || c.ByCPU != 2 || c.QuotaCores != 12 || !strings.Contains(c.Why, "bounded by the CPU") {
|
||||
@@ -163,23 +163,23 @@ func TestPullHoldsAtMostItsCapacity(t *testing.T) {
|
||||
}
|
||||
}
|
||||
mu.Unlock()
|
||||
if got, err := delivery.DecodeCapacity(raw); err != nil || got.Effective != 2 || capLines != 1 {
|
||||
if got, err := sdkstep.DecodeCapacity(raw); err != nil || got.Effective != 2 || capLines != 1 {
|
||||
t.Fatalf("capacity.shanks = %s (%v), said %d times; want effective 2, said once", raw, err, capLines)
|
||||
}
|
||||
waitFor(t, "a phase on the step key", func() bool {
|
||||
key, _ := delivery.AskStepKey("shanks", p.Running()[0].ID)
|
||||
key, _ := sdkstep.AskStepKey("shanks", p.Running()[0].ID)
|
||||
s, _ := st.get(key)
|
||||
return s.State == delivery.StepRunning && s.Phase == "unit: tests of 3 packages" && !s.PhaseAt.IsZero() && s.Owner == sdkstep.Owner // 1890/AC5
|
||||
return s.State == sdkstep.StepRunning && s.Phase == "unit: tests of 3 packages" && !s.PhaseAt.IsZero() && s.Owner == buildrun.Owner // 1890/AC5
|
||||
})
|
||||
release <- struct{}{}
|
||||
waitFor(t, "the third taken", func() bool { return src.Waiting(delivery.AskDelivery) == 0 })
|
||||
waitFor(t, "the third taken", func() bool { return src.Waiting(sdkstep.AskDelivery) == 0 })
|
||||
// Taken from the source comes a moment before its run starts: wait for the start, not only the take.
|
||||
waitFor(t, "the third started", func() bool { mu.Lock(); defer mu.Unlock(); return started == 3 })
|
||||
close(release)
|
||||
waitFor(t, "all acked", func() bool { a, _, _ := src.Fates(); return len(a) == 3 })
|
||||
for _, a := range asks {
|
||||
key, _ := delivery.AskStepKey("shanks", a.ID)
|
||||
if s, _ := st.get(key); s.State != delivery.StepPassed || s.Phase != "unit: tests of 3 packages" || s.Owner != sdkstep.Owner {
|
||||
key, _ := sdkstep.AskStepKey("shanks", a.ID)
|
||||
if s, _ := st.get(key); s.State != sdkstep.StepPassed || s.Phase != "unit: tests of 3 packages" || s.Owner != buildrun.Owner {
|
||||
t.Errorf("the end of %s = %+v, want passed in its last phase", a.ID, s)
|
||||
}
|
||||
}
|
||||
@@ -200,18 +200,18 @@ func TestPullHoldsAtMostItsCapacity(t *testing.T) {
|
||||
// dropped for the asker to put again), and runs on a node that has it.
|
||||
func TestPullDropsWhatIsNoAsk(t *testing.T) {
|
||||
src, st := NewFakeAsks(), newSteps()
|
||||
src.Publish(delivery.AskDelivery, []byte(`{"v":1}`))
|
||||
other := testAsk(delivery.AskDelivery)
|
||||
other.Language = delivery.AskNode
|
||||
src.Publish(sdkstep.AskDelivery, []byte(`{"v":1}`))
|
||||
other := testAsk(sdkstep.AskDelivery)
|
||||
other.Language = sdkstep.AskNode
|
||||
_ = src.PublishAsk(other)
|
||||
placed := testAsk(delivery.AskCheck)
|
||||
placed.Needs = []string{delivery.NeedNodeBuildNode}
|
||||
placed := testAsk(sdkstep.AskCheck)
|
||||
placed.Needs = []string{sdkstep.NeedNodeBuildNode}
|
||||
if err := src.PublishAsk(placed); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ran := false
|
||||
p := &Puller{Source: src, Language: delivery.AskGo, Node: "ace", Put: st.put, Capacity: func() int { return 1 },
|
||||
PullWait: 10 * time.Millisecond, Run: func(ctx context.Context, a delivery.StepAsk) (any, error) { ran = true; return nil, nil }}
|
||||
p := &Puller{Source: src, Language: sdkstep.AskGo, Node: "ace", Put: st.put, Capacity: func() int { return 1 },
|
||||
PullWait: 10 * time.Millisecond, Run: func(ctx context.Context, a sdkstep.StepAsk) (any, error) { ran = true; return nil, nil }}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
go p.Pull(ctx)
|
||||
@@ -219,31 +219,31 @@ func TestPullDropsWhatIsNoAsk(t *testing.T) {
|
||||
if ran {
|
||||
t.Fatal("a wrong or a declined ask ran")
|
||||
}
|
||||
key, _ := delivery.AskStepKey("ace", placed.ID)
|
||||
key, _ := sdkstep.AskStepKey("ace", placed.ID)
|
||||
s, _ := st.get(key)
|
||||
if d, ok := delivery.DeclinedOf(s); !ok || len(d.Lacks) != 1 || d.Lacks[0] != delivery.NeedNodeBuildNode || !strings.Contains(s.Reason, "lacks node-build-node") {
|
||||
if d, ok := sdkstep.DeclinedOf(s); !ok || len(d.Lacks) != 1 || d.Lacks[0] != sdkstep.NeedNodeBuildNode || !strings.Contains(s.Reason, "lacks node-build-node") {
|
||||
t.Fatalf("the declined step = %+v", s)
|
||||
}
|
||||
has, hasSteps := NewFakeAsks(), newSteps()
|
||||
_ = has.PublishAsk(placed)
|
||||
q := &Puller{Source: has, Language: delivery.AskGo, Node: "g14", Put: hasSteps.put, Capacity: func() int { return 1 },
|
||||
PullWait: 10 * time.Millisecond, Has: func(n string) bool { return n == delivery.NeedNodeBuildNode },
|
||||
Run: func(ctx context.Context, a delivery.StepAsk) (any, error) { return nil, nil }}
|
||||
q := &Puller{Source: has, Language: sdkstep.AskGo, Node: "g14", Put: hasSteps.put, Capacity: func() int { return 1 },
|
||||
PullWait: 10 * time.Millisecond, Has: func(n string) bool { return n == sdkstep.NeedNodeBuildNode },
|
||||
Run: func(ctx context.Context, a sdkstep.StepAsk) (any, error) { return nil, nil }}
|
||||
go q.Pull(ctx)
|
||||
waitFor(t, "the placed ask acked where its need is met", func() bool { a, _, _ := has.Fates(); return len(a) == 1 })
|
||||
// A run that finds its node is not on the agreed toolchain declines the ask after it began (ADR 0584 clause 2); a node on it builds.
|
||||
sit, sitSteps := NewFakeAsks(), newSteps()
|
||||
toolchain := testAsk(delivery.AskDelivery)
|
||||
toolchain := testAsk(sdkstep.AskDelivery)
|
||||
_ = sit.PublishAsk(toolchain)
|
||||
s2 := &Puller{Source: sit, Language: delivery.AskGo, Node: "ace", Put: sitSteps.put, Capacity: func() int { return 1 }, PullWait: 10 * time.Millisecond,
|
||||
Run: func(ctx context.Context, a delivery.StepAsk) (any, error) {
|
||||
s2 := &Puller{Source: sit, Language: sdkstep.AskGo, Node: "ace", Put: sitSteps.put, Capacity: func() int { return 1 }, PullWait: 10 * time.Millisecond,
|
||||
Run: func(ctx context.Context, a sdkstep.StepAsk) (any, error) {
|
||||
return nil, &Declining{Text: "the ask is for 26.13.0 and this node builds with 26.12.1", Lacks: []string{"the agreed toolchain 26.13.0 (recipe 3)"}}
|
||||
}}
|
||||
go s2.Pull(ctx)
|
||||
waitFor(t, "the ask termed on the node that sits out", func() bool { _, tm, _ := sit.Fates(); return len(tm) == 1 })
|
||||
sk, _ := delivery.AskStepKey("ace", toolchain.ID)
|
||||
sk, _ := sdkstep.AskStepKey("ace", toolchain.ID)
|
||||
ss, _ := sitSteps.get(sk)
|
||||
if d, ok := delivery.DeclinedOf(ss); !ok || len(d.Lacks) != 1 || !strings.Contains(ss.Reason, "26.12.1") {
|
||||
if d, ok := sdkstep.DeclinedOf(ss); !ok || len(d.Lacks) != 1 || !strings.Contains(ss.Reason, "26.12.1") {
|
||||
t.Fatalf("the sitting-out step = %+v", ss)
|
||||
}
|
||||
}
|
||||
@@ -278,17 +278,17 @@ func (k *kv) load(key string) ([]byte, error) {
|
||||
func TestPullAtCapacityZeroTakesNothing(t *testing.T) {
|
||||
t.Run("settings file", func(t *testing.T) {
|
||||
src, st := NewFakeAsks(), &kv{}
|
||||
_ = src.PublishAsk(testAsk(delivery.AskDelivery))
|
||||
_ = src.PublishAsk(testAsk(sdkstep.AskDelivery))
|
||||
var mu sync.Mutex
|
||||
capacity := 0
|
||||
p := &Puller{Source: src, Language: delivery.AskGo, Node: "novox", Put: st.put, PullWait: 10 * time.Millisecond,
|
||||
p := &Puller{Source: src, Language: sdkstep.AskGo, Node: "novox", Put: st.put, PullWait: 10 * time.Millisecond,
|
||||
Capacity: func() int { mu.Lock(); defer mu.Unlock(); return capacity },
|
||||
Run: func(ctx context.Context, a delivery.StepAsk) (any, error) { return nil, nil }}
|
||||
Run: func(ctx context.Context, a sdkstep.StepAsk) (any, error) { return nil, nil }}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
go p.Pull(ctx)
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
if src.Waiting(delivery.AskDelivery) != 1 {
|
||||
if src.Waiting(sdkstep.AskDelivery) != 1 {
|
||||
t.Fatal("a node at capacity 0 took an ask")
|
||||
}
|
||||
mu.Lock()
|
||||
@@ -299,18 +299,18 @@ func TestPullAtCapacityZeroTakesNothing(t *testing.T) {
|
||||
})
|
||||
t.Run("config watch wakes", func(t *testing.T) {
|
||||
src, st := NewFakeAsks(), newSteps()
|
||||
_ = src.PublishAsk(testAsk(delivery.AskDelivery))
|
||||
_ = src.PublishAsk(testAsk(sdkstep.AskDelivery))
|
||||
w := &configWatch{value: "0"}
|
||||
p := &Puller{Source: src, Language: delivery.AskGo, Node: "g14", Put: st.put, PullWait: 10 * time.Millisecond,
|
||||
p := &Puller{Source: src, Language: sdkstep.AskGo, Node: "g14", Put: st.put, PullWait: 10 * time.Millisecond,
|
||||
Capacity: func() int { return 0 }, // the settings file, not refilled yet
|
||||
WatchConfig: w.watch,
|
||||
Run: func(ctx context.Context, a delivery.StepAsk) (any, error) { return nil, nil }}
|
||||
Run: func(ctx context.Context, a sdkstep.StepAsk) (any, error) { return nil, nil }}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
go p.Pull(ctx)
|
||||
waitFor(t, "the watch of the capacity", func() bool { w.mu.Lock(); defer w.mu.Unlock(); return w.handle != nil })
|
||||
time.Sleep(30 * time.Millisecond)
|
||||
if src.Waiting(delivery.AskDelivery) != 1 {
|
||||
if src.Waiting(sdkstep.AskDelivery) != 1 {
|
||||
t.Fatal("a node at capacity 0 took an ask")
|
||||
}
|
||||
if w.asked != sdkstep.CapacitySetting {
|
||||
@@ -323,7 +323,7 @@ func TestPullAtCapacityZeroTakesNothing(t *testing.T) {
|
||||
// 2026-10-28: a replaced builder held the old "shanks=2,..." in its settings file, its config follow timed out, and
|
||||
// the puller parked at 0 with nothing to wake it when the node-engine refilled the file.
|
||||
src, st := NewFakeAsks(), newSteps()
|
||||
_ = src.PublishAsk(testAsk(delivery.AskDelivery))
|
||||
_ = src.PublishAsk(testAsk(sdkstep.AskDelivery))
|
||||
file := filepath.Join(t.TempDir(), "settings.json")
|
||||
if err := os.WriteFile(file, []byte(`{"capacity":"shanks=2,ace=2"}`), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -347,14 +347,14 @@ func TestPullAtCapacityZeroTakesNothing(t *testing.T) {
|
||||
parked.Add(1)
|
||||
}
|
||||
}
|
||||
p := &Puller{Source: src, Language: delivery.AskGo, Node: "shanks", Put: st.put, PullWait: 10 * time.Millisecond,
|
||||
p := &Puller{Source: src, Language: sdkstep.AskGo, Node: "shanks", Put: st.put, PullWait: 10 * time.Millisecond,
|
||||
SettingsFile: file, WatchConfig: watch, Logf: logf, watching: func() { armed <- struct{}{} },
|
||||
Run: func(ctx context.Context, a delivery.StepAsk) (any, error) { return nil, nil }}
|
||||
Run: func(ctx context.Context, a sdkstep.StepAsk) (any, error) { return nil, nil }}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
go p.Pull(ctx)
|
||||
waitFor(t, "the parked puller to say why", func() bool { why, fault := p.CapacityNote(); return fault && why != "" })
|
||||
if src.Waiting(delivery.AskDelivery) != 1 {
|
||||
if src.Waiting(sdkstep.AskDelivery) != 1 {
|
||||
t.Fatal("a node whose capacity cannot be read took an ask")
|
||||
}
|
||||
<-armed // the file watch is in place
|
||||
@@ -377,7 +377,7 @@ func TestPullAtCapacityZeroTakesNothing(t *testing.T) {
|
||||
}
|
||||
<-armed // armed again
|
||||
waitFor(t, "the puller parked with no settings file", func() bool { return parks() > before })
|
||||
_ = src.PublishAsk(testAsk(delivery.AskDelivery))
|
||||
_ = src.PublishAsk(testAsk(sdkstep.AskDelivery))
|
||||
if err := os.WriteFile(file, []byte(`{"capacity":2}`), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -397,9 +397,9 @@ func TestPullAtCapacityZeroTakesNothing(t *testing.T) {
|
||||
}, true},
|
||||
} {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
p := &Puller{Source: NewFakeAsks(), Language: delivery.AskGo, Node: "g14", Put: newSteps().put,
|
||||
p := &Puller{Source: NewFakeAsks(), Language: sdkstep.AskGo, Node: "g14", Put: newSteps().put,
|
||||
Capacity: func() int { return 1 }, WatchConfig: c.watch,
|
||||
Run: func(ctx context.Context, a delivery.StepAsk) (any, error) { return nil, nil }}
|
||||
Run: func(ctx context.Context, a sdkstep.StepAsk) (any, error) { return nil, nil }}
|
||||
p.watchCapacity()
|
||||
if p.room() != c.room {
|
||||
t.Fatalf("room = %v, want %v", p.room(), c.room)
|
||||
@@ -412,30 +412,30 @@ func TestPullAtCapacityZeroTakesNothing(t *testing.T) {
|
||||
// stays paused until resumed.
|
||||
func TestPullPauseOutlivesARestart(t *testing.T) {
|
||||
src, st, steps := NewFakeAsks(), &kv{}, &kv{}
|
||||
run := func(ctx context.Context, a delivery.StepAsk) (any, error) { return nil, nil }
|
||||
p := &Puller{Source: src, Language: delivery.AskGo, Node: "shanks", Put: steps.put, PausePut: st.put, Load: st.load,
|
||||
run := func(ctx context.Context, a sdkstep.StepAsk) (any, error) { return nil, nil }
|
||||
p := &Puller{Source: src, Language: sdkstep.AskGo, Node: "shanks", Put: steps.put, PausePut: st.put, Load: st.load,
|
||||
PullWait: 10 * time.Millisecond, Capacity: func() int { return 2 }, Run: run}
|
||||
if err := p.Pause("the operator"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
key, _ := delivery.PauseKey("shanks")
|
||||
key, _ := sdkstep.PauseKey("shanks")
|
||||
raw, _ := st.load(key)
|
||||
if v, err := delivery.DecodePause(raw); err != nil || !v.Paused || v.By != "the operator" {
|
||||
if v, err := sdkstep.DecodePause(raw); err != nil || !v.Paused || v.By != "the operator" {
|
||||
t.Fatalf("the pause key holds %s (%v)", raw, err)
|
||||
}
|
||||
if raw, _ := steps.load(key); raw != nil {
|
||||
t.Fatalf("the pause was written in the steps state, whose keys age out: %s", raw)
|
||||
}
|
||||
// The restart: a new puller on the same state.
|
||||
again := &Puller{Source: src, Language: delivery.AskGo, Node: "shanks", Put: steps.put, PausePut: st.put, Load: st.load,
|
||||
again := &Puller{Source: src, Language: sdkstep.AskGo, Node: "shanks", Put: steps.put, PausePut: st.put, Load: st.load,
|
||||
PullWait: 10 * time.Millisecond, Capacity: func() int { return 2 }, Run: run}
|
||||
_ = src.PublishAsk(testAsk(delivery.AskDelivery))
|
||||
_ = src.PublishAsk(testAsk(sdkstep.AskDelivery))
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
go again.Pull(ctx)
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
if !again.Paused() || src.Waiting(delivery.AskDelivery) != 1 {
|
||||
t.Fatalf("restarted: paused %v, waiting %d; want paused and 1", again.Paused(), src.Waiting(delivery.AskDelivery))
|
||||
if !again.Paused() || src.Waiting(sdkstep.AskDelivery) != 1 {
|
||||
t.Fatalf("restarted: paused %v, waiting %d; want paused and 1", again.Paused(), src.Waiting(sdkstep.AskDelivery))
|
||||
}
|
||||
if err := again.Resume("the operator"); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -445,7 +445,7 @@ func TestPullPauseOutlivesARestart(t *testing.T) {
|
||||
t.Run("a pause that cannot be read is said and pulled as not paused", func(t *testing.T) {
|
||||
src := NewFakeAsks()
|
||||
var said atomic.Value
|
||||
p := &Puller{Source: src, Language: delivery.AskGo, Node: "shanks", Put: steps.put, PausePut: st.put,
|
||||
p := &Puller{Source: src, Language: sdkstep.AskGo, Node: "shanks", Put: steps.put, PausePut: st.put,
|
||||
Load: func(string) ([]byte, error) { return nil, errors.New("no runtime yet") },
|
||||
PullWait: 10 * time.Millisecond, Capacity: func() int { return 2 }, Run: run,
|
||||
Logf: func(f string, a ...any) {
|
||||
@@ -453,7 +453,7 @@ func TestPullPauseOutlivesARestart(t *testing.T) {
|
||||
said.Store(m)
|
||||
}
|
||||
}}
|
||||
_ = src.PublishAsk(testAsk(delivery.AskDelivery))
|
||||
_ = src.PublishAsk(testAsk(sdkstep.AskDelivery))
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
go p.Pull(ctx)
|
||||
@@ -468,12 +468,12 @@ func TestPullPauseOutlivesARestart(t *testing.T) {
|
||||
// acknowledges its ask, so the stream gives it to another node.
|
||||
func TestPullHeartbeatAndStop(t *testing.T) {
|
||||
src, st := NewFakeAsks(), newSteps()
|
||||
a := testAsk(delivery.AskDelivery)
|
||||
a := testAsk(sdkstep.AskDelivery)
|
||||
_ = src.PublishAsk(a)
|
||||
running := make(chan struct{})
|
||||
p := &Puller{Source: src, Language: delivery.AskGo, Node: "shanks", Put: st.put, Capacity: func() int { return 1 },
|
||||
p := &Puller{Source: src, Language: sdkstep.AskGo, Node: "shanks", Put: st.put, Capacity: func() int { return 1 },
|
||||
PullWait: 10 * time.Millisecond, Heartbeat: 5 * time.Millisecond,
|
||||
Run: func(ctx context.Context, a delivery.StepAsk) (any, error) {
|
||||
Run: func(ctx context.Context, a sdkstep.StepAsk) (any, error) {
|
||||
close(running)
|
||||
<-ctx.Done()
|
||||
return nil, ctx.Err()
|
||||
@@ -490,8 +490,8 @@ func TestPullHeartbeatAndStop(t *testing.T) {
|
||||
if len(acked)+len(termed)+len(naked) != 0 {
|
||||
t.Fatalf("a stopped holder gave its ask a fate: acked %v termed %v naked %v", acked, termed, naked)
|
||||
}
|
||||
key, _ := delivery.AskStepKey("shanks", a.ID)
|
||||
if s, ok := st.get(key); !ok || s.State != delivery.StepRunning {
|
||||
key, _ := sdkstep.AskStepKey("shanks", a.ID)
|
||||
if s, ok := st.get(key); !ok || s.State != sdkstep.StepRunning {
|
||||
t.Fatalf("the step %s = %+v, want left running", key, s)
|
||||
}
|
||||
}
|
||||
@@ -500,19 +500,19 @@ func TestPullHeartbeatAndStop(t *testing.T) {
|
||||
// asker's key fails, and it is ended on the stream, not offered to another node.
|
||||
func TestAPoisonedAskIsSaidAndRunNoMore(t *testing.T) {
|
||||
src, st := NewFakeAsks(), newSteps()
|
||||
src.Delivered = delivery.AskRuns + 1
|
||||
a := testAsk(delivery.AskDelivery)
|
||||
src.Delivered = sdkstep.AskRuns + 1
|
||||
a := testAsk(sdkstep.AskDelivery)
|
||||
_ = src.PublishAsk(a)
|
||||
ran := false
|
||||
p := &Puller{Source: src, Language: delivery.AskGo, Node: "shanks", Put: st.put, Capacity: func() int { return 1 },
|
||||
PullWait: 10 * time.Millisecond, Run: func(ctx context.Context, a delivery.StepAsk) (any, error) { ran = true; return nil, nil }}
|
||||
p := &Puller{Source: src, Language: sdkstep.AskGo, Node: "shanks", Put: st.put, Capacity: func() int { return 1 },
|
||||
PullWait: 10 * time.Millisecond, Run: func(ctx context.Context, a sdkstep.StepAsk) (any, error) { ran = true; return nil, nil }}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
go p.Pull(ctx)
|
||||
waitFor(t, "the ask termed", func() bool { _, tm, _ := src.Fates(); return len(tm) == 1 })
|
||||
key, _ := delivery.AskStepKey("shanks", a.ID)
|
||||
key, _ := sdkstep.AskStepKey("shanks", a.ID)
|
||||
s, ok := st.get(key)
|
||||
if ran || !ok || s.State != delivery.StepFailed || !strings.Contains(s.Reason, "taken 3 times") || !strings.Contains(s.Reason, "poison") {
|
||||
if ran || !ok || s.State != sdkstep.StepFailed || !strings.Contains(s.Reason, "taken 3 times") || !strings.Contains(s.Reason, "poison") {
|
||||
t.Fatalf("ran %v, step %+v", ran, s)
|
||||
}
|
||||
if acked, _, naked := src.Fates(); len(acked)+len(naked) != 0 {
|
||||
|
||||
@@ -17,7 +17,7 @@ func (r *Refusal) Error() string { return r.Text }
|
||||
// Refuse is a Refusal with its text formatted.
|
||||
func Refuse(format string, args ...any) *Refusal { return &Refusal{Text: fmt.Sprintf(format, args...)} }
|
||||
|
||||
// Declining is a step a node does not run because another node should: it ends refused as declined (delivery.StepDeclined),
|
||||
// Declining is a step a node does not run because another node should: it ends refused as declined (sdkstep.StepDeclined),
|
||||
// the ask's message is dropped, and the asker puts the ask again under a new id (issue 890). A build ask naming a
|
||||
// toolchain other than this node's is one (novox/hq ADR 0584 clause 2): a node that is not in the toolchain majority
|
||||
// sits out and never fails a build.
|
||||
|
||||
@@ -2,10 +2,9 @@ package buildstep
|
||||
|
||||
import (
|
||||
"context"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// 1887/A9a, ADR 0525 §10: a build seat's verbs are <seat>.current, .pause and .resume; current names the ask running,
|
||||
@@ -13,8 +12,8 @@ import (
|
||||
func TestSeatToolsCurrentPauseResume(t *testing.T) {
|
||||
src, st := NewFakeAsks(), newSteps()
|
||||
release := make(chan struct{})
|
||||
p := &Puller{Source: src, Language: delivery.AskGo, Node: "shanks", Put: st.put, Capacity: func() int { return 1 },
|
||||
PullWait: 10 * time.Millisecond, Run: func(ctx context.Context, a delivery.StepAsk) (any, error) { <-release; return nil, nil }}
|
||||
p := &Puller{Source: src, Language: sdkstep.AskGo, Node: "shanks", Put: st.put, Capacity: func() int { return 1 },
|
||||
PullWait: 10 * time.Millisecond, Run: func(ctx context.Context, a sdkstep.StepAsk) (any, error) { <-release; return nil, nil }}
|
||||
tools := map[string]func(map[string]any) (any, error){}
|
||||
for _, tool := range SeatTools("node-build-go", "Go", p) {
|
||||
tools[tool.Name] = tool.Run
|
||||
@@ -24,7 +23,7 @@ func TestSeatToolsCurrentPauseResume(t *testing.T) {
|
||||
t.Fatalf("no %s", name)
|
||||
}
|
||||
}
|
||||
a := testAsk(delivery.AskDelivery)
|
||||
a := testAsk(sdkstep.AskDelivery)
|
||||
_ = src.PublishAsk(a)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
@@ -40,13 +39,13 @@ func TestSeatToolsCurrentPauseResume(t *testing.T) {
|
||||
t.Fatalf("pause = %v, %v", out, err)
|
||||
}
|
||||
close(release)
|
||||
_ = src.PublishAsk(testAsk(delivery.AskDelivery))
|
||||
_ = src.PublishAsk(testAsk(sdkstep.AskDelivery))
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
if src.Waiting(delivery.AskDelivery) != 1 {
|
||||
if src.Waiting(sdkstep.AskDelivery) != 1 {
|
||||
t.Fatal("a paused node took an ask")
|
||||
}
|
||||
if _, err := tools["node-build-go.resume"](nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
waitFor(t, "the ask taken after the resume", func() bool { return src.Waiting(delivery.AskDelivery) == 0 })
|
||||
waitFor(t, "the ask taken after the resume", func() bool { return src.Waiting(sdkstep.AskDelivery) == 0 })
|
||||
}
|
||||
|
||||
@@ -9,14 +9,14 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
buildrun "git.novox.be/novox/mesh-delivery/sdk/buildrun"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// Put writes one key of a state: stdio.State(delivery.StepsState).Put's shape (go/state.go).
|
||||
// Put writes one key of a state: the steps state.Put's shape (go/state.go).
|
||||
type Put func(key string, value any) (uint64, error)
|
||||
|
||||
// EndAttempts and EndRetry bound how a step's end is written: a write that fails is tried again, since the end
|
||||
@@ -43,20 +43,20 @@ func Start(ctx context.Context, put Put, node, tool string, run func(ctx context
|
||||
}
|
||||
|
||||
// begin writes the step running under id, owned by this process (Owner).
|
||||
func begin(put Put, node, tool, id, what string) (delivery.Step, string, error) {
|
||||
step := delivery.Step{ID: id, Tool: tool, Node: node, State: delivery.StepRunning, Started: now().UTC(), What: what, Owner: sdkstep.Owner}
|
||||
key, err := delivery.StepKey(node, step.ID)
|
||||
func begin(put Put, node, tool, id, what string) (sdkstep.Step, string, error) {
|
||||
step := sdkstep.Step{ID: id, Tool: tool, Node: node, State: sdkstep.StepRunning, Started: now().UTC(), What: what, Owner: buildrun.Owner}
|
||||
key, err := sdkstep.StepKey(node, step.ID)
|
||||
if err != nil {
|
||||
return delivery.Step{}, "", err
|
||||
return sdkstep.Step{}, "", err
|
||||
}
|
||||
if err := write(put, key, step); err != nil {
|
||||
return delivery.Step{}, "", fmt.Errorf("buildstep: writing the running step %s: %w", key, err)
|
||||
return sdkstep.Step{}, "", fmt.Errorf("buildstep: writing the running step %s: %w", key, err)
|
||||
}
|
||||
return step, key, nil
|
||||
}
|
||||
|
||||
// end runs run and writes the step's end.
|
||||
func end(ctx context.Context, put Put, key string, step delivery.Step, run func(ctx context.Context) (any, error)) {
|
||||
func end(ctx context.Context, put Put, key string, step sdkstep.Step, run func(ctx context.Context) (any, error)) {
|
||||
result, err := protect(ctx, run)
|
||||
_ = finish(put, key, step, result, err, log.Printf) // finish says a refused end itself
|
||||
}
|
||||
@@ -66,23 +66,23 @@ func end(ctx context.Context, put Put, key string, step delivery.Step, run func(
|
||||
// refusal is logged and a small failed end without the result is written instead, naming the write's error, so the
|
||||
// step's reader learns it is over and why. finish answers an error only when no end could be written at all, and then
|
||||
// the step's ask is not acknowledged as done.
|
||||
func finish(put Put, key string, step delivery.Step, result any, err error, logf func(format string, args ...any)) error {
|
||||
func finish(put Put, key string, step sdkstep.Step, result any, err error, logf func(format string, args ...any)) error {
|
||||
step.Ended = now().UTC()
|
||||
switch ref := (*Refusal)(nil); {
|
||||
case err == nil:
|
||||
step.State = delivery.StepPassed
|
||||
step.State = sdkstep.StepPassed
|
||||
if result != nil {
|
||||
raw, merr := json.Marshal(result)
|
||||
if merr != nil {
|
||||
step.State, step.Reason = delivery.StepFailed, "the result is not JSON: "+merr.Error()
|
||||
step.State, step.Reason = sdkstep.StepFailed, "the result is not JSON: "+merr.Error()
|
||||
} else {
|
||||
step.Result = raw
|
||||
}
|
||||
}
|
||||
case errors.As(err, &ref):
|
||||
step.State, step.Reason = delivery.StepRefused, err.Error()
|
||||
step.State, step.Reason = sdkstep.StepRefused, err.Error()
|
||||
default:
|
||||
step.State, step.Reason = delivery.StepFailed, err.Error()
|
||||
step.State, step.Reason = sdkstep.StepFailed, err.Error()
|
||||
// A failed step may answer what it found on the way (a build's out-of-step files): kept on its end.
|
||||
if result != nil {
|
||||
if raw, merr := json.Marshal(result); merr == nil {
|
||||
@@ -102,7 +102,7 @@ func finish(put Put, key string, step delivery.Step, result any, err error, logf
|
||||
if step.Reason != "" {
|
||||
why = fmt.Sprintf("the step ended %s (%s), and its end could not be written: %v", ended, step.Reason, werr)
|
||||
}
|
||||
step.State, step.Reason, step.Result = delivery.StepFailed, why, nil
|
||||
step.State, step.Reason, step.Result = sdkstep.StepFailed, why, nil
|
||||
if ferr := writeEnd(put, key, step); ferr != nil {
|
||||
if logf != nil {
|
||||
logf("the failed end of the step %s could not be written either: %v", key, ferr)
|
||||
@@ -113,7 +113,7 @@ func finish(put Put, key string, step delivery.Step, result any, err error, logf
|
||||
}
|
||||
|
||||
// writeEnd writes the step's end, trying EndAttempts times.
|
||||
func writeEnd(put Put, key string, step delivery.Step) error {
|
||||
func writeEnd(put Put, key string, step sdkstep.Step) error {
|
||||
for attempt := 1; ; attempt++ {
|
||||
werr := write(put, key, step)
|
||||
if werr == nil || attempt >= EndAttempts {
|
||||
@@ -133,8 +133,8 @@ func protect(ctx context.Context, run func(ctx context.Context) (any, error)) (r
|
||||
return run(ctx)
|
||||
}
|
||||
|
||||
func write(put Put, key string, step delivery.Step) error {
|
||||
raw, err := delivery.EncodeStep(step)
|
||||
func write(put Put, key string, step sdkstep.Step) error {
|
||||
raw, err := sdkstep.EncodeStep(step)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -4,25 +4,24 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
buildrun "git.novox.be/novox/mesh-delivery/sdk/buildrun"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// keys is a steps state in memory: every value put, in order, per key, and a signal for each end.
|
||||
type keys struct {
|
||||
mu sync.Mutex
|
||||
puts map[string][]delivery.Step
|
||||
puts map[string][]sdkstep.Step
|
||||
ended chan string
|
||||
}
|
||||
|
||||
func newKeys() *keys { return &keys{puts: map[string][]delivery.Step{}, ended: make(chan string, 16)} }
|
||||
func newKeys() *keys { return &keys{puts: map[string][]sdkstep.Step{}, ended: make(chan string, 16)} }
|
||||
|
||||
func (k *keys) put(key string, value any) (uint64, error) {
|
||||
step, err := delivery.DecodeStep(value.(json.RawMessage))
|
||||
step, err := sdkstep.DecodeStep(value.(json.RawMessage))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -30,13 +29,13 @@ func (k *keys) put(key string, value any) (uint64, error) {
|
||||
k.puts[key] = append(k.puts[key], step)
|
||||
n := uint64(len(k.puts[key]))
|
||||
k.mu.Unlock()
|
||||
if step.State != delivery.StepRunning {
|
||||
if step.State != sdkstep.StepRunning {
|
||||
k.ended <- key
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (k *keys) last(key string) delivery.Step {
|
||||
func (k *keys) last(key string) sdkstep.Step {
|
||||
k.mu.Lock()
|
||||
defer k.mu.Unlock()
|
||||
s := k.puts[key]
|
||||
@@ -72,8 +71,8 @@ func TestStartAnswersAtOnceWithTheStepRunning(t *testing.T) {
|
||||
}
|
||||
s := k.last("ace." + id)
|
||||
// 1890/AC5: the running key names this process as its owner.
|
||||
if s.State != delivery.StepRunning || s.Tool != "fetch" || s.Node != "ace" || s.ID != id || s.Started.IsZero() || s.Owner != sdkstep.Owner || sdkstep.Owner == "" {
|
||||
t.Errorf("step = %+v, want owner %q", s, sdkstep.Owner)
|
||||
if s.State != sdkstep.StepRunning || s.Tool != "fetch" || s.Node != "ace" || s.ID != id || s.Started.IsZero() || s.Owner != buildrun.Owner || buildrun.Owner == "" {
|
||||
t.Errorf("step = %+v, want owner %q", s, buildrun.Owner)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,11 +85,11 @@ func TestStepEndsPassedFailedOrRefusedOnItsKey(t *testing.T) {
|
||||
reason string
|
||||
result string
|
||||
}{
|
||||
{"result", func(context.Context) (any, error) { return map[string]string{"sha": "abc"}, nil }, delivery.StepPassed, "", `{"sha":"abc"}`},
|
||||
{"error", func(context.Context) (any, error) { return nil, errors.New("exit 2") }, delivery.StepFailed, "exit 2", ""},
|
||||
{"result", func(context.Context) (any, error) { return map[string]string{"sha": "abc"}, nil }, sdkstep.StepPassed, "", `{"sha":"abc"}`},
|
||||
{"error", func(context.Context) (any, error) { return nil, errors.New("exit 2") }, sdkstep.StepFailed, "exit 2", ""},
|
||||
{"refusal", func(context.Context) (any, error) {
|
||||
return nil, &Refusal{Text: "go 1.27.1 installed, the recipe builds with go 1.27.0", Want: "1.27.0", Have: "1.27.1"}
|
||||
}, delivery.StepRefused, "go 1.27.1 installed, the recipe builds with go 1.27.0", ""},
|
||||
}, sdkstep.StepRefused, "go 1.27.1 installed, the recipe builds with go 1.27.0", ""},
|
||||
} {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
k := newKeys()
|
||||
@@ -102,7 +101,7 @@ func TestStepEndsPassedFailedOrRefusedOnItsKey(t *testing.T) {
|
||||
t.Fatalf("ended key %q", got)
|
||||
}
|
||||
s := k.last("ace." + id)
|
||||
if s.State != c.state || s.Reason != c.reason || string(s.Result) != c.result || s.Ended.IsZero() || s.Owner != sdkstep.Owner {
|
||||
if s.State != c.state || s.Reason != c.reason || string(s.Result) != c.result || s.Ended.IsZero() || s.Owner != buildrun.Owner {
|
||||
t.Errorf("step = %+v", s)
|
||||
}
|
||||
})
|
||||
@@ -115,7 +114,7 @@ func TestAPanicEndsTheStepFailed(t *testing.T) {
|
||||
id, _ := Start(context.Background(), k.put, "ace", "build", func(context.Context) (any, error) { panic("boom") })
|
||||
k.waitEnd(t)
|
||||
s := k.last("ace." + id)
|
||||
if s.State != delivery.StepFailed || s.Reason != "the step panicked: boom" {
|
||||
if s.State != sdkstep.StepFailed || s.Reason != "the step panicked: boom" {
|
||||
t.Errorf("step = %+v", s)
|
||||
}
|
||||
}
|
||||
@@ -134,7 +133,7 @@ func TestTwoIDsInOneMillisecondDiffer(t *testing.T) {
|
||||
t.Fatalf("id %q handed out twice", id)
|
||||
}
|
||||
seen[id] = true
|
||||
if _, err := delivery.StepKey("ace", id); err != nil {
|
||||
if _, err := sdkstep.StepKey("ace", id); err != nil {
|
||||
t.Errorf("id %q: %v", id, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package buildstep
|
||||
|
||||
import (
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
buildrun "git.novox.be/novox/mesh-delivery/sdk/buildrun"
|
||||
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
)
|
||||
|
||||
// StepEndTool is the module's step-end tool, named name (build_go_step_end): it ends one step left running, with a
|
||||
// reason, refusing one this process started. p may be nil.
|
||||
func StepEndTool(name string, node string, scan sdkstep.Scan, p *Puller) stdio.Tool {
|
||||
func StepEndTool(name string, node string, scan buildrun.Scan, p *Puller) stdio.Tool {
|
||||
return stdio.Tool{Name: name,
|
||||
Description: "End one step of this machine that is stuck in state running (for instance left by a restart of the module) " +
|
||||
"as failed, with the reason given; its asker then sees it over, so ending an ask still in its stream fails its asker. " +
|
||||
@@ -31,6 +31,6 @@ func StepEndTool(name string, node string, scan sdkstep.Scan, p *Puller) stdio.T
|
||||
return false
|
||||
}
|
||||
}
|
||||
return sdkstep.EndOne(scan, node, id, why, live)
|
||||
return buildrun.EndOne(scan, node, id, why, live)
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package buildstep
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ToolchainVersion is the layout version of a toolchains value.
|
||||
const ToolchainVersion = 1
|
||||
|
||||
// Toolchain is the value of a build module's state toolchains under the key <node>.<language>: the version of the
|
||||
// language's toolchain installed on the node, the build module's recipe version, and when the module saw them. It is
|
||||
// the layout every node-build seat gives that state (the seats' generated Toolchain); the three build modules share
|
||||
// this form because their runner does (design 26 *Where a seat's code goes*, a module's own package). A test holds it
|
||||
// to each seat's decoder.
|
||||
type Toolchain struct {
|
||||
V int `json:"v,omitempty"`
|
||||
Language string `json:"language"`
|
||||
Version string `json:"version"`
|
||||
Recipe string `json:"recipe"`
|
||||
At time.Time `json:"at,omitzero"`
|
||||
}
|
||||
|
||||
func checkToolchainLanguage(language string) error {
|
||||
switch language {
|
||||
case "go", "typescript":
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("buildstep: the toolchain language %q is not go or typescript", language)
|
||||
}
|
||||
|
||||
// ToolchainKey is the key of a node's toolchain of one language: <node>.<language>. It refuses an empty node, a node
|
||||
// with a dot and a language other than go or typescript.
|
||||
func ToolchainKey(node, language string) (string, error) {
|
||||
if node == "" || strings.ContainsAny(node, ". ") {
|
||||
return "", fmt.Errorf("buildstep: %q is not a node name", node)
|
||||
}
|
||||
if err := checkToolchainLanguage(language); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return node + "." + language, nil
|
||||
}
|
||||
|
||||
func (t Toolchain) check() error {
|
||||
if err := checkToolchainLanguage(t.Language); err != nil {
|
||||
return err
|
||||
}
|
||||
if t.Version == "" || t.Recipe == "" {
|
||||
return fmt.Errorf("buildstep: a %s toolchain needs its version and its recipe", t.Language)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EncodeToolchain is t as JSON; it refuses a language other than go or typescript and an empty version or recipe.
|
||||
func EncodeToolchain(t Toolchain) ([]byte, error) {
|
||||
if err := t.check(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.V = ToolchainVersion
|
||||
return json.Marshal(t)
|
||||
}
|
||||
|
||||
// DecodeToolchain reads a toolchains value, refusing an unknown field, another layout version and what
|
||||
// EncodeToolchain refuses.
|
||||
func DecodeToolchain(data []byte) (Toolchain, error) {
|
||||
var t Toolchain
|
||||
dec := json.NewDecoder(bytes.NewReader(data))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&t); err != nil {
|
||||
return Toolchain{}, fmt.Errorf("buildstep: a toolchain does not read: %w", err)
|
||||
}
|
||||
if t.V != ToolchainVersion {
|
||||
return Toolchain{}, fmt.Errorf("buildstep: a toolchain of version %d, this reads %d", t.V, ToolchainVersion)
|
||||
}
|
||||
if err := t.check(); err != nil {
|
||||
return Toolchain{}, err
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package buildstep
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
nodebuildgo "git.novox.be/novox/mesh-delivery/contracts/go/seats/nodebuildgo"
|
||||
nodebuildimage "git.novox.be/novox/mesh-delivery/contracts/go/seats/nodebuildimage"
|
||||
nodebuildnode "git.novox.be/novox/mesh-delivery/contracts/go/seats/nodebuildnode"
|
||||
)
|
||||
|
||||
// The toolchains value and key are the layout all three node-build seats generate: each seat's strict decoder reads
|
||||
// what the shared runner writes, and each seat's key function agrees with ToolchainKey.
|
||||
func TestTheToolchainsValueAndKeyAreTheSeatsLayout(t *testing.T) {
|
||||
raw, err := EncodeToolchain(Toolchain{Language: "go", Version: "go1.27.1", Recipe: "3", At: time.Date(2026, 11, 5, 10, 0, 0, 0, time.UTC)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := nodebuildgo.DecodeToolchainsByNodeLanguageValue(raw); err != nil {
|
||||
t.Errorf("node-build-go does not read the value: %v", err)
|
||||
}
|
||||
if _, err := nodebuildimage.DecodeToolchainsByNodeLanguageValue(raw); err != nil {
|
||||
t.Errorf("node-build-image does not read the value: %v", err)
|
||||
}
|
||||
if _, err := nodebuildnode.DecodeToolchainsByNodeLanguageValue(raw); err != nil {
|
||||
t.Errorf("node-build-node does not read the value: %v", err)
|
||||
}
|
||||
back, err := DecodeToolchain(raw)
|
||||
if err != nil || back.Version != "go1.27.1" || back.At.IsZero() {
|
||||
t.Errorf("the value reads back as %+v, %v", back, err)
|
||||
}
|
||||
key, err := ToolchainKey("ace", "typescript")
|
||||
if err != nil || key != nodebuildnode.ToolchainsByNodeLanguageKey("ace", "typescript") || key != nodebuildgo.ToolchainsByNodeLanguageKey("ace", "typescript") {
|
||||
t.Errorf("key %q, %v", key, err)
|
||||
}
|
||||
for _, bad := range [][2]string{{"", "go"}, {"a.b", "go"}, {"ace", "rust"}} {
|
||||
if _, err := ToolchainKey(bad[0], bad[1]); err == nil {
|
||||
t.Errorf("the key of %v was made", bad)
|
||||
}
|
||||
}
|
||||
if _, err := EncodeToolchain(Toolchain{Language: "go", Version: "go1.27"}); err == nil {
|
||||
t.Error("a toolchain without its recipe was written")
|
||||
}
|
||||
if _, err := DecodeToolchain([]byte(`{"v":1,"language":"go","version":"x","recipe":"1","extra":1}`)); err == nil || !strings.Contains(err.Error(), "extra") {
|
||||
t.Errorf("a field the layout lacks was read: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -15,8 +16,6 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// Issue 1589: the build step packs an archive artifact (novox/hq ADR 0478 §1), as the build agent packs it
|
||||
@@ -146,7 +145,7 @@ func pinnedModule(t *testing.T, path, sha, extra string) (*Module, *keptStates)
|
||||
func TestAnArchiveOfADirectoryHasTheBuildAgentsDigest(t *testing.T) {
|
||||
m, states, calls := archiveModule(t, "")
|
||||
res, st := runBuild(t, m, states, "files")
|
||||
if st.State != delivery.StepPassed {
|
||||
if st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
|
||||
}
|
||||
sameAsBuildAgent(t, res, buildAgentDirectoryStream, buildAgentDirectoryDigest)
|
||||
@@ -177,7 +176,7 @@ func TestAnArchiveFromOutsideTheModuleOrMissingFailsTheStep(t *testing.T) {
|
||||
"nothing": "nothing",
|
||||
} {
|
||||
m, states, _ := archiveModule(t, `{"module":"demo","version":"1","build":{"artifacts":[{"name":"files","kind":"archive","from":"`+from+`"}]}}`)
|
||||
if _, st := runBuild(t, m, states, "files"); st.State != delivery.StepFailed || !strings.Contains(st.Reason, want) {
|
||||
if _, st := runBuild(t, m, states, "files"); st.State != sdkstep.StepFailed || !strings.Contains(st.Reason, want) {
|
||||
t.Errorf("from %q: %s %q", from, st.State, st.Reason)
|
||||
}
|
||||
}
|
||||
@@ -193,7 +192,7 @@ func pinned(url, sha, extra string) string {
|
||||
func TestAPinnedArchiveIsUnpackedWithinAndHasTheBuildAgentsDigest(t *testing.T) {
|
||||
m, states := pinnedModule(t, "/release.tar.gz", releaseSHA256, `,"within":"release/app"`)
|
||||
res, st := runBuild(t, m, states, "files")
|
||||
if st.State != delivery.StepPassed {
|
||||
if st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("%s %q", st.State, st.Reason)
|
||||
}
|
||||
sameAsBuildAgent(t, res, buildAgentWithinStream, buildAgentWithinDigest)
|
||||
@@ -202,7 +201,7 @@ func TestAPinnedArchiveIsUnpackedWithinAndHasTheBuildAgentsDigest(t *testing.T)
|
||||
func TestAPinnedFileKeptWholeHasTheBuildAgentsDigest(t *testing.T) {
|
||||
m, states := pinnedModule(t, "/release.tar.gz", releaseSHA256, `,"file":"tool.bin"`)
|
||||
res, st := runBuild(t, m, states, "files")
|
||||
if st.State != delivery.StepPassed {
|
||||
if st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("%s %q", st.State, st.Reason)
|
||||
}
|
||||
sameAsBuildAgent(t, res, buildAgentFileStream, buildAgentFileDigest)
|
||||
@@ -213,7 +212,7 @@ func TestAPinnedFileOfAnotherDigestIsRefused(t *testing.T) {
|
||||
wrong := strings.Repeat("ab", 32)
|
||||
m, states := pinnedModule(t, "/release.tar.gz", wrong, `,"within":"release/app"`)
|
||||
_, st := runBuild(t, m, states, "files")
|
||||
if st.State != delivery.StepFailed || !strings.Contains(st.Reason, "sha256:"+wrong) || !strings.Contains(st.Reason, "sha256:"+releaseSHA256) {
|
||||
if st.State != sdkstep.StepFailed || !strings.Contains(st.Reason, "sha256:"+wrong) || !strings.Contains(st.Reason, "sha256:"+releaseSHA256) {
|
||||
t.Errorf("the step ended %s: %q", st.State, st.Reason)
|
||||
}
|
||||
if entries, _ := os.ReadDir(filepath.Join(m.Work, "ws1", "artifacts")); len(entries) != 0 {
|
||||
@@ -223,7 +222,7 @@ func TestAPinnedFileOfAnotherDigestIsRefused(t *testing.T) {
|
||||
|
||||
func TestAPinnedFileTheServerRefusesFailsTheStep(t *testing.T) {
|
||||
m, states := pinnedModule(t, "/gone", releaseSHA256, `,"file":"x"`)
|
||||
if _, st := runBuild(t, m, states, "files"); st.State != delivery.StepFailed || !strings.Contains(st.Reason, "404") {
|
||||
if _, st := runBuild(t, m, states, "files"); st.State != sdkstep.StepFailed || !strings.Contains(st.Reason, "404") {
|
||||
t.Errorf("the step ended %s: %q", st.State, st.Reason)
|
||||
}
|
||||
}
|
||||
@@ -231,7 +230,7 @@ func TestAPinnedFileTheServerRefusesFailsTheStep(t *testing.T) {
|
||||
// Entries outside `within` are refused, as the build agent refuses them.
|
||||
func TestAPinnedArchiveWithEntriesOutsideWithinIsRefused(t *testing.T) {
|
||||
m, states := pinnedModule(t, "/release.tar.gz", releaseSHA256, `,"within":"release/other"`)
|
||||
if _, st := runBuild(t, m, states, "files"); st.State != delivery.StepFailed || !strings.Contains(st.Reason, "release/other") {
|
||||
if _, st := runBuild(t, m, states, "files"); st.State != sdkstep.StepFailed || !strings.Contains(st.Reason, "release/other") {
|
||||
t.Errorf("the step ended %s: %q", st.State, st.Reason)
|
||||
}
|
||||
}
|
||||
@@ -239,7 +238,7 @@ func TestAPinnedArchiveWithEntriesOutsideWithinIsRefused(t *testing.T) {
|
||||
// Other kinds are still refused by name.
|
||||
func TestAnImageArtifactIsStillRefusedByName(t *testing.T) {
|
||||
m, states, _ := archiveModule(t, `{"module":"demo","version":"1","build":{"artifacts":[{"name":"files","kind":"image","from":"files"}]}}`)
|
||||
if _, st := runBuild(t, m, states, "files"); st.State != delivery.StepFailed || !strings.Contains(st.Reason, "image") {
|
||||
if _, st := runBuild(t, m, states, "files"); st.State != sdkstep.StepFailed || !strings.Contains(st.Reason, "image") {
|
||||
t.Errorf("the step ended %s: %q", st.State, st.Reason)
|
||||
}
|
||||
}
|
||||
@@ -259,7 +258,7 @@ func TestAnArchiveBehindASymlinkedParentIsRefused(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, st := runBuild(t, m, states, "files")
|
||||
if st.State != delivery.StepFailed || !strings.Contains(st.Reason, "outside") || !strings.Contains(st.Reason, outside) {
|
||||
if st.State != sdkstep.StepFailed || !strings.Contains(st.Reason, "outside") || !strings.Contains(st.Reason, outside) {
|
||||
t.Errorf("the step ended %s: %q", st.State, st.Reason)
|
||||
}
|
||||
if entries, _ := os.ReadDir(filepath.Join(m.Work, "ws1", "artifacts")); len(entries) != 0 {
|
||||
@@ -288,7 +287,7 @@ func TestAnArchiveOfALinkedModuleDirectoryIsRefused(t *testing.T) {
|
||||
}
|
||||
}
|
||||
st := states.waitEnd(t)
|
||||
if st.State != delivery.StepFailed || !strings.Contains(st.Reason, "outside") || !strings.Contains(st.Reason, outside) {
|
||||
if st.State != sdkstep.StepFailed || !strings.Contains(st.Reason, "outside") || !strings.Contains(st.Reason, outside) {
|
||||
t.Errorf("the step ended %s: %q", st.State, st.Reason)
|
||||
}
|
||||
if entries, _ := os.ReadDir(filepath.Join(m.Work, "ws1", "artifacts")); len(entries) != 0 {
|
||||
|
||||
@@ -4,13 +4,15 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
checkerseat "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdeliverychecker"
|
||||
seat "git.novox.be/novox/mesh-delivery/contracts/go/seats/nodebuildgo"
|
||||
buildrun "git.novox.be/novox/mesh-delivery/sdk/buildrun"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"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 module pulls its seat's asks** (novox/hq ADR 0525 §2-§6, ADR 0536 §4; issue 1887 A3go). It holds the seat
|
||||
@@ -24,7 +26,10 @@ const Seat = "node-build-go"
|
||||
|
||||
// KeptState is the checker's kept verdicts, which a check ask takes for the packages whose digest has one (issue
|
||||
// 1887's design limit 1).
|
||||
const KeptState = delivery.CheckerModule + ".kept"
|
||||
const KeptState = checkerModule + "." + checkerseat.StateKept
|
||||
|
||||
// checkerModule is the module that holds the seat mesh-delivery-checker, whose kept state a check ask reads.
|
||||
const checkerModule = "mesh-delivery-checker"
|
||||
|
||||
// askSteps are the module's step bodies for an ask, each holding its workspace while it runs, as a tool's does.
|
||||
func (m *Module) askSteps(steps []StepTool) buildstep.AskSteps {
|
||||
@@ -40,12 +45,12 @@ func (m *Module) askSteps(steps []StepTool) buildstep.AskSteps {
|
||||
Fetch: byName["build_go_fetch"], Build: byName["build_go_build"], Digest: byName["build_go_digest"], Test: byName["build_go_test"],
|
||||
Imports: byName[ImportsTool],
|
||||
Compare: buildstep.CompareStep(buildstep.GeneratedRootGo, 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
|
||||
},
|
||||
Kept: m.KeptLookup,
|
||||
Has: m.Has,
|
||||
@@ -65,25 +70,22 @@ func (m *Module) keptValue(key string) (json.RawMessage, error) {
|
||||
|
||||
// KeptLookup is whether the checker kept a verdict for the level and digest, and how many tests it counted.
|
||||
func (m *Module) KeptLookup(ctx context.Context, level, digest string) (int, bool, error) {
|
||||
key, err := delivery.KeptKey(level, digest)
|
||||
if err != nil {
|
||||
return 0, false, err
|
||||
}
|
||||
key := checkerseat.KeptByLevelDigestKey(level, digest)
|
||||
raw, err := m.keptValue(key)
|
||||
if err != nil || raw == nil {
|
||||
return 0, false, err
|
||||
}
|
||||
v, err := delivery.DecodeKept(raw)
|
||||
v, err := checkerseat.DecodeKeptByLevelDigestValue(raw)
|
||||
if err != nil {
|
||||
return 0, false, fmt.Errorf("%s holds what is not a kept verdict: %w", key, err)
|
||||
}
|
||||
return v.Tests, true, nil
|
||||
return int(v.Tests), true, nil
|
||||
}
|
||||
|
||||
// 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.AskGo}
|
||||
source = buildstep.SeatAsks{Language: sdkstep.AskGo}
|
||||
}
|
||||
capacity := m.Capacity
|
||||
// The capacity's own change wakes the puller (issue 1887): the runtime's watch of the config value as mesh-config
|
||||
@@ -101,7 +103,7 @@ 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.AskGo], m.Say)
|
||||
cores = buildstep.NodeCoresPerStep(m.Settings, buildstep.DefaultCoresPerStep[sdkstep.AskGo], m.Say)
|
||||
}
|
||||
ask := m.askSteps(steps)
|
||||
var load func(string) ([]byte, error)
|
||||
@@ -111,9 +113,9 @@ func (m *Module) Puller(steps []StepTool, source buildstep.AskSource) *buildstep
|
||||
return raw, err
|
||||
}
|
||||
}
|
||||
return &buildstep.Puller{Source: source, Language: delivery.AskGo, Node: m.Node, Put: m.Steps, PausePut: m.Pauses, Load: load,
|
||||
return &buildstep.Puller{Source: source, Language: sdkstep.AskGo, Node: m.Node, Put: m.Steps, PausePut: m.Pauses, Load: load,
|
||||
Capacity: capacity, SettingsFile: settings, WatchConfig: watch, CoresPerStep: cores, BuildHours: hours, Logf: m.Say, Has: m.Has,
|
||||
Run: func(ctx context.Context, a delivery.StepAsk) (any, error) {
|
||||
Run: func(ctx context.Context, a sdkstep.StepAsk) (any, error) {
|
||||
if err := m.Check(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -136,13 +138,13 @@ func (m *Module) SeatTools(p *buildstep.Puller) []stdio.Tool {
|
||||
const StepEndTool = "build_go_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))
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -12,7 +13,6 @@ import (
|
||||
"github.com/nats-io/nats.go/jetstream"
|
||||
|
||||
"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/testbus"
|
||||
)
|
||||
|
||||
@@ -42,17 +42,17 @@ func TestTheModuleTakesADeliveryAskBeforeAnEarlierCheck(t *testing.T) {
|
||||
}
|
||||
ctx, stop := context.WithCancel(context.Background())
|
||||
defer stop()
|
||||
if err := buildstep.DeclareAskWorkers(ctx, js, delivery.AskGo, 30*time.Second); err != nil {
|
||||
if err := buildstep.DeclareAskWorkers(ctx, js, sdkstep.AskGo, 30*time.Second); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { _ = js.DeleteStream(context.Background(), "SEAT_NODE_BUILD_GO") })
|
||||
check := deliveryAsk()
|
||||
check.Kind = delivery.AskCheck
|
||||
check.Build.Fetch["commit"] = checkCommit
|
||||
check.Kind = sdkstep.AskCheck
|
||||
check.Build.Fetch.Commit = checkCommit
|
||||
first := deliveryAsk()
|
||||
for _, a := range []delivery.StepAsk{check, first} {
|
||||
raw, _ := delivery.EncodeStepAsk(a)
|
||||
subject, _ := delivery.AskSubject(a.Language, a.Kind)
|
||||
for _, a := range []sdkstep.StepAsk{check, first} {
|
||||
raw, _ := sdkstep.EncodeStepAsk(a)
|
||||
subject, _ := sdkstep.AskSubject(a.Language, a.Kind)
|
||||
if _, err := js.Publish(ctx, subject, raw); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -74,19 +74,19 @@ func TestTheModuleTakesADeliveryAskBeforeAnEarlierCheck(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
p := m.Puller(steps, &buildstep.JetStreamAsks{JS: js, Language: delivery.AskGo})
|
||||
p := m.Puller(steps, &buildstep.JetStreamAsks{JS: js, Language: sdkstep.AskGo})
|
||||
p.PullWait = 200 * time.Millisecond
|
||||
go p.Pull(ctx)
|
||||
var ran map[string][]delivery.Step
|
||||
var ran map[string][]sdkstep.Step
|
||||
for deadline := time.Now().Add(10 * time.Second); ; time.Sleep(10 * time.Millisecond) {
|
||||
ran = map[string][]delivery.Step{}
|
||||
ran = map[string][]sdkstep.Step{}
|
||||
ended := 0
|
||||
states.mu.Lock()
|
||||
for _, a := range []delivery.StepAsk{first, check} {
|
||||
key, _ := delivery.AskStepKey("ace", a.ID)
|
||||
v := append([]delivery.Step(nil), states.steps[key]...)
|
||||
for _, a := range []sdkstep.StepAsk{first, check} {
|
||||
key, _ := sdkstep.AskStepKey("ace", a.ID)
|
||||
v := append([]sdkstep.Step(nil), states.steps[key]...)
|
||||
ran[a.ID] = v
|
||||
if len(v) > 0 && v[len(v)-1].State != delivery.StepRunning {
|
||||
if len(v) > 0 && v[len(v)-1].State != sdkstep.StepRunning {
|
||||
ended++
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,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"
|
||||
@@ -34,11 +33,11 @@ func fakeSteps(calls *[]string) []StepTool {
|
||||
}
|
||||
}
|
||||
|
||||
func deliveryAsk() delivery.StepAsk {
|
||||
return delivery.StepAsk{ID: sdkstep.NewID(), Kind: delivery.AskDelivery, Language: delivery.AskGo, Asker: "mesh-delivery-builder",
|
||||
At: time.Now(), Build: &delivery.BuildJob{Module: "mesh-delivery-view", Path: "modules/mesh-delivery-view", Publish: true,
|
||||
Fetch: map[string]any{"repository": "novox/mesh-delivery", "commit": askCommit},
|
||||
Artifacts: []delivery.AskArtifact{{Artifact: "program"}}}}
|
||||
func deliveryAsk() sdkstep.StepAsk {
|
||||
return sdkstep.StepAsk{ID: sdkstep.NewID(), Kind: sdkstep.AskDelivery, Language: sdkstep.AskGo, Asker: "mesh-delivery-builder",
|
||||
At: time.Now(), Build: &sdkstep.BuildJob{Module: "mesh-delivery-view", Path: "modules/mesh-delivery-view", Publish: true,
|
||||
Fetch: &sdkstep.Fetch{Repository: "novox/mesh-delivery", Commit: askCommit},
|
||||
Artifacts: []sdkstep.AskArtifact{{Artifact: "program"}}}}
|
||||
}
|
||||
|
||||
// 1887/A3go: an ask pulled runs build_go_build after build_go_fetch, and the step key <node>.<ask id> names the
|
||||
@@ -58,13 +57,13 @@ func TestAPulledAskRunsTheBuildAndItsKeyNamesTheReference(t *testing.T) {
|
||||
ctx, stop := context.WithCancel(context.Background())
|
||||
defer stop()
|
||||
go p.Pull(ctx)
|
||||
key, _ := delivery.AskStepKey("ace", a.ID)
|
||||
var step delivery.Step
|
||||
key, _ := sdkstep.AskStepKey("ace", a.ID)
|
||||
var step sdkstep.Step
|
||||
for deadline := time.Now().Add(30 * time.Second); ; time.Sleep(5 * time.Millisecond) {
|
||||
states.mu.Lock()
|
||||
v := states.steps[key]
|
||||
states.mu.Unlock()
|
||||
if len(v) > 0 && v[len(v)-1].State != delivery.StepRunning {
|
||||
if len(v) > 0 && v[len(v)-1].State != sdkstep.StepRunning {
|
||||
step = v[len(v)-1]
|
||||
break
|
||||
}
|
||||
@@ -72,7 +71,7 @@ func TestAPulledAskRunsTheBuildAndItsKeyNamesTheReference(t *testing.T) {
|
||||
t.Fatalf("the ask's step %s never ended: %v", key, v)
|
||||
}
|
||||
}
|
||||
if step.State != delivery.StepPassed || !strings.Contains(string(step.Result), "artifact-store://mesh-build-go/tools@sha256:aa") {
|
||||
if step.State != sdkstep.StepPassed || !strings.Contains(string(step.Result), "artifact-store://mesh-build-go/tools@sha256:aa") {
|
||||
t.Fatalf("the step = %+v", step)
|
||||
}
|
||||
if strings.Join(calls, ",") != "build_go_fetch,build_go_build" {
|
||||
@@ -98,8 +97,8 @@ func TestAtCapacityZeroNothingIsPulledAndTheToolsAnswer(t *testing.T) {
|
||||
defer stop()
|
||||
go p.Pull(ctx)
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
if src.Waiting(delivery.AskDelivery) != 1 || len(calls) != 0 {
|
||||
t.Fatalf("a node at capacity 0 pulled: waiting %d, calls %v", src.Waiting(delivery.AskDelivery), calls)
|
||||
if src.Waiting(sdkstep.AskDelivery) != 1 || len(calls) != 0 {
|
||||
t.Fatalf("a node at capacity 0 pulled: waiting %d, calls %v", src.Waiting(sdkstep.AskDelivery), calls)
|
||||
}
|
||||
for _, tool := range m.Tools(steps) {
|
||||
if tool.Name == "build_go_fetch" {
|
||||
@@ -134,10 +133,10 @@ func TestManifestClaimsTheGoBuildSeatAndReadsItsCapacity(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.AskGo]) {
|
||||
t.Errorf("the cores-per-step default = %s, want %g", cores, buildstep.DefaultCoresPerStep[delivery.AskGo])
|
||||
if cores := string(doc.Config[buildstep.CoresPerStepSetting].Default); cores != fmt.Sprint(buildstep.DefaultCoresPerStep[sdkstep.AskGo]) {
|
||||
t.Errorf("the cores-per-step default = %s, want %g", cores, buildstep.DefaultCoresPerStep[sdkstep.AskGo])
|
||||
}
|
||||
if got := buildstep.NodeCoresPerStep(moduleDir+"/files/settings.json", 0, nil)(); got != buildstep.DefaultCoresPerStep[delivery.AskGo] {
|
||||
if got := buildstep.NodeCoresPerStep(moduleDir+"/files/settings.json", 0, nil)(); got != buildstep.DefaultCoresPerStep[sdkstep.AskGo] {
|
||||
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.
|
||||
@@ -198,13 +197,13 @@ func TestACheckAsksDigestIsTheToolsAnswerInTheCall(t *testing.T) {
|
||||
}
|
||||
}
|
||||
ask := m.askSteps(steps)
|
||||
a := delivery.StepAsk{ID: sdkstep.NewID(), Kind: delivery.AskCheck, Language: delivery.AskGo, Asker: "mesh-delivery-checker",
|
||||
At: time.Now(), Check: &delivery.CheckJob{Fetch: map[string]any{"repository": "novox/x", "commit": askCommit}, Levels: []string{delivery.LevelUnit}}}
|
||||
a := sdkstep.StepAsk{ID: sdkstep.NewID(), Kind: sdkstep.AskCheck, Language: sdkstep.AskGo, Asker: "mesh-delivery-checker",
|
||||
At: time.Now(), Check: &sdkstep.CheckJob{Fetch: sdkstep.Fetch{Repository: "novox/x", Commit: askCommit}, Levels: []string{sdkstep.LevelUnit}}}
|
||||
out, err := buildstep.RunAsk(context.Background(), ask, a)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if l := out.(delivery.CheckAnswer).Levels; len(l) != 1 || l[0].Digest.Map()["x/a"] != "d1" {
|
||||
if l := out.(buildstep.CheckAnswer).Levels; len(l) != 1 || l[0].Digest.Map()["x/a"] != "d1" {
|
||||
t.Fatalf("levels = %+v, want the digest's packages", l)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,12 @@ package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// writeSiblings records the siblings the fetch placed, as the fetch does.
|
||||
@@ -93,7 +92,7 @@ func TestTheFetchRecordsTheSiblingsItPlaced(t *testing.T) {
|
||||
st := states.waitEnd(t)
|
||||
var res FetchResult
|
||||
json.Unmarshal(st.Result, &res)
|
||||
if st.State != delivery.StepPassed {
|
||||
if st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
|
||||
}
|
||||
raw, err := os.ReadFile(filepath.Join(m.Work, res.Workspace, besideRecord))
|
||||
|
||||
@@ -9,13 +9,13 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
builds "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep"
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// The Go recipe of the build agent (toolchain.go, the entry of language go): static, trimmed of its own paths and of
|
||||
@@ -177,7 +177,7 @@ func (m *Module) packInto(ctx context.Context, workspace, module, name, artifact
|
||||
return BuildResult{}, fmt.Errorf("packing %s: %w", artifact, err)
|
||||
}
|
||||
if err := m.publishArchive(ctx, name, artifact, kind, digest, archive); err != nil {
|
||||
return BuildResult{}, fmt.Errorf("%s%s: %v", delivery.NotStored, artifact, err)
|
||||
return BuildResult{}, fmt.Errorf("%s%s: %v", builds.NotStored, artifact, err)
|
||||
}
|
||||
return BuildResult{Artifact: artifact, Digest: digest, Size: len(archive), Reference: catalogue.SeatReference(ArtifactSeat, name, artifact, digest)}, nil
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -14,7 +15,6 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/forgekey"
|
||||
)
|
||||
|
||||
@@ -56,7 +56,7 @@ func buildModule(t *testing.T, manifest, content string) (*Module, *keptStates,
|
||||
return m, states, calls
|
||||
}
|
||||
|
||||
func runBuild(t *testing.T, m *Module, states *keptStates, artifact string) (BuildResult, delivery.Step) {
|
||||
func runBuild(t *testing.T, m *Module, states *keptStates, artifact string) (BuildResult, sdkstep.Step) {
|
||||
t.Helper()
|
||||
for _, tool := range m.Tools(m.StepTools()) {
|
||||
if tool.Name == "build_go_build" {
|
||||
@@ -67,7 +67,7 @@ func runBuild(t *testing.T, m *Module, states *keptStates, artifact string) (Bui
|
||||
}
|
||||
st := states.waitEnd(t)
|
||||
var res BuildResult
|
||||
if st.State == delivery.StepPassed {
|
||||
if st.State == sdkstep.StepPassed {
|
||||
if err := json.Unmarshal(st.Result, &res); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -106,7 +106,7 @@ func goEntry(t *testing.T) (compile []string, linker []string, stamp string) {
|
||||
func TestTheBuildCommandCarriesTodaysFlagsAndEnvironment(t *testing.T) {
|
||||
m, states, calls := buildModule(t, manifestWith(goBundle), "binary")
|
||||
res, st := runBuild(t, m, states, "tools")
|
||||
if st.State != delivery.StepPassed {
|
||||
if st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
|
||||
}
|
||||
compile, linker, stamp := goEntry(t)
|
||||
@@ -177,12 +177,12 @@ func TestAnArtifactOfAnotherLanguageFailsTheStepNamingIt(t *testing.T) {
|
||||
} {
|
||||
m, states, calls := buildModule(t, manifestWith(artifact), "x")
|
||||
_, st := runBuild(t, m, states, "tools")
|
||||
if st.State != delivery.StepFailed || !strings.Contains(st.Reason, "tools") || !strings.Contains(st.Reason, want) || len(*calls) != 0 {
|
||||
if st.State != sdkstep.StepFailed || !strings.Contains(st.Reason, "tools") || !strings.Contains(st.Reason, want) || len(*calls) != 0 {
|
||||
t.Errorf("%s: the step ended %s: %q after %d commands", artifact, st.State, st.Reason, len(*calls))
|
||||
}
|
||||
}
|
||||
m, states, _ := buildModule(t, manifestWith(goBundle), "x")
|
||||
if _, st := runBuild(t, m, states, "nope"); st.State != delivery.StepFailed || !strings.Contains(st.Reason, `"nope"`) || !strings.Contains(st.Reason, "tools") {
|
||||
if _, st := runBuild(t, m, states, "nope"); st.State != sdkstep.StepFailed || !strings.Contains(st.Reason, `"nope"`) || !strings.Contains(st.Reason, "tools") {
|
||||
t.Errorf("an unknown artifact: %s %q", st.State, st.Reason)
|
||||
}
|
||||
}
|
||||
@@ -209,7 +209,7 @@ func TestAModuleDirectoryOutsideTheWorkspaceIsRefused(t *testing.T) {
|
||||
tool.Run(map[string]any{"workspace": "ws1", "module": "../../..", "artifact": "tools"})
|
||||
}
|
||||
}
|
||||
if st := states.waitEnd(t); st.State != delivery.StepFailed || len(*calls) != 0 {
|
||||
if st := states.waitEnd(t); st.State != sdkstep.StepFailed || len(*calls) != 0 {
|
||||
t.Errorf("the step ended %s after %d commands", st.State, len(*calls))
|
||||
}
|
||||
}
|
||||
@@ -230,7 +230,7 @@ func TestABuildOutputDirectoryLinkedOutsideTheModuleIsRefused(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, st := runBuild(t, m, states, "tools")
|
||||
if st.State != delivery.StepFailed || !strings.Contains(st.Reason, "outside") || !strings.Contains(st.Reason, outside) || len(*calls) != 0 {
|
||||
if st.State != sdkstep.StepFailed || !strings.Contains(st.Reason, "outside") || !strings.Contains(st.Reason, outside) || len(*calls) != 0 {
|
||||
t.Errorf("the step ended %s: %q after %d commands", st.State, st.Reason, len(*calls))
|
||||
}
|
||||
if body, err := os.ReadFile(kept); err != nil || string(body) != "mine" {
|
||||
@@ -248,7 +248,7 @@ func TestAManifestFallingBackToAPredefinedKeyBuilds(t *testing.T) {
|
||||
}
|
||||
m, states, _ := buildModule(t, string(raw), "binary")
|
||||
res, st := runBuild(t, m, states, "tools")
|
||||
if st.State != delivery.StepPassed {
|
||||
if st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
|
||||
}
|
||||
if res.Artifact != "tools" || res.Binary != "claude-code" || !strings.HasPrefix(res.Digest, "sha256:") {
|
||||
|
||||
@@ -3,13 +3,13 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/forgekey"
|
||||
)
|
||||
|
||||
@@ -88,14 +88,14 @@ func newCandidateRig(t *testing.T) *candidateRig {
|
||||
return cr
|
||||
}
|
||||
|
||||
func (cr *candidateRig) test(t *testing.T, args map[string]any) (TestResult, delivery.Step) {
|
||||
func (cr *candidateRig) test(t *testing.T, args map[string]any) (TestResult, sdkstep.Step) {
|
||||
t.Helper()
|
||||
if _, err := stepTool(t, cr.m, "build_go_test")(args); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
st := cr.states.waitEnd(t)
|
||||
var res TestResult
|
||||
if st.State == delivery.StepPassed {
|
||||
if st.State == sdkstep.StepPassed {
|
||||
if err := json.Unmarshal(st.Result, &res); err != nil {
|
||||
t.Fatalf("result %s: %v", st.Result, err)
|
||||
}
|
||||
@@ -119,7 +119,7 @@ func TestTheDigestAndTestToolsAnnounceTheRunner(t *testing.T) { // 1898 K1, ADR
|
||||
func TestTheUnitLevelUnderTheWorkspaceRunnerRunsTheCandidateShutOff(t *testing.T) { // 1898 K1, K2 (AC4's unit part)
|
||||
cr := newCandidateRig(t)
|
||||
res, st := cr.test(t, map[string]any{"workspace": "ws1", "level": LevelUnit, "runner": RunnerWorkspace, "packages": []any{"./..."}})
|
||||
if st.State != delivery.StepPassed {
|
||||
if st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
|
||||
}
|
||||
if res.Tests != 42 || res.Why != "mark" || res.JudgedBy != "candidate "+candidateSHA {
|
||||
@@ -146,7 +146,7 @@ func TestTheUnitLevelUnderTheWorkspaceRunnerRunsTheCandidateShutOff(t *testing.T
|
||||
func TestTheIntegrationLevelUnderTheWorkspaceRunnerRunsTheCandidate(t *testing.T) { // 1898 K2
|
||||
cr := newCandidateRig(t)
|
||||
res, st := cr.test(t, map[string]any{"workspace": "ws1", "level": LevelIntegration, "runner": RunnerWorkspace})
|
||||
if st.State != delivery.StepPassed || res.JudgedBy != "candidate "+candidateSHA {
|
||||
if st.State != sdkstep.StepPassed || res.JudgedBy != "candidate "+candidateSHA {
|
||||
t.Fatalf("the step ended %s (%s): %+v", st.State, st.Reason, res)
|
||||
}
|
||||
if len(cr.specs) != 1 || cr.specs[0].Step != stepIntegration || cr.specs[0].Bound != IntegrationRun || cr.specs[0].Setup != IntegrationSetup {
|
||||
@@ -177,7 +177,7 @@ func TestACandidateThatDoesNotCompileFailsTheStepWithTheCompilersFirstErrorAndRu
|
||||
cr := newCandidateRig(t)
|
||||
cr.compile = forgekey.Ran{Status: 1, Stderr: "# meshbuildgo/cmd/mesh-build-go\ncmd/mesh-build-go/digest.go:12:2: undefined: x\ncmd/mesh-build-go/digest.go:13:2: undefined: y\n"}
|
||||
_, st := cr.test(t, map[string]any{"workspace": "ws1", "level": LevelUnit, "runner": RunnerWorkspace})
|
||||
if st.State == delivery.StepPassed || !strings.Contains(st.Reason, "does not compile: cmd/mesh-build-go/digest.go:12:2: undefined: x") || strings.Contains(st.Reason, "undefined: y") {
|
||||
if st.State == sdkstep.StepPassed || !strings.Contains(st.Reason, "does not compile: cmd/mesh-build-go/digest.go:12:2: undefined: x") || strings.Contains(st.Reason, "undefined: y") {
|
||||
t.Fatalf("the step ended %s: %q, want it failed naming the first compile error", st.State, st.Reason)
|
||||
}
|
||||
if len(cr.specs) != 0 {
|
||||
@@ -202,7 +202,7 @@ func TestTheInstalledProgramSaysItJudged(t *testing.T) { // 1898 K1, ADR 0535 §
|
||||
args["runner"] = runner
|
||||
}
|
||||
res, st := runUnitWith(t, m, states, args)
|
||||
if st.State != delivery.StepPassed || res.JudgedBy != RunnerInstalled {
|
||||
if st.State != sdkstep.StepPassed || res.JudgedBy != RunnerInstalled {
|
||||
t.Errorf("runner %v: the step ended %s, judged by %q, want installed", runner, st.State, res.JudgedBy)
|
||||
}
|
||||
}
|
||||
@@ -246,14 +246,14 @@ func TestTheShutOffProcessRunsTheStepItIsAsked(t *testing.T) { // 1898 K2: the c
|
||||
}
|
||||
}
|
||||
|
||||
func runUnitWith(t *testing.T, m *Module, states *keptStates, args map[string]any) (TestResult, delivery.Step) {
|
||||
func runUnitWith(t *testing.T, m *Module, states *keptStates, args map[string]any) (TestResult, sdkstep.Step) {
|
||||
t.Helper()
|
||||
if _, err := stepTool(t, m, "build_go_test")(args); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
st := states.waitEnd(t)
|
||||
var res TestResult
|
||||
if st.State == delivery.StepPassed {
|
||||
if st.State == sdkstep.StepPassed {
|
||||
if err := json.Unmarshal(st.Result, &res); err != nil {
|
||||
t.Fatalf("result %s: %v", st.Result, err)
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@ package main
|
||||
// engine cannot judge its own next code. The module fetches the commit
|
||||
// (its fetch step: the forge key and the network, none of the change's code), builds the composer package in the Go
|
||||
// module the ask names, in a run step shut off with nothing fetched, and keeps the program under its key
|
||||
// (delivery.ComposerKey), so each commit is built once on a node. It fills the request's manifests from the delivery
|
||||
// (sdkstep.ComposerKey), so each commit is built once on a node. It fills the request's manifests from the delivery
|
||||
// indexer's manifests state, by digest, and runs the program shut off: no network, none of the account, the request a
|
||||
// file of a workspace of its own. The answer is the composer's verdict whole (delivery.ComposeAnswer); the module
|
||||
// file of a workspace of its own. The answer is the composer's verdict whole (sdkstep.ComposeAnswer); the module
|
||||
// judges nothing of it.
|
||||
|
||||
import (
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
catalogclient "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -29,14 +30,16 @@ import (
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"git.novox.be/novox/mesh-delivery/sdk/composer"
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/views"
|
||||
)
|
||||
|
||||
// ManifestsTool is the delivery indexer's read tool of its manifests by digest, which a compose ask's request names (the
|
||||
// module invokes it, module.json invokes). A manifest by its digest never changes and a compose ask is rare, so the
|
||||
// module asks for the one key it needs when it needs it, and keeps no picture of the indexer's whole state (ADR 0615).
|
||||
const ManifestsTool = delivery.IndexerModule + ".indexer_read_manifests"
|
||||
// indexerModule is the module that holds the seat mesh-catalog, whose manifests state a compose ask reads.
|
||||
const indexerModule = "mesh-delivery-indexer"
|
||||
|
||||
const ManifestsTool = indexerModule + ".indexer_read_manifests"
|
||||
|
||||
// composerLocks hold one mutex per composer key, so a composer two asks need at once is built once.
|
||||
var composerLocks sync.Map
|
||||
@@ -47,15 +50,14 @@ func (m *Module) composerPath(key string) string {
|
||||
}
|
||||
|
||||
// compose runs a compose ask: the composer of its commit, built once, over its request with the manifests filled.
|
||||
func (m *Module) compose(ctx context.Context, fetch buildstep.StepFunc, a delivery.StepAsk) (any, error) {
|
||||
func (m *Module) compose(ctx context.Context, fetch buildstep.StepFunc, a sdkstep.StepAsk) (any, error) {
|
||||
job := *a.Compose
|
||||
repository, _ := job.Fetch["repository"].(string)
|
||||
commit, _ := job.Fetch["commit"].(string)
|
||||
repository, commit := job.Fetch.Repository, job.Fetch.Commit
|
||||
installed, err := m.Probe(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key := delivery.ComposerKey(repository, commit, job.Dir, job.Package, delivery.ToolchainUsed{Version: installed, Recipe: m.Recipe.Version()})
|
||||
key := sdkstep.ComposerKey(repository, commit, job.Dir, job.Package, sdkstep.ToolchainUsed{Version: installed, Recipe: m.Recipe.Version()})
|
||||
exe, built, err := m.composerOf(ctx, fetch, job, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -71,7 +73,7 @@ func (m *Module) compose(ctx context.Context, fetch buildstep.StepFunc, a delive
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return delivery.ComposeAnswer{Built: built, Key: key, Verdict: verdict}, nil
|
||||
return sdkstep.ComposeAnswer{Built: built, Key: key, Verdict: verdict}, nil
|
||||
}
|
||||
|
||||
// askManifest asks the indexer's read tool for one manifest by its key: nil when it keeps none. A call the indexer does not
|
||||
@@ -81,7 +83,7 @@ func askManifest(key string) (json.RawMessage, error) {
|
||||
}
|
||||
|
||||
func askManifestVia(ask func(string, any) (json.RawMessage, error), findings views.FindingWriter, now func() time.Time, key string) (json.RawMessage, error) {
|
||||
st := views.Status{Owner: delivery.IndexerModule, Tool: ManifestsTool, State: "manifests", Asked: now().UTC()}
|
||||
st := views.Status{Owner: indexerModule, Tool: ManifestsTool, State: "manifests", Asked: now().UTC()}
|
||||
raw, err := ask(ManifestsTool, map[string]any{"key": key})
|
||||
if err != nil {
|
||||
st.Error = err.Error()
|
||||
@@ -107,7 +109,7 @@ func (m *Module) manifestOf(digest string) ([]byte, error) {
|
||||
if m.ReadManifest == nil {
|
||||
return nil, errors.New("this module reads no manifests state")
|
||||
}
|
||||
key, err := delivery.ManifestsKey(digest)
|
||||
key, err := catalogclient.ManifestsKey(digest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -118,15 +120,15 @@ func (m *Module) manifestOf(digest string) ([]byte, error) {
|
||||
if raw == nil {
|
||||
return nil, fmt.Errorf("the indexer keeps no manifest of digest %s", digest)
|
||||
}
|
||||
man, err := delivery.DecodeManifest(raw)
|
||||
man, err := catalogclient.DecodeManifest(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []byte(man.JSON), nil
|
||||
return []byte(man.Manifest), nil
|
||||
}
|
||||
|
||||
// composerOf is the composer kept under key, or, when none is, the one built now from the fetched commit (built true).
|
||||
func (m *Module) composerOf(ctx context.Context, fetch buildstep.StepFunc, job delivery.ComposeJob, key string) (string, bool, error) {
|
||||
func (m *Module) composerOf(ctx context.Context, fetch buildstep.StepFunc, job sdkstep.ComposeJob, key string) (string, bool, error) {
|
||||
exe := m.composerPath(key)
|
||||
mu, _ := composerLocks.LoadOrStore(exe, &sync.Mutex{})
|
||||
mu.(*sync.Mutex).Lock()
|
||||
@@ -137,7 +139,11 @@ func (m *Module) composerOf(ctx context.Context, fetch buildstep.StepFunc, job d
|
||||
if fetch == nil {
|
||||
return "", false, buildstep.Refuse("this build module fetches nothing")
|
||||
}
|
||||
out, err := fetch(ctx, job.Fetch)
|
||||
args, err := buildstep.FetchArgs(job.Fetch)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
out, err := fetch(ctx, args)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
@@ -156,7 +162,7 @@ func (m *Module) composerOf(ctx context.Context, fetch buildstep.StepFunc, job d
|
||||
defer done()
|
||||
dir := filepath.Join(m.Work, f.Workspace, "tree", job.Dir)
|
||||
if _, err := os.Stat(filepath.Join(dir, "go.mod")); err != nil {
|
||||
return "", false, fmt.Errorf("%s at %s holds no go.mod in %s, so no composer can be built there", job.Fetch["repository"], job.Fetch["commit"], job.Dir)
|
||||
return "", false, fmt.Errorf("%s at %s holds no go.mod in %s, so no composer can be built there", job.Fetch.Repository, job.Fetch.Commit, job.Dir)
|
||||
}
|
||||
into := filepath.Join(m.Work, f.Workspace, "composer", "mesh-compose")
|
||||
if err := os.MkdirAll(filepath.Dir(into), 0o700); err != nil {
|
||||
@@ -171,7 +177,7 @@ func (m *Module) composerOf(ctx context.Context, fetch buildstep.StepFunc, job d
|
||||
if r.Status != 0 || r.Err != "" {
|
||||
// The words Go says when the module holds no such package (a mesh-sdk from before the composer) are kept whole,
|
||||
// so the asker tells that apart from a fault.
|
||||
return "", false, fmt.Errorf("the composer %s does not build at %s: %s", job.Package, job.Fetch["commit"], compileError(r.Err+"\n"+r.Stderr+"\n"+r.Stdout))
|
||||
return "", false, fmt.Errorf("the composer %s does not build at %s: %s", job.Package, job.Fetch.Commit, compileError(r.Err+"\n"+r.Stderr+"\n"+r.Stdout))
|
||||
}
|
||||
if err := copyExecutable(into, exe); err != nil {
|
||||
return "", false, fmt.Errorf("the composer could not be kept: %w", err)
|
||||
|
||||
@@ -5,6 +5,7 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
catalogclient "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient"
|
||||
"io/fs"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -19,7 +20,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-delivery/sdk/composer"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/forgekey"
|
||||
"git.novox.be/novox/mesh-sdk/go/testbus"
|
||||
)
|
||||
@@ -111,7 +111,7 @@ func TestAComposeAskIsBuiltOnceAndAnsweredOnTheBus(t *testing.T) {
|
||||
}
|
||||
ctx, stop := context.WithCancel(context.Background())
|
||||
defer stop()
|
||||
if err := buildstep.DeclareAskWorkers(ctx, js, delivery.AskGo, 2*time.Minute); err != nil {
|
||||
if err := buildstep.DeclareAskWorkers(ctx, js, sdkstep.AskGo, 2*time.Minute); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { _ = js.DeleteStream(context.Background(), "SEAT_NODE_BUILD_GO") })
|
||||
@@ -158,39 +158,39 @@ func TestAComposeAskIsBuiltOnceAndAnsweredOnTheBus(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
p := m.Puller(steps, &buildstep.JetStreamAsks{JS: js, Language: delivery.AskGo})
|
||||
p := m.Puller(steps, &buildstep.JetStreamAsks{JS: js, Language: sdkstep.AskGo})
|
||||
p.PullWait = 200 * time.Millisecond
|
||||
go p.Pull(ctx)
|
||||
|
||||
ask := func() delivery.Step {
|
||||
ask := func() sdkstep.Step {
|
||||
a := composeAskOf(t, digests[0])
|
||||
a.Compose.Fetch["repository"], a.Compose.Dir, a.Compose.Package = "novox/mesh-host", delivery.RootPath, "github.com/novox/mesh-host/cmd/mesh-compose"
|
||||
raw, err := delivery.EncodeStepAsk(a)
|
||||
a.Compose.Fetch.Repository, a.Compose.Dir, a.Compose.Package = "novox/mesh-host", catalogclient.RootPath, "github.com/novox/mesh-host/cmd/mesh-compose"
|
||||
raw, err := sdkstep.EncodeStepAsk(a)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
subject, _ := delivery.AskSubject(delivery.AskGo, delivery.AskCheck)
|
||||
subject, _ := sdkstep.AskSubject(sdkstep.AskGo, sdkstep.AskCheck)
|
||||
if _, err := js.Publish(ctx, subject, raw); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
key, _ := delivery.AskStepKey("ace", a.ID)
|
||||
key, _ := sdkstep.AskStepKey("ace", a.ID)
|
||||
for deadline := time.Now().Add(90 * time.Second); time.Now().Before(deadline); time.Sleep(50 * time.Millisecond) {
|
||||
states.mu.Lock()
|
||||
v := states.steps[key]
|
||||
states.mu.Unlock()
|
||||
if len(v) > 0 && v[len(v)-1].State != delivery.StepRunning {
|
||||
if len(v) > 0 && v[len(v)-1].State != sdkstep.StepRunning {
|
||||
return v[len(v)-1]
|
||||
}
|
||||
}
|
||||
t.Fatalf("the compose ask %s never ended", a.ID)
|
||||
return delivery.Step{}
|
||||
return sdkstep.Step{}
|
||||
}
|
||||
for n, wantBuilt := range []bool{true, false} {
|
||||
step := ask()
|
||||
if step.State != delivery.StepPassed {
|
||||
if step.State != sdkstep.StepPassed {
|
||||
t.Fatalf("ask %d: %s: %s", n+1, step.State, step.Reason)
|
||||
}
|
||||
var ans delivery.ComposeAnswer
|
||||
var ans sdkstep.ComposeAnswer
|
||||
var v composer.Verdict
|
||||
if err := json.Unmarshal(step.Result, &ans); err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
catalogclient "git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient"
|
||||
meshcat "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
@@ -13,7 +15,6 @@ import (
|
||||
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"git.novox.be/novox/mesh-delivery/sdk/composer"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/forgekey"
|
||||
"git.novox.be/novox/mesh-sdk/go/views"
|
||||
)
|
||||
@@ -29,9 +30,9 @@ func indexerHolds(t *testing.T, bodies ...string) (func(string) (json.RawMessage
|
||||
t.Helper()
|
||||
kept, digests := map[string]json.RawMessage{}, []string{}
|
||||
for _, b := range bodies {
|
||||
d := delivery.ManifestDigest([]byte(b))
|
||||
key, _ := delivery.ManifestsKey(d)
|
||||
raw, err := delivery.EncodeManifest(delivery.Manifest{Module: "demo", JSON: b})
|
||||
d := catalogclient.ManifestDigest([]byte(b))
|
||||
key, _ := catalogclient.ManifestsKey(d)
|
||||
raw, err := catalogclient.EncodeManifest(meshcat.ManifestsByDigestValue{Module: "demo", Manifest: b})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -40,12 +41,12 @@ func indexerHolds(t *testing.T, bodies ...string) (func(string) (json.RawMessage
|
||||
return func(key string) (json.RawMessage, error) { return kept[key], nil }, digests
|
||||
}
|
||||
|
||||
func composeAskOf(t *testing.T, digest string) delivery.StepAsk {
|
||||
func composeAskOf(t *testing.T, digest string) sdkstep.StepAsk {
|
||||
t.Helper()
|
||||
req, _ := json.Marshal(composer.Request{V: composer.Version, Cases: []composer.Case{{Name: "change",
|
||||
Manifests: map[string]composer.Manifest{"demo": {Digest: digest}}}}})
|
||||
return delivery.StepAsk{ID: sdkstep.NewID(), Kind: delivery.AskCheck, Language: delivery.AskGo, Asker: "mesh-delivery-assigner",
|
||||
At: time.Now(), Compose: &delivery.ComposeJob{Fetch: map[string]any{"repository": "novox/mesh-sdk", "commit": composeCommit},
|
||||
return sdkstep.StepAsk{ID: sdkstep.NewID(), Kind: sdkstep.AskCheck, Language: sdkstep.AskGo, Asker: "mesh-delivery-assigner",
|
||||
At: time.Now(), Compose: &sdkstep.ComposeJob{Fetch: sdkstep.Fetch{Repository: "novox/mesh-sdk", Commit: composeCommit},
|
||||
Dir: "go", Package: composer.Package, Request: req}}
|
||||
}
|
||||
|
||||
@@ -66,7 +67,7 @@ func TestAKeptComposerIsRunWithTheManifestsFilled(t *testing.T) {
|
||||
read, digests := indexerHolds(t, `{"module":"demo","version":"1"}`)
|
||||
m.ReadManifest = read
|
||||
a := composeAskOf(t, digests[0])
|
||||
key := delivery.ComposerKey("novox/mesh-sdk", composeCommit, "go", composer.Package, delivery.ToolchainUsed{Version: "go1.26.0", Recipe: m.Recipe.Version()})
|
||||
key := sdkstep.ComposerKey("novox/mesh-sdk", composeCommit, "go", composer.Package, sdkstep.ToolchainUsed{Version: "go1.26.0", Recipe: m.Recipe.Version()})
|
||||
exe := m.composerPath(key)
|
||||
if err := os.MkdirAll(filepath.Dir(exe), 0o700); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -84,7 +85,7 @@ func TestAKeptComposerIsRunWithTheManifestsFilled(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ans := out.(delivery.ComposeAnswer)
|
||||
ans := out.(sdkstep.ComposeAnswer)
|
||||
var v composer.Verdict
|
||||
if err := json.Unmarshal(ans.Verdict, &v); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -103,7 +104,7 @@ func TestAComposeAskFailsLoudly(t *testing.T) {
|
||||
m := composeModule(t)
|
||||
m.ReadManifest, _ = indexerHolds(t)
|
||||
a := composeAskOf(t, "feedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed")
|
||||
key := delivery.ComposerKey("novox/mesh-sdk", composeCommit, "go", composer.Package, delivery.ToolchainUsed{Version: "go1.26.0", Recipe: m.Recipe.Version()})
|
||||
key := sdkstep.ComposerKey("novox/mesh-sdk", composeCommit, "go", composer.Package, sdkstep.ToolchainUsed{Version: "go1.26.0", Recipe: m.Recipe.Version()})
|
||||
os.MkdirAll(filepath.Dir(m.composerPath(key)), 0o700)
|
||||
os.WriteFile(m.composerPath(key), []byte("#!/bin/sh\nexit 0\n"), 0o700)
|
||||
if _, err := m.compose(context.Background(), nil, a); err == nil || !strings.Contains(err.Error(), "feedfeed") {
|
||||
@@ -127,7 +128,7 @@ func TestAComposeAskFailsLoudly(t *testing.T) {
|
||||
// 1622, ADR 0615: the module asks the delivery indexer's read tool for the manifests a compose ask names by digest, and
|
||||
// reads none of the indexer's state.
|
||||
func TestTheModuleReadsTheIndexersManifests(t *testing.T) {
|
||||
if m := readManifest(t); !slices.Contains(m.Invokes, ManifestsTool) || slices.Contains(m.Reads, delivery.IndexerModule+".manifests") {
|
||||
if m := readManifest(t); !slices.Contains(m.Invokes, ManifestsTool) || slices.Contains(m.Reads, indexerModule+".manifests") {
|
||||
t.Errorf("invokes = %v, reads = %v: it asks %s", m.Invokes, m.Reads, ManifestsTool)
|
||||
}
|
||||
}
|
||||
@@ -161,7 +162,7 @@ func TestAComposeAskBuildsTheNodeEnginesComposerAtItsRoot(t *testing.T) {
|
||||
read, digests := indexerHolds(t, `{"module":"demo","version":"1"}`)
|
||||
m.ReadManifest = read
|
||||
a := composeAskOf(t, digests[0])
|
||||
a.Compose.Fetch["repository"], a.Compose.Dir, a.Compose.Package = "novox/mesh-host", delivery.RootPath, nodeEngineComposer
|
||||
a.Compose.Fetch.Repository, a.Compose.Dir, a.Compose.Package = "novox/mesh-host", catalogclient.RootPath, nodeEngineComposer
|
||||
tree := ""
|
||||
var built []string
|
||||
real := m.Run
|
||||
@@ -191,8 +192,8 @@ func TestAComposeAskBuildsTheNodeEnginesComposerAtItsRoot(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ans := out.(delivery.ComposeAnswer)
|
||||
key := delivery.ComposerKey("novox/mesh-host", composeCommit, delivery.RootPath, nodeEngineComposer, delivery.ToolchainUsed{Version: "go1.26.0", Recipe: m.Recipe.Version()})
|
||||
ans := out.(sdkstep.ComposeAnswer)
|
||||
key := sdkstep.ComposerKey("novox/mesh-host", composeCommit, catalogclient.RootPath, nodeEngineComposer, sdkstep.ToolchainUsed{Version: "go1.26.0", Recipe: m.Recipe.Version()})
|
||||
var v composer.Verdict
|
||||
if err := json.Unmarshal(ans.Verdict, &v); err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -13,6 +13,8 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
trunk "git.novox.be/novox/mesh-delivery/sdk/trunk"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
@@ -21,8 +23,6 @@ import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// DigestVersion is the form of the digest's text: changed whenever what the digest hashes changes in kind.
|
||||
@@ -56,7 +56,7 @@ const namesVariable = "MESH_NODE_NAMES"
|
||||
const ReadsOutsideMarker = "reads-outside"
|
||||
|
||||
// NeedsMarker is the name of a file in a package's directory that names, one per line, what its integration tests need
|
||||
// of the node they run on (issue 890; mesh-sdk delivery.AskNeeds): `node-build-node` for tests that read the Node build
|
||||
// of the node they run on (issue 890; mesh-delivery-sdk buildstep.AskNeeds): `node-build-node` for tests that read the Node build
|
||||
// module's installed toolchain. The digest reports it (DigestResult.Needs); a check ask leaves such a package untested on
|
||||
// a node that lacks it, and the checker asks it again of a node that has it. A marker beside the tests, as
|
||||
// ReadsOutsideMarker is, and not a manifest field: it is a property of the package's tests, read only where they are
|
||||
@@ -149,7 +149,7 @@ type DigestResult struct {
|
||||
neverKept map[string]string
|
||||
hashedFiles map[string]string
|
||||
// Needs are the packages whose tests at this level need something of the node, as their NeedsMarker names it.
|
||||
Needs []delivery.PackageNeeds `json:"needs,omitempty"`
|
||||
Needs []sdkstep.PackageNeeds `json:"needs,omitempty"`
|
||||
// JudgedBy is the program that ran the digest (ADR 0535 §5): "installed", or "candidate <merge commit>" under the
|
||||
// runner workspace.
|
||||
JudgedBy string `json:"judged_by,omitempty"`
|
||||
@@ -168,8 +168,8 @@ func (m *Module) digestTool() StepTool {
|
||||
"workspace": map[string]any{"type": "string", "description": "the workspace id build_go_fetch answered"},
|
||||
"level": map[string]any{"type": "string", "enum": []string{LevelUnit, LevelIntegration}, "description": "the level"},
|
||||
"runner": runnerProperty,
|
||||
delivery.NodeNamesArg: map[string]any{"type": "array", "items": map[string]any{"type": "string"},
|
||||
"description": "optional, the names of the mesh's nodes: a package whose tests read " + delivery.NodeNamesEnv + " has them in its digest"},
|
||||
sdkstep.NodeNamesArg: map[string]any{"type": "array", "items": map[string]any{"type": "string"},
|
||||
"description": "optional, the names of the mesh's nodes: a package whose tests read " + sdkstep.NodeNamesEnv + " has them in its digest"},
|
||||
},
|
||||
"required": []string{"workspace", "level"},
|
||||
},
|
||||
@@ -184,7 +184,7 @@ func (m *Module) digest(ctx context.Context, args map[string]any) (any, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
names, err := nodeNamesArg(args[delivery.NodeNamesArg])
|
||||
names, err := nodeNamesArg(args[sdkstep.NodeNamesArg])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -344,7 +344,7 @@ func digestModule(res *DigestResult, pkgs map[string]*goPackage, tree string, he
|
||||
return fmt.Errorf("%s: its %s: %w", path, NeedsMarker, err)
|
||||
}
|
||||
if len(n) > 0 {
|
||||
res.Needs = append(res.Needs, delivery.PackageNeeds{Package: path, Needs: n})
|
||||
res.Needs = append(res.Needs, sdkstep.PackageNeeds{Package: path, Needs: n})
|
||||
}
|
||||
}
|
||||
declared, why, err := declaredReads(tree, root, res.hashed())
|
||||
@@ -401,7 +401,7 @@ func packageDigest(root, testMain *goPackage, pkgs map[string]*goPackage, tree s
|
||||
// A module outside the tree counts by its version and checksum; a package of the mesh's own forge by the content of
|
||||
// its files, as a local replacement does, so a new version of a mesh package whose reached files are the same keeps
|
||||
// every verdict kept for them (ADR 0593 §5, ADR 0444 §3).
|
||||
if !mod.Main && mod.Version != "" && !strings.HasPrefix(mod.Path, delivery.ForgeModulePrefix) {
|
||||
if !mod.Main && mod.Version != "" && !strings.HasPrefix(mod.Path, trunk.ForgeModulePrefix) {
|
||||
lines = append(lines, "module "+mod.Path+"@"+mod.Version+" "+mod.Sum)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -10,7 +11,6 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/forgekey"
|
||||
)
|
||||
|
||||
@@ -112,7 +112,7 @@ func TestFetchAsksGitForTheSshRemoteAndDownloadsEachModule(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
st := states.waitEnd(t)
|
||||
if st.State != delivery.StepPassed {
|
||||
if st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
|
||||
}
|
||||
lines := strings.Join(f.lines(), "\n")
|
||||
@@ -222,7 +222,7 @@ func TestAVerifyThatFailsEndsTheStepNamingTheDirectoryAndTheLine(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
st := states.waitEnd(t)
|
||||
if st.State != delivery.StepFailed {
|
||||
if st.State != sdkstep.StepFailed {
|
||||
t.Fatalf("the step ended %s, want failed", st.State)
|
||||
}
|
||||
if !strings.Contains(st.Reason, "tools") || !strings.Contains(st.Reason, "example.org/x v1.0.0: dir has been modified") || strings.Contains(st.Reason, "second line") {
|
||||
@@ -251,7 +251,7 @@ func TestTheCallAnswersAStepIdBeforeTheFetchEnds(t *testing.T) {
|
||||
states.mu.Lock()
|
||||
running := states.steps["ace."+id]
|
||||
states.mu.Unlock()
|
||||
if len(running) != 1 || running[0].State != delivery.StepRunning {
|
||||
if len(running) != 1 || running[0].State != sdkstep.StepRunning {
|
||||
t.Errorf("the key holds %+v, want one running step", running)
|
||||
}
|
||||
close(f.gate)
|
||||
@@ -267,7 +267,7 @@ func TestTheCallAnswersAStepIdBeforeTheFetchEnds(t *testing.T) {
|
||||
last := states.steps["ace."+id][len(states.steps["ace."+id])-1]
|
||||
states.mu.Unlock()
|
||||
var res FetchResult
|
||||
if last.State != delivery.StepPassed || json.Unmarshal(last.Result, &res) != nil || res.Workspace == "" {
|
||||
if last.State != sdkstep.StepPassed || json.Unmarshal(last.Result, &res) != nil || res.Workspace == "" {
|
||||
t.Errorf("the key ends %+v", last)
|
||||
}
|
||||
}
|
||||
@@ -278,7 +278,7 @@ func TestARepositoryThatIsNotOwnerSlashNameIsRefusedBeforeAnythingRuns(t *testin
|
||||
m, states := fetchModule(t, f)
|
||||
fetchTool(t, m)(map[string]any{"repository": repo, "commit": commitSHA})
|
||||
st := states.waitEnd(t)
|
||||
if st.State != delivery.StepFailed || len(f.lines()) != 0 {
|
||||
if st.State != sdkstep.StepFailed || len(f.lines()) != 0 {
|
||||
t.Errorf("%q: the step ended %s after %d commands", repo, st.State, len(f.lines()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
builds "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -23,7 +24,6 @@ import (
|
||||
modzip "golang.org/x/mod/zip"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// GoPackageSeat is the registry seat a Go package is published on.
|
||||
@@ -82,7 +82,7 @@ func (m *Module) buildGoPackage(ctx context.Context, workspace, dir string, art
|
||||
return res, nil
|
||||
}
|
||||
if err := m.publishOn(ctx, seat, path, "package", version, digest, transferName(path, version, digest), body); err != nil {
|
||||
return BuildResult{}, fmt.Errorf("%s%s: %v", delivery.NotStored, art.Name, err)
|
||||
return BuildResult{}, fmt.Errorf("%s%s: %v", builds.NotStored, art.Name, err)
|
||||
}
|
||||
res.Reference = path + "@" + version
|
||||
return res, nil
|
||||
|
||||
@@ -9,13 +9,13 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
trunk "git.novox.be/novox/mesh-delivery/sdk/trunk"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/forgekey"
|
||||
)
|
||||
|
||||
@@ -94,14 +94,14 @@ func ModuleImports(ctx context.Context, run forgekey.Runner, env []string, dir s
|
||||
out = append(out, p)
|
||||
}
|
||||
slices.Sort(out)
|
||||
if err := delivery.CheckImports(out); err != nil {
|
||||
if err := trunk.CheckImports(out); err != nil {
|
||||
return ImportsResult{}, err
|
||||
}
|
||||
return ImportsResult{Imports: out}, nil
|
||||
}
|
||||
|
||||
func meshPackage(pkg string) bool {
|
||||
_, _, ok := delivery.PackageRepository(pkg)
|
||||
_, _, ok := trunk.PackageRepository(pkg)
|
||||
return ok
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
@@ -12,7 +13,6 @@ import (
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep/buildcpu"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/forgekey"
|
||||
"git.novox.be/novox/mesh-sdk/go/health"
|
||||
)
|
||||
@@ -73,7 +73,7 @@ func TestTheFetchStepsGoCommandsSayGotoolchainLocalAndFetchNoToolchain(t *testin
|
||||
if _, err := fetchTool(t, m)(fetchArgs); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if st := states.waitEnd(t); st.State != delivery.StepPassed {
|
||||
if st := states.waitEnd(t); st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
|
||||
}
|
||||
goCalls := 0
|
||||
|
||||
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -11,7 +12,6 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/forgekey"
|
||||
)
|
||||
|
||||
@@ -126,7 +126,7 @@ func integModule(t *testing.T, f *integFake) (*Module, *keptStates) {
|
||||
return m, states
|
||||
}
|
||||
|
||||
func runLevelTool(t *testing.T, m *Module, states *keptStates, level string) (TestResult, delivery.Step) {
|
||||
func runLevelTool(t *testing.T, m *Module, states *keptStates, level string) (TestResult, sdkstep.Step) {
|
||||
t.Helper()
|
||||
for _, tool := range m.Tools(m.StepTools()) {
|
||||
if tool.Name != "build_go_test" {
|
||||
@@ -138,7 +138,7 @@ func runLevelTool(t *testing.T, m *Module, states *keptStates, level string) (Te
|
||||
}
|
||||
st := states.waitEnd(t)
|
||||
var res TestResult
|
||||
if st.State == delivery.StepPassed {
|
||||
if st.State == sdkstep.StepPassed {
|
||||
if err := json.Unmarshal(st.Result, &res); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -152,7 +152,7 @@ func TestTheStoreListensOnASocketUnderTheRunAndNowhereElse(t *testing.T) {
|
||||
f := newIntegFake(t, "slow")
|
||||
m, states := integModule(t, f)
|
||||
res, st := runLevelTool(t, m, states, "integration")
|
||||
if st.State != delivery.StepPassed || !res.Passed || res.Level != "integration" {
|
||||
if st.State != sdkstep.StepPassed || !res.Passed || res.Level != "integration" {
|
||||
t.Fatalf("the step ended %s (%s): %+v", st.State, st.Reason, res)
|
||||
}
|
||||
var start, initdb string
|
||||
@@ -219,14 +219,14 @@ func TestAnotherPostgresMajorRefusesTheIntegrationLevelOnly(t *testing.T) {
|
||||
f.postgres = "postgres (PostgreSQL) 17.2"
|
||||
m, states := integModule(t, f)
|
||||
_, st := runLevelTool(t, m, states, "integration")
|
||||
if st.State != delivery.StepRefused || !strings.Contains(st.Reason, "17") || !strings.Contains(st.Reason, "18") {
|
||||
if st.State != sdkstep.StepRefused || !strings.Contains(st.Reason, "17") || !strings.Contains(st.Reason, "18") {
|
||||
t.Fatalf("the integration step ended %s: %q", st.State, st.Reason)
|
||||
}
|
||||
if strings.Contains(f.lines(), "initdb") || strings.Contains(f.lines(), "-tags") {
|
||||
t.Errorf("the refused level ran something:\n%s", f.lines())
|
||||
}
|
||||
res, st := runLevelTool(t, m, states, "unit")
|
||||
if st.State != delivery.StepPassed || !res.Passed || res.Level != "unit" {
|
||||
if st.State != sdkstep.StepPassed || !res.Passed || res.Level != "unit" {
|
||||
t.Errorf("the unit level: %s %+v", st.State, res)
|
||||
}
|
||||
}
|
||||
@@ -308,7 +308,7 @@ func TestAStoreThatDoesNotStartEndsTheStepFailedAndLeavesNothing(t *testing.T) {
|
||||
return r
|
||||
}
|
||||
_, st := runLevelTool(t, m, states, "integration")
|
||||
if st.State != delivery.StepFailed || !strings.Contains(st.Reason, "pg_ctl: could not start server") {
|
||||
if st.State != sdkstep.StepFailed || !strings.Contains(st.Reason, "pg_ctl: could not start server") {
|
||||
t.Errorf("the step ended %s: %q", st.State, st.Reason)
|
||||
}
|
||||
if !strings.Contains(f.lines(), " stop") {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
@@ -11,7 +12,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/forgekey"
|
||||
)
|
||||
|
||||
@@ -118,7 +118,7 @@ func TestTheUnitLevelRunsEveryGoTestShutOff(t *testing.T) {
|
||||
m, states, ws, calls := workspaceFor(t, "failing")
|
||||
var seen []shutOff
|
||||
m.Run = throughBubblewrap(t, m.Run, &seen)
|
||||
if _, st := runUnit(t, m, states, ws); st.State != delivery.StepPassed {
|
||||
if _, st := runUnit(t, m, states, ws); st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
|
||||
}
|
||||
ran := 0
|
||||
@@ -174,7 +174,7 @@ func TestABuildRunsShutOffAndOnlyATrunkBuildWritesTheCaches(t *testing.T) {
|
||||
if _, err := run(args); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if st := states.waitEnd(t); st.State != delivery.StepPassed {
|
||||
if st := states.waitEnd(t); st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("publish %v: the step ended %s: %s", publish, st.State, st.Reason)
|
||||
}
|
||||
if len(seen) != 1 || !strings.HasPrefix(seen[0].command, "go ") || !strings.Contains(seen[0].command, " build ") {
|
||||
@@ -230,7 +230,7 @@ func TestTheIntegrationLevelRunsInOneShutOffProcess(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
st := states.waitEnd(t)
|
||||
if st.State != delivery.StepPassed {
|
||||
if st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
|
||||
}
|
||||
var got TestResult
|
||||
|
||||
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
buildrun "git.novox.be/novox/mesh-delivery/sdk/buildrun"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -27,7 +28,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)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -17,7 +18,6 @@ import (
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// levelArg is the first argument that makes this binary a step's shut-off process: the integration level's, and, when
|
||||
@@ -148,7 +148,7 @@ func (m *Module) nodeToolchains() []string {
|
||||
const nodeToolchainMark = ".installed"
|
||||
|
||||
// Has says whether this node has a need a check ask or a package's tests name (issue 890, ADR 0552; mesh-sdk
|
||||
// delivery.AskNeeds), read from the node itself at every ask: node-build-node when the Node build module's toolchains
|
||||
// sdkstep.AskNeeds), read from the node itself at every ask: node-build-node when the Node build module's toolchains
|
||||
// directory, given to the integration level as nodeToolchains gives it, holds a toolchain installed whole (its mark and
|
||||
// its package-lock.json readable by this account), and node, npm and bwrap are installed: what mesh-build-node's
|
||||
// integration tests (realMachine) look for before they run. Any other need it lacks.
|
||||
@@ -157,7 +157,7 @@ func (m *Module) Has(need string) bool {
|
||||
return m.HasNeed(need)
|
||||
}
|
||||
switch need {
|
||||
case delivery.NeedNodeBuildNode:
|
||||
case sdkstep.NeedNodeBuildNode:
|
||||
dirs := m.nodeToolchains()
|
||||
if len(dirs) == 0 {
|
||||
return false
|
||||
|
||||
@@ -8,13 +8,14 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
seat "git.novox.be/novox/mesh-delivery/contracts/go/seats/nodebuildgo"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"os"
|
||||
"os/user"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// The directories the module's manifest places outside its home (ADR 0466 §1): the workspaces, and the Go module
|
||||
@@ -37,13 +38,13 @@ func main() {
|
||||
m := &Module{
|
||||
Node: strings.TrimSpace(os.Getenv("MESH_NODE")),
|
||||
Recipe: Current(),
|
||||
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)),
|
||||
Ctx: ctx,
|
||||
Say: logf,
|
||||
Work: WorkDir,
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -11,8 +12,6 @@ import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
func fetchOnce(t *testing.T, m *Module, states *keptStates, args map[string]any) FetchResult {
|
||||
@@ -21,7 +20,7 @@ func fetchOnce(t *testing.T, m *Module, states *keptStates, args map[string]any)
|
||||
t.Fatal(err)
|
||||
}
|
||||
st := states.waitEnd(t)
|
||||
if st.State != delivery.StepPassed {
|
||||
if st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
|
||||
}
|
||||
var res FetchResult
|
||||
|
||||
@@ -4,13 +4,13 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
buildrun "git.novox.be/novox/mesh-delivery/sdk/buildrun"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"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"
|
||||
"git.novox.be/novox/mesh-sdk/go/forgekey"
|
||||
"git.novox.be/novox/mesh-sdk/go/health"
|
||||
)
|
||||
@@ -129,7 +129,7 @@ func (m *Module) Start(ctx context.Context) {
|
||||
m.Say("the toolchain: %v", err)
|
||||
}
|
||||
m.CleanWorkspaces(time.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
|
||||
@@ -183,8 +183,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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,29 +4,30 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
buildstep "git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"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
|
||||
}
|
||||
@@ -37,27 +38,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:
|
||||
@@ -67,7 +68,7 @@ func (s *keptStates) waitEnd(t *testing.T) delivery.Step {
|
||||
return v[len(v)-1]
|
||||
case <-time.After(30 * time.Second):
|
||||
t.Fatal("the step never ended on its key")
|
||||
return delivery.Step{}
|
||||
return sdkstep.Step{}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +86,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(30 * time.Second); time.Now().Before(deadline); time.Sleep(5 * time.Millisecond) {
|
||||
if got, ok := states.toolchain("ace.go"); ok {
|
||||
tc = got
|
||||
@@ -127,7 +128,7 @@ func TestAGoThatCannotBeRunRefusesEveryStepAndHealthIsUnhealthy(t *testing.T) {
|
||||
if out.(map[string]any)["step"] == "" {
|
||||
t.Fatalf("%s answered no step id: %v", tool.Name, out)
|
||||
}
|
||||
if st := states.waitEnd(t); st.State != delivery.StepRefused {
|
||||
if st := states.waitEnd(t); st.State != sdkstep.StepRefused {
|
||||
t.Errorf("%s: the step ended %s, want refused", tool.Name, st.State)
|
||||
} else {
|
||||
reason = st.Reason
|
||||
|
||||
@@ -3,12 +3,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// ADR 0499 §4 (issue 1758): a fetch asked with the module's directory and its named paths leaves the workspace holding
|
||||
@@ -28,7 +27,7 @@ func TestAFetchWithNamedPathsNarrowsTheWorkspace(t *testing.T) {
|
||||
})
|
||||
m, states := f.module(t)
|
||||
res, st := fetched(t, m, states, map[string]any{"repository": "novox/narrow", "commit": head, "dir": "lib", "paths": []any{"cmd", "go.mod"}})
|
||||
if st.State != delivery.StepPassed {
|
||||
if st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("the fetch ended %s: %s", st.State, st.Reason)
|
||||
}
|
||||
tree := filepath.Join(m.Work, res.Workspace, "tree")
|
||||
@@ -46,7 +45,7 @@ func TestAFetchWithNamedPathsNarrowsTheWorkspace(t *testing.T) {
|
||||
t.Errorf("the fetch answered the modules %v, want lib alone", res.Modules)
|
||||
}
|
||||
_, st = fetched(t, m, states, map[string]any{"repository": "novox/narrow", "commit": head, "paths": []any{"cmd"}})
|
||||
if st.State != delivery.StepFailed || !strings.Contains(st.Reason, "no module directory") {
|
||||
if st.State != sdkstep.StepFailed || !strings.Contains(st.Reason, "no module directory") {
|
||||
t.Errorf("paths without a directory: %s %q", st.State, st.Reason)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// Issue 1309: a test that runs a step in bubblewrap is proof only where it runs. Where a build module judges (it hands
|
||||
@@ -32,11 +31,11 @@ func TestAMissingToolSkipsExceptBubblewrapOnABuildNode(t *testing.T) {
|
||||
// The levels a build module judges hand the tests the build node mark, with the siblings they read.
|
||||
func TestEveryLevelHandsTheTestsTheBuildNodeMark(t *testing.T) {
|
||||
m := &Module{Work: t.TempDir()}
|
||||
if env := m.checkEnv("ws1", nil); !slices.Contains(env, BuildNodeEnv+"=1") || slices.ContainsFunc(env, func(e string) bool { return strings.HasPrefix(e, delivery.NodeNamesEnv) }) {
|
||||
if env := m.checkEnv("ws1", nil); !slices.Contains(env, BuildNodeEnv+"=1") || slices.ContainsFunc(env, func(e string) bool { return strings.HasPrefix(e, sdkstep.NodeNamesEnv) }) {
|
||||
t.Errorf("env %v lacks %s=1, or names nodes when none were asked", env, BuildNodeEnv)
|
||||
}
|
||||
// And the mesh's node names, when the ask carried them (ADR 0567 §12).
|
||||
if env := m.checkEnv("ws1", []string{"a", "b"}); !slices.Contains(env, delivery.NodeNamesEnv+"=a,b") {
|
||||
if env := m.checkEnv("ws1", []string{"a", "b"}); !slices.Contains(env, sdkstep.NodeNamesEnv+"=a,b") {
|
||||
t.Errorf("env %v lacks the node names", env)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,13 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/forgekey"
|
||||
)
|
||||
|
||||
@@ -36,7 +36,7 @@ func integrationShutOffWith(t *testing.T, toolchains string) shutOff {
|
||||
if _, err := run(map[string]any{"workspace": ws, "level": LevelIntegration}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if st := states.waitEnd(t); st.State != delivery.StepPassed {
|
||||
if st := states.waitEnd(t); st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
|
||||
}
|
||||
if len(seen) != 1 {
|
||||
@@ -81,12 +81,12 @@ func TestANodeWithoutNodeToolchainsRunsTheLevelWithNothingMoreBound(t *testing.T
|
||||
}
|
||||
t.Setenv("PATH", bin)
|
||||
m := &Module{NodeToolchains: missing}
|
||||
if m.Has(delivery.NeedNodeBuildNode) {
|
||||
if m.Has(sdkstep.NeedNodeBuildNode) {
|
||||
t.Error("a node with no Node toolchain has node-build-node")
|
||||
}
|
||||
installed := t.TempDir()
|
||||
m.NodeToolchains = installed
|
||||
if m.Has(delivery.NeedNodeBuildNode) {
|
||||
if m.Has(sdkstep.NeedNodeBuildNode) {
|
||||
t.Error("a node with an empty toolchains directory has node-build-node")
|
||||
}
|
||||
half := filepath.Join(installed, "0.1.0")
|
||||
@@ -96,19 +96,19 @@ func TestANodeWithoutNodeToolchainsRunsTheLevelWithNothingMoreBound(t *testing.T
|
||||
if err := os.WriteFile(filepath.Join(half, "package-lock.json"), []byte("{}"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if m.Has(delivery.NeedNodeBuildNode) {
|
||||
if m.Has(sdkstep.NeedNodeBuildNode) {
|
||||
t.Error("a toolchain without its installed mark counts")
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(half, nodeToolchainMark), nil, 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !m.Has(delivery.NeedNodeBuildNode) || m.Has("gpu") {
|
||||
t.Errorf("with a toolchain installed: node-build-node %v, gpu %v; want true, false", m.Has(delivery.NeedNodeBuildNode), m.Has("gpu"))
|
||||
if !m.Has(sdkstep.NeedNodeBuildNode) || m.Has("gpu") {
|
||||
t.Errorf("with a toolchain installed: node-build-node %v, gpu %v; want true, false", m.Has(sdkstep.NeedNodeBuildNode), m.Has("gpu"))
|
||||
}
|
||||
if err := os.Remove(filepath.Join(bin, "bwrap")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if m.Has(delivery.NeedNodeBuildNode) {
|
||||
if m.Has(sdkstep.NeedNodeBuildNode) {
|
||||
t.Error("a node with no bubblewrap installed has node-build-node")
|
||||
}
|
||||
}
|
||||
@@ -120,7 +120,7 @@ func TestTheUnitLevelIsNotGivenTheNodeToolchains(t *testing.T) {
|
||||
m.NodeToolchains = dir
|
||||
var seen []shutOff
|
||||
m.Run = throughBubblewrap(t, m.Run, &seen)
|
||||
if _, st := runUnit(t, m, states, ws); st.State != delivery.StepPassed {
|
||||
if _, st := runUnit(t, m, states, ws); st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
|
||||
}
|
||||
for _, s := range seen {
|
||||
|
||||
@@ -9,7 +9,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"
|
||||
)
|
||||
|
||||
// 1925/AC1: packagesArg takes the list as the bus's JSON shape ([]any) and as the typed list an ask hands in process
|
||||
@@ -64,8 +63,8 @@ func TestACheckAskReachesTheTestStepPastItsPackages(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
a := delivery.StepAsk{ID: sdkstep.NewID(), Kind: delivery.AskCheck, Language: delivery.AskGo, Asker: "mesh-delivery-checker",
|
||||
At: time.Now(), Check: &delivery.CheckJob{Fetch: map[string]any{"repository": "novox/x", "commit": askCommit}, Levels: []string{delivery.LevelUnit}}}
|
||||
a := sdkstep.StepAsk{ID: sdkstep.NewID(), Kind: sdkstep.AskCheck, Language: sdkstep.AskGo, Asker: "mesh-delivery-checker",
|
||||
At: time.Now(), Check: &sdkstep.CheckJob{Fetch: sdkstep.Fetch{Repository: "novox/x", Commit: askCommit}, Levels: []string{sdkstep.LevelUnit}}}
|
||||
out, err := buildstep.RunAsk(context.Background(), m.askSteps(steps), a)
|
||||
got := ""
|
||||
if err != nil {
|
||||
|
||||
@@ -8,12 +8,11 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// 1148/AC2 as issue 1585 S15 has it (novox/hq ADR 0573 §4): given a forge with a mesh-sdk-like pull request whose branch
|
||||
@@ -46,7 +45,7 @@ func TestRealFetchAndBuildOfAMemberAskedAPullHeadItsGoModDoesNotName(t *testing.
|
||||
|
||||
res, st := fetched(t, m, states, map[string]any{"repository": "novox/member", "commit": memberHead,
|
||||
"packages": map[string]any{"git.novox.be/novox/dep/go": head}})
|
||||
if st.State != delivery.StepPassed {
|
||||
if st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("the fetch ended %s: %s", st.State, st.Reason)
|
||||
}
|
||||
if len(res.Packages) != 1 || res.Packages[0].Built != head || res.Packages[0].Named != "v0.0.0-20261021120000-"+fork[:12] {
|
||||
|
||||
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -10,7 +11,6 @@ import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/forgekey"
|
||||
)
|
||||
|
||||
@@ -71,14 +71,14 @@ func newPinHarness(t *testing.T, gomod string, vendor bool, pulls string) (*pinH
|
||||
return h, states
|
||||
}
|
||||
|
||||
func (h *pinHarness) fetch(states *keptStates) (FetchResult, delivery.Step) {
|
||||
func (h *pinHarness) fetch(states *keptStates) (FetchResult, sdkstep.Step) {
|
||||
h.t.Helper()
|
||||
if _, err := fetchTool(h.t, h.m)(fetchArgs); err != nil {
|
||||
h.t.Fatal(err)
|
||||
}
|
||||
st := states.waitEnd(h.t)
|
||||
var res FetchResult
|
||||
if st.State == delivery.StepPassed {
|
||||
if st.State == sdkstep.StepPassed {
|
||||
json.Unmarshal(st.Result, &res)
|
||||
}
|
||||
return res, st
|
||||
@@ -101,7 +101,7 @@ const pullList = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\trefs/pull/6/head\n"
|
||||
func TestAPinOnAPullHeadIsPlacedBesideAndHandedToGoByAModfile(t *testing.T) {
|
||||
h, states := newPinHarness(t, pinnedGoMod, false, pullList)
|
||||
res, st := h.fetch(states)
|
||||
if st.State != delivery.StepPassed {
|
||||
if st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
|
||||
}
|
||||
placed := filepath.Join(h.m.Work, res.Workspace, "pinned", "mesh-sdk@"+commitSHA, "beside", "mesh-sdk")
|
||||
@@ -144,7 +144,7 @@ func TestAPinGoCanDownloadIsLeftAsToday(t *testing.T) {
|
||||
h, states := newPinHarness(t, pinnedGoMod, false, pullList)
|
||||
h.onTrunk = true
|
||||
res, st := h.fetch(states)
|
||||
if st.State != delivery.StepPassed {
|
||||
if st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
|
||||
}
|
||||
if h.ran("ls-remote") || h.ran("pinned") {
|
||||
@@ -165,7 +165,7 @@ func TestAPinGoCanDownloadIsLeftAsToday(t *testing.T) {
|
||||
func TestAPinOnNoRefFailsNamingTheModuleAndTheCommit(t *testing.T) {
|
||||
h, states := newPinHarness(t, pinnedGoMod, false, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\trefs/pull/6/head\n")
|
||||
_, st := h.fetch(states)
|
||||
if st.State != delivery.StepFailed {
|
||||
if st.State != sdkstep.StepFailed {
|
||||
t.Fatalf("the step ended %s, want failed", st.State)
|
||||
}
|
||||
if !strings.Contains(st.Reason, "no pull request head") || !strings.Contains(st.Reason, "unknown revision") ||
|
||||
@@ -181,7 +181,7 @@ func TestAPinOnNoRefFailsNamingTheModuleAndTheCommit(t *testing.T) {
|
||||
func TestAVendoringDirectoryResolvesNothing(t *testing.T) {
|
||||
h, states := newPinHarness(t, pinnedGoMod, true, pullList)
|
||||
_, st := h.fetch(states)
|
||||
if st.State != delivery.StepPassed {
|
||||
if st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
|
||||
}
|
||||
if h.ran("go -C") || h.ran("ls-remote") || h.ran("pinned") {
|
||||
@@ -195,7 +195,7 @@ func TestTwoDirectoriesPinningOneHeadShareOnePlacement(t *testing.T) {
|
||||
h, states := newPinHarness(t, pinnedGoMod, false, pullList)
|
||||
h.f.gomods = "go.mod\nsub/go.mod\n"
|
||||
res, st := h.fetch(states)
|
||||
if st.State != delivery.StepPassed {
|
||||
if st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
|
||||
}
|
||||
checkouts := 0
|
||||
@@ -240,7 +240,7 @@ func TestTwoCommitsOfOneRepositoryGetTwoPlacements(t *testing.T) {
|
||||
return base(ctx, env, name, args...)
|
||||
}
|
||||
_, st := h.fetch(states)
|
||||
if st.State != delivery.StepPassed {
|
||||
if st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
|
||||
}
|
||||
for _, c := range []string{commitSHA, other} {
|
||||
|
||||
@@ -7,6 +7,7 @@ package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -15,7 +16,6 @@ import (
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
func git(t *testing.T, dir string, args ...string) string {
|
||||
@@ -86,7 +86,7 @@ func (f *forge) module(t *testing.T) (*Module, *keptStates) {
|
||||
}
|
||||
|
||||
// waitLong is waitEnd for a step that runs the real go, whose first build in a cold cache takes longer than 3 s.
|
||||
func waitLong(t *testing.T, s *keptStates) delivery.Step {
|
||||
func waitLong(t *testing.T, s *keptStates) sdkstep.Step {
|
||||
t.Helper()
|
||||
select {
|
||||
case key := <-s.ended:
|
||||
@@ -96,18 +96,18 @@ func waitLong(t *testing.T, s *keptStates) delivery.Step {
|
||||
return v[len(v)-1]
|
||||
case <-time.After(2 * time.Minute):
|
||||
t.Fatal("the step never ended on its key")
|
||||
return delivery.Step{}
|
||||
return sdkstep.Step{}
|
||||
}
|
||||
}
|
||||
|
||||
func fetched(t *testing.T, m *Module, states *keptStates, args map[string]any) (FetchResult, delivery.Step) {
|
||||
func fetched(t *testing.T, m *Module, states *keptStates, args map[string]any) (FetchResult, sdkstep.Step) {
|
||||
t.Helper()
|
||||
if _, err := fetchTool(t, m)(args); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
st := states.waitEnd(t)
|
||||
var res FetchResult
|
||||
if st.State == delivery.StepPassed {
|
||||
if st.State == sdkstep.StepPassed {
|
||||
if err := json.Unmarshal(st.Result, &res); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -136,7 +136,7 @@ func TestRealFetchMergesConflictsAndPlainlyFetches(t *testing.T) {
|
||||
m, states := f.module(t)
|
||||
|
||||
res, st := fetched(t, m, states, map[string]any{"repository": "o/r", "commit": clean, "ref": "refs/pull/1/head", "base": trunk})
|
||||
if st.State != delivery.StepPassed || res.Merge == "" || res.Base != trunk || res.Commit != clean || len(res.Conflicts) != 0 {
|
||||
if st.State != sdkstep.StepPassed || res.Merge == "" || res.Base != trunk || res.Commit != clean || len(res.Conflicts) != 0 {
|
||||
t.Fatalf("clean: %s %s", st.State, st.Result)
|
||||
}
|
||||
if strings.Join(res.Changed, ",") != "modules/m/m.txt" {
|
||||
@@ -154,7 +154,7 @@ func TestRealFetchMergesConflictsAndPlainlyFetches(t *testing.T) {
|
||||
}
|
||||
|
||||
res, st = fetched(t, m, states, map[string]any{"repository": "o/r", "commit": clash, "ref": "refs/pull/2/head", "base": trunk})
|
||||
if st.State != delivery.StepPassed || res.Merge != "" || strings.Join(res.Conflicts, ",") != "a.txt" {
|
||||
if st.State != sdkstep.StepPassed || res.Merge != "" || strings.Join(res.Conflicts, ",") != "a.txt" {
|
||||
t.Fatalf("conflict: %s %s", st.State, st.Result)
|
||||
}
|
||||
if got := git(t, filepath.Join(m.Work, res.Workspace, "tree"), "rev-parse", "HEAD"); got != trunk {
|
||||
@@ -162,7 +162,7 @@ func TestRealFetchMergesConflictsAndPlainlyFetches(t *testing.T) {
|
||||
}
|
||||
|
||||
res, st = fetched(t, m, states, map[string]any{"repository": "o/r", "commit": clean, "ref": "refs/pull/1/head"})
|
||||
if st.State != delivery.StepPassed || res.Merge != "" || res.Base != "" || res.Trees != nil || len(res.Modules) != 1 {
|
||||
if st.State != sdkstep.StepPassed || res.Merge != "" || res.Base != "" || res.Trees != nil || len(res.Modules) != 1 {
|
||||
t.Errorf("no base: %s %s", st.State, st.Result)
|
||||
}
|
||||
}
|
||||
@@ -192,13 +192,13 @@ func TestRealTestsReadTheSiblingBesideTheTree(t *testing.T) {
|
||||
var res TestResult
|
||||
st := waitLong(t, states)
|
||||
json.Unmarshal(st.Result, &res)
|
||||
if st.State != delivery.StepPassed || res.Passed || len(res.Failed) != 1 || !strings.Contains(res.Failed[0].Output, "MESH_CHECK_BESIDE is not set") {
|
||||
if st.State != sdkstep.StepPassed || res.Passed || len(res.Failed) != 1 || !strings.Contains(res.Failed[0].Output, "MESH_CHECK_BESIDE is not set") {
|
||||
t.Fatalf("without the sibling: %s %s", st.State, st.Result)
|
||||
}
|
||||
|
||||
with, st2 := fetched(t, m, states, map[string]any{"repository": "o/r", "commit": head,
|
||||
"beside": []any{map[string]any{"repository": "o/sibling", "commit": sibling}}})
|
||||
if st2.State != delivery.StepPassed || len(with.Beside) != 1 {
|
||||
if st2.State != sdkstep.StepPassed || len(with.Beside) != 1 {
|
||||
t.Fatalf("with the sibling: %s %s", st2.State, st2.Result)
|
||||
}
|
||||
m.testToolRun(t, with.Workspace)
|
||||
@@ -223,7 +223,7 @@ func TestRealTestsReadTheSiblingBesideTheTree(t *testing.T) {
|
||||
git(t, work, "push", "-q", "-f", filepath.Join(f.root, "forge", "o/linked.git"), "HEAD:refs/heads/main")
|
||||
_, st3 := fetched(t, m, states, map[string]any{"repository": "o/r", "commit": head,
|
||||
"beside": []any{map[string]any{"repository": "o/linked", "commit": bad}}})
|
||||
if st3.State != delivery.StepFailed || !strings.Contains(st3.Reason, "etc-link") {
|
||||
if st3.State != sdkstep.StepFailed || !strings.Contains(st3.Reason, "etc-link") {
|
||||
t.Errorf("a sibling with a symlink: %s %s", st3.State, st3.Reason)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@ import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/forgekey"
|
||||
)
|
||||
|
||||
@@ -69,7 +69,7 @@ func TestFetchWithABaseMergesAndAnswersThePreview(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
st := states.waitEnd(t)
|
||||
if st.State != delivery.StepPassed {
|
||||
if st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
|
||||
}
|
||||
var res FetchResult
|
||||
@@ -102,7 +102,7 @@ func TestFetchWithAConflictEndsPassedAndNamesTheFiles(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
st := states.waitEnd(t)
|
||||
if st.State != delivery.StepPassed {
|
||||
if st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("a conflict ended the step %s: %s", st.State, st.Reason)
|
||||
}
|
||||
var res FetchResult
|
||||
@@ -137,7 +137,7 @@ func TestABaseThatIsNotACommitIsRefusedBeforeAnythingRuns(t *testing.T) {
|
||||
f := &fakeStep{}
|
||||
m, states := fetchModule(t, f)
|
||||
fetchTool(t, m)(map[string]any{"repository": "novox/mesh-sdk", "commit": commitSHA, "base": "main"})
|
||||
if st := states.waitEnd(t); st.State != delivery.StepFailed || len(f.lines()) != 0 {
|
||||
if st := states.waitEnd(t); st.State != sdkstep.StepFailed || len(f.lines()) != 0 {
|
||||
t.Errorf("the step ended %s after %d commands", st.State, len(f.lines()))
|
||||
}
|
||||
}
|
||||
@@ -156,7 +156,7 @@ func TestSiblingsArePlacedBesideAndTheUnitLevelIsGivenTheirDirectory(t *testing.
|
||||
st := states.waitEnd(t)
|
||||
var res FetchResult
|
||||
json.Unmarshal(st.Result, &res)
|
||||
if st.State != delivery.StepPassed {
|
||||
if st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
|
||||
}
|
||||
if len(res.Beside) != 2 || res.Beside[0].Dir != "mesh-catalog" || res.Beside[1].Dir != "mesh-host" {
|
||||
@@ -233,7 +233,7 @@ func TestSiblingsThatAreNotRepositoriesOrCommitsAreRefused(t *testing.T) {
|
||||
f := &fakeStep{}
|
||||
m, states := fetchModule(t, f)
|
||||
fetchTool(t, m)(map[string]any{"repository": "novox/mesh-sdk", "commit": commitSHA, "beside": beside})
|
||||
if st := states.waitEnd(t); st.State != delivery.StepFailed || len(f.lines()) != 0 {
|
||||
if st := states.waitEnd(t); st.State != sdkstep.StepFailed || len(f.lines()) != 0 {
|
||||
t.Errorf("%s: the step ended %s after %d commands", name, st.State, len(f.lines()))
|
||||
}
|
||||
}
|
||||
@@ -285,7 +285,7 @@ func TestTheIntegrationLevelIsGivenTheBesideDirectoryOnlyWhenSiblingsWerePlaced(
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if _, st := runLevelTool(t, m, states, "integration"); st.State != delivery.StepPassed {
|
||||
if _, st := runLevelTool(t, m, states, "integration"); st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
|
||||
}
|
||||
tested := 0
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
builds "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -15,7 +17,6 @@ import (
|
||||
"testing"
|
||||
|
||||
registry "git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// pushed are the blobs the fake artifact store took, by digest, for the tests that read an archive back.
|
||||
@@ -105,7 +106,7 @@ func TestADoneBuildAnswersTheReferenceOfTheArchiveItPushed(t *testing.T) {
|
||||
m, states, _ := buildModule(t, manifestWith(goBundle), "the program")
|
||||
store := withStore(t, m)
|
||||
res, st := runBuild(t, m, states, "tools")
|
||||
if st.State != delivery.StepPassed {
|
||||
if st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
|
||||
}
|
||||
if want := "oci-artifact-registry/demo/tools@" + res.Digest; res.Reference != want {
|
||||
@@ -117,7 +118,7 @@ func TestADoneBuildAnswersTheReferenceOfTheArchiveItPushed(t *testing.T) {
|
||||
a, states2, _ := archiveModule(t, "")
|
||||
withStore(t, a)
|
||||
arch, st := runBuild(t, a, states2, "files")
|
||||
if st.State != delivery.StepPassed || arch.Reference != "oci-artifact-registry/demo/files@"+arch.Digest {
|
||||
if st.State != sdkstep.StepPassed || arch.Reference != "oci-artifact-registry/demo/files@"+arch.Digest {
|
||||
t.Errorf("the archive artifact: %s %q, reference %q", st.State, st.Reason, arch.Reference)
|
||||
}
|
||||
}
|
||||
@@ -128,13 +129,13 @@ func TestARefusedPushFailsTheStepNotStored(t *testing.T) {
|
||||
m, states, _ := buildModule(t, manifestWith(goBundle), "the program")
|
||||
store := withStore(t, m)
|
||||
store.fail = errors.New("refused: the forge did not take it: disk full")
|
||||
if _, st := runBuild(t, m, states, "tools"); st.State != delivery.StepFailed || !strings.HasPrefix(st.Reason, delivery.NotStored+"tools: ") || !strings.Contains(st.Reason, "disk full") {
|
||||
if _, st := runBuild(t, m, states, "tools"); st.State != sdkstep.StepFailed || !strings.HasPrefix(st.Reason, builds.NotStored+"tools: ") || !strings.Contains(st.Reason, "disk full") {
|
||||
t.Errorf("the step ended %s: %q", st.State, st.Reason)
|
||||
}
|
||||
m.Call = func(string, any) (json.RawMessage, error) {
|
||||
return nil, errors.New("nats: no responders available for request")
|
||||
}
|
||||
if _, st := runBuild(t, m, states, "tools"); st.State != delivery.StepFailed || !strings.HasPrefix(st.Reason, delivery.NotStored+"tools: ") ||
|
||||
if _, st := runBuild(t, m, states, "tools"); st.State != sdkstep.StepFailed || !strings.HasPrefix(st.Reason, builds.NotStored+"tools: ") ||
|
||||
!strings.Contains(st.Reason, "oci-artifact-registry") {
|
||||
t.Errorf("no holder: the step ended %s: %q", st.State, st.Reason)
|
||||
}
|
||||
@@ -178,7 +179,7 @@ func TestAGoPackageIsBuiltAsAModuleZipAndPublishedUnderItsModulePath(t *testing.
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
run := func(args map[string]any) (BuildResult, delivery.Step) {
|
||||
run := func(args map[string]any) (BuildResult, sdkstep.Step) {
|
||||
t.Helper()
|
||||
for _, tool := range m.Tools(m.StepTools()) {
|
||||
if tool.Name == "build_go_build" {
|
||||
@@ -189,7 +190,7 @@ func TestAGoPackageIsBuiltAsAModuleZipAndPublishedUnderItsModulePath(t *testing.
|
||||
}
|
||||
st := states.waitEnd(t)
|
||||
var res BuildResult
|
||||
if st.State == delivery.StepPassed {
|
||||
if st.State == sdkstep.StepPassed {
|
||||
_ = json.Unmarshal(st.Result, &res)
|
||||
}
|
||||
return res, st
|
||||
@@ -197,12 +198,12 @@ func TestAGoPackageIsBuiltAsAModuleZipAndPublishedUnderItsModulePath(t *testing.
|
||||
store := withStore(t, m)
|
||||
args := map[string]any{"workspace": "ws1", "module": "modules/demo", "artifact": "go"}
|
||||
cand, st := run(args)
|
||||
if st.State != delivery.StepPassed || !strings.HasPrefix(cand.Digest, "sha256:") || cand.Reference != "" || len(store.asked) != 0 {
|
||||
if st.State != sdkstep.StepPassed || !strings.HasPrefix(cand.Digest, "sha256:") || cand.Reference != "" || len(store.asked) != 0 {
|
||||
t.Fatalf("a candidate's Go package: %s %q, %+v, asked %v", st.State, st.Reason, cand, store.asked)
|
||||
}
|
||||
args["version"], args["seat"] = "v0.1.15", GoPackageSeat
|
||||
res, st := run(args)
|
||||
if st.State != delivery.StepPassed || res.Reference != "git.example/demo/go@v0.1.15" {
|
||||
if st.State != sdkstep.StepPassed || res.Reference != "git.example/demo/go@v0.1.15" {
|
||||
t.Fatalf("a trunk Go package: %s %q, %+v", st.State, st.Reason, res)
|
||||
}
|
||||
if len(store.published) != 1 {
|
||||
@@ -226,7 +227,7 @@ func TestAGoPackageIsBuiltAsAModuleZipAndPublishedUnderItsModulePath(t *testing.
|
||||
if err := os.Remove(filepath.Join(src, "go.mod")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, st := run(args); st.State != delivery.StepFailed || !strings.Contains(st.Reason, "go.mod") {
|
||||
if _, st := run(args); st.State != sdkstep.StepFailed || !strings.Contains(st.Reason, "go.mod") {
|
||||
t.Errorf("no go.mod: %s %q", st.State, st.Reason)
|
||||
}
|
||||
}
|
||||
@@ -268,7 +269,7 @@ func TestEachGoPackageOfAModuleInAFolderIsPublishedUnderItsOwnModulePath(t *test
|
||||
}
|
||||
st := states.waitEnd(t)
|
||||
var res BuildResult
|
||||
if st.State != delivery.StepPassed {
|
||||
if st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("%s: %s %q", c.artifact, st.State, st.Reason)
|
||||
}
|
||||
_ = json.Unmarshal(st.Result, &res)
|
||||
@@ -298,7 +299,7 @@ func TestTheNumberedEventPublishesAHeldDigestUnderItsVersion(t *testing.T) {
|
||||
digest := "sha256:" + strings.Repeat("ab", 32)
|
||||
other := "sha256:" + strings.Repeat("cd", 32)
|
||||
numbered := func(kind, seat, digest string) []byte {
|
||||
raw, err := delivery.EncodeVersionNumbered(delivery.VersionNumbered{Name: "mod/tools", Kind: kind, Version: "0.1.4",
|
||||
raw, err := builds.EncodeVersionNumbered(builds.VersionNumbered{Name: "mod/tools", Kind: kind, Version: "0.1.4",
|
||||
Module: "mod", Fingerprint: "fp", Commit: "c0", Digest: digest, Seat: seat})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -7,6 +7,8 @@ package main
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
buildstep "git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"log"
|
||||
"sort"
|
||||
"strconv"
|
||||
@@ -15,7 +17,6 @@ import (
|
||||
"time"
|
||||
|
||||
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_go_ 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)
|
||||
}
|
||||
|
||||
@@ -4,13 +4,13 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
buildstep "git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// fakeState is a state as stored: key to raw value.
|
||||
@@ -29,13 +29,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 +60,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_go_fetch", Node: "ace", State: delivery.StepPassed, Started: t0, Ended: t0.Add(time.Second),
|
||||
putStep(t, steps, sdkstep.Step{ID: "abc1", Tool: "build_go_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_go_test", Node: "ace", State: delivery.StepRefused, Started: t0, Reason: "go cannot be run"})
|
||||
putStep(t, steps, sdkstep.Step{ID: "abc2", Tool: "build_go_test", Node: "ace", State: sdkstep.StepRefused, Started: t0, Reason: "go 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_go_fetch" {
|
||||
st := out.(sdkstep.Step)
|
||||
if st.State != sdkstep.StepPassed || string(st.Result) != `{"workspace":"w42"}` || st.Tool != "build_go_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 != "go cannot be run" {
|
||||
if st := out.(sdkstep.Step); st.State != sdkstep.StepRefused || st.Reason != "go cannot be run" {
|
||||
t.Errorf("refused step = %+v", st)
|
||||
}
|
||||
}
|
||||
@@ -94,10 +94,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_go_fetch", Node: "ace", State: delivery.StepPassed, Started: t0, Ended: t0, Result: json.RawMessage(`1`)})
|
||||
putStep(t, steps, delivery.Step{ID: "b", Tool: "build_go_test", Node: "ace", State: delivery.StepRunning, Started: t0.Add(time.Minute)})
|
||||
putStep(t, steps, delivery.Step{ID: "c", Tool: "build_go_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_go_build", Node: "g14", State: delivery.StepPassed, Started: t0.Add(time.Hour)})
|
||||
putStep(t, steps, sdkstep.Step{ID: "a", Tool: "build_go_fetch", Node: "ace", State: sdkstep.StepPassed, Started: t0, Ended: t0, Result: json.RawMessage(`1`)})
|
||||
putStep(t, steps, sdkstep.Step{ID: "b", Tool: "build_go_test", Node: "ace", State: sdkstep.StepRunning, Started: t0.Add(time.Minute)})
|
||||
putStep(t, steps, sdkstep.Step{ID: "c", Tool: "build_go_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_go_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 +111,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 +123,7 @@ func TestStepsToolListsRecentStepsOfThisNode(t *testing.T) {
|
||||
// 1562 (1483/AC5): build_go_toolchains answers each toolchains key as stored.
|
||||
func TestToolchainsToolAnswersTheStoredKeys(t *testing.T) {
|
||||
tcs := fakeState{}
|
||||
b, err := delivery.EncodeToolchain(delivery.Toolchain{Language: "go", Version: "go1.27.1", Recipe: "1", At: time.Date(2026, 10, 21, 9, 0, 0, 0, time.UTC)})
|
||||
b, err := buildstep.EncodeToolchain(buildstep.Toolchain{Language: "go", Version: "go1.27.1", Recipe: "1", At: time.Date(2026, 10, 21, 9, 0, 0, 0, time.UTC)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -133,7 +133,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.go"].Version != "go1.27.1" || got["ace.go"].Recipe != "1" {
|
||||
t.Errorf("toolchains = %+v", got)
|
||||
}
|
||||
@@ -166,7 +166,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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -9,7 +10,6 @@ import (
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/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"
|
||||
)
|
||||
@@ -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":
|
||||
@@ -95,7 +95,7 @@ func TestAFailedFetchLeavesNoWorkspace(t *testing.T) {
|
||||
if _, err := fetchTool(t, m)(fetchArgs); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if st := states.waitEnd(t); st.State != delivery.StepFailed {
|
||||
if st := states.waitEnd(t); st.State != sdkstep.StepFailed {
|
||||
t.Fatalf("the step ended %s, want failed", st.State)
|
||||
}
|
||||
if entries, _ := os.ReadDir(m.Work); len(entries) != 0 {
|
||||
|
||||
@@ -3,14 +3,13 @@ package main
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// threeModules gives the workspace three go.mod directories, a, b and c, whose modules are fixture/a, fixture/b and
|
||||
@@ -31,7 +30,7 @@ func threeModules(t *testing.T, f *integFake) (*Module, *keptStates) {
|
||||
return m, states
|
||||
}
|
||||
|
||||
func runIntegrationOf(t *testing.T, m *Module, states *keptStates, packages ...any) (TestResult, delivery.Step) {
|
||||
func runIntegrationOf(t *testing.T, m *Module, states *keptStates, packages ...any) (TestResult, sdkstep.Step) {
|
||||
t.Helper()
|
||||
for _, tool := range m.Tools(m.StepTools()) {
|
||||
if tool.Name != "build_go_test" {
|
||||
@@ -47,7 +46,7 @@ func runIntegrationOf(t *testing.T, m *Module, states *keptStates, packages ...a
|
||||
}
|
||||
st := states.waitEnd(t)
|
||||
var res TestResult
|
||||
if st.State == delivery.StepPassed {
|
||||
if st.State == sdkstep.StepPassed {
|
||||
if err := json.Unmarshal(st.Result, &res); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -60,7 +59,7 @@ func runIntegrationOf(t *testing.T, m *Module, states *keptStates, packages ...a
|
||||
func TestOnlyTheSelectedDirectoryCompilesAndRuns(t *testing.T) {
|
||||
f := newIntegFake(t, "slow")
|
||||
m, states := threeModules(t, f)
|
||||
if _, st := runIntegrationOf(t, m, states, "fixture/b/x"); st.State != delivery.StepPassed {
|
||||
if _, st := runIntegrationOf(t, m, states, "fixture/b/x"); st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
|
||||
}
|
||||
tree := filepath.Join(m.Work, "ws1", "tree")
|
||||
@@ -131,7 +130,7 @@ func TestSelectedDirectoriesRunAtOnceAndAnswerInDirectoryOrder(t *testing.T) {
|
||||
m, states := threeModules(t, f)
|
||||
m.Parallel = 3
|
||||
res, st := runIntegrationOf(t, m, states, "fixture/c/x", "fixture/a/x")
|
||||
if st.State != delivery.StepPassed {
|
||||
if st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
|
||||
}
|
||||
if most != 2 {
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
builds "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -25,7 +26,6 @@ import (
|
||||
|
||||
registry "git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry"
|
||||
"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
|
||||
@@ -96,7 +96,7 @@ func uploadBlob(base, name, digest string, body []byte) error {
|
||||
|
||||
// resolve reads a done artifact's reference from the store as a node-engine does (catalogue.Routed): the seat's form
|
||||
// oci-artifact-registry/<module>/<artifact>@<digest> is <store>/v2/<module>/<artifact>/blobs/<digest> (ADR 0605 §9).
|
||||
func resolve(t *testing.T, addr string, a delivery.BuiltArtifact) []byte {
|
||||
func resolve(t *testing.T, addr string, a builds.BuiltArtifact) []byte {
|
||||
t.Helper()
|
||||
if _, _, ok := catalogue.InRegistrySeat(a.Reference); !ok {
|
||||
t.Fatalf("%s's reference %q is not in the form a registry seat answers", a.Name, a.Reference)
|
||||
@@ -136,16 +136,16 @@ func TestADoneBuildsArtifactsResolveInARealArtifactStore(t *testing.T) {
|
||||
m := &Module{Home: home, Work: work, Cache: t.TempDir()}
|
||||
m.Run = StepRunner(home)
|
||||
addr := realStore(t, m)
|
||||
done := delivery.BuilderBuild{V: 1, State: delivery.BuildDone, Manifest: strings.Repeat("0", 64)}
|
||||
done := builds.BuilderBuild{V: 1, State: builds.BuildDone, Manifest: strings.Repeat("0", 64)}
|
||||
for _, a := range []struct{ name, kind string }{{"tools", "bundle"}, {"files", "archive"}} {
|
||||
out, err := m.build(context.Background(), map[string]any{"workspace": "ws1", "module": ".", "artifact": a.name})
|
||||
if err != nil {
|
||||
t.Fatalf("%s: %v", a.name, err)
|
||||
}
|
||||
r := out.(BuildResult)
|
||||
done.Artifacts = append(done.Artifacts, delivery.BuiltArtifact{Name: a.name, Kind: a.kind, Digest: r.Digest, Reference: r.Reference})
|
||||
done.Artifacts = append(done.Artifacts, builds.BuiltArtifact{Name: a.name, Kind: a.kind, Digest: r.Digest, Reference: r.Reference})
|
||||
}
|
||||
if _, err := delivery.EncodeBuilderBuild(done); err != nil {
|
||||
if _, err := builds.EncodeBuilderBuild(done); err != nil {
|
||||
t.Errorf("the done key the builder would write is refused: %v", err)
|
||||
}
|
||||
if ok, missing := done.Referenced(); !ok {
|
||||
|
||||
@@ -17,7 +17,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"
|
||||
"git.novox.be/novox/mesh-sdk/go/forgekey"
|
||||
)
|
||||
|
||||
@@ -41,8 +40,8 @@ func (m *Module) testTool() StepTool {
|
||||
"packages": map[string]any{"type": "array", "items": map[string]any{"type": "string"},
|
||||
"description": "package patterns of each module, ./... when none"},
|
||||
"run": map[string]any{"type": "string", "description": "optional go test -run pattern: only the tests it matches run (a try's tests)"},
|
||||
delivery.NodeNamesArg: map[string]any{"type": "array", "items": map[string]any{"type": "string"},
|
||||
"description": "optional, the names of the mesh's nodes: the tests get them comma-separated in " + delivery.NodeNamesEnv + " (a check that keeps node names out of the repository reads them there)"},
|
||||
sdkstep.NodeNamesArg: map[string]any{"type": "array", "items": map[string]any{"type": "string"},
|
||||
"description": "optional, the names of the mesh's nodes: the tests get them comma-separated in " + sdkstep.NodeNamesEnv + " (a check that keeps node names out of the repository reads them there)"},
|
||||
"runner": runnerProperty,
|
||||
},
|
||||
"required": []string{"workspace", "level"},
|
||||
@@ -82,7 +81,7 @@ func (m *Module) test(ctx context.Context, args map[string]any) (any, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
names, err := nodeNamesArg(args[delivery.NodeNamesArg])
|
||||
names, err := nodeNamesArg(args[sdkstep.NodeNamesArg])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -173,11 +172,11 @@ func (m *Module) besideEnv(workspace string) []string {
|
||||
const BuildNodeEnv = "MESH_BUILD_NODE"
|
||||
|
||||
// checkEnv is what every level hands the tests it runs: the build node mark, the siblings (besideEnv) and, when the
|
||||
// ask carried them, the mesh's node names (delivery.NodeNamesEnv, novox/hq ADR 0567 §12).
|
||||
// ask carried them, the mesh's node names (sdkstep.NodeNamesEnv, novox/hq ADR 0567 §12).
|
||||
func (m *Module) checkEnv(workspace string, names []string) []string {
|
||||
env := append([]string{BuildNodeEnv + "=1"}, m.besideEnv(workspace)...)
|
||||
if len(names) > 0 {
|
||||
env = append(env, delivery.NodeNamesEnv+"="+strings.Join(names, ","))
|
||||
env = append(env, sdkstep.NodeNamesEnv+"="+strings.Join(names, ","))
|
||||
}
|
||||
return env
|
||||
}
|
||||
@@ -194,13 +193,13 @@ func nodeNamesArg(raw any) ([]string, error) {
|
||||
for _, e := range v {
|
||||
n, ok := e.(string)
|
||||
if !ok || n == "" || strings.ContainsAny(n, ", \n") {
|
||||
return nil, fmt.Errorf("%s is a list of node names, not %v", delivery.NodeNamesArg, e)
|
||||
return nil, fmt.Errorf("%s is a list of node names, not %v", sdkstep.NodeNamesArg, e)
|
||||
}
|
||||
out = append(out, n)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
return nil, fmt.Errorf("%s is a list of node names", delivery.NodeNamesArg)
|
||||
return nil, fmt.Errorf("%s is a list of node names", sdkstep.NodeNamesArg)
|
||||
}
|
||||
|
||||
// packagesArg reads the packages argument: a list of package patterns, as []any when it came over the bus as JSON and
|
||||
|
||||
@@ -3,13 +3,13 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/forgekey"
|
||||
)
|
||||
|
||||
@@ -53,7 +53,7 @@ func workspaceFor(t *testing.T, captured string) (*Module, *keptStates, string,
|
||||
return m, states, "ws1", calls
|
||||
}
|
||||
|
||||
func runUnit(t *testing.T, m *Module, states *keptStates, ws string) (TestResult, delivery.Step) {
|
||||
func runUnit(t *testing.T, m *Module, states *keptStates, ws string) (TestResult, sdkstep.Step) {
|
||||
t.Helper()
|
||||
var run func(map[string]any) (any, error)
|
||||
for _, tool := range m.Tools(m.StepTools()) {
|
||||
@@ -66,7 +66,7 @@ func runUnit(t *testing.T, m *Module, states *keptStates, ws string) (TestResult
|
||||
}
|
||||
st := states.waitEnd(t)
|
||||
var res TestResult
|
||||
if st.State == delivery.StepPassed {
|
||||
if st.State == sdkstep.StepPassed {
|
||||
if err := json.Unmarshal(st.Result, &res); err != nil {
|
||||
t.Fatalf("result %s: %v", st.Result, err)
|
||||
}
|
||||
@@ -79,7 +79,7 @@ func runUnit(t *testing.T, m *Module, states *keptStates, ws string) (TestResult
|
||||
func TestUnitLevelNamesTheFailingTestAndFailsTheLevel(t *testing.T) {
|
||||
m, states, ws, _ := workspaceFor(t, "failing")
|
||||
res, st := runUnit(t, m, states, ws)
|
||||
if st.State != delivery.StepPassed {
|
||||
if st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
|
||||
}
|
||||
if res.Passed || res.Level != "unit" {
|
||||
@@ -109,7 +109,7 @@ func TestUnitLevelNamesTheFailingTestAndFailsTheLevel(t *testing.T) {
|
||||
func TestUnitLevelNamesEveryFailingTestOfEveryPackage(t *testing.T) {
|
||||
m, states, ws, calls := workspaceFor(t, "twofailing")
|
||||
res, st := runUnit(t, m, states, ws)
|
||||
if st.State != delivery.StepPassed || res.Passed {
|
||||
if st.State != sdkstep.StepPassed || res.Passed {
|
||||
t.Fatalf("step %s (%s), result %+v: want the step done and the level failed", st.State, st.Reason, res)
|
||||
}
|
||||
var got []string
|
||||
@@ -173,7 +173,7 @@ func TestUnitLevelOfAFixtureWithOnlyAnIntegrationTestHasNoTests(t *testing.T) {
|
||||
func TestUnitLevelOfAFixtureWithNoTestsSaysSo(t *testing.T) {
|
||||
m, states, ws, _ := workspaceFor(t, "empty")
|
||||
res, st := runUnit(t, m, states, ws)
|
||||
if st.State != delivery.StepPassed || !res.Passed || !res.NoTests || res.Why != "the repository has no tests at the unit level" || len(res.Failed) != 0 {
|
||||
if st.State != sdkstep.StepPassed || !res.Passed || !res.NoTests || res.Why != "the repository has no tests at the unit level" || len(res.Failed) != 0 {
|
||||
t.Errorf("result = %+v (%s)", res, st.State)
|
||||
}
|
||||
}
|
||||
@@ -206,7 +206,7 @@ func TestUnitLevelOfAContractsModuleOutOfStepFailsNamingEachPath(t *testing.T) {
|
||||
t.Errorf("%s is not under the Go root", p)
|
||||
}
|
||||
}
|
||||
if st.State != delivery.StepPassed || res.Passed || !stray || !strings.Contains(res.Why, "go/stray.go") {
|
||||
if st.State != sdkstep.StepPassed || res.Passed || !stray || !strings.Contains(res.Why, "go/stray.go") {
|
||||
t.Errorf("result = %+v (%s %s)", res, st.State, st.Reason)
|
||||
}
|
||||
}
|
||||
@@ -240,7 +240,7 @@ func TestUnitLevelOfAContractsModuleInAFolderOutOfStepFailsNamingEachPath(t *tes
|
||||
t.Errorf("%s is not under the folder's Go root", p)
|
||||
}
|
||||
}
|
||||
if st.State != delivery.StepPassed || res.Passed || !stray || !strings.Contains(res.Why, "domain/go/stray.go") {
|
||||
if st.State != sdkstep.StepPassed || res.Passed || !stray || !strings.Contains(res.Why, "domain/go/stray.go") {
|
||||
t.Errorf("result = %+v (%s %s)", res, st.State, st.Reason)
|
||||
}
|
||||
}
|
||||
@@ -291,7 +291,7 @@ func TestAGoTestThatCouldNotRunEndsTheStepFailed(t *testing.T) {
|
||||
return forgekey.Ran{Stdout: "go.mod\n"}
|
||||
}, &[]shutOff{})
|
||||
_, st := runUnit(t, m, states, ws)
|
||||
if st.State != delivery.StepFailed || !strings.Contains(st.Reason, "go: cannot find main module") {
|
||||
if st.State != sdkstep.StepFailed || !strings.Contains(st.Reason, "go: cannot find main module") {
|
||||
t.Errorf("the step ended %s: %q", st.State, st.Reason)
|
||||
}
|
||||
}
|
||||
@@ -312,7 +312,7 @@ func TestATestOfAnUnknownWorkspaceOrAnotherLevelIsRefused(t *testing.T) {
|
||||
{"workspace": "ws1", "level": "unit", "packages": []any{"../x"}},
|
||||
} {
|
||||
run(args)
|
||||
if st := states.waitEnd(t); st.State != delivery.StepFailed {
|
||||
if st := states.waitEnd(t); st.State != sdkstep.StepFailed {
|
||||
t.Errorf("%v: the step ended %s", args, st.State)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package main
|
||||
|
||||
// The asked versions of the mesh's packages (novox/hq ADR 0573 §2, §4; issue 1585): every module builds against the latest
|
||||
// published version of each mesh package it uses, and a delivery group's check against the group's own heads, whatever
|
||||
// version its go.mod names. The builder and the checker name each version in the ask (delivery.StepAsk.Packages, a full
|
||||
// version its go.mod names. The builder and the checker name each version in the ask (sdkstep.StepAsk.Packages, a full
|
||||
// commit by package); before Go downloads anything, the fetch writes them into the temporary -modfile of each directory
|
||||
// whose go.mod requires the package (pins.go), so the fetch's go commands, the build and the tests all take them:
|
||||
//
|
||||
|
||||
@@ -3,12 +3,13 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
builds "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/forgekey"
|
||||
)
|
||||
|
||||
@@ -101,13 +102,13 @@ func TestAnAskedVersionIsWrittenIntoTheModfileWhateverGoModNames(t *testing.T) {
|
||||
}
|
||||
st := states.waitEnd(t)
|
||||
if c.failsWith != "" {
|
||||
if st.State != delivery.StepFailed || !strings.Contains(st.Reason, c.failsWith) || strings.Contains(st.Reason, "no pull request head") ||
|
||||
!strings.Contains(st.Reason, "may be retaken") || delivery.FailedUntilChanged(st.Reason) {
|
||||
if st.State != sdkstep.StepFailed || !strings.Contains(st.Reason, c.failsWith) || strings.Contains(st.Reason, "no pull request head") ||
|
||||
!strings.Contains(st.Reason, "may be retaken") || builds.FailedUntilChanged(st.Reason) {
|
||||
t.Errorf("the download failure ended %s: %q", st.State, st.Reason)
|
||||
}
|
||||
return
|
||||
}
|
||||
if st.State != delivery.StepPassed {
|
||||
if st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
|
||||
}
|
||||
var res FetchResult
|
||||
|
||||
@@ -2,14 +2,13 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
sdkstep "git.novox.be/novox/mesh-delivery/sdk/buildstep"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// releaseXzSHA256 is the sha256 of testdata/upstream-release.tar.xz: the tar stream of upstream-release.tar.gz,
|
||||
@@ -45,7 +44,7 @@ func pinnedModuleServing(t *testing.T, fixture, sha, extra string) (*Module, *ke
|
||||
func TestAPinnedXzArchiveIsUnpackedAsTheBuildAgentDoes(t *testing.T) {
|
||||
m, states := pinnedModuleServing(t, "upstream-release.tar.xz", releaseXzSHA256, `,"within":"release/app"`)
|
||||
res, st := runBuild(t, m, states, "files")
|
||||
if st.State != delivery.StepPassed {
|
||||
if st.State != sdkstep.StepPassed {
|
||||
t.Fatalf("the step ended %s: %q", st.State, st.Reason)
|
||||
}
|
||||
sameAsBuildAgent(t, res, buildAgentWithinStream, buildAgentWithinDigest)
|
||||
|
||||
@@ -3,6 +3,8 @@ module meshbuildgo
|
||||
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/contracts/go/seats/meshdeliverychecker v0.0.0-00010101000000-000000000000
|
||||
git.novox.be/novox/mesh-delivery/sdk v0.0.0-00010101000000-000000000000
|
||||
git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient v0.0.0-00010101000000-000000000000
|
||||
git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry v0.0.0-00010101000000-000000000000
|
||||
|
||||
Reference in New Issue
Block a user