CAT-RD3: mesh-build-go asks the indexer's read tool for the manifest a compose ask names, no reads of the indexer

This commit is contained in:
2026-11-04 12:12:01 +01:00
parent 550bb4fdd1
commit d618cbb9ca
4 changed files with 52 additions and 14 deletions
@@ -24,14 +24,16 @@ import (
"sync"
"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep"
stdio "git.novox.be/novox/mesh-sdk/go"
sdkstep "git.novox.be/novox/mesh-sdk/go/buildstep"
"git.novox.be/novox/mesh-sdk/go/composer"
"git.novox.be/novox/mesh-sdk/go/delivery"
)
// ManifestsState is the delivery indexer's manifests by digest, which a compose ask's request names (the module reads
// it, module.json reads).
const ManifestsState = delivery.IndexerModule + ".manifests"
// 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"
// composerLocks hold one mutex per composer key, so a composer two asks need at once is built once.
var composerLocks sync.Map
@@ -69,6 +71,25 @@ func (m *Module) compose(ctx context.Context, fetch buildstep.StepFunc, a delive
return delivery.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.
func askManifest(key string) (json.RawMessage, error) {
raw, err := stdio.Ask(ManifestsTool, map[string]any{"key": key})
if err != nil {
return nil, err
}
var a struct {
Found bool `json:"found"`
Value json.RawMessage `json:"value"`
}
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)
}
if !a.Found {
return nil, nil
}
return a.Value, 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 {
@@ -122,10 +122,11 @@ func TestAComposeAskFailsLoudly(t *testing.T) {
}
}
// 1622: the module reads the delivery indexer's manifests, which a compose ask names by digest.
// 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.Reads, ManifestsState) {
t.Errorf("reads = %v, lacks %s", m.Reads, ManifestsState)
if m := readManifest(t); !slices.Contains(m.Invokes, ManifestsTool) || slices.Contains(m.Reads, delivery.IndexerModule+".manifests") {
t.Errorf("invokes = %v, reads = %v: it asks %s", m.Invokes, m.Reads, ManifestsTool)
}
}
@@ -57,7 +57,7 @@ func main() {
m.RegistryFile = m.Home + "/registry"
m.Settings = m.Home + "/settings.json"
m.ReadKept = readOf(stdio.State(KeptState)).Get
m.ReadManifest = readOf(stdio.State(ManifestsState)).Get
m.ReadManifest = askManifest
m.Probe = ProbeGo(m.Home, m.Cache)
m.Bubblewrap = ProbeBubblewrap
// Outside a launch by the runtime — a build, a check — there is no node and no state to write.