Files
mesh-delivery/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/step.go
T
jschoubben 71a35a6376 MV1: move contracts and sdk under modules/, importers follow
The delivery repository's contracts and SDK live under modules/ as
mesh-delivery-contracts and mesh-delivery-sdk, their Go module paths
renamed to match the folders (the TypeScript names already did), and
every importer in mesh-delivery, mesh-catalog, mesh-host, mesh-tools
and mesh-sdk follows in the same group: a design as it should be, one
path per module, checked by the group's check building every importer.

Builds: design 47 G §427, §88

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 05:09:56 +01:00

102 lines
3.7 KiB
Go

package main
import (
"context"
"encoding/json"
"fmt"
"sync"
"github.com/nats-io/nats.go"
"github.com/nats-io/nats.go/jetstream"
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/clients/meshcatalogclient"
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/seats/meshbuilds"
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-contracts/go/seats/meshcatalog"
"git.novox.be/novox/mesh-sdk/go/process"
"mesh-delivery-builder/internal/build"
)
// **The runner's build step ask is the builder's one entry point for a candidate** (design 47 G A-97, A-84, A-99): the seat
// mesh-builds' accepted kind `step`, heard on its subject, names the entry of one module by fingerprint; the builder reads it through
// the seat mesh-catalog's `entry`, builds it (build.Builder.BuildStep) and says the end as the seat's event step-ended.<node>, the
// ask's id as the event's id, which the runner holds the end to.
// entryWaits answers an entry by fingerprint from the catalog half or the seat's `entry`, waiting for the answer the entries
// keeper is told of (meshcatalogclient.Entries.Changed), never a timer.
type entryWaits struct {
branches *meshcatalogclient.Branches
manifest *entryManifests
mu sync.Mutex
waiting map[string][]chan meshcatalogclient.EntryRow
}
func newEntryWaits(branches *meshcatalogclient.Branches, manifests *entryManifests) *entryWaits {
w := &entryWaits{branches: branches, manifest: manifests, waiting: map[string][]chan meshcatalogclient.EntryRow{}}
prior := branches.Entries.Changed
branches.Entries.Changed = func(row meshcatalogclient.EntryRow) {
w.mu.Lock()
chans := w.waiting[row.Fingerprint]
delete(w.waiting, row.Fingerprint)
w.mu.Unlock()
for _, ch := range chans {
ch <- row
}
if prior != nil {
prior(row)
}
}
return w
}
// entry is build.Builder.StepEntry.
func (w *entryWaits) entry(ctx context.Context, fingerprint string) (meshcatalog.CatalogEntry, error) {
ch := make(chan meshcatalogclient.EntryRow, 1)
w.mu.Lock()
w.waiting[fingerprint] = append(w.waiting[fingerprint], ch)
w.mu.Unlock()
e, state := w.branches.Resolve(ctx, fingerprint)
if state == meshcatalogclient.EntryAsked {
select {
case row := <-ch:
if row.Entry == nil {
return meshcatalog.CatalogEntry{}, fmt.Errorf("the seat mesh-catalog holds no entry of it (%s)", row.State)
}
e, state = *row.Entry, row.State
case <-ctx.Done():
return meshcatalog.CatalogEntry{}, ctx.Err()
}
}
if state != meshcatalogclient.EntryAnswered {
return meshcatalog.CatalogEntry{}, fmt.Errorf("the seat mesh-catalog's entry of it is %s", state)
}
return e, w.manifest.keep(e) // the manifest the build is planned from
}
// serveSteps hears the step asks on the seat's accept subject and answers each one's end, in its own goroutine.
func serveSteps(ctx context.Context, conn *nats.Conn, js jetstream.JetStream, cred process.Account, b *build.Builder, logf func(string, ...any)) error {
sub, err := conn.Subscribe(meshbuilds.AcceptSubjectStep, func(m *nats.Msg) {
id := m.Header.Get("x-event-id")
if id == "" {
id = m.Header.Get(nats.MsgIdHdr)
}
var run meshbuilds.StepRun
if err := json.Unmarshal(m.Data, &run); err != nil || id == "" || run.Step != meshbuilds.StepRunStepBuild {
logf("a step ask on %s that is no build step with an id is left: %v", m.Subject, err)
return
}
go func() {
end := b.BuildStep(ctx, run)
end.ID = id
if err := emitSeatEvent(context.WithoutCancel(ctx), js, cred, meshbuilds.SubjectStepEnded(cred.Node), id, end); err != nil {
logf("saying the end of the build step %s: %v", id, err)
}
}()
})
if err != nil {
return err
}
go func() { <-ctx.Done(); _ = sub.Drain() }()
return nil
}