diff --git a/modules/mesh-delivery-indexer/internal/index/catalog_graph.go b/modules/mesh-delivery-indexer/internal/index/catalog_graph.go index eed3191a..a9fd8e87 100644 --- a/modules/mesh-delivery-indexer/internal/index/catalog_graph.go +++ b/modules/mesh-delivery-indexer/internal/index/catalog_graph.go @@ -6,11 +6,13 @@ import ( "encoding/json" "fmt" "maps" + "reflect" "slices" "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog" "git.novox.be/novox/mesh-delivery/sdk/graph" "git.novox.be/novox/mesh-sdk/go/catalogue" + "git.novox.be/novox/mesh-sdk/go/keys" ) // The catalog graph (novox/hq ADR 0656; design 47, *Two graphs, one source of truth*): at each revision one entry per @@ -22,13 +24,24 @@ import ( const CatalogEntryLayout = 1 // CatalogEntryOf is the catalog graph entry of one module: the module graph's node of it as a recompute computed it -// (its build, provision and verb edges, the verb edges left out on a cycle, ADR 0614), with where the trunk holds it and -// its manifest's digest and the main package its process runs (MainOf). The verdict and the acting rules are the revision's to add (they are not the node's). +// (its build, provision and verb edges, the verb edges left out on a cycle, ADR 0614), with where the trunk holds it, +// its manifest's digest and verdict, the main package its process runs (MainOf), the package versions it builds against +// and what the indexer computes its next revision from (Inputs: the lead's ruling on milestone 47/G finding 1), so that +// ModuleValueOf gives the module's value back from the entry alone. The acting rules are the revision's to add. func CatalogEntryOf(module string, cur meshcatalog.ModulesByModuleValue, m catalogue.Manifest, n graph.Node) meshcatalog.CatalogEntry { e := meshcatalog.CatalogEntry{V: CatalogEntryLayout, Module: module, Repository: cur.Repository, Path: cur.Path, - Commit: cur.Commit, Manifest: cur.Manifest, Fp: n.FP, None: n.None, Bases: n.Bases, Packages: n.Packages, - Provides: n.Provides, Requires: n.Requires, Awaits: n.Awaits, Invokes: n.Invokes, Serves: n.Serves, LeftOut: n.LeftOut, - Main: MainOf(m)} + Commit: cur.Commit, Manifest: cur.Manifest, Verdict: cur.Verdict, Fp: n.FP, None: n.None, Bases: n.Bases, + Packages: maps.Clone(cur.Packages), Provides: n.Provides, Requires: n.Requires, Awaits: n.Awaits, Invokes: n.Invokes, + Serves: n.Serves, LeftOut: n.LeftOut, Main: MainOf(m)} + in := meshcatalog.EntryInputs{Toolchains: maps.Clone(cur.Toolchains), Paths: maps.Clone(cur.Paths), Problems: cur.Problems, + Warnings: cur.Warnings, Unknown: cur.Unknown, Src: cur.Src, FpSrc: cur.FpSrc} + if cur.Inputs2 != nil { + from := meshcatalog.FingerprintInputs(*cur.Inputs2) + in.From = &from + } + if !reflect.ValueOf(in).IsZero() { + e.Inputs = &in + } for language, t := range n.Toolchains { if e.Toolchains == nil { e.Toolchains = map[string]meshcatalog.BuildEdge{} @@ -38,6 +51,24 @@ func CatalogEntryOf(module string, cur meshcatalog.ModulesByModuleValue, m catal return e } +// ModuleValueOf is the module's value as the indexer computes with it, read back from its catalog graph entry: the +// inverse of CatalogEntryOf, by which the indexer's view of the trunk is the newest trunk revision's entries (the lead's +// ruling on milestone 47/G finding 1; ADR 0656 ยง6: one form of the fact). The old line's fields (the old-form +// fingerprint, the reader needs) are not kept. +func ModuleValueOf(e meshcatalog.CatalogEntry) meshcatalog.ModulesByModuleValue { + v := meshcatalog.ModulesByModuleValue{V: keys.Version, Repository: e.Repository, Path: e.Path, Commit: e.Commit, Manifest: e.Manifest, + Verdict: e.Verdict, Fp2: e.Fp, None2: e.None, Packages: maps.Clone(e.Packages)} + if in := e.Inputs; in != nil { + v.Toolchains, v.Paths, v.Problems, v.Warnings, v.Unknown = maps.Clone(in.Toolchains), maps.Clone(in.Paths), in.Problems, in.Warnings, in.Unknown + v.Src, v.FpSrc = in.Src, in.FpSrc + if in.From != nil { + from := meshcatalog.ModulesByModuleValueInputs2(*in.From) + v.Inputs2 = &from + } + } + return v +} + // EncodeEntry is an entry's bytes and their sha256 in hex, the name of its object: the same entry in two revisions is // one object (design 47, *An unchanged entry is stored once*). func EncodeEntry(e meshcatalog.CatalogEntry) (string, []byte, error) { diff --git a/modules/mesh-delivery-indexer/internal/index/catalog_revisions.go b/modules/mesh-delivery-indexer/internal/index/catalog_revisions.go index cab54e63..256e16ff 100644 --- a/modules/mesh-delivery-indexer/internal/index/catalog_revisions.go +++ b/modules/mesh-delivery-indexer/internal/index/catalog_revisions.go @@ -176,9 +176,19 @@ func (ix *Indexer) keepTrunkRevision(ctx context.Context, edges map[string]graph if ix.Revisions == nil { return nil } + // Each entry is the module's value as this recompute left it in modules (its fingerprint, toolchains, package versions + // and inputs written), so the revision holds what the next one is computed from. + held, err := ix.heldModules(ctx) + if err != nil { + return err + } entries := make(map[string]meshcatalog.CatalogEntry, len(edges)) for name, e := range edges { - entries[name] = CatalogEntryOf(name, e.cur, e.manifest, whole[name]) + cur, ok := held[name] + if !ok { + cur = e.cur + } + entries[name] = CatalogEntryOf(name, cur, e.manifest, whole[name]) } ix.mu.Lock() src := int64(ix.source.rev) diff --git a/modules/mesh-delivery-indexer/internal/index/toolchains_test.go b/modules/mesh-delivery-indexer/internal/index/toolchains_test.go index db367a8c..8d1d2753 100644 --- a/modules/mesh-delivery-indexer/internal/index/toolchains_test.go +++ b/modules/mesh-delivery-indexer/internal/index/toolchains_test.go @@ -308,6 +308,19 @@ func TestBasesAreComputedFirstAndNoFingerprintTakesTheBuilder(t *testing.T) { // e.Toolchains["go"] != (meshcatalog.BuildEdge{Module: "mesh-build-go", Version: "go1.27.1", Recipe: "1"}) { t.Errorf("the catalog entry of avahi = %+v", e) } + // W3.1's inputs (the lead's ruling on finding 1): an entry carries what the next revision is computed from (the + // fingerprint's inputs, the toolchains used, the package versions), so the module's value read back from it is the + // value modules held, but for the old line's fields. + for name := range mods.vals { + m, _ := moduleOf(t, mods, name) + m.Fp, m.None, m.Needs = "", "", nil + if back := ModuleValueOf(entries[name]); !reflect.DeepEqual(back, m) { + t.Errorf("%s read back from its entry = %+v, want %+v", name, back, m) + } + } + if e := entries["avahi"]; e.Inputs == nil || e.Inputs.From == nil || e.Inputs.Toolchains["go"].Version != "go1.27.1" { + t.Errorf("the catalog entry of avahi carries inputs %+v, want what its fingerprint is computed from", e.Inputs) + } rev, objects, err := CatalogRevisionOf(meshcatalog.CatalogRevision{Revision: 1, Kind: "trunk", Source: 7}, entries) if err != nil || len(rev.Entries) != len(mods.vals) || len(objects) != len(mods.vals) || rev.V != CatalogEntryLayout { t.Fatalf("revision %+v with %d objects (%v), want one entry per module of %d", rev, len(objects), err, len(mods.vals))