The check names a changed build module that invokes a verb no running build serves as one it cannot run (ADR 0614 §7; issue 774)
What the running builds serve is read when a changed build module is met: the node-engine's haves, the builder's build of each, its manifest. The modules it builds are composed from their manifests, as for any changed build module, and the composition's warnings name the verb. Claude-Session: https://claude.ai/code/session_01JG73KG7AfSdh6FP8VrBqCn
This commit is contained in:
@@ -17,6 +17,8 @@ import (
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/process"
|
||||
|
||||
"git.novox.be/novox/mesh-delivery/sdk/graph"
|
||||
|
||||
"mesh-delivery-checker/internal/check"
|
||||
)
|
||||
|
||||
@@ -95,6 +97,58 @@ func (n nodeNames) Names(ctx context.Context) ([]string, error) {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// runningServes says what the running builds serve (novox/hq ADR 0614 §7), read when a check asks it: every module's have
|
||||
// in the node-engine's have that runs healthy, the builder's build of its fingerprint, and the verbs its manifest serves
|
||||
// (graph.Served). A build whose manifest cannot be read serves nothing here, said in the log.
|
||||
type runningServes struct {
|
||||
have *nodeNames
|
||||
builds check.BuildStates
|
||||
read check.ManifestReader
|
||||
logf func(format string, args ...any)
|
||||
}
|
||||
|
||||
func (r runningServes) Served(ctx context.Context) (func(string) bool, error) {
|
||||
if r.have == nil || r.have.kv == nil || r.builds == nil || r.read == nil {
|
||||
return nil, fmt.Errorf("the node-engine's have, the builder's builds or the indexer's manifests cannot be read")
|
||||
}
|
||||
lister, err := r.have.kv.ListKeys(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
haves := map[string]map[string]delivery.Have{}
|
||||
for key := range lister.Keys() {
|
||||
node, module, isNode, err := delivery.ParseHaveKey(key)
|
||||
if err != nil || isNode {
|
||||
continue
|
||||
}
|
||||
e, err := r.have.kv.Get(ctx, key)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if h, err := delivery.DecodeHave(e.Value()); err == nil {
|
||||
if haves[node] == nil {
|
||||
haves[node] = map[string]delivery.Have{}
|
||||
}
|
||||
haves[node][module] = h
|
||||
}
|
||||
}
|
||||
served, unread := graph.Served(haves, func(module, fp string) ([]string, bool) {
|
||||
b, found, err := r.builds.Build(ctx, module, fp)
|
||||
if err != nil || !found || b.Manifest == "" {
|
||||
return nil, false
|
||||
}
|
||||
m, err := r.read.Manifest(ctx, b.Manifest)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
return graph.ServesOf(m), true
|
||||
}, nil)
|
||||
if len(unread) > 0 && r.logf != nil {
|
||||
r.logf("the manifest of the running build of %v cannot be read, so it serves nothing the check counts (ADR 0614 §7)", unread)
|
||||
}
|
||||
return served, nil
|
||||
}
|
||||
|
||||
func newJudge(d judgeDeps) check.Judge {
|
||||
var nodes interface {
|
||||
Names(ctx context.Context) ([]string, error)
|
||||
@@ -102,8 +156,12 @@ func newJudge(d judgeDeps) check.Judge {
|
||||
if d.nodes != nil {
|
||||
nodes = d.nodes
|
||||
}
|
||||
return &check.Examiner{Nodes: nodes, Compositions: d.compositions, Asks: d.asks, Toolchains: d.tools,
|
||||
ex := &check.Examiner{Nodes: nodes, Compositions: d.compositions, Asks: d.asks, Toolchains: d.tools,
|
||||
Kept: check.Kept{Store: d.kept}, Previews: d.previews, Modules: d.repos, Packages: d.repos, Published: d.published, Forge: d.forge, Runs: d.repos, Builds: d.builds, BuildEnds: d.ends, BuildWait: d.buildWait, Announce: d.announce, Trunks: d.trunks, Manifests: d.repos, Read: d.repos, Predefined: d.predefined, Logf: d.logf}
|
||||
if d.nodes != nil && d.builds != nil && d.repos != nil {
|
||||
ex.Serving = runningServes{have: d.nodes, builds: d.builds, read: d.repos, logf: d.logf}
|
||||
}
|
||||
return ex
|
||||
}
|
||||
|
||||
// manifestKV reads a manifest by its digest.
|
||||
|
||||
@@ -28,6 +28,9 @@ import (
|
||||
// trunk's base; the base's own build is waited on.
|
||||
//
|
||||
// A module the trunk holds no record of yet is not built by the builder until it is merged, so it is not waited on either.
|
||||
//
|
||||
// A changed build module whose manifest invokes a seat verb or module tool no running build serves is one the check cannot
|
||||
// run (ADR 0614 §7): the modules it builds are composed from their manifests, as above, and the words name the verb.
|
||||
|
||||
// buildsOfTheCheck are the builds the check waits on before it composes (module to fingerprint) and the words for the
|
||||
// changed modules it composes without waiting on their build, one line each, sorted. A manifest that cannot be read leaves
|
||||
@@ -88,6 +91,14 @@ func (st *state) buildsOfTheCheck(ctx context.Context) (map[string]string, []str
|
||||
words = append(words, fmt.Sprintf("the change alters the build module %s: %s, which it builds, are built by the running %s, not the changed one, which the check cannot run, so the check does not wait on their builds; they are built again once the agreed toolchain moves (ADR 0603 §3, §10)",
|
||||
bm, strings.Join(built, ", "), bm))
|
||||
}
|
||||
if unserved := st.unservedVerbs(ctx, changed[bm]); len(unserved) > 0 {
|
||||
what := "nothing it builds is in the change"
|
||||
if len(built) > 0 {
|
||||
what = strings.Join(built, ", ") + ", which it builds, are composed from their manifests"
|
||||
}
|
||||
words = append(words, fmt.Sprintf("the changed build module %s invokes %s, which no running build serves, so the check cannot run it: %s (ADR 0614 §7)",
|
||||
bm, strings.Join(unserved, ", "), what))
|
||||
}
|
||||
}
|
||||
g := graph.OfManifests(changed)
|
||||
for _, base := range slices.Sorted(maps.Keys(changed)) {
|
||||
@@ -109,3 +120,23 @@ func buildsWith(m catalogue.Manifest, language string) bool {
|
||||
return a.Kind == catalogue.ArtifactBundle && a.Language == language
|
||||
})
|
||||
}
|
||||
|
||||
// unservedVerbs are the seat verbs and module tools m invokes (graph.InvokesOf) that no running build serves, sorted; none
|
||||
// when nothing reads what runs, or what runs cannot be read (said in the log: nothing is said that was not read).
|
||||
func (st *state) unservedVerbs(ctx context.Context, m catalogue.Manifest) []string {
|
||||
if st.j.Serving == nil {
|
||||
return nil
|
||||
}
|
||||
served, err := st.j.Serving.Served(ctx)
|
||||
if err != nil {
|
||||
st.j.logf("%s: what the running builds serve cannot be read, so no build module is named for a verb it invokes: %v", st.c.Key, err)
|
||||
return nil
|
||||
}
|
||||
var out []string
|
||||
for _, v := range graph.InvokesOf(m) {
|
||||
if !served(v) {
|
||||
out = append(out, v)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -127,6 +127,13 @@ func (f *fakeEnds) Ended(_ context.Context, builds map[string]string) (map[strin
|
||||
|
||||
func (f *fakeEnds) Done(context.Context, map[string]string) bool { return false }
|
||||
|
||||
// fixedServing are the verbs the running builds serve.
|
||||
type fixedServing []string
|
||||
|
||||
func (f fixedServing) Served(context.Context) (func(string) bool, error) {
|
||||
return func(v string) bool { return slices.Contains(f, v) }, nil
|
||||
}
|
||||
|
||||
// knownRuns are the modules the trunk holds a record of (the indexer's modules): any commit.
|
||||
type knownRuns []string
|
||||
|
||||
@@ -154,10 +161,17 @@ func aChangeToABuildModuleOrBaseNamesTheModulesBuiltOnIt(t *testing.T) {
|
||||
builds map[string]delivery.BuilderBuild // by module, at fp; nil leaves the check without the builds' ends
|
||||
runs knownRuns // nil: the trunk holds every module
|
||||
want []string
|
||||
waited string // the modules waited on, sorted
|
||||
failed string // the failure's test, when the check fails on a build
|
||||
tests bool // the tests fail: the check fails before it would wait
|
||||
waited string // the modules waited on, sorted
|
||||
failed string // the failure's test, when the check fails on a build
|
||||
tests bool // the tests fail: the check fails before it would wait
|
||||
served []string // what the running builds serve (ADR 0614 §7); nil reads nothing of what runs
|
||||
}{
|
||||
{name: "the Go build module invokes a verb no running build serves",
|
||||
manifest: map[string]catalogue.Manifest{"mesh-build-go": {Module: "mesh-build-go", Build: goBuild,
|
||||
Invokes: []string{"seat:package-registry.publish", "seat:artifact-store.put"}}, "web": {Module: "web", Build: goBuild}},
|
||||
builds: map[string]delivery.BuilderBuild{"mesh-build-go": done}, served: []string{"seat:artifact-store.put"},
|
||||
want: []string{"the changed build module mesh-build-go invokes seat:package-registry.publish, which no running build serves, " +
|
||||
"so the check cannot run it: web, which it builds, are composed from their manifests (ADR 0614 §7)"}, waited: "mesh-build-go"},
|
||||
{name: "the Go build module changes",
|
||||
manifest: map[string]catalogue.Manifest{"mesh-build-go": {Module: "mesh-build-go", Build: goBuild}, "web": {Module: "web", Build: goBuild},
|
||||
"mail": {Module: "mail", Build: goBuild}, "docs": {Module: "docs"}},
|
||||
@@ -211,6 +225,9 @@ func aChangeToABuildModuleOrBaseNamesTheModulesBuiltOnIt(t *testing.T) {
|
||||
if row.runs != nil {
|
||||
b.ex.Runs = row.runs
|
||||
}
|
||||
if row.served != nil {
|
||||
b.ex.Serving = fixedServing(row.served)
|
||||
}
|
||||
read, fps := fixedManifests{}, map[string]delivery.CandidateModule{}
|
||||
for module, m := range row.manifest {
|
||||
d := dig(string(rune('a' + len(read))))
|
||||
|
||||
@@ -122,6 +122,12 @@ type Examiner struct {
|
||||
// Read reads a kept manifest by its digest: the change's module graph is worked out from them (graph_cycle.go, novox/hq
|
||||
// ADR 0603 §2); nil works out no graph.
|
||||
Read ManifestReader
|
||||
// Serving says, from what runs, whether a running healthy build serves a seat verb or module tool (novox/hq ADR 0614
|
||||
// §7): the node-engines' haves, the builder's build of each, its manifest (graph.Served). Nil reads nothing, and no
|
||||
// changed build module is named for a verb it invokes.
|
||||
Serving interface {
|
||||
Served(ctx context.Context) (func(invoke string) bool, error)
|
||||
}
|
||||
// Predefined reads the catalogue's predefined keys the indexer keeps (manifests_judge.go); nil reads none.
|
||||
Predefined PredefinedList
|
||||
// Compositions makes a check's composition (ADR 0593 §7): the engines' answers (EngineCompositions); nil leaves the
|
||||
|
||||
Reference in New Issue
Block a user