A build names its needs and every form of an artifact (ADR 0618 §1, ADR 0624; issue 774)
BuilderBuild.Needs (form:<name>, done keys only) and BuiltArtifact.References (Reference among them, each checked as Reference is) on the delivery layouts mesh-sdk keeps until that area moves (ADR 0613 §3). The node-engine seat's verb and event of ADR 0618 go into mesh-contracts with the seat, not here (ADR 0613 §5). Claude-Session: https://claude.ai/code/session_01JG73KG7AfSdh6FP8VrBqCn
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
contractseats "git.novox.be/novox/mesh-contracts/go/seats"
|
||||
@@ -89,6 +90,10 @@ type BuiltArtifact struct {
|
||||
Kind string `json:"kind,omitempty"`
|
||||
Digest string `json:"digest"`
|
||||
Reference string `json:"reference,omitempty"`
|
||||
// References are every form this artifact is named in, Reference among them (novox/hq ADR 0618, ADR 0624): a build
|
||||
// keeps each form that a counted node-engine states, away nodes included, and Reference stays the form every one
|
||||
// of them reads. A reader that does not know the field reads Reference alone.
|
||||
References []string `json:"references,omitempty"`
|
||||
}
|
||||
|
||||
const artifactStore = "artifact-store://"
|
||||
@@ -211,6 +216,10 @@ type BuilderBuild struct {
|
||||
// Imports are, on a done key, the mesh packages the built programs import, as the build module answered them
|
||||
// (BuildAnswer.Imports; novox/hq ADR 0593 §2): the builder carries them onto the build's end (BuildLogEntry.Imports).
|
||||
Imports []string `json:"imports,omitempty"`
|
||||
// Needs are, on a done key only, the reader capabilities (`form:<name>`) the build's reference forms need of a
|
||||
// node-engine, beside the ones its manifest needs (novox/hq ADR 0618 §1). A reader that does not know the field
|
||||
// reads the build as needing none.
|
||||
Needs []string `json:"needs,omitempty"`
|
||||
}
|
||||
|
||||
func (b BuilderBuild) check() error {
|
||||
@@ -245,6 +254,22 @@ func (b BuilderBuild) check() error {
|
||||
if p := a.referenceProblem(); p != "" {
|
||||
return fmt.Errorf("delivery: the reference %q of the %s %q %s", a.Reference, a.Kind, a.Name, p)
|
||||
}
|
||||
if len(a.References) > 0 && !slices.Contains(a.References, a.Reference) {
|
||||
return fmt.Errorf("delivery: the references of the %s %q do not name its reference %q", a.Kind, a.Name, a.Reference)
|
||||
}
|
||||
for _, r := range a.References {
|
||||
if p := (BuiltArtifact{Name: a.Name, Kind: a.Kind, Digest: a.Digest, Reference: r}).referenceProblem(); p != "" {
|
||||
return fmt.Errorf("delivery: the reference %q of the %s %q %s", r, a.Kind, a.Name, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, n := range b.Needs {
|
||||
if !strings.HasPrefix(n, "form:") || len(n) == len("form:") {
|
||||
return fmt.Errorf("delivery: the need %q of the build is not form:<name>", n)
|
||||
}
|
||||
}
|
||||
if len(b.Needs) > 0 && b.State != BuildDone {
|
||||
return fmt.Errorf("delivery: a %s build names needs; only a done key does", b.State)
|
||||
}
|
||||
for language, id := range b.Asks {
|
||||
if b.State != BuildBuilding {
|
||||
|
||||
@@ -26,11 +26,15 @@ func doneBuild() BuilderBuild {
|
||||
|
||||
func builderSamples() map[string]BuilderBuild {
|
||||
failed := BuilderBuild{State: BuildFailed, By: "builder.novox.1", Step: "test", Error: "go test: 2 failed"}
|
||||
both := doneBuild()
|
||||
both.Needs = []string{"form:artifact-registry"}
|
||||
both.Artifacts[1].References = []string{both.Artifacts[1].Reference, "artifact-store://mail/server@" + both.Artifacts[1].Digest}
|
||||
return map[string]BuilderBuild{
|
||||
"queued": {State: BuildQueued, Manifest: digestOf('4')},
|
||||
"building": {State: BuildBuilding, By: "builder.novox.1", Step: "fetch", Manifest: digestOf('4')},
|
||||
"done": doneBuild(),
|
||||
"failed": failed,
|
||||
"done with needs and both forms": both,
|
||||
"queued": {State: BuildQueued, Manifest: digestOf('4')},
|
||||
"building": {State: BuildBuilding, By: "builder.novox.1", Step: "fetch", Manifest: digestOf('4')},
|
||||
"done": doneBuild(),
|
||||
"failed": failed,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,10 +94,17 @@ func TestBuilderBuildChecked(t *testing.T) {
|
||||
edit func(*BuilderBuild)
|
||||
words []string
|
||||
}{
|
||||
"done without a manifest": {func(b *BuilderBuild) { b.Manifest = "" }, []string{"manifest"}},
|
||||
"a malformed manifest": {func(b *BuilderBuild) { b.Manifest = "abc" }, []string{"abc"}},
|
||||
"an artifact without a digest": {func(b *BuilderBuild) { b.Artifacts[1].Digest = "" }, []string{"digest", "server"}},
|
||||
"an artifact without a name": {func(b *BuilderBuild) { b.Artifacts[0].Name = "" }, []string{"name"}},
|
||||
"done without a manifest": {func(b *BuilderBuild) { b.Manifest = "" }, []string{"manifest"}},
|
||||
"a malformed manifest": {func(b *BuilderBuild) { b.Manifest = "abc" }, []string{"abc"}},
|
||||
"an artifact without a digest": {func(b *BuilderBuild) { b.Artifacts[1].Digest = "" }, []string{"digest", "server"}},
|
||||
"an artifact without a name": {func(b *BuilderBuild) { b.Artifacts[0].Name = "" }, []string{"name"}},
|
||||
"a need that is not a form": {func(b *BuilderBuild) { b.Needs = []string{"word:x"} }, []string{"word:x", "form:"}},
|
||||
"a reference not among the references": {func(b *BuilderBuild) {
|
||||
b.Artifacts[1].References = []string{"artifact-store://mail/server@" + b.Artifacts[1].Digest}
|
||||
}, []string{"server", "references"}},
|
||||
"a reference of another artifact": {func(b *BuilderBuild) {
|
||||
b.Artifacts[1].References = []string{b.Artifacts[1].Reference, "registry.example/mail/other@" + b.Artifacts[1].Digest}
|
||||
}, []string{"server", "other"}},
|
||||
"an unknown state": {func(b *BuilderBuild) { b.State = "paused" }, []string{"paused"}},
|
||||
"a toolchain without a version": {func(b *BuilderBuild) { b.Toolchains["go"] = ToolchainUsed{Recipe: "3"} }, []string{"go", "version"}},
|
||||
"a toolchain without a recipe": {func(b *BuilderBuild) { b.Toolchains["go"] = ToolchainUsed{Version: "1.26.0"} }, []string{"go", "recipe"}},
|
||||
@@ -119,6 +130,11 @@ func TestBuilderBuildChecked(t *testing.T) {
|
||||
}
|
||||
// a queued or building value may not already carry an outcome
|
||||
q := builderSamples()["queued"]
|
||||
q.Needs = []string{"form:x"}
|
||||
if _, err := EncodeBuilderBuild(q); err == nil {
|
||||
t.Error("a queued build with needs accepted")
|
||||
}
|
||||
q.Needs = nil
|
||||
q.Artifacts = doneBuild().Artifacts
|
||||
if _, err := EncodeBuilderBuild(q); err == nil {
|
||||
t.Error("a queued build with artifacts accepted")
|
||||
|
||||
Reference in New Issue
Block a user