Files
mesh-delivery/modules/mesh-delivery-sdk/steprun/narrate.go
T
jschoubben 3e61b70517 SD5, BG11, BLD10: the build modules' shared code moves into the delivery SDK
All of mesh-build-go's buildstep Go module leaves mesh-catalog for
modules/mesh-delivery-sdk: the step run, fetch and release and the
regenerate-and-compare step as the package steprun, the CPU share as
steprun/buildcpu, the conformance kit as steprun/conformance, the seat
generator as seatgen with cmd/seatgen, and cmd/mesh-pack. The three
build modules and the builder's and checker's tests import the SDK and
keep no copy; the SDK requires nothing of mesh-catalog. Code the build
modules share is the delivery domain's SDK, not one module's package.
It carries the build-module round's two new files of that package
(dropdetected.go 247c422f, storedbuilds.go 904b8aed).

Builds: design 47 G §464, §680, §327, §420, §495

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

107 lines
3.3 KiB
Go

package steprun
// **A build narrates its asks of a seat** (CAT-NR1; design 51 *Publishing*): asked, waiting on the seat (said again every
// NarrateEvery while it waits), stored, refused. Each line is the ask's phase (phase.go): logged with the ask's id and
// written to its running step key, so the builder and the delivery view read from the bus where a publish or a copy
// stands; outside an ask's run (a late publish, a tool called by hand) it is logged. One place for every build module:
// PublishOne and PublishFiles narrate through it, and a step that waits on a seat's event (an image's copy) uses
// NarrateSeat itself.
import (
"context"
"fmt"
"log"
"time"
)
// NarrateEvery is how often a build says again that it still waits on a seat.
var NarrateEvery = 5 * time.Minute
// narrateTicks is the clock of the waiting lines; a test hands its own.
var narrateTicks = func(every time.Duration) (<-chan time.Time, func()) {
t := time.NewTicker(every)
return t.C, t.Stop
}
// SeatNarration is one ask of a build on a seat, said as it moves.
type SeatNarration struct {
ctx context.Context
seat, what string
asked time.Time
tick <-chan time.Time
stop func()
}
// NarrateSeat says that the build asked seat for what (a verb and its object, such as "publish of nats/server") and
// starts the waiting lines; Stored or Refused ends it.
func NarrateSeat(ctx context.Context, seat, what string) *SeatNarration {
n := &SeatNarration{ctx: ctx, seat: seat, what: what, asked: now()}
n.tick, n.stop = narrateTicks(NarrateEvery)
n.say("asked %s: %s", seat, what)
return n
}
// Waiting is the clock a waiting step selects on beside its outcome; on each tick it calls StillWaiting.
func (n *SeatNarration) Waiting() <-chan time.Time { return n.tick }
// StillWaiting says that the build still waits on the seat, and since when.
func (n *SeatNarration) StillWaiting() {
n.say("waiting on %s: %s, %s since asked", n.seat, n.what, now().Sub(n.asked).Round(time.Second))
}
// Stored says that the seat holds what the build asked it to keep (how: where, or what travelled).
func (n *SeatNarration) Stored(how string) {
n.stop()
if how != "" {
how = " (" + how + ")"
}
n.say("stored on %s: %s%s", n.seat, n.what, how)
}
// Refused says that the seat did not keep it, and why.
func (n *SeatNarration) Refused(err error) {
n.stop()
n.say("refused by %s: %s: %v", n.seat, n.what, err)
}
func (n *SeatNarration) say(format string, a ...any) {
line := fmt.Sprintf(format, a...)
if r, ok := n.ctx.Value(phaseKey{}).(*phases); ok && r != nil {
r.enter(line)
return
}
log.Print(line)
}
// OnSeat runs call as one narrated ask of the build on seat: asked, waiting every NarrateEvery while call runs, then
// stored (how(answer)) or refused (its error).
func OnSeat[T any](ctx context.Context, seat, what string, call func() (T, error), how func(T) string) (T, error) {
n := NarrateSeat(ctx, seat, what)
type answer struct {
v T
err error
}
done := make(chan answer, 1)
go func() {
v, err := call()
done <- answer{v, err}
}()
for {
select {
case a := <-done:
if a.err != nil {
n.Refused(a.err)
} else {
h := ""
if how != nil {
h = how(a.v)
}
n.Stored(h)
}
return a.v, a.err
case <-n.Waiting():
n.StillWaiting()
}
}
}