diff --git a/modules/mesh-build-go/cmd/mesh-build-go/announce.go b/modules/mesh-build-go/cmd/mesh-build-go/announce.go index 4ee60ceae..e4145bbf7 100644 --- a/modules/mesh-build-go/cmd/mesh-build-go/announce.go +++ b/modules/mesh-build-go/cmd/mesh-build-go/announce.go @@ -17,7 +17,7 @@ var Kept = []announce.State{ {Name: "pauses", Readable: true}, // The steps detected for each artifact, kept by its fingerprint (design 47 G §326, §414). {Name: "detected-steps", Readable: true}, - // The condition keeper raises a first view of the indexer's manifests it could not get (ADR 0615 §6) from this. + // The condition keeper raises an entry of the seat mesh-catalog it could not get (ADR 0615 §6) from this. {Name: "findings", Readable: true}, } diff --git a/modules/mesh-build-go/cmd/mesh-build-go/compose.go b/modules/mesh-build-go/cmd/mesh-build-go/compose.go index a528d8590..08786083f 100644 --- a/modules/mesh-build-go/cmd/mesh-build-go/compose.go +++ b/modules/mesh-build-go/cmd/mesh-build-go/compose.go @@ -7,8 +7,7 @@ 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 -// (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 +// (sdkstep.ComposerKey), so each commit is built once on a node. It fills the request's manifests from the catalog graph's entries, by fingerprint, 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 (sdkstep.ComposeAnswer); the module // judges nothing of it. @@ -26,6 +25,7 @@ import ( seat "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions" catalogclient "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/clients/meshcatalogclient" + meshcat "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/seats/meshcatalog" sdkstep "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-sdk/buildstep" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-sdk/composer" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-sdk/steprun" @@ -34,18 +34,10 @@ import ( "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). -// indexerModule is the module that holds the seat mesh-catalog, whose manifests state a compose ask reads. // findingsModule is the module whose findings these are: this one, by the name the runtime gives it (design 47 G §7: no // fixed module name in a build module's code). func findingsModule() string { return os.Getenv("MESH_MODULE") } -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 @@ -85,17 +77,18 @@ func (m *Module) compose(ctx context.Context, fetch steprun.StepFunc, a sdkstep. 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 -// answer is a finding of this module's own, which the condition keeper raises (ADR 0615 §6) and the next answer clears. -func askManifest(key string) (json.RawMessage, error) { - return askManifestVia(stdio.Ask, manifestFindings, time.Now, key) +// askEntry asks the seat mesh-catalog's verb entry for the catalog graph entry of one fingerprint (design 47 G §615), and +// reads the manifest whole from it. A call the seat does not answer is a finding of this module's own, which the condition +// keeper raises (ADR 0615 §6) and the next answer clears. +func askEntry(fingerprint string) (meshcat.CatalogEntry, error) { + return askEntryVia(stdio.Ask, manifestFindings, time.Now, fingerprint) } -// manifestFindings is where askManifest writes its finding: the module's writer once main has started it (design 47 G §689, +// manifestFindings is where askEntry writes its finding: the module's writer once main has started it (design 47 G §689, // A-16); outside a launch by the runtime, which has no node and no state, nothing is written. var manifestFindings findingWriter = noFindings{} -// findingWriter is the module's writer (views.Writer) as askManifestVia writes and clears through it. +// findingWriter is the module's writer (views.Writer) as askEntryVia writes and clears through it. type findingWriter interface { Write(ctx context.Context, finding seat.Finding) error Clear(ctx context.Context, scope, id, kind, why string) error @@ -106,18 +99,18 @@ type noFindings struct{} func (noFindings) Write(context.Context, seat.Finding) error { return nil } func (noFindings) Clear(context.Context, string, string, string, string) error { return nil } -// unansweredManifest is the finding of an ask the indexer did not answer, laid out as mesh-sdk lays out a views reader's +// unansweredEntry is the finding of an ask the seat mesh-catalog did not answer, laid out as mesh-sdk lays out a views reader's // first-view-unanswered (design 47 G §687, A-28): the id is the view's name, escaped as a key's name is. -func unansweredManifest(err string, asked, at time.Time) seat.Finding { - id := keys.EscapeName(findingsModule() + "." + indexerModule + ".manifests") +func unansweredEntry(err string, asked, at time.Time) seat.Finding { + id := keys.EscapeName(findingsModule() + "." + meshcat.Seat + ".entry") return seat.Finding{Kind: views.UnansweredKind, Scope: "module", ID: id, Severity: seat.FindingSeverityWarning, Source: "views", Subject: seat.Subject{Scope: "module", ID: findingsModule(), Machine: os.Getenv("MESH_NODE")}, - Summary: fmt.Sprintf("%s has no first view of %s's manifests: %s has not answered since %s (%s)", findingsModule(), indexerModule, ManifestsTool, + Summary: fmt.Sprintf("%s has no entry of the seat %s: %s has not answered since %s (%s)", findingsModule(), meshcat.Seat, catalogclient.EntryVerb, asked.Format(time.RFC3339), strings.TrimSpace(err)), - Headline: fmt.Sprintf("%s waits for %s", findingsModule(), indexerModule), At: at.UTC()} + Headline: fmt.Sprintf("%s waits for the seat %s", findingsModule(), meshcat.Seat), At: at.UTC()} } -// newFindingsWriter is this module's finding writer: it states the finding of askManifest and observes none of its own. +// newFindingsWriter is this module's finding writer: it states the finding of askEntry and observes none of its own. func newFindingsWriter() (*views.Writer[seat.Finding, seat.FindingCleared, seat.FindingsStated, seat.FindingsAskedEvent], error) { return views.NewStdioWriter(views.WriterConfig[seat.Finding, seat.FindingCleared, seat.FindingsStated, seat.FindingsAskedEvent]{ Asked: seat.SubjectFindingsAsked, Module: findingsModule(), State: stdio.State("findings"), @@ -126,49 +119,41 @@ func newFindingsWriter() (*views.Writer[seat.Finding, seat.FindingCleared, seat. }}) } -func askManifestVia(ask func(string, any) (json.RawMessage, error), findings findingWriter, now func() time.Time, key string) (json.RawMessage, error) { +func askEntryVia(ask func(string, any) (json.RawMessage, error), findings findingWriter, now func() time.Time, fingerprint string) (meshcat.CatalogEntry, error) { asked := now().UTC() - raw, err := ask(ManifestsTool, map[string]any{"key": key}) - f := unansweredManifest("", asked, now()) + raw, err := ask(catalogclient.EntryVerb, map[string]any{"fingerprint": fingerprint}) + f := unansweredEntry("", asked, now()) if err != nil { - _ = findings.Write(context.Background(), unansweredManifest(err.Error(), asked, now())) - return nil, err - } - _ = findings.Clear(context.Background(), f.Scope, f.ID, f.Kind, "the indexer answered") - var a struct { - Found bool `json:"found"` - Value json.RawMessage `json:"value"` + _ = findings.Write(context.Background(), unansweredEntry(err.Error(), asked, now())) + return meshcat.CatalogEntry{}, err } + _ = findings.Clear(context.Background(), f.Scope, f.ID, f.Kind, "the seat answered") + var a meshcat.EntryAnswer if err := json.Unmarshal(raw, &a); err != nil { - return nil, fmt.Errorf("%s answered something that is not a key's value: %w", ManifestsTool, err) + return meshcat.CatalogEntry{}, fmt.Errorf("%s answered something that is not an entry: %w", catalogclient.EntryVerb, err) } - if !a.Found { - return nil, nil - } - return a.Value, nil + return a.Entry, nil } -// manifestOf is the manifest the indexer keeps under digest, as its repository holds it. -func (m *Module) manifestOf(digest string) ([]byte, error) { - if m.ReadManifest == nil { - return nil, errors.New("this module reads no manifests state") +// manifestOf is the manifest a compose request names by digest, whole from the catalog graph entry of the fingerprint it +// names beside it (design 47 G §615, A-310). A digest with no fingerprint, and an entry whose manifest-digest is another, +// refuse the request; composer.Fill names the module. +func (m *Module) manifestOf(man composer.Manifest) ([]byte, error) { + if m.ReadEntry == nil { + return nil, errors.New("this module reads no catalog entries") } - key, err := catalogclient.ManifestsKey(digest) + if man.Fingerprint == "" { + return nil, fmt.Errorf("the manifest is named by its digest %s with no fingerprint, so no catalog entry holds it", man.Digest) + } + entry, err := m.ReadEntry(man.Fingerprint) if err != nil { return nil, err } - raw, err := m.ReadManifest(key) - if err != nil { - return nil, err + if strings.TrimPrefix(entry.ManifestDigest, "sha256:") != strings.TrimPrefix(man.Digest, "sha256:") { + return nil, fmt.Errorf("the entry at fingerprint %s holds the manifest %s, not the digest %s the request names", + man.Fingerprint, entry.ManifestDigest, man.Digest) } - if raw == nil { - return nil, fmt.Errorf("the indexer keeps no manifest of digest %s", digest) - } - man, err := catalogclient.DecodeManifest(raw) - if err != nil { - return nil, err - } - return []byte(man.Manifest), nil + return json.Marshal(entry.Manifest) } // composerOf is the composer kept under key, or, when none is, the one built now from the fetched commit (built true). diff --git a/modules/mesh-build-go/cmd/mesh-build-go/compose_integration_test.go b/modules/mesh-build-go/cmd/mesh-build-go/compose_integration_test.go index ed2923b9e..c1bf03f98 100644 --- a/modules/mesh-build-go/cmd/mesh-build-go/compose_integration_test.go +++ b/modules/mesh-build-go/cmd/mesh-build-go/compose_integration_test.go @@ -166,8 +166,8 @@ func TestAComposeAskIsBuiltOnceAndAnsweredOnTheBus(t *testing.T) { } return run(ctx, env, name, args...) } - read, digests := indexerHolds(t, `{"module":"demo","version":"1"}`) - m.ReadManifest = read + read, digests := catalogHolds(t, `{"module":"demo","version":"1"}`) + m.ReadEntry = read fetches := 0 steps := fakeSteps(new([]string)) diff --git a/modules/mesh-build-go/cmd/mesh-build-go/compose_name_test.go b/modules/mesh-build-go/cmd/mesh-build-go/compose_name_test.go deleted file mode 100644 index 0ebc84abb..000000000 --- a/modules/mesh-build-go/cmd/mesh-build-go/compose_name_test.go +++ /dev/null @@ -1,15 +0,0 @@ -package main - -import ( - "testing" - "time" -) - -// Design 47 G §7 (BG12, BLD16): the finding names the module as the runtime does, never a fixed name. -func TestUnansweredManifestNamesTheModuleTheRuntimeNames(t *testing.T) { - t.Setenv("MESH_MODULE", "mesh-build-go-renamed") - f := unansweredManifest("no answer", time.Unix(0, 0), time.Unix(60, 0)) - if f.Subject.ID != "mesh-build-go-renamed" || f.Headline != "mesh-build-go-renamed waits for "+indexerModule { - t.Fatalf("the finding names %q, %q", f.Subject.ID, f.Headline) - } -} diff --git a/modules/mesh-build-go/cmd/mesh-build-go/compose_test.go b/modules/mesh-build-go/cmd/mesh-build-go/compose_test.go index 95ba6a006..20a5c522a 100644 --- a/modules/mesh-build-go/cmd/mesh-build-go/compose_test.go +++ b/modules/mesh-build-go/cmd/mesh-build-go/compose_test.go @@ -25,26 +25,35 @@ const nodeEngineComposer = "github.com/novox/mesh-host/cmd/mesh-compose" const composeCommit = "4444444444444444444444444444444444444444" -// indexerHolds is the delivery indexer's manifests state holding each body under its digest, as the indexer writes it. -func indexerHolds(t *testing.T, bodies ...string) (func(string) (json.RawMessage, error), []string) { +// fingerprintOf is the fingerprint the catalog graph's entry of a manifest's digest is kept under in these tests. +func fingerprintOf(digest string) string { return "fp-" + digest } + +// catalogHolds is the seat mesh-catalog's entries, each body's under its fingerprint, with its manifest and the manifest's +// digest as the indexer writes them (design 47 G §506); it answers the digests. +func catalogHolds(t *testing.T, bodies ...string) (func(string) (meshcat.CatalogEntry, error), []string) { t.Helper() - kept, digests := map[string]json.RawMessage{}, []string{} + kept, digests := map[string]meshcat.CatalogEntry{}, []string{} for _, b := range bodies { d := catalogclient.ManifestDigest([]byte(b)) - key, _ := catalogclient.ManifestsKey(d) - raw, err := catalogclient.EncodeManifest(meshcat.ManifestsByDigestValue{Module: "demo", Manifest: b}) - if err != nil { + var whole map[string]any + if err := json.Unmarshal([]byte(b), &whole); err != nil { t.Fatal(err) } - kept[key], digests = raw, append(digests, d) + kept[fingerprintOf(d)] = meshcat.CatalogEntry{Module: "demo", Fp: fingerprintOf(d), Manifest: whole, ManifestDigest: "sha256:" + d} + digests = append(digests, d) } - return func(key string) (json.RawMessage, error) { return kept[key], nil }, digests + return func(fp string) (meshcat.CatalogEntry, error) { + if e, ok := kept[fp]; ok { + return e, nil + } + return meshcat.CatalogEntry{}, errors.New("the seat keeps no entry of " + fp) + }, digests } 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}}}}}) + Manifests: map[string]composer.Manifest{"demo": {Digest: digest, Fingerprint: fingerprintOf(digest)}}}}}) 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}} @@ -64,8 +73,8 @@ func composeModule(t *testing.T) *Module { // indexer's state. func TestAKeptComposerIsRunWithTheManifestsFilled(t *testing.T) { m := composeModule(t) - read, digests := indexerHolds(t, `{"module":"demo","version":"1"}`) - m.ReadManifest = read + read, digests := catalogHolds(t, `{"module":"demo","version":"1"}`) + m.ReadEntry = read a := composeAskOf(t, digests[0]) key := sdkstep.ComposerKey("novox/mesh-sdk", composeCommit, "go", composer.Package, sdkstep.ToolchainUsed{Version: "go1.26.0", Recipe: m.Recipe.Version()}, nil) exe := m.composerPath(key) @@ -106,7 +115,7 @@ func TestAKeptComposerIsRunWithTheManifestsFilled(t *testing.T) { // another; an ask naming none keeps the key it always had. func TestAComposerIsKeptPerAskedVersions(t *testing.T) { m := composeModule(t) - m.ReadManifest, _ = indexerHolds(t) + m.ReadEntry, _ = catalogHolds(t) a := composeAskOf(t, "feedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed") tc := sdkstep.ToolchainUsed{Version: "go1.26.0", Recipe: m.Recipe.Version()} plain := sdkstep.ComposerKey("novox/mesh-sdk", composeCommit, "go", composer.Package, tc, nil) @@ -133,7 +142,7 @@ func TestAComposerIsKeptPerAskedVersions(t *testing.T) { // the ask builds fails it naming the directory, and its workspace is released. func TestAComposeAskFailsLoudly(t *testing.T) { m := composeModule(t) - m.ReadManifest, _ = indexerHolds(t) + m.ReadEntry, _ = catalogHolds(t) a := composeAskOf(t, "feedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed") key := sdkstep.ComposerKey("novox/mesh-sdk", composeCommit, "go", composer.Package, sdkstep.ToolchainUsed{Version: "go1.26.0", Recipe: m.Recipe.Version()}, nil) os.MkdirAll(filepath.Dir(m.composerPath(key)), 0o700) @@ -156,32 +165,64 @@ 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. 1921 (B0): a run's input is asked of the checker seat's verb input, as the manifest -// invokes it, and no state of the checker's inputs is read (ADR 0596 §2). -func TestTheModuleReadsTheIndexersManifests(t *testing.T) { +// Design 47 G §615: the module reads the manifests a compose ask names from the catalog graph's entry, by the seat's verb +// entry, and invokes no tool of the delivery indexer. 1921 (B0): a run's input is asked of the checker seat's verb input, as +// the manifest invokes it, and no state of the checker's inputs is read (ADR 0596 §2). +func TestTheModuleReadsTheCatalogEntries(t *testing.T) { m := readManifest(t) - if !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) + if !slices.Contains(m.Invokes, catalogclient.EntryVerb) || slices.ContainsFunc(m.Invokes, func(i string) bool { return strings.HasPrefix(i, "mesh-delivery-indexer") }) { + t.Errorf("invokes = %v: want %s and no tool of the indexer", m.Invokes, catalogclient.EntryVerb) } if !slices.Contains(m.Invokes, runInputVerb) || slices.Contains(m.Reads, "mesh-delivery-checker.inputs") { t.Errorf("invokes = %v, reads = %v: want %s invoked and no inputs state read", m.Invokes, m.Reads, runInputVerb) } } -// ADR 0615 §6: an indexer that does not answer is a finding of the module's own, and its next answer clears it. -func TestAnIndexerThatDoesNotAnswerIsAFinding(t *testing.T) { +// Design 47 G §615, A-310: a manifest named by a digest is taken whole from the entry of the fingerprint beside it; a digest +// with no fingerprint, and an entry holding another digest, refuse the request, naming the manifest's module. +func TestAManifestComesWholeFromItsEntryAndAnyMismatchRefuses(t *testing.T) { + read, digests := catalogHolds(t, `{"module":"demo","version":"1"}`, `{"module":"demo","version":"2"}`) + m := composeModule(t) + m.ReadEntry = read + for _, c := range []struct { + name string + man composer.Manifest + want string // empty: the manifest whole + }{ + {"the entry's manifest", composer.Manifest{Digest: digests[0], Fingerprint: fingerprintOf(digests[0])}, ""}, + {"a digest with no fingerprint", composer.Manifest{Digest: digests[0]}, "no fingerprint"}, + {"an entry holding another digest", composer.Manifest{Digest: digests[0], Fingerprint: fingerprintOf(digests[1])}, "not the digest"}, + {"a fingerprint no entry has", composer.Manifest{Digest: digests[0], Fingerprint: "fp-none"}, "keeps no entry"}, + } { + req := composer.Request{V: composer.Version, Cases: []composer.Case{{Name: "change", Manifests: map[string]composer.Manifest{"demo": c.man}}}} + err := composer.Fill(&req, m.manifestOf) + switch { + case c.want == "" && (err != nil || string(req.Cases[0].Manifests["demo"].Raw) != `{"module":"demo","version":"1"}`): + t.Errorf("%s: %v, %s", c.name, err, req.Cases[0].Manifests["demo"].Raw) + case c.want != "" && (err == nil || !strings.Contains(err.Error(), c.want) || !strings.Contains(err.Error(), "demo")): + t.Errorf("%s: %v", c.name, err) + } + } +} + +// ADR 0615 §6: a seat that does not answer is a finding of the module's own, and its next answer clears it. +func TestACatalogThatDoesNotAnswerIsAFinding(t *testing.T) { + // Design 47 G §7 (BG12, BLD16): the finding names the module as the runtime does, never a fixed name. + t.Setenv("MESH_MODULE", "mesh-build-go-renamed") + if f := unansweredEntry("no answer", time.Unix(0, 0), time.Unix(60, 0)); f.Subject.ID != "mesh-build-go-renamed" || f.Headline != "mesh-build-go-renamed waits for the seat mesh-catalog" { + t.Fatalf("the finding names %q, %q", f.Subject.ID, f.Headline) + } held := map[string][]byte{} f := mapFindings(held) at := func() time.Time { return time.Date(2026, 11, 4, 12, 0, 0, 0, time.UTC) } - if _, err := askManifestVia(func(string, any) (json.RawMessage, error) { return nil, errors.New("no responders") }, f, at, "k"); err == nil || len(held) != 1 { + if _, err := askEntryVia(func(string, any) (json.RawMessage, error) { return nil, errors.New("no responders") }, f, at, "fp"); err == nil || len(held) != 1 { t.Fatalf("err %v, findings %v", err, held) } - raw, err := askManifestVia(func(string, any) (json.RawMessage, error) { - return json.RawMessage(`{"found":true,"value":{"x":1}}`), nil - }, f, at, "k") - if err != nil || string(raw) != `{"x":1}` || len(held) != 0 { - t.Errorf("raw %s, err %v, findings %v", raw, err, held) + e, err := askEntryVia(func(string, any) (json.RawMessage, error) { + return json.RawMessage(`{"entry":{"module":"demo","repository":"r","commit":"c","manifest":{"x":1},"manifest-digest":"sha256:ab"}}`), nil + }, f, at, "fp") + if err != nil || e.Module != "demo" || e.ManifestDigest != "sha256:ab" || len(held) != 0 { + t.Errorf("entry %+v, err %v, findings %v", e, err, held) } } @@ -201,8 +242,8 @@ func (m mapFindings) Clear(_ context.Context, _, id, kind, _ string) error { // manifests filled; the module's answer is the program's verdict whole. func TestAComposeAskBuildsTheNodeEnginesComposerAtItsRoot(t *testing.T) { m := composeModule(t) - read, digests := indexerHolds(t, `{"module":"demo","version":"1"}`) - m.ReadManifest = read + read, digests := catalogHolds(t, `{"module":"demo","version":"1"}`) + m.ReadEntry = read a := composeAskOf(t, digests[0]) a.Compose.Fetch.Repository, a.Compose.Dir, a.Compose.Package = "novox/mesh-host", catalogclient.RootPath, nodeEngineComposer tree := "" diff --git a/modules/mesh-build-go/cmd/mesh-build-go/main.go b/modules/mesh-build-go/cmd/mesh-build-go/main.go index 589e70156..1c404a996 100644 --- a/modules/mesh-build-go/cmd/mesh-build-go/main.go +++ b/modules/mesh-build-go/cmd/mesh-build-go/main.go @@ -67,7 +67,7 @@ func main() { m.GoSeatFile = m.Home + "/go-package-registry.json" m.ArtifactStoreFile = m.Home + "/artifact-store.json" m.Settings = m.Home + "/settings.json" - m.ReadManifest = askManifest + m.ReadEntry = askEntry m.AskInput = stdio.Ask m.KeptBuilds = busstate.ObjectsOf(KeptBuildsStore) m.Outputs = busstate.ObjectsOf(steprun.OutputsStore) diff --git a/modules/mesh-build-go/cmd/mesh-build-go/module.go b/modules/mesh-build-go/cmd/mesh-build-go/module.go index 8ea1f0182..38329cb9a 100644 --- a/modules/mesh-build-go/cmd/mesh-build-go/module.go +++ b/modules/mesh-build-go/cmd/mesh-build-go/module.go @@ -10,6 +10,7 @@ import ( "sync" "time" + meshcat "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/seats/meshcatalog" buildrun "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-sdk/buildrun" sdkstep "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-sdk/buildstep" "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-sdk/steprun" @@ -114,9 +115,9 @@ type Module struct { Capacity func() int // SayStep says a pipeline step's end (steprun.AskSteps.SayStep); the seat's publish on this node when nil. SayStep func(sdkstep.StepEnded) error - // ReadManifest asks the delivery indexer's read tool (ManifestsTool) for one manifest by its key, which a compose ask's - // request names its manifests by (compose.go); it reads none of the indexer's state (ADR 0596); GoModCache, when set, is the Go module cache in place of Cache's (a test). - ReadManifest func(key string) (json.RawMessage, error) + // ReadEntry asks the seat mesh-catalog's verb entry for the catalog graph entry of one fingerprint, which a compose ask's + // request names each manifest it gives by digest with (compose.go, design 47 G §615); GoModCache, when set, is the Go module cache in place of Cache's (a test). + ReadEntry func(fingerprint string) (meshcat.CatalogEntry, error) // AskInput asks a seat verb the module invokes (module.json) as the runtime calls it (stdio.Ask): the verb a run ask's // input names, answering the input by its digest (meshdeliverycheckerclient.ReadInput). AskInput func(key string, body any) (json.RawMessage, error) diff --git a/modules/mesh-build-go/cmd/mesh-build-go/run_test.go b/modules/mesh-build-go/cmd/mesh-build-go/run_test.go index 87e2b4ef2..599c796c8 100644 --- a/modules/mesh-build-go/cmd/mesh-build-go/run_test.go +++ b/modules/mesh-build-go/cmd/mesh-build-go/run_test.go @@ -127,11 +127,11 @@ func TestAKeptProgramIsRunOverItsHeadsAndInput(t *testing.T) { // and used to drop the seats. func TestAComposeAskKeepsItsSeats(t *testing.T) { m := composeModule(t) - read, digests := indexerHolds(t, `{"module":"demo","version":"1"}`) - m.ReadManifest = read + read, digests := catalogHolds(t, `{"module":"demo","version":"1"}`) + m.ReadEntry = read a := composeAskOf(t, digests[0]) req := composer.Request{V: composer.Version, Seats: []json.RawMessage{json.RawMessage(`{"seat":"node-x"}`)}, - Cases: []composer.Case{{Name: "change", Manifests: map[string]composer.Manifest{"demo": {Digest: digests[0]}}}}} + Cases: []composer.Case{{Name: "change", Manifests: map[string]composer.Manifest{"demo": {Digest: digests[0], Fingerprint: fingerprintOf(digests[0])}}}}} a.Compose.Request, _ = json.Marshal(req) key := sdkstep.ComposerKey("novox/mesh-sdk", composeCommit, "go", composer.Package, sdkstep.ToolchainUsed{Version: "go1.26.0", Recipe: m.Recipe.Version()}, nil) exe := m.composerPath(key) diff --git a/modules/mesh-build-go/module.json b/modules/mesh-build-go/module.json index c73830420..df53dbd90 100644 --- a/modules/mesh-build-go/module.json +++ b/modules/mesh-build-go/module.json @@ -41,7 +41,6 @@ ], "invokes": [ "seat:mesh-hand-acts.hand-act", - "mesh-delivery-indexer.indexer_read_manifests", "seat:oci-artifact-registry.publish", "seat:oci-artifact-registry.publish-part", "seat:go-package-registry.publish", @@ -50,7 +49,8 @@ "seat:mesh-catalog.input", "seat:mesh-wants.input", "seat:mesh-module-assigner.assignments", - "seat:mesh-catalog.module" + "seat:mesh-catalog.module", + "seat:mesh-catalog.entry" ], "config": { "capacity": {