Merge branch 'feat/47-G-build' into feat/47-G-build-runner
This commit is contained in:
@@ -675,6 +675,8 @@ type Wait struct {
|
||||
Artifact string `json:"artifact,omitempty"`
|
||||
// the watched branch, or the branch of a run on request
|
||||
Branch string `json:"branch"`
|
||||
// the time the bound counts from (design 47 G section 266, section 269, section 498)
|
||||
ClockFrom *time.Time `json:"clock-from,omitempty"`
|
||||
// the commit the module's entry was last changed at
|
||||
Commit string `json:"commit,omitempty"`
|
||||
// the module's build-input fingerprint at the commit
|
||||
|
||||
@@ -39,6 +39,7 @@ const (
|
||||
EventRoom = "room"
|
||||
EventStepEnded = "step-ended"
|
||||
EventVersionDelivered = "version-delivered"
|
||||
EventStepProgress = "step-progress"
|
||||
)
|
||||
|
||||
// Verbs is every verb the seat serves, in its definition's order: a new copy each call.
|
||||
@@ -48,7 +49,7 @@ func Verbs() []string {
|
||||
|
||||
// Events is every event published on the seat's subject: a new copy each call.
|
||||
func Events() []string {
|
||||
return []string{EventPolicyChanged, EventAssignmentRevision, EventDifference, EventWanted, EventWaitHeld, EventWaitEnded, EventFilling, EventFull, EventRoom, EventStepEnded, EventVersionDelivered}
|
||||
return []string{EventPolicyChanged, EventAssignmentRevision, EventDifference, EventWanted, EventWaitHeld, EventWaitEnded, EventFilling, EventFull, EventRoom, EventStepEnded, EventVersionDelivered, EventStepProgress}
|
||||
}
|
||||
|
||||
// Family is where the seat's events are kept: the shared events stream, filtered to the seat's subjects.
|
||||
@@ -99,6 +100,17 @@ const StreamVersionDelivered = "SEAT_mesh-wants_version-delivered"
|
||||
// SubjectVersionDelivered is where version-delivered is published.
|
||||
const SubjectVersionDelivered = "mesh.seat.mesh-wants.event.version-delivered"
|
||||
|
||||
// StreamStepProgress is the stream step-progress is kept in.
|
||||
const StreamStepProgress = "SEAT_mesh-wants_step-progress"
|
||||
|
||||
// SubjectStepProgress is where step-progress is published, on mesh.seat.mesh-wants.event.step-progress.<node>.
|
||||
func SubjectStepProgress(node string) string {
|
||||
return "mesh.seat.mesh-wants.event.step-progress." + node
|
||||
}
|
||||
|
||||
// SubjectStepProgressAll hears every step-progress.
|
||||
const SubjectStepProgressAll = "mesh.seat.mesh-wants.event.step-progress.*"
|
||||
|
||||
// AcceptSubjectStep is where step is sent.
|
||||
const AcceptSubjectStep = "mesh.seat.mesh-wants.accept.step"
|
||||
|
||||
@@ -647,20 +659,43 @@ type Wait struct {
|
||||
Bound string `json:"bound,omitempty"`
|
||||
// on a release, who released it and why
|
||||
By string `json:"by,omitempty"`
|
||||
Fp string `json:"fp,omitempty"`
|
||||
// the time the bound counts from (design 47 G section 266, section 269, section 498)
|
||||
ClockFrom *time.Time `json:"clock-from,omitempty"`
|
||||
// a put's wait: the fingerprint of the build put
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
Fp string `json:"fp,omitempty"`
|
||||
// on wait-ended: ran or released
|
||||
How string `json:"how,omitempty"`
|
||||
// a dependent's delivery wait: the machines the dependency's version has yet to reach
|
||||
Machines []string `json:"machines,omitempty"`
|
||||
// the entry that waits
|
||||
Module string `json:"module"`
|
||||
Node string `json:"node,omitempty"`
|
||||
// the acting part waited for
|
||||
Part string `json:"part"`
|
||||
Part string `json:"part,omitempty"`
|
||||
Since time.Time `json:"since"`
|
||||
Step string `json:"step"`
|
||||
// which wait: an acting step of section 203 or put, delivery, kept-build, backup (design 47 G section 498)
|
||||
Step string `json:"step"`
|
||||
// its new version's fingerprint
|
||||
Version string `json:"version"`
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// The values of Wait's step.
|
||||
const (
|
||||
WaitStepTakeIn = "take-in"
|
||||
WaitStepValidate = "validate"
|
||||
WaitStepCheck = "check"
|
||||
WaitStepBuild = "build"
|
||||
WaitStepCompose = "compose"
|
||||
WaitStepGrant = "grant"
|
||||
WaitStepPlace = "place"
|
||||
WaitStepPipeline = "pipeline"
|
||||
WaitStepPut = "put"
|
||||
WaitStepDelivery = "delivery"
|
||||
WaitStepKeptBuild = "kept-build"
|
||||
WaitStepBackup = "backup"
|
||||
)
|
||||
|
||||
// UpgradeInput is what upgrade takes.
|
||||
type UpgradeInput struct {
|
||||
// "true": each node's own composition, by its digest, and the assigner's findings
|
||||
@@ -823,6 +858,22 @@ type VersionDeliveredEvent struct {
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// StepProgressEvent is the body of the event step-progress.
|
||||
type StepProgressEvent struct {
|
||||
Ask string `json:"ask"`
|
||||
Asker string `json:"asker"`
|
||||
At time.Time `json:"at"`
|
||||
Phase string `json:"phase"`
|
||||
}
|
||||
|
||||
// The values of StepProgressEvent's phase.
|
||||
const (
|
||||
StepProgressEventPhaseFetch = "fetch"
|
||||
StepProgressEventPhaseBuild = "build"
|
||||
StepProgressEventPhaseRun = "run"
|
||||
StepProgressEventPhaseReport = "report"
|
||||
)
|
||||
|
||||
// WantsByWantNodeModuleValue is one value of the state wants under the key want.<node>.<module>.
|
||||
type WantsByWantNodeModuleValue struct {
|
||||
// again or skip
|
||||
|
||||
@@ -43,6 +43,12 @@ func TestFixtures(t *testing.T) {
|
||||
decode: func(b []byte) (any, error) { var v StepEndedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.mesh-wants\\.event\\.step-ended\\.[^.*> ]+$")},
|
||||
{name: "a version delivered with the machines away", subject: "mesh.seat.mesh-wants.event.version-delivered", given: "{\"away\":[\"laptop\"],\"module\":\"mesh-sdk\",\"parts\":{\"mesh-sdk:go\":\"sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\"},\"v\":1,\"version\":\"0.1.15\"}", wire: "{\"away\":[\"laptop\"],\"module\":\"mesh-sdk\",\"parts\":{\"mesh-sdk:go\":\"sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\"},\"v\":1,\"version\":\"0.1.15\"}",
|
||||
decode: func(b []byte) (any, error) { var v VersionDeliveredEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.mesh-wants\\.event\\.version-delivered$")},
|
||||
{name: "a composition step's progress", subject: "mesh.seat.mesh-wants.event.step-progress.ace", given: "{\"ask\":\"a1b2\",\"asker\":\"assigner\",\"at\":\"2026-11-11T10:01:00Z\",\"phase\":\"build\"}", wire: "{\"ask\":\"a1b2\",\"asker\":\"assigner\",\"at\":\"2026-11-11T10:01:00Z\",\"phase\":\"build\"}",
|
||||
decode: func(b []byte) (any, error) { var v StepProgressEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.mesh-wants\\.event\\.step-progress\\.[^.*> ]+$")},
|
||||
{name: "a put without hold waits, counted from its start", subject: "mesh.seat.mesh-wants.event.wait-held", given: "{\"bound\":\"30m\",\"clock-from\":\"2026-11-11T10:00:00Z\",\"fingerprint\":\"fp-0123\",\"module\":\"nats\",\"node\":\"ace\",\"since\":\"2026-11-11T10:00:00Z\",\"step\":\"put\"}", wire: "{\"bound\":\"30m\",\"clock-from\":\"2026-11-11T10:00:00Z\",\"fingerprint\":\"fp-0123\",\"module\":\"nats\",\"node\":\"ace\",\"since\":\"2026-11-11T10:00:00Z\",\"step\":\"put\"}",
|
||||
decode: func(b []byte) (any, error) { var v WaitHeldEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.mesh-wants\\.event\\.wait-held$")},
|
||||
{name: "a dependent's delivery waits for a dependency's version", subject: "mesh.seat.mesh-wants.event.wait-held", given: "{\"bound\":\"60m\",\"clock-from\":\"2026-11-11T09:58:00Z\",\"machines\":[\"ace\",\"g14\"],\"module\":\"nats\",\"part\":\"mesh-sdk\",\"since\":\"2026-11-11T10:00:00Z\",\"step\":\"delivery\",\"version\":\"0.1.4\"}", wire: "{\"bound\":\"60m\",\"clock-from\":\"2026-11-11T09:58:00Z\",\"machines\":[\"ace\",\"g14\"],\"module\":\"nats\",\"part\":\"mesh-sdk\",\"since\":\"2026-11-11T10:00:00Z\",\"step\":\"delivery\",\"version\":\"0.1.4\"}",
|
||||
decode: func(b []byte) (any, error) { var v WaitHeldEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.mesh-wants\\.event\\.wait-held$")},
|
||||
{name: "a want", subject: "", given: "{\"fp\":\"ab12\",\"rule\":3,\"v\":1}", wire: "{\"fp\":\"ab12\",\"rule\":3,\"v\":1}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v WantsByWantNodeModuleValue
|
||||
|
||||
@@ -33,7 +33,9 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
EventStepEnded = "step-ended"
|
||||
EventStepEnded = "step-ended"
|
||||
EventToolchainReported = "toolchain-reported"
|
||||
EventStepProgress = "step-progress"
|
||||
)
|
||||
|
||||
// Verbs is every verb the seat serves, in its definition's order: a new copy each call.
|
||||
@@ -43,7 +45,7 @@ func Verbs() []string {
|
||||
|
||||
// Events is every event published on the seat's subject: a new copy each call.
|
||||
func Events() []string {
|
||||
return []string{EventStepEnded}
|
||||
return []string{EventStepEnded, EventToolchainReported, EventStepProgress}
|
||||
}
|
||||
|
||||
// Family is where the seat's events are kept: the shared events stream, filtered to the seat's subjects.
|
||||
@@ -61,6 +63,28 @@ func SubjectStepEnded(node string) string { return "mesh.seat.node-build-go.even
|
||||
// SubjectStepEndedAll hears every step-ended.
|
||||
const SubjectStepEndedAll = "mesh.seat.node-build-go.event.step-ended.*"
|
||||
|
||||
// StreamToolchainReported is the stream toolchain-reported is kept in.
|
||||
const StreamToolchainReported = "SEAT_node-build-go_toolchain-reported"
|
||||
|
||||
// SubjectToolchainReported is where toolchain-reported is published, on mesh.seat.node-build-go.event.toolchain-reported.<node>.
|
||||
func SubjectToolchainReported(node string) string {
|
||||
return "mesh.seat.node-build-go.event.toolchain-reported." + node
|
||||
}
|
||||
|
||||
// SubjectToolchainReportedAll hears every toolchain-reported.
|
||||
const SubjectToolchainReportedAll = "mesh.seat.node-build-go.event.toolchain-reported.*"
|
||||
|
||||
// StreamStepProgress is the stream step-progress is kept in.
|
||||
const StreamStepProgress = "SEAT_node-build-go_step-progress"
|
||||
|
||||
// SubjectStepProgress is where step-progress is published, on mesh.seat.node-build-go.event.step-progress.<node>.
|
||||
func SubjectStepProgress(node string) string {
|
||||
return "mesh.seat.node-build-go.event.step-progress." + node
|
||||
}
|
||||
|
||||
// SubjectStepProgressAll hears every step-progress.
|
||||
const SubjectStepProgressAll = "mesh.seat.node-build-go.event.step-progress.*"
|
||||
|
||||
// AcceptSubjectDelivery is where delivery is sent.
|
||||
const AcceptSubjectDelivery = "mesh.seat.node-build-go.accept.delivery"
|
||||
|
||||
@@ -86,6 +110,47 @@ const (
|
||||
StateDetectedSteps = "detected-steps"
|
||||
)
|
||||
|
||||
// StepsByAskerNodeIDKey is the key of one value of the state steps under the form "<asker>.<node>.<id>".
|
||||
func StepsByAskerNodeIDKey(asker, node, id string) string { return asker + "." + node + "." + id }
|
||||
|
||||
// ParseStepsByAskerNodeIDKey reads the parts of a key of the form "<asker>.<node>.<id>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseStepsByAskerNodeIDKey(key string) (string, string, string, bool) {
|
||||
rest := key
|
||||
v0, after0, found0 := strings.Cut(rest, ".")
|
||||
if !found0 || v0 == "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
rest = after0
|
||||
v2, after2, found2 := strings.Cut(rest, ".")
|
||||
if !found2 || v2 == "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
rest = after2
|
||||
if rest == "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
v4 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
return v0, v2, v4, true
|
||||
}
|
||||
|
||||
// DecodeStepsByAskerNodeIDValue reads one value of the state steps under the form "<asker>.<node>.<id>", refusing a field StepsByAskerNodeIDValue does not know and any text after the value.
|
||||
func DecodeStepsByAskerNodeIDValue(raw []byte) (StepsByAskerNodeIDValue, error) {
|
||||
var v StepsByAskerNodeIDValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// StepsByNodeIDKey is the key of one value of the state steps under the form "<node>.<id>".
|
||||
func StepsByNodeIDKey(node, id string) string { return node + "." + id }
|
||||
|
||||
@@ -317,11 +382,21 @@ type BuildAnswer struct {
|
||||
Toolchain *ToolchainUsed `json:"toolchain,omitempty"`
|
||||
}
|
||||
|
||||
// BuiltArtifactResolvedItem is a type of the contract.
|
||||
type BuiltArtifactResolvedItem struct {
|
||||
// sha256: of the package's bytes as the build fetched them
|
||||
Digest string `json:"digest"`
|
||||
Package string `json:"package"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// BuiltArtifact is one artifact a build made: its digest and the reference it was published under.
|
||||
type BuiltArtifact struct {
|
||||
Artifact string `json:"artifact"`
|
||||
Digest string `json:"digest"`
|
||||
Reference string `json:"reference,omitempty"`
|
||||
// one per mesh package the build resolved (the catalog graph entry's packages, the edges of kind 1)
|
||||
Resolved []BuiltArtifactResolvedItem `json:"resolved,omitempty"`
|
||||
}
|
||||
|
||||
// Capacity is how many steps the node runs at once.
|
||||
@@ -396,6 +471,16 @@ type FetchBesideItem struct {
|
||||
Repository string `json:"repository"`
|
||||
}
|
||||
|
||||
// FetchBuildsItem is a type of the contract.
|
||||
type FetchBuildsItem struct {
|
||||
Artifact string `json:"artifact"`
|
||||
// the member's build digest, sha256:
|
||||
Digest string `json:"digest"`
|
||||
Module string `json:"module"`
|
||||
// the name in its ecosystem (the Go module path, the npm name)
|
||||
Package string `json:"package"`
|
||||
}
|
||||
|
||||
// FetchPackage is the TypeScript module's group package: the group's tagged version of the SDK ({name, version}), placed from the registry in each member whatever its lock names, or the group's package member ({repository, commit, ref}), packed by the fetch and served to the members in place of the registry's.
|
||||
type FetchPackage struct {
|
||||
Commit string `json:"commit,omitempty"`
|
||||
@@ -411,6 +496,8 @@ type Fetch struct {
|
||||
Base string `json:"base,omitempty"`
|
||||
// the sibling repositories placed beside the fetched tree
|
||||
Beside []FetchBesideItem `json:"beside,omitempty"`
|
||||
// a group run's package members, each by its build's digest: the build module reads each from the artifact store at artifact-store://<module>/<artifact>/blobs/<digest>, checks the bytes against the digest and lays them as the group package; a package named here is named in neither packages nor package
|
||||
Builds []FetchBuildsItem `json:"builds,omitempty"`
|
||||
// the full commit
|
||||
Commit string `json:"commit"`
|
||||
// the module's directory in the repository ("." for its root), with paths
|
||||
@@ -735,6 +822,22 @@ type StepNames struct {
|
||||
Repositories []string `json:"repositories"`
|
||||
}
|
||||
|
||||
// StepProgress is a running ask's progress, said once a minute while the holder runs it (design 47 G section 496).
|
||||
type StepProgress struct {
|
||||
Ask string `json:"ask"`
|
||||
Asker string `json:"asker"`
|
||||
At time.Time `json:"at"`
|
||||
Phase string `json:"phase"`
|
||||
}
|
||||
|
||||
// The values of StepProgress's phase.
|
||||
const (
|
||||
StepProgressPhaseFetch = "fetch"
|
||||
StepProgressPhaseBuild = "build"
|
||||
StepProgressPhaseRun = "run"
|
||||
StepProgressPhaseReport = "report"
|
||||
)
|
||||
|
||||
// StepResult is what a step answered, by the kind of ask: a build's artifacts, toolchain, imports, out-of-step modules and the artifact it kept; a check's fetch and levels; a compose's built, key and verdict; a run's built, key, exit, output and said; a declined ask's declined, needs and lacks. The fields of the kinds do not meet, so one layout holds the five.
|
||||
type StepResult struct {
|
||||
Artifacts []BuiltArtifact `json:"artifacts,omitempty"`
|
||||
@@ -828,8 +931,20 @@ type Toolchain struct {
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// ToolchainReported is a node's toolchain of a language, said at the build module's start and each time its toolchains key of the language changes (design 47 G section 492).
|
||||
type ToolchainReported struct {
|
||||
// the digest of the program of the provision the build module requires for that language's toolchain, never the build module's own
|
||||
Digest string `json:"digest"`
|
||||
Language string `json:"language"`
|
||||
Node string `json:"node"`
|
||||
Recipe string `json:"recipe"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// ToolchainUsed is the toolchain a build was made with: the language, the installed version and the build module's recipe.
|
||||
type ToolchainUsed struct {
|
||||
// the building node's digest of its toolchain program (kind 3's entry), never the build module's own program
|
||||
Digest string `json:"digest,omitempty"`
|
||||
Language string `json:"language"`
|
||||
Recipe string `json:"recipe"`
|
||||
Version string `json:"version"`
|
||||
@@ -912,20 +1027,28 @@ const (
|
||||
type StepsInput struct {
|
||||
// find them anew, not from the kept answer
|
||||
Again bool `json:"again,omitempty"`
|
||||
// the artifact reference, <module>:<artifact>
|
||||
Artifact string `json:"artifact"`
|
||||
Fetch Fetch `json:"fetch"`
|
||||
// the key of detected-steps, <module>.<artifact>
|
||||
Artifact string `json:"artifact"`
|
||||
// delete the artifact's kept key instead of detecting; needs why
|
||||
Drop bool `json:"drop,omitempty"`
|
||||
Fetch *Fetch `json:"fetch,omitempty"`
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
Module string `json:"module"`
|
||||
// with drop, why: a non-empty text, written in the hand-act log with the caller the bus proved
|
||||
Why string `json:"why,omitempty"`
|
||||
}
|
||||
|
||||
// StepsAnswer is what steps answers.
|
||||
type StepsAnswer struct {
|
||||
Artifact string `json:"artifact"`
|
||||
Artifact string `json:"artifact,omitempty"`
|
||||
// the artifact whose key a drop deleted
|
||||
Dropped string `json:"dropped,omitempty"`
|
||||
// why a drop deleted nothing, naming the hand-act log when it refused
|
||||
Error string `json:"error,omitempty"`
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
// answered from the kept answer
|
||||
Kept bool `json:"kept,omitempty"`
|
||||
Steps []DetectedStep `json:"steps"`
|
||||
Steps []DetectedStep `json:"steps,omitempty"`
|
||||
}
|
||||
|
||||
// DeliveryAccept is the body of delivery.
|
||||
@@ -949,6 +1072,15 @@ type CheckRunAccept = StepAsk
|
||||
// StepEndedEvent is the body of the event step-ended.
|
||||
type StepEndedEvent = StepEnded
|
||||
|
||||
// ToolchainReportedEvent is the body of the event toolchain-reported.
|
||||
type ToolchainReportedEvent = ToolchainReported
|
||||
|
||||
// StepProgressEvent is the body of the event step-progress.
|
||||
type StepProgressEvent = StepProgress
|
||||
|
||||
// StepsByAskerNodeIDValue is one value of the state steps under the key <asker>.<node>.<id>.
|
||||
type StepsByAskerNodeIDValue = Step
|
||||
|
||||
// StepsByNodeIDValue is one value of the state steps under the key <node>.<id>.
|
||||
type StepsByNodeIDValue = Step
|
||||
|
||||
|
||||
@@ -21,6 +21,10 @@ func TestFixtures(t *testing.T) {
|
||||
decode: func(b []byte) (any, error) { var v StepEndedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-build-go\\.event\\.step-ended\\.[^.*> ]+$")},
|
||||
{name: "an integration step that could not run, naming the part not heard", subject: "mesh.seat.node-build-go.event.step-ended.g14", given: "{\"artifact\":\"nats:program\",\"commit\":\"0123456789abcdef0123456789abcdef01234567\",\"fingerprint\":\"fp-0123\",\"id\":\"m2kx9a1b3c4d5f\",\"module\":\"nats\",\"node\":\"g14\",\"outcome\":\"could not run\",\"output-object\":\"mesh-build-go_sent-objects/m2kx9a1b3c4d5f\",\"pipeline\":\"main.12.nats\",\"said\":\"the sandbox could not fetch novox/mesh-sdk\",\"step\":\"integration\",\"v\":1}", wire: "{\"artifact\":\"nats:program\",\"commit\":\"0123456789abcdef0123456789abcdef01234567\",\"fingerprint\":\"fp-0123\",\"id\":\"m2kx9a1b3c4d5f\",\"module\":\"nats\",\"node\":\"g14\",\"outcome\":\"could not run\",\"output-object\":\"mesh-build-go_sent-objects/m2kx9a1b3c4d5f\",\"pipeline\":\"main.12.nats\",\"said\":\"the sandbox could not fetch novox/mesh-sdk\",\"step\":\"integration\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) { var v StepEndedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-build-go\\.event\\.step-ended\\.[^.*> ]+$")},
|
||||
{name: "a node's toolchain reported", subject: "mesh.seat.node-build-go.event.toolchain-reported.ace", given: "{\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"language\":\"go\",\"node\":\"ace\",\"recipe\":\"1\",\"version\":\"1.26.0\"}", wire: "{\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"language\":\"go\",\"node\":\"ace\",\"recipe\":\"1\",\"version\":\"1.26.0\"}",
|
||||
decode: func(b []byte) (any, error) { var v ToolchainReportedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-build-go\\.event\\.toolchain-reported\\.[^.*> ]+$")},
|
||||
{name: "a running ask says its progress", subject: "mesh.seat.node-build-go.event.step-progress.ace", given: "{\"ask\":\"a1b2\",\"asker\":\"runner\",\"at\":\"2026-11-11T10:01:00Z\",\"phase\":\"fetch\"}", wire: "{\"ask\":\"a1b2\",\"asker\":\"runner\",\"at\":\"2026-11-11T10:01:00Z\",\"phase\":\"fetch\"}",
|
||||
decode: func(b []byte) (any, error) { var v StepProgressEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-build-go\\.event\\.step-progress\\.[^.*> ]+$")},
|
||||
{name: "a running step", subject: "", given: "{\"id\":\"a1b2\",\"node\":\"ace\",\"started\":\"2026-11-05T10:00:00Z\",\"state\":\"running\",\"tool\":\"build\",\"v\":1}", wire: "{\"id\":\"a1b2\",\"node\":\"ace\",\"started\":\"2026-11-05T10:00:00Z\",\"state\":\"running\",\"tool\":\"build\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) { var v StepsByNodeIDValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a paused node", subject: "", given: "{\"at\":\"2026-11-05T10:00:00Z\",\"by\":\"operator@shanks\",\"paused\":true,\"v\":1}", wire: "{\"at\":\"2026-11-05T10:00:00Z\",\"by\":\"operator@shanks\",\"paused\":true,\"v\":1}",
|
||||
@@ -75,6 +79,12 @@ func TestFixtures(t *testing.T) {
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
{name: "a step keyed under its asker", subject: "", given: "{\"id\":\"a1b2\",\"node\":\"ace\",\"started\":\"2026-11-05T10:00:00Z\",\"state\":\"running\",\"tool\":\"build\",\"v\":1}", wire: "{\"id\":\"a1b2\",\"node\":\"ace\",\"started\":\"2026-11-05T10:00:00Z\",\"state\":\"running\",\"tool\":\"build\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v StepsByAskerNodeIDValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
{name: "drop names the ask and why", subject: "", given: "{\"by\":\"mesh-delivery-builder\",\"id\":\"a1b2\",\"why\":\"a newer fingerprint supersedes it\"}", wire: "{\"by\":\"mesh-delivery-builder\",\"id\":\"a1b2\",\"why\":\"a newer fingerprint supersedes it\"}",
|
||||
decode: func(b []byte) (any, error) { var v DropInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "kept-artifact asks", subject: "", given: "{\"digest\":\"dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd\",\"fp\":\"src2:1480985e\",\"module\":\"artifact-store\"}", wire: "{\"digest\":\"dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd\",\"fp\":\"src2:1480985e\",\"module\":\"artifact-store\"}",
|
||||
@@ -93,6 +103,12 @@ func TestFixtures(t *testing.T) {
|
||||
decode: func(b []byte) (any, error) { var v StepsInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "steps answers lint and unit tests", subject: "", given: "{\"artifact\":\"nats:program\",\"fingerprint\":\"fp-0123\",\"steps\":[{\"step\":\"lint\"},{\"packages\":[\"./modules/nats\"],\"step\":\"unit\"}]}", wire: "{\"artifact\":\"nats:program\",\"fingerprint\":\"fp-0123\",\"steps\":[{\"step\":\"lint\"},{\"packages\":[\"./modules/nats\"],\"step\":\"unit\"}]}",
|
||||
decode: func(b []byte) (any, error) { var v StepsAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "steps drops an artifact's kept steps with a reason", subject: "", given: "{\"artifact\":\"nats.program\",\"drop\":true,\"module\":\"nats\",\"why\":\"the detection was wrong\"}", wire: "{\"artifact\":\"nats.program\",\"drop\":true,\"module\":\"nats\",\"why\":\"the detection was wrong\"}",
|
||||
decode: func(b []byte) (any, error) { var v StepsInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "steps answers a drop with the artifact", subject: "", given: "{\"dropped\":\"nats.program\"}", wire: "{\"dropped\":\"nats.program\"}",
|
||||
decode: func(b []byte) (any, error) { var v StepsAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "steps answers a drop that the hand-act log refused", subject: "", given: "{\"error\":\"the hand-act log refused: nothing was deleted\"}", wire: "{\"error\":\"the hand-act log refused: nothing was deleted\"}",
|
||||
decode: func(b []byte) (any, error) { var v StepsAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
}
|
||||
for _, c := range cases {
|
||||
v, err := c.decode([]byte(c.given))
|
||||
|
||||
@@ -32,7 +32,9 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
EventStepEnded = "step-ended"
|
||||
EventStepEnded = "step-ended"
|
||||
EventToolchainReported = "toolchain-reported"
|
||||
EventStepProgress = "step-progress"
|
||||
)
|
||||
|
||||
// Verbs is every verb the seat serves, in its definition's order: a new copy each call.
|
||||
@@ -42,7 +44,7 @@ func Verbs() []string {
|
||||
|
||||
// Events is every event published on the seat's subject: a new copy each call.
|
||||
func Events() []string {
|
||||
return []string{EventStepEnded}
|
||||
return []string{EventStepEnded, EventToolchainReported, EventStepProgress}
|
||||
}
|
||||
|
||||
// Family is where the seat's events are kept: the shared events stream, filtered to the seat's subjects.
|
||||
@@ -62,6 +64,28 @@ func SubjectStepEnded(node string) string {
|
||||
// SubjectStepEndedAll hears every step-ended.
|
||||
const SubjectStepEndedAll = "mesh.seat.node-build-image.event.step-ended.*"
|
||||
|
||||
// StreamToolchainReported is the stream toolchain-reported is kept in.
|
||||
const StreamToolchainReported = "SEAT_node-build-image_toolchain-reported"
|
||||
|
||||
// SubjectToolchainReported is where toolchain-reported is published, on mesh.seat.node-build-image.event.toolchain-reported.<node>.
|
||||
func SubjectToolchainReported(node string) string {
|
||||
return "mesh.seat.node-build-image.event.toolchain-reported." + node
|
||||
}
|
||||
|
||||
// SubjectToolchainReportedAll hears every toolchain-reported.
|
||||
const SubjectToolchainReportedAll = "mesh.seat.node-build-image.event.toolchain-reported.*"
|
||||
|
||||
// StreamStepProgress is the stream step-progress is kept in.
|
||||
const StreamStepProgress = "SEAT_node-build-image_step-progress"
|
||||
|
||||
// SubjectStepProgress is where step-progress is published, on mesh.seat.node-build-image.event.step-progress.<node>.
|
||||
func SubjectStepProgress(node string) string {
|
||||
return "mesh.seat.node-build-image.event.step-progress." + node
|
||||
}
|
||||
|
||||
// SubjectStepProgressAll hears every step-progress.
|
||||
const SubjectStepProgressAll = "mesh.seat.node-build-image.event.step-progress.*"
|
||||
|
||||
// AcceptSubjectDelivery is where delivery is sent.
|
||||
const AcceptSubjectDelivery = "mesh.seat.node-build-image.accept.delivery"
|
||||
|
||||
@@ -84,6 +108,47 @@ const (
|
||||
StateDetectedSteps = "detected-steps"
|
||||
)
|
||||
|
||||
// StepsByAskerNodeIDKey is the key of one value of the state steps under the form "<asker>.<node>.<id>".
|
||||
func StepsByAskerNodeIDKey(asker, node, id string) string { return asker + "." + node + "." + id }
|
||||
|
||||
// ParseStepsByAskerNodeIDKey reads the parts of a key of the form "<asker>.<node>.<id>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseStepsByAskerNodeIDKey(key string) (string, string, string, bool) {
|
||||
rest := key
|
||||
v0, after0, found0 := strings.Cut(rest, ".")
|
||||
if !found0 || v0 == "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
rest = after0
|
||||
v2, after2, found2 := strings.Cut(rest, ".")
|
||||
if !found2 || v2 == "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
rest = after2
|
||||
if rest == "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
v4 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
return v0, v2, v4, true
|
||||
}
|
||||
|
||||
// DecodeStepsByAskerNodeIDValue reads one value of the state steps under the form "<asker>.<node>.<id>", refusing a field StepsByAskerNodeIDValue does not know and any text after the value.
|
||||
func DecodeStepsByAskerNodeIDValue(raw []byte) (StepsByAskerNodeIDValue, error) {
|
||||
var v StepsByAskerNodeIDValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// StepsByNodeIDKey is the key of one value of the state steps under the form "<node>.<id>".
|
||||
func StepsByNodeIDKey(node, id string) string { return node + "." + id }
|
||||
|
||||
@@ -315,11 +380,21 @@ type BuildAnswer struct {
|
||||
Toolchain *ToolchainUsed `json:"toolchain,omitempty"`
|
||||
}
|
||||
|
||||
// BuiltArtifactResolvedItem is a type of the contract.
|
||||
type BuiltArtifactResolvedItem struct {
|
||||
// sha256: of the package's bytes as the build fetched them
|
||||
Digest string `json:"digest"`
|
||||
Package string `json:"package"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// BuiltArtifact is one artifact a build made: its digest and the reference it was published under.
|
||||
type BuiltArtifact struct {
|
||||
Artifact string `json:"artifact"`
|
||||
Digest string `json:"digest"`
|
||||
Reference string `json:"reference,omitempty"`
|
||||
// one per mesh package the build resolved (the catalog graph entry's packages, the edges of kind 1)
|
||||
Resolved []BuiltArtifactResolvedItem `json:"resolved,omitempty"`
|
||||
}
|
||||
|
||||
// Capacity is how many steps the node runs at once.
|
||||
@@ -394,6 +469,16 @@ type FetchBesideItem struct {
|
||||
Repository string `json:"repository"`
|
||||
}
|
||||
|
||||
// FetchBuildsItem is a type of the contract.
|
||||
type FetchBuildsItem struct {
|
||||
Artifact string `json:"artifact"`
|
||||
// the member's build digest, sha256:
|
||||
Digest string `json:"digest"`
|
||||
Module string `json:"module"`
|
||||
// the name in its ecosystem (the Go module path, the npm name)
|
||||
Package string `json:"package"`
|
||||
}
|
||||
|
||||
// FetchPackage is the TypeScript module's group package: the group's tagged version of the SDK ({name, version}), placed from the registry in each member whatever its lock names, or the group's package member ({repository, commit, ref}), packed by the fetch and served to the members in place of the registry's.
|
||||
type FetchPackage struct {
|
||||
Commit string `json:"commit,omitempty"`
|
||||
@@ -409,6 +494,8 @@ type Fetch struct {
|
||||
Base string `json:"base,omitempty"`
|
||||
// the sibling repositories placed beside the fetched tree
|
||||
Beside []FetchBesideItem `json:"beside,omitempty"`
|
||||
// a group run's package members, each by its build's digest: the build module reads each from the artifact store at artifact-store://<module>/<artifact>/blobs/<digest>, checks the bytes against the digest and lays them as the group package; a package named here is named in neither packages nor package
|
||||
Builds []FetchBuildsItem `json:"builds,omitempty"`
|
||||
// the full commit
|
||||
Commit string `json:"commit"`
|
||||
// the module's directory in the repository ("." for its root), with paths
|
||||
@@ -733,6 +820,22 @@ type StepNames struct {
|
||||
Repositories []string `json:"repositories"`
|
||||
}
|
||||
|
||||
// StepProgress is a running ask's progress, said once a minute while the holder runs it (design 47 G section 496).
|
||||
type StepProgress struct {
|
||||
Ask string `json:"ask"`
|
||||
Asker string `json:"asker"`
|
||||
At time.Time `json:"at"`
|
||||
Phase string `json:"phase"`
|
||||
}
|
||||
|
||||
// The values of StepProgress's phase.
|
||||
const (
|
||||
StepProgressPhaseFetch = "fetch"
|
||||
StepProgressPhaseBuild = "build"
|
||||
StepProgressPhaseRun = "run"
|
||||
StepProgressPhaseReport = "report"
|
||||
)
|
||||
|
||||
// StepResult is what a step answered, by the kind of ask: a build's artifacts, toolchain, imports, out-of-step modules and the artifact it kept; a check's fetch and levels; a compose's built, key and verdict; a run's built, key, exit, output and said; a declined ask's declined, needs and lacks. The fields of the kinds do not meet, so one layout holds the five.
|
||||
type StepResult struct {
|
||||
Artifacts []BuiltArtifact `json:"artifacts,omitempty"`
|
||||
@@ -826,8 +929,20 @@ type Toolchain struct {
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// ToolchainReported is a node's toolchain of a language, said at the build module's start and each time its toolchains key of the language changes (design 47 G section 492).
|
||||
type ToolchainReported struct {
|
||||
// the digest of the program of the provision the build module requires for that language's toolchain, never the build module's own
|
||||
Digest string `json:"digest"`
|
||||
Language string `json:"language"`
|
||||
Node string `json:"node"`
|
||||
Recipe string `json:"recipe"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// ToolchainUsed is the toolchain a build was made with: the language, the installed version and the build module's recipe.
|
||||
type ToolchainUsed struct {
|
||||
// the building node's digest of its toolchain program (kind 3's entry), never the build module's own program
|
||||
Digest string `json:"digest,omitempty"`
|
||||
Language string `json:"language"`
|
||||
Recipe string `json:"recipe"`
|
||||
Version string `json:"version"`
|
||||
@@ -910,20 +1025,28 @@ const (
|
||||
type StepsInput struct {
|
||||
// find them anew, not from the kept answer
|
||||
Again bool `json:"again,omitempty"`
|
||||
// the artifact reference, <module>:<artifact>
|
||||
Artifact string `json:"artifact"`
|
||||
Fetch Fetch `json:"fetch"`
|
||||
// the key of detected-steps, <module>.<artifact>
|
||||
Artifact string `json:"artifact"`
|
||||
// delete the artifact's kept key instead of detecting; needs why
|
||||
Drop bool `json:"drop,omitempty"`
|
||||
Fetch *Fetch `json:"fetch,omitempty"`
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
Module string `json:"module"`
|
||||
// with drop, why: a non-empty text, written in the hand-act log with the caller the bus proved
|
||||
Why string `json:"why,omitempty"`
|
||||
}
|
||||
|
||||
// StepsAnswer is what steps answers.
|
||||
type StepsAnswer struct {
|
||||
Artifact string `json:"artifact"`
|
||||
Artifact string `json:"artifact,omitempty"`
|
||||
// the artifact whose key a drop deleted
|
||||
Dropped string `json:"dropped,omitempty"`
|
||||
// why a drop deleted nothing, naming the hand-act log when it refused
|
||||
Error string `json:"error,omitempty"`
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
// answered from the kept answer
|
||||
Kept bool `json:"kept,omitempty"`
|
||||
Steps []DetectedStep `json:"steps"`
|
||||
Steps []DetectedStep `json:"steps,omitempty"`
|
||||
}
|
||||
|
||||
// DeliveryAccept is the body of delivery.
|
||||
@@ -944,6 +1067,15 @@ type RecheckAccept = StepAsk
|
||||
// StepEndedEvent is the body of the event step-ended.
|
||||
type StepEndedEvent = StepEnded
|
||||
|
||||
// ToolchainReportedEvent is the body of the event toolchain-reported.
|
||||
type ToolchainReportedEvent = ToolchainReported
|
||||
|
||||
// StepProgressEvent is the body of the event step-progress.
|
||||
type StepProgressEvent = StepProgress
|
||||
|
||||
// StepsByAskerNodeIDValue is one value of the state steps under the key <asker>.<node>.<id>.
|
||||
type StepsByAskerNodeIDValue = Step
|
||||
|
||||
// StepsByNodeIDValue is one value of the state steps under the key <node>.<id>.
|
||||
type StepsByNodeIDValue = Step
|
||||
|
||||
|
||||
@@ -21,6 +21,10 @@ func TestFixtures(t *testing.T) {
|
||||
decode: func(b []byte) (any, error) { var v StepEndedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-build-image\\.event\\.step-ended\\.[^.*> ]+$")},
|
||||
{name: "a lint step that could not run, naming the part not heard", subject: "mesh.seat.node-build-image.event.step-ended.g14", given: "{\"artifact\":\"nats:program\",\"commit\":\"0123456789abcdef0123456789abcdef01234567\",\"fingerprint\":\"fp-0123\",\"id\":\"m2kx9a1b3c4d5f\",\"module\":\"nats\",\"node\":\"g14\",\"outcome\":\"could not run\",\"output-object\":\"mesh-build-go_sent-objects/m2kx9a1b3c4d5f\",\"pipeline\":\"main.12.nats\",\"said\":\"the sandbox could not fetch novox/mesh-sdk\",\"step\":\"lint\",\"v\":1}", wire: "{\"artifact\":\"nats:program\",\"commit\":\"0123456789abcdef0123456789abcdef01234567\",\"fingerprint\":\"fp-0123\",\"id\":\"m2kx9a1b3c4d5f\",\"module\":\"nats\",\"node\":\"g14\",\"outcome\":\"could not run\",\"output-object\":\"mesh-build-go_sent-objects/m2kx9a1b3c4d5f\",\"pipeline\":\"main.12.nats\",\"said\":\"the sandbox could not fetch novox/mesh-sdk\",\"step\":\"lint\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) { var v StepEndedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-build-image\\.event\\.step-ended\\.[^.*> ]+$")},
|
||||
{name: "a node's toolchain reported", subject: "mesh.seat.node-build-image.event.toolchain-reported.ace", given: "{\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"language\":\"go\",\"node\":\"ace\",\"recipe\":\"1\",\"version\":\"1.26.0\"}", wire: "{\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"language\":\"go\",\"node\":\"ace\",\"recipe\":\"1\",\"version\":\"1.26.0\"}",
|
||||
decode: func(b []byte) (any, error) { var v ToolchainReportedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-build-image\\.event\\.toolchain-reported\\.[^.*> ]+$")},
|
||||
{name: "a running ask says its progress", subject: "mesh.seat.node-build-image.event.step-progress.ace", given: "{\"ask\":\"a1b2\",\"asker\":\"runner\",\"at\":\"2026-11-11T10:01:00Z\",\"phase\":\"fetch\"}", wire: "{\"ask\":\"a1b2\",\"asker\":\"runner\",\"at\":\"2026-11-11T10:01:00Z\",\"phase\":\"fetch\"}",
|
||||
decode: func(b []byte) (any, error) { var v StepProgressEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-build-image\\.event\\.step-progress\\.[^.*> ]+$")},
|
||||
{name: "a running step", subject: "", given: "{\"id\":\"a1b2\",\"node\":\"ace\",\"started\":\"2026-11-05T10:00:00Z\",\"state\":\"running\",\"tool\":\"build\",\"v\":1}", wire: "{\"id\":\"a1b2\",\"node\":\"ace\",\"started\":\"2026-11-05T10:00:00Z\",\"state\":\"running\",\"tool\":\"build\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) { var v StepsByNodeIDValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a paused node", subject: "", given: "{\"at\":\"2026-11-05T10:00:00Z\",\"by\":\"operator@shanks\",\"paused\":true,\"v\":1}", wire: "{\"at\":\"2026-11-05T10:00:00Z\",\"by\":\"operator@shanks\",\"paused\":true,\"v\":1}",
|
||||
@@ -57,6 +61,12 @@ func TestFixtures(t *testing.T) {
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
{name: "a step keyed under its asker", subject: "", given: "{\"id\":\"a1b2\",\"node\":\"ace\",\"started\":\"2026-11-05T10:00:00Z\",\"state\":\"running\",\"tool\":\"build\",\"v\":1}", wire: "{\"id\":\"a1b2\",\"node\":\"ace\",\"started\":\"2026-11-05T10:00:00Z\",\"state\":\"running\",\"tool\":\"build\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v StepsByAskerNodeIDValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
{name: "drop names the ask and why", subject: "", given: "{\"by\":\"mesh-delivery-builder\",\"id\":\"a1b2\",\"why\":\"a newer fingerprint supersedes it\"}", wire: "{\"by\":\"mesh-delivery-builder\",\"id\":\"a1b2\",\"why\":\"a newer fingerprint supersedes it\"}",
|
||||
decode: func(b []byte) (any, error) { var v DropInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "kept-artifact asks", subject: "", given: "{\"digest\":\"dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd\",\"fp\":\"src2:1480985e\",\"module\":\"artifact-store\"}", wire: "{\"digest\":\"dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd\",\"fp\":\"src2:1480985e\",\"module\":\"artifact-store\"}",
|
||||
@@ -75,6 +85,12 @@ func TestFixtures(t *testing.T) {
|
||||
decode: func(b []byte) (any, error) { var v StepsInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "steps answers lint and unit tests", subject: "", given: "{\"artifact\":\"nats:program\",\"fingerprint\":\"fp-0123\",\"steps\":[{\"step\":\"lint\"},{\"packages\":[\"./modules/nats\"],\"step\":\"unit\"}]}", wire: "{\"artifact\":\"nats:program\",\"fingerprint\":\"fp-0123\",\"steps\":[{\"step\":\"lint\"},{\"packages\":[\"./modules/nats\"],\"step\":\"unit\"}]}",
|
||||
decode: func(b []byte) (any, error) { var v StepsAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "steps drops an artifact's kept steps with a reason", subject: "", given: "{\"artifact\":\"nats.program\",\"drop\":true,\"module\":\"nats\",\"why\":\"the detection was wrong\"}", wire: "{\"artifact\":\"nats.program\",\"drop\":true,\"module\":\"nats\",\"why\":\"the detection was wrong\"}",
|
||||
decode: func(b []byte) (any, error) { var v StepsInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "steps answers a drop with the artifact", subject: "", given: "{\"dropped\":\"nats.program\"}", wire: "{\"dropped\":\"nats.program\"}",
|
||||
decode: func(b []byte) (any, error) { var v StepsAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "steps answers a drop that the hand-act log refused", subject: "", given: "{\"error\":\"the hand-act log refused: nothing was deleted\"}", wire: "{\"error\":\"the hand-act log refused: nothing was deleted\"}",
|
||||
decode: func(b []byte) (any, error) { var v StepsAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
}
|
||||
for _, c := range cases {
|
||||
v, err := c.decode([]byte(c.given))
|
||||
|
||||
@@ -32,7 +32,9 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
EventStepEnded = "step-ended"
|
||||
EventStepEnded = "step-ended"
|
||||
EventToolchainReported = "toolchain-reported"
|
||||
EventStepProgress = "step-progress"
|
||||
)
|
||||
|
||||
// Verbs is every verb the seat serves, in its definition's order: a new copy each call.
|
||||
@@ -42,7 +44,7 @@ func Verbs() []string {
|
||||
|
||||
// Events is every event published on the seat's subject: a new copy each call.
|
||||
func Events() []string {
|
||||
return []string{EventStepEnded}
|
||||
return []string{EventStepEnded, EventToolchainReported, EventStepProgress}
|
||||
}
|
||||
|
||||
// Family is where the seat's events are kept: the shared events stream, filtered to the seat's subjects.
|
||||
@@ -62,6 +64,28 @@ func SubjectStepEnded(node string) string {
|
||||
// SubjectStepEndedAll hears every step-ended.
|
||||
const SubjectStepEndedAll = "mesh.seat.node-build-node.event.step-ended.*"
|
||||
|
||||
// StreamToolchainReported is the stream toolchain-reported is kept in.
|
||||
const StreamToolchainReported = "SEAT_node-build-node_toolchain-reported"
|
||||
|
||||
// SubjectToolchainReported is where toolchain-reported is published, on mesh.seat.node-build-node.event.toolchain-reported.<node>.
|
||||
func SubjectToolchainReported(node string) string {
|
||||
return "mesh.seat.node-build-node.event.toolchain-reported." + node
|
||||
}
|
||||
|
||||
// SubjectToolchainReportedAll hears every toolchain-reported.
|
||||
const SubjectToolchainReportedAll = "mesh.seat.node-build-node.event.toolchain-reported.*"
|
||||
|
||||
// StreamStepProgress is the stream step-progress is kept in.
|
||||
const StreamStepProgress = "SEAT_node-build-node_step-progress"
|
||||
|
||||
// SubjectStepProgress is where step-progress is published, on mesh.seat.node-build-node.event.step-progress.<node>.
|
||||
func SubjectStepProgress(node string) string {
|
||||
return "mesh.seat.node-build-node.event.step-progress." + node
|
||||
}
|
||||
|
||||
// SubjectStepProgressAll hears every step-progress.
|
||||
const SubjectStepProgressAll = "mesh.seat.node-build-node.event.step-progress.*"
|
||||
|
||||
// AcceptSubjectDelivery is where delivery is sent.
|
||||
const AcceptSubjectDelivery = "mesh.seat.node-build-node.accept.delivery"
|
||||
|
||||
@@ -84,6 +108,47 @@ const (
|
||||
StateDetectedSteps = "detected-steps"
|
||||
)
|
||||
|
||||
// StepsByAskerNodeIDKey is the key of one value of the state steps under the form "<asker>.<node>.<id>".
|
||||
func StepsByAskerNodeIDKey(asker, node, id string) string { return asker + "." + node + "." + id }
|
||||
|
||||
// ParseStepsByAskerNodeIDKey reads the parts of a key of the form "<asker>.<node>.<id>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseStepsByAskerNodeIDKey(key string) (string, string, string, bool) {
|
||||
rest := key
|
||||
v0, after0, found0 := strings.Cut(rest, ".")
|
||||
if !found0 || v0 == "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
rest = after0
|
||||
v2, after2, found2 := strings.Cut(rest, ".")
|
||||
if !found2 || v2 == "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
rest = after2
|
||||
if rest == "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
v4 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
return v0, v2, v4, true
|
||||
}
|
||||
|
||||
// DecodeStepsByAskerNodeIDValue reads one value of the state steps under the form "<asker>.<node>.<id>", refusing a field StepsByAskerNodeIDValue does not know and any text after the value.
|
||||
func DecodeStepsByAskerNodeIDValue(raw []byte) (StepsByAskerNodeIDValue, error) {
|
||||
var v StepsByAskerNodeIDValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// StepsByNodeIDKey is the key of one value of the state steps under the form "<node>.<id>".
|
||||
func StepsByNodeIDKey(node, id string) string { return node + "." + id }
|
||||
|
||||
@@ -315,11 +380,21 @@ type BuildAnswer struct {
|
||||
Toolchain *ToolchainUsed `json:"toolchain,omitempty"`
|
||||
}
|
||||
|
||||
// BuiltArtifactResolvedItem is a type of the contract.
|
||||
type BuiltArtifactResolvedItem struct {
|
||||
// sha256: of the package's bytes as the build fetched them
|
||||
Digest string `json:"digest"`
|
||||
Package string `json:"package"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// BuiltArtifact is one artifact a build made: its digest and the reference it was published under.
|
||||
type BuiltArtifact struct {
|
||||
Artifact string `json:"artifact"`
|
||||
Digest string `json:"digest"`
|
||||
Reference string `json:"reference,omitempty"`
|
||||
// one per mesh package the build resolved (the catalog graph entry's packages, the edges of kind 1)
|
||||
Resolved []BuiltArtifactResolvedItem `json:"resolved,omitempty"`
|
||||
}
|
||||
|
||||
// Capacity is how many steps the node runs at once.
|
||||
@@ -394,6 +469,16 @@ type FetchBesideItem struct {
|
||||
Repository string `json:"repository"`
|
||||
}
|
||||
|
||||
// FetchBuildsItem is a type of the contract.
|
||||
type FetchBuildsItem struct {
|
||||
Artifact string `json:"artifact"`
|
||||
// the member's build digest, sha256:
|
||||
Digest string `json:"digest"`
|
||||
Module string `json:"module"`
|
||||
// the name in its ecosystem (the Go module path, the npm name)
|
||||
Package string `json:"package"`
|
||||
}
|
||||
|
||||
// FetchPackage is the TypeScript module's group package: the group's tagged version of the SDK ({name, version}), placed from the registry in each member whatever its lock names, or the group's package member ({repository, commit, ref}), packed by the fetch and served to the members in place of the registry's.
|
||||
type FetchPackage struct {
|
||||
Commit string `json:"commit,omitempty"`
|
||||
@@ -409,6 +494,8 @@ type Fetch struct {
|
||||
Base string `json:"base,omitempty"`
|
||||
// the sibling repositories placed beside the fetched tree
|
||||
Beside []FetchBesideItem `json:"beside,omitempty"`
|
||||
// a group run's package members, each by its build's digest: the build module reads each from the artifact store at artifact-store://<module>/<artifact>/blobs/<digest>, checks the bytes against the digest and lays them as the group package; a package named here is named in neither packages nor package
|
||||
Builds []FetchBuildsItem `json:"builds,omitempty"`
|
||||
// the full commit
|
||||
Commit string `json:"commit"`
|
||||
// the module's directory in the repository ("." for its root), with paths
|
||||
@@ -733,6 +820,22 @@ type StepNames struct {
|
||||
Repositories []string `json:"repositories"`
|
||||
}
|
||||
|
||||
// StepProgress is a running ask's progress, said once a minute while the holder runs it (design 47 G section 496).
|
||||
type StepProgress struct {
|
||||
Ask string `json:"ask"`
|
||||
Asker string `json:"asker"`
|
||||
At time.Time `json:"at"`
|
||||
Phase string `json:"phase"`
|
||||
}
|
||||
|
||||
// The values of StepProgress's phase.
|
||||
const (
|
||||
StepProgressPhaseFetch = "fetch"
|
||||
StepProgressPhaseBuild = "build"
|
||||
StepProgressPhaseRun = "run"
|
||||
StepProgressPhaseReport = "report"
|
||||
)
|
||||
|
||||
// StepResult is what a step answered, by the kind of ask: a build's artifacts, toolchain, imports, out-of-step modules and the artifact it kept; a check's fetch and levels; a compose's built, key and verdict; a run's built, key, exit, output and said; a declined ask's declined, needs and lacks. The fields of the kinds do not meet, so one layout holds the five.
|
||||
type StepResult struct {
|
||||
Artifacts []BuiltArtifact `json:"artifacts,omitempty"`
|
||||
@@ -826,8 +929,20 @@ type Toolchain struct {
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// ToolchainReported is a node's toolchain of a language, said at the build module's start and each time its toolchains key of the language changes (design 47 G section 492).
|
||||
type ToolchainReported struct {
|
||||
// the digest of the program of the provision the build module requires for that language's toolchain, never the build module's own
|
||||
Digest string `json:"digest"`
|
||||
Language string `json:"language"`
|
||||
Node string `json:"node"`
|
||||
Recipe string `json:"recipe"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// ToolchainUsed is the toolchain a build was made with: the language, the installed version and the build module's recipe.
|
||||
type ToolchainUsed struct {
|
||||
// the building node's digest of its toolchain program (kind 3's entry), never the build module's own program
|
||||
Digest string `json:"digest,omitempty"`
|
||||
Language string `json:"language"`
|
||||
Recipe string `json:"recipe"`
|
||||
Version string `json:"version"`
|
||||
@@ -910,20 +1025,28 @@ const (
|
||||
type StepsInput struct {
|
||||
// find them anew, not from the kept answer
|
||||
Again bool `json:"again,omitempty"`
|
||||
// the artifact reference, <module>:<artifact>
|
||||
Artifact string `json:"artifact"`
|
||||
Fetch Fetch `json:"fetch"`
|
||||
// the key of detected-steps, <module>.<artifact>
|
||||
Artifact string `json:"artifact"`
|
||||
// delete the artifact's kept key instead of detecting; needs why
|
||||
Drop bool `json:"drop,omitempty"`
|
||||
Fetch *Fetch `json:"fetch,omitempty"`
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
Module string `json:"module"`
|
||||
// with drop, why: a non-empty text, written in the hand-act log with the caller the bus proved
|
||||
Why string `json:"why,omitempty"`
|
||||
}
|
||||
|
||||
// StepsAnswer is what steps answers.
|
||||
type StepsAnswer struct {
|
||||
Artifact string `json:"artifact"`
|
||||
Artifact string `json:"artifact,omitempty"`
|
||||
// the artifact whose key a drop deleted
|
||||
Dropped string `json:"dropped,omitempty"`
|
||||
// why a drop deleted nothing, naming the hand-act log when it refused
|
||||
Error string `json:"error,omitempty"`
|
||||
Fingerprint string `json:"fingerprint,omitempty"`
|
||||
// answered from the kept answer
|
||||
Kept bool `json:"kept,omitempty"`
|
||||
Steps []DetectedStep `json:"steps"`
|
||||
Steps []DetectedStep `json:"steps,omitempty"`
|
||||
}
|
||||
|
||||
// DeliveryAccept is the body of delivery.
|
||||
@@ -944,6 +1067,15 @@ type RecheckAccept = StepAsk
|
||||
// StepEndedEvent is the body of the event step-ended.
|
||||
type StepEndedEvent = StepEnded
|
||||
|
||||
// ToolchainReportedEvent is the body of the event toolchain-reported.
|
||||
type ToolchainReportedEvent = ToolchainReported
|
||||
|
||||
// StepProgressEvent is the body of the event step-progress.
|
||||
type StepProgressEvent = StepProgress
|
||||
|
||||
// StepsByAskerNodeIDValue is one value of the state steps under the key <asker>.<node>.<id>.
|
||||
type StepsByAskerNodeIDValue = Step
|
||||
|
||||
// StepsByNodeIDValue is one value of the state steps under the key <node>.<id>.
|
||||
type StepsByNodeIDValue = Step
|
||||
|
||||
|
||||
@@ -21,6 +21,10 @@ func TestFixtures(t *testing.T) {
|
||||
decode: func(b []byte) (any, error) { var v StepEndedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-build-node\\.event\\.step-ended\\.[^.*> ]+$")},
|
||||
{name: "an integration step that could not run, naming the part not heard", subject: "mesh.seat.node-build-node.event.step-ended.g14", given: "{\"artifact\":\"nats:program\",\"commit\":\"0123456789abcdef0123456789abcdef01234567\",\"fingerprint\":\"fp-0123\",\"id\":\"m2kx9a1b3c4d5f\",\"module\":\"nats\",\"node\":\"g14\",\"outcome\":\"could not run\",\"output-object\":\"mesh-build-go_sent-objects/m2kx9a1b3c4d5f\",\"pipeline\":\"main.12.nats\",\"said\":\"the sandbox could not fetch novox/mesh-sdk\",\"step\":\"integration\",\"v\":1}", wire: "{\"artifact\":\"nats:program\",\"commit\":\"0123456789abcdef0123456789abcdef01234567\",\"fingerprint\":\"fp-0123\",\"id\":\"m2kx9a1b3c4d5f\",\"module\":\"nats\",\"node\":\"g14\",\"outcome\":\"could not run\",\"output-object\":\"mesh-build-go_sent-objects/m2kx9a1b3c4d5f\",\"pipeline\":\"main.12.nats\",\"said\":\"the sandbox could not fetch novox/mesh-sdk\",\"step\":\"integration\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) { var v StepEndedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-build-node\\.event\\.step-ended\\.[^.*> ]+$")},
|
||||
{name: "a node's toolchain reported", subject: "mesh.seat.node-build-node.event.toolchain-reported.ace", given: "{\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"language\":\"go\",\"node\":\"ace\",\"recipe\":\"1\",\"version\":\"1.26.0\"}", wire: "{\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"language\":\"go\",\"node\":\"ace\",\"recipe\":\"1\",\"version\":\"1.26.0\"}",
|
||||
decode: func(b []byte) (any, error) { var v ToolchainReportedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-build-node\\.event\\.toolchain-reported\\.[^.*> ]+$")},
|
||||
{name: "a running ask says its progress", subject: "mesh.seat.node-build-node.event.step-progress.ace", given: "{\"ask\":\"a1b2\",\"asker\":\"runner\",\"at\":\"2026-11-11T10:01:00Z\",\"phase\":\"fetch\"}", wire: "{\"ask\":\"a1b2\",\"asker\":\"runner\",\"at\":\"2026-11-11T10:01:00Z\",\"phase\":\"fetch\"}",
|
||||
decode: func(b []byte) (any, error) { var v StepProgressEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-build-node\\.event\\.step-progress\\.[^.*> ]+$")},
|
||||
{name: "a running step", subject: "", given: "{\"id\":\"a1b2\",\"node\":\"ace\",\"started\":\"2026-11-05T10:00:00Z\",\"state\":\"running\",\"tool\":\"build\",\"v\":1}", wire: "{\"id\":\"a1b2\",\"node\":\"ace\",\"started\":\"2026-11-05T10:00:00Z\",\"state\":\"running\",\"tool\":\"build\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) { var v StepsByNodeIDValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a paused node", subject: "", given: "{\"at\":\"2026-11-05T10:00:00Z\",\"by\":\"operator@shanks\",\"paused\":true,\"v\":1}", wire: "{\"at\":\"2026-11-05T10:00:00Z\",\"by\":\"operator@shanks\",\"paused\":true,\"v\":1}",
|
||||
@@ -61,6 +65,12 @@ func TestFixtures(t *testing.T) {
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
{name: "a step keyed under its asker", subject: "", given: "{\"id\":\"a1b2\",\"node\":\"ace\",\"started\":\"2026-11-05T10:00:00Z\",\"state\":\"running\",\"tool\":\"build\",\"v\":1}", wire: "{\"id\":\"a1b2\",\"node\":\"ace\",\"started\":\"2026-11-05T10:00:00Z\",\"state\":\"running\",\"tool\":\"build\",\"v\":1}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v StepsByAskerNodeIDValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
{name: "drop names the ask and why", subject: "", given: "{\"by\":\"mesh-delivery-builder\",\"id\":\"a1b2\",\"why\":\"a newer fingerprint supersedes it\"}", wire: "{\"by\":\"mesh-delivery-builder\",\"id\":\"a1b2\",\"why\":\"a newer fingerprint supersedes it\"}",
|
||||
decode: func(b []byte) (any, error) { var v DropInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "kept-artifact asks", subject: "", given: "{\"digest\":\"dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd\",\"fp\":\"src2:1480985e\",\"module\":\"artifact-store\"}", wire: "{\"digest\":\"dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd\",\"fp\":\"src2:1480985e\",\"module\":\"artifact-store\"}",
|
||||
@@ -79,6 +89,12 @@ func TestFixtures(t *testing.T) {
|
||||
decode: func(b []byte) (any, error) { var v StepsInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "steps answers lint and unit tests", subject: "", given: "{\"artifact\":\"nats:program\",\"fingerprint\":\"fp-0123\",\"steps\":[{\"step\":\"lint\"},{\"packages\":[\"./modules/nats\"],\"step\":\"unit\"}]}", wire: "{\"artifact\":\"nats:program\",\"fingerprint\":\"fp-0123\",\"steps\":[{\"step\":\"lint\"},{\"packages\":[\"./modules/nats\"],\"step\":\"unit\"}]}",
|
||||
decode: func(b []byte) (any, error) { var v StepsAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "steps drops an artifact's kept steps with a reason", subject: "", given: "{\"artifact\":\"nats.program\",\"drop\":true,\"module\":\"nats\",\"why\":\"the detection was wrong\"}", wire: "{\"artifact\":\"nats.program\",\"drop\":true,\"module\":\"nats\",\"why\":\"the detection was wrong\"}",
|
||||
decode: func(b []byte) (any, error) { var v StepsInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "steps answers a drop with the artifact", subject: "", given: "{\"dropped\":\"nats.program\"}", wire: "{\"dropped\":\"nats.program\"}",
|
||||
decode: func(b []byte) (any, error) { var v StepsAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "steps answers a drop that the hand-act log refused", subject: "", given: "{\"error\":\"the hand-act log refused: nothing was deleted\"}", wire: "{\"error\":\"the hand-act log refused: nothing was deleted\"}",
|
||||
decode: func(b []byte) (any, error) { var v StepsAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
}
|
||||
for _, c := range cases {
|
||||
v, err := c.decode([]byte(c.given))
|
||||
|
||||
+164
-10
@@ -159,10 +159,12 @@
|
||||
},
|
||||
{
|
||||
"name": "steps",
|
||||
"description": "Which pipeline steps an artifact can run, found by its language's convention (design 47 *Pipeline steps and the policy*): lint always, unit and integration tests where its packages hold them. Kept by the artifact's fingerprint; again finds them anew.",
|
||||
"description": "Which pipeline steps an artifact can run, found by its language's convention (design 47 *Pipeline steps and the policy*): lint always, unit and integration tests where its packages hold them. Kept by the artifact's fingerprint; again finds them anew. With drop and why it deletes the artifact's kept key instead, writes the act in the hand-act log of the seat mesh-hand-acts first (a log that refuses deletes nothing and answers error), and answers dropped (design 47 G section 463, section 480).",
|
||||
"callers": [
|
||||
"seat:mesh-pipeline-runner",
|
||||
"seat:mesh-catalog"
|
||||
"seat:mesh-catalog",
|
||||
"operator",
|
||||
"sessions"
|
||||
],
|
||||
"input": {
|
||||
"type": "object",
|
||||
@@ -172,7 +174,7 @@
|
||||
},
|
||||
"artifact": {
|
||||
"type": "string",
|
||||
"description": "the artifact reference, <module>:<artifact>"
|
||||
"description": "the key of detected-steps, <module>.<artifact>"
|
||||
},
|
||||
"fingerprint": {
|
||||
"type": "string"
|
||||
@@ -183,13 +185,21 @@
|
||||
"again": {
|
||||
"type": "boolean",
|
||||
"description": "find them anew, not from the kept answer"
|
||||
},
|
||||
"drop": {
|
||||
"type": "boolean",
|
||||
"description": "delete the artifact's kept key instead of detecting; needs why"
|
||||
},
|
||||
"why": {
|
||||
"type": "string",
|
||||
"description": "with drop, why: a non-empty text, written in the hand-act log with the caller the bus proved"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"module",
|
||||
"artifact",
|
||||
"fetch"
|
||||
]
|
||||
"artifact"
|
||||
],
|
||||
"description": "fetch is required unless drop is given, and why with drop"
|
||||
},
|
||||
"answer": {
|
||||
"type": "object",
|
||||
@@ -209,12 +219,17 @@
|
||||
"kept": {
|
||||
"type": "boolean",
|
||||
"description": "answered from the kept answer"
|
||||
},
|
||||
"dropped": {
|
||||
"type": "string",
|
||||
"description": "the artifact whose key a drop deleted"
|
||||
},
|
||||
"error": {
|
||||
"type": "string",
|
||||
"description": "why a drop deleted nothing, naming the hand-act log when it refused"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"artifact",
|
||||
"steps"
|
||||
]
|
||||
"required": []
|
||||
},
|
||||
"class": "act"
|
||||
}
|
||||
@@ -857,6 +872,35 @@
|
||||
]
|
||||
},
|
||||
"description": "the sibling repositories placed beside the fetched tree"
|
||||
},
|
||||
"builds": {
|
||||
"type": "array",
|
||||
"description": "a group run's package members, each by its build's digest: the build module reads each from the artifact store at artifact-store://<module>/<artifact>/blobs/<digest>, checks the bytes against the digest and lays them as the group package; a package named here is named in neither packages nor package",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"package": {
|
||||
"type": "string",
|
||||
"description": "the name in its ecosystem (the Go module path, the npm name)"
|
||||
},
|
||||
"module": {
|
||||
"type": "string"
|
||||
},
|
||||
"artifact": {
|
||||
"type": "string"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"description": "the member's build digest, sha256:"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"package",
|
||||
"module",
|
||||
"artifact",
|
||||
"digest"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -875,6 +919,10 @@
|
||||
},
|
||||
"recipe": {
|
||||
"type": "string"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"description": "the building node's digest of its toolchain program (kind 3's entry), never the build module's own program"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -916,6 +964,30 @@
|
||||
},
|
||||
"reference": {
|
||||
"type": "string"
|
||||
},
|
||||
"resolved": {
|
||||
"type": "array",
|
||||
"description": "one per mesh package the build resolved (the catalog graph entry's packages, the edges of kind 1)",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"package": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"description": "sha256: of the package's bytes as the build fetched them"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"package",
|
||||
"version",
|
||||
"digest"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -1628,6 +1700,66 @@
|
||||
"fingerprint",
|
||||
"steps"
|
||||
]
|
||||
},
|
||||
"ToolchainReported": {
|
||||
"type": "object",
|
||||
"description": "a node's toolchain of a language, said at the build module's start and each time its toolchains key of the language changes (design 47 G section 492)",
|
||||
"properties": {
|
||||
"node": {
|
||||
"type": "string"
|
||||
},
|
||||
"language": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"recipe": {
|
||||
"type": "string"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string",
|
||||
"description": "the digest of the program of the provision the build module requires for that language's toolchain, never the build module's own"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"node",
|
||||
"language",
|
||||
"version",
|
||||
"recipe",
|
||||
"digest"
|
||||
]
|
||||
},
|
||||
"StepProgress": {
|
||||
"type": "object",
|
||||
"description": "a running ask's progress, said once a minute while the holder runs it (design 47 G section 496)",
|
||||
"properties": {
|
||||
"asker": {
|
||||
"type": "string"
|
||||
},
|
||||
"ask": {
|
||||
"type": "string"
|
||||
},
|
||||
"phase": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"fetch",
|
||||
"build",
|
||||
"run",
|
||||
"report"
|
||||
]
|
||||
},
|
||||
"at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"asker",
|
||||
"ask",
|
||||
"phase",
|
||||
"at"
|
||||
]
|
||||
}
|
||||
},
|
||||
"events": [
|
||||
@@ -1641,6 +1773,28 @@
|
||||
"max-age-days": 90,
|
||||
"max-mib": 128
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "toolchain-reported",
|
||||
"subject": "toolchain-reported.<node>",
|
||||
"body": {
|
||||
"$ref": "#/defs/ToolchainReported"
|
||||
},
|
||||
"keep": {
|
||||
"max-age-days": 90,
|
||||
"max-mib": 16
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "step-progress",
|
||||
"subject": "step-progress.<node>",
|
||||
"body": {
|
||||
"$ref": "#/defs/StepProgress"
|
||||
},
|
||||
"keep": {
|
||||
"max-age-days": 1,
|
||||
"max-mib": 16
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1483,6 +1483,11 @@
|
||||
"how": {
|
||||
"type": "string",
|
||||
"description": "for its end, how it ended: answered, ran, released, superseded"
|
||||
},
|
||||
"clock-from": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "the time the bound counts from (design 47 G section 266, section 269, section 498)"
|
||||
}
|
||||
},
|
||||
"description": "a step's wait, held and ended; a wait on a person is said as waiting on that person, never a stall (ADR 0283)",
|
||||
|
||||
@@ -166,5 +166,48 @@
|
||||
"laptop"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a composition step's progress",
|
||||
"of": "event:step-progress",
|
||||
"subject": "mesh.seat.mesh-wants.event.step-progress.ace",
|
||||
"given": {
|
||||
"asker": "assigner",
|
||||
"ask": "a1b2",
|
||||
"phase": "build",
|
||||
"at": "2026-11-11T10:01:00Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a put without hold waits, counted from its start",
|
||||
"of": "event:wait-held",
|
||||
"subject": "mesh.seat.mesh-wants.event.wait-held",
|
||||
"given": {
|
||||
"step": "put",
|
||||
"node": "ace",
|
||||
"module": "nats",
|
||||
"fingerprint": "fp-0123",
|
||||
"since": "2026-11-11T10:00:00Z",
|
||||
"clock-from": "2026-11-11T10:00:00Z",
|
||||
"bound": "30m"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a dependent's delivery waits for a dependency's version",
|
||||
"of": "event:wait-held",
|
||||
"subject": "mesh.seat.mesh-wants.event.wait-held",
|
||||
"given": {
|
||||
"step": "delivery",
|
||||
"part": "mesh-sdk",
|
||||
"version": "0.1.4",
|
||||
"machines": [
|
||||
"ace",
|
||||
"g14"
|
||||
],
|
||||
"module": "nats",
|
||||
"since": "2026-11-11T10:00:00Z",
|
||||
"clock-from": "2026-11-11T09:58:00Z",
|
||||
"bound": "60m"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -427,6 +427,45 @@
|
||||
"parts"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "step-progress",
|
||||
"subject": "step-progress.<node>",
|
||||
"keep": {
|
||||
"max-age-days": 1,
|
||||
"max-mib": 16
|
||||
},
|
||||
"body": {
|
||||
"type": "object",
|
||||
"description": "a composition step's progress, said once a minute while the assigner runs it (design 47 G section 496)",
|
||||
"properties": {
|
||||
"asker": {
|
||||
"type": "string"
|
||||
},
|
||||
"ask": {
|
||||
"type": "string"
|
||||
},
|
||||
"phase": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"fetch",
|
||||
"build",
|
||||
"run",
|
||||
"report"
|
||||
]
|
||||
},
|
||||
"at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"asker",
|
||||
"ask",
|
||||
"phase",
|
||||
"at"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"family": {
|
||||
@@ -986,7 +1025,22 @@
|
||||
"description": "a step's wait the assigner holds: placing, wanting or composing until an acting part's new version runs",
|
||||
"properties": {
|
||||
"step": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"take-in",
|
||||
"validate",
|
||||
"check",
|
||||
"build",
|
||||
"compose",
|
||||
"grant",
|
||||
"place",
|
||||
"pipeline",
|
||||
"put",
|
||||
"delivery",
|
||||
"kept-build",
|
||||
"backup"
|
||||
],
|
||||
"description": "which wait: an acting step of section 203 or put, delivery, kept-build, backup (design 47 G section 498)"
|
||||
},
|
||||
"part": {
|
||||
"type": "string",
|
||||
@@ -1021,12 +1075,26 @@
|
||||
"by": {
|
||||
"type": "string",
|
||||
"description": "on a release, who released it and why"
|
||||
},
|
||||
"clock-from": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "the time the bound counts from (design 47 G section 266, section 269, section 498)"
|
||||
},
|
||||
"fingerprint": {
|
||||
"type": "string",
|
||||
"description": "a put's wait: the fingerprint of the build put"
|
||||
},
|
||||
"machines": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "a dependent's delivery wait: the machines the dependency's version has yet to reach"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"step",
|
||||
"part",
|
||||
"version",
|
||||
"module",
|
||||
"since"
|
||||
]
|
||||
|
||||
@@ -53,5 +53,28 @@
|
||||
"said": "the sandbox could not fetch novox/mesh-sdk",
|
||||
"output-object": "mesh-build-go_sent-objects/m2kx9a1b3c4d5f"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a node's toolchain reported",
|
||||
"of": "event:toolchain-reported",
|
||||
"subject": "mesh.seat.node-build-go.event.toolchain-reported.ace",
|
||||
"given": {
|
||||
"node": "ace",
|
||||
"language": "go",
|
||||
"version": "1.26.0",
|
||||
"recipe": "1",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a running ask says its progress",
|
||||
"of": "event:step-progress",
|
||||
"subject": "mesh.seat.node-build-go.event.step-progress.ace",
|
||||
"given": {
|
||||
"asker": "runner",
|
||||
"ask": "a1b2",
|
||||
"phase": "fetch",
|
||||
"at": "2026-11-11T10:01:00Z"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -526,5 +526,17 @@
|
||||
],
|
||||
"at": "2026-11-10T10:00:00Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a step keyed under its asker",
|
||||
"of": "state:steps/<asker>.<node>.<id>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"id": "a1b2",
|
||||
"tool": "build",
|
||||
"node": "ace",
|
||||
"state": "running",
|
||||
"started": "2026-11-05T10:00:00Z"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -100,5 +100,29 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "steps drops an artifact's kept steps with a reason",
|
||||
"of": "verb:steps.input",
|
||||
"given": {
|
||||
"module": "nats",
|
||||
"artifact": "nats.program",
|
||||
"drop": true,
|
||||
"why": "the detection was wrong"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "steps answers a drop with the artifact",
|
||||
"of": "verb:steps.answer",
|
||||
"given": {
|
||||
"dropped": "nats.program"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "steps answers a drop that the hand-act log refused",
|
||||
"of": "verb:steps.answer",
|
||||
"given": {
|
||||
"error": "the hand-act log refused: nothing was deleted"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -87,12 +87,19 @@
|
||||
"name": "steps",
|
||||
"template": "state",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "<asker>.<node>.<id>",
|
||||
"value": {
|
||||
"$ref": "#/defs/Step"
|
||||
},
|
||||
"description": "a step on a node, keyed by the asker the bus proves (design 47 G section 467, section 481)"
|
||||
},
|
||||
{
|
||||
"key": "<node>.<id>",
|
||||
"value": {
|
||||
"$ref": "#/defs/Step"
|
||||
},
|
||||
"description": "a step on a node"
|
||||
"description": "a step whose ask came on main's subject, with no asker's name (design 47 G section 483)"
|
||||
}
|
||||
],
|
||||
"description": "the steps this module runs",
|
||||
|
||||
@@ -36,5 +36,28 @@
|
||||
"said": "the sandbox could not fetch novox/mesh-sdk",
|
||||
"output-object": "mesh-build-go_sent-objects/m2kx9a1b3c4d5f"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a node's toolchain reported",
|
||||
"of": "event:toolchain-reported",
|
||||
"subject": "mesh.seat.node-build-image.event.toolchain-reported.ace",
|
||||
"given": {
|
||||
"node": "ace",
|
||||
"language": "go",
|
||||
"version": "1.26.0",
|
||||
"recipe": "1",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a running ask says its progress",
|
||||
"of": "event:step-progress",
|
||||
"subject": "mesh.seat.node-build-image.event.step-progress.ace",
|
||||
"given": {
|
||||
"asker": "runner",
|
||||
"ask": "a1b2",
|
||||
"phase": "fetch",
|
||||
"at": "2026-11-11T10:01:00Z"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -186,5 +186,17 @@
|
||||
],
|
||||
"at": "2026-11-10T10:00:00Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a step keyed under its asker",
|
||||
"of": "state:steps/<asker>.<node>.<id>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"id": "a1b2",
|
||||
"tool": "build",
|
||||
"node": "ace",
|
||||
"state": "running",
|
||||
"started": "2026-11-05T10:00:00Z"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -100,5 +100,29 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "steps drops an artifact's kept steps with a reason",
|
||||
"of": "verb:steps.input",
|
||||
"given": {
|
||||
"module": "nats",
|
||||
"artifact": "nats.program",
|
||||
"drop": true,
|
||||
"why": "the detection was wrong"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "steps answers a drop with the artifact",
|
||||
"of": "verb:steps.answer",
|
||||
"given": {
|
||||
"dropped": "nats.program"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "steps answers a drop that the hand-act log refused",
|
||||
"of": "verb:steps.answer",
|
||||
"given": {
|
||||
"error": "the hand-act log refused: nothing was deleted"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -65,12 +65,19 @@
|
||||
"name": "steps",
|
||||
"template": "state",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "<asker>.<node>.<id>",
|
||||
"value": {
|
||||
"$ref": "#/defs/Step"
|
||||
},
|
||||
"description": "a step on a node, keyed by the asker the bus proves (design 47 G section 467, section 481)"
|
||||
},
|
||||
{
|
||||
"key": "<node>.<id>",
|
||||
"value": {
|
||||
"$ref": "#/defs/Step"
|
||||
},
|
||||
"description": "a step on a node"
|
||||
"description": "a step whose ask came on main's subject, with no asker's name (design 47 G section 483)"
|
||||
}
|
||||
],
|
||||
"description": "the steps this module runs",
|
||||
|
||||
@@ -53,5 +53,28 @@
|
||||
"said": "the sandbox could not fetch novox/mesh-sdk",
|
||||
"output-object": "mesh-build-go_sent-objects/m2kx9a1b3c4d5f"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a node's toolchain reported",
|
||||
"of": "event:toolchain-reported",
|
||||
"subject": "mesh.seat.node-build-node.event.toolchain-reported.ace",
|
||||
"given": {
|
||||
"node": "ace",
|
||||
"language": "go",
|
||||
"version": "1.26.0",
|
||||
"recipe": "1",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a running ask says its progress",
|
||||
"of": "event:step-progress",
|
||||
"subject": "mesh.seat.node-build-node.event.step-progress.ace",
|
||||
"given": {
|
||||
"asker": "runner",
|
||||
"ask": "a1b2",
|
||||
"phase": "fetch",
|
||||
"at": "2026-11-11T10:01:00Z"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -274,5 +274,17 @@
|
||||
],
|
||||
"at": "2026-11-10T10:00:00Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "a step keyed under its asker",
|
||||
"of": "state:steps/<asker>.<node>.<id>",
|
||||
"given": {
|
||||
"v": 1,
|
||||
"id": "a1b2",
|
||||
"tool": "build",
|
||||
"node": "ace",
|
||||
"state": "running",
|
||||
"started": "2026-11-05T10:00:00Z"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -100,5 +100,29 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "steps drops an artifact's kept steps with a reason",
|
||||
"of": "verb:steps.input",
|
||||
"given": {
|
||||
"module": "nats",
|
||||
"artifact": "nats.program",
|
||||
"drop": true,
|
||||
"why": "the detection was wrong"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "steps answers a drop with the artifact",
|
||||
"of": "verb:steps.answer",
|
||||
"given": {
|
||||
"dropped": "nats.program"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "steps answers a drop that the hand-act log refused",
|
||||
"of": "verb:steps.answer",
|
||||
"given": {
|
||||
"error": "the hand-act log refused: nothing was deleted"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -72,12 +72,19 @@
|
||||
"name": "steps",
|
||||
"template": "state",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "<asker>.<node>.<id>",
|
||||
"value": {
|
||||
"$ref": "#/defs/Step"
|
||||
},
|
||||
"description": "a step on a node, keyed by the asker the bus proves (design 47 G section 467, section 481)"
|
||||
},
|
||||
{
|
||||
"key": "<node>.<id>",
|
||||
"value": {
|
||||
"$ref": "#/defs/Step"
|
||||
},
|
||||
"description": "a step on a node"
|
||||
"description": "a step whose ask came on main's subject, with no asker's name (design 47 G section 483)"
|
||||
}
|
||||
],
|
||||
"description": "the steps this module runs",
|
||||
|
||||
@@ -364,6 +364,8 @@ export interface Wait {
|
||||
"artifact"?: string;
|
||||
/** the watched branch, or the branch of a run on request */
|
||||
"branch": string;
|
||||
/** the time the bound counts from (design 47 G section 266, section 269, section 498) */
|
||||
"clock-from"?: string;
|
||||
/** the commit the module's entry was last changed at */
|
||||
"commit"?: string;
|
||||
/** the module's build-input fingerprint at the commit */
|
||||
|
||||
+25
-3
@@ -29,6 +29,7 @@ export declare const events: {
|
||||
readonly room: "room";
|
||||
readonly stepEnded: "step-ended";
|
||||
readonly versionDelivered: "version-delivered";
|
||||
readonly stepProgress: "step-progress";
|
||||
};
|
||||
|
||||
export declare const family: { readonly name: "mesh-wants.events"; readonly maxAgeDays: 7 };
|
||||
@@ -61,6 +62,12 @@ export declare const versionDeliveredStream: "SEAT_mesh-wants_version-delivered"
|
||||
|
||||
export declare const versionDeliveredSubject: "mesh.seat.mesh-wants.event.version-delivered";
|
||||
|
||||
export declare const stepProgressStream: "SEAT_mesh-wants_step-progress";
|
||||
|
||||
export declare function stepProgressSubject(node: string): string;
|
||||
|
||||
export declare const stepProgressSubjectAll: "mesh.seat.mesh-wants.event.step-progress.*";
|
||||
|
||||
export declare const stepSubject: "mesh.seat.mesh-wants.accept.step";
|
||||
|
||||
export declare const states: {
|
||||
@@ -304,18 +311,25 @@ export interface Wait {
|
||||
"bound"?: string;
|
||||
/** on a release, who released it and why */
|
||||
"by"?: string;
|
||||
/** the time the bound counts from (design 47 G section 266, section 269, section 498) */
|
||||
"clock-from"?: string;
|
||||
/** a put's wait: the fingerprint of the build put */
|
||||
"fingerprint"?: string;
|
||||
"fp"?: string;
|
||||
/** on wait-ended: ran or released */
|
||||
"how"?: string;
|
||||
/** a dependent's delivery wait: the machines the dependency's version has yet to reach */
|
||||
"machines"?: string[];
|
||||
/** the entry that waits */
|
||||
"module": string;
|
||||
"node"?: string;
|
||||
/** the acting part waited for */
|
||||
"part": string;
|
||||
"part"?: string;
|
||||
"since": string;
|
||||
"step": string;
|
||||
/** which wait: an acting step of section 203 or put, delivery, kept-build, backup (design 47 G section 498) */
|
||||
"step": "take-in" | "validate" | "check" | "build" | "compose" | "grant" | "place" | "pipeline" | "put" | "delivery" | "kept-build" | "backup";
|
||||
/** its new version's fingerprint */
|
||||
"version": string;
|
||||
"version"?: string;
|
||||
}
|
||||
|
||||
/** is what upgrade takes. */
|
||||
@@ -474,6 +488,14 @@ export interface VersionDeliveredEvent {
|
||||
"version": string;
|
||||
}
|
||||
|
||||
/** is the body of the event step-progress. */
|
||||
export interface StepProgressEvent {
|
||||
"ask": string;
|
||||
"asker": string;
|
||||
"at": string;
|
||||
"phase": "fetch" | "build" | "run" | "report";
|
||||
}
|
||||
|
||||
/** is one value of the state wants under the key want.<node>.<module>. */
|
||||
export interface WantsByWantNodeModuleValue {
|
||||
/** again or skip */
|
||||
|
||||
@@ -29,6 +29,7 @@ export const events = {
|
||||
room: "room",
|
||||
stepEnded: "step-ended",
|
||||
versionDelivered: "version-delivered",
|
||||
stepProgress: "step-progress",
|
||||
};
|
||||
|
||||
export const family = { name: "mesh-wants.events", maxAgeDays: 7 };
|
||||
@@ -63,6 +64,14 @@ export const versionDeliveredStream = "SEAT_mesh-wants_version-delivered";
|
||||
|
||||
export const versionDeliveredSubject = "mesh.seat.mesh-wants.event.version-delivered";
|
||||
|
||||
export const stepProgressStream = "SEAT_mesh-wants_step-progress";
|
||||
|
||||
export function stepProgressSubject(node) {
|
||||
return "mesh.seat.mesh-wants.event.step-progress." + node;
|
||||
}
|
||||
|
||||
export const stepProgressSubjectAll = "mesh.seat.mesh-wants.event.step-progress.*";
|
||||
|
||||
export const stepSubject = "mesh.seat.mesh-wants.accept.step";
|
||||
|
||||
export const states = {
|
||||
|
||||
+29
@@ -26,6 +26,8 @@ export declare const accepts: {
|
||||
|
||||
export declare const events: {
|
||||
readonly stepEnded: "step-ended";
|
||||
readonly toolchainReported: "toolchain-reported";
|
||||
readonly stepProgress: "step-progress";
|
||||
};
|
||||
|
||||
export declare const family: { readonly name: "node-build-go.events"; readonly maxAgeDays: 7 };
|
||||
@@ -36,6 +38,18 @@ export declare function stepEndedSubject(node: string): string;
|
||||
|
||||
export declare const stepEndedSubjectAll: "mesh.seat.node-build-go.event.step-ended.*";
|
||||
|
||||
export declare const toolchainReportedStream: "SEAT_node-build-go_toolchain-reported";
|
||||
|
||||
export declare function toolchainReportedSubject(node: string): string;
|
||||
|
||||
export declare const toolchainReportedSubjectAll: "mesh.seat.node-build-go.event.toolchain-reported.*";
|
||||
|
||||
export declare const stepProgressStream: "SEAT_node-build-go_step-progress";
|
||||
|
||||
export declare function stepProgressSubject(node: string): string;
|
||||
|
||||
export declare const stepProgressSubjectAll: "mesh.seat.node-build-go.event.step-progress.*";
|
||||
|
||||
export declare const deliverySubject: "mesh.seat.node-build-go.accept.delivery";
|
||||
|
||||
export declare const checkSubject: "mesh.seat.node-build-go.accept.check";
|
||||
@@ -55,6 +69,12 @@ export declare const states: {
|
||||
readonly detectedSteps: "detected-steps";
|
||||
};
|
||||
|
||||
/** The key of one value of the state steps under the form "<asker>.<node>.<id>". */
|
||||
export declare function stepsByAskerNodeIDKey(asker: string, node: string, id: string): string;
|
||||
|
||||
/** The parts of a key of the form "<asker>.<node>.<id>", or undefined for a key of another form. */
|
||||
export declare function parseStepsByAskerNodeIDKey(_key: string): { asker: string; node: string; id: string } | undefined;
|
||||
|
||||
/** The key of one value of the state steps under the form "<node>.<id>". */
|
||||
export declare function stepsByNodeIDKey(node: string, id: string): string;
|
||||
|
||||
@@ -94,6 +114,7 @@ export declare function parseDetectedStepsByModuleArtifactKey(_key: string): { m
|
||||
export type AskArtifact = buildStep.AskArtifact;
|
||||
export type BuildAnswer = buildStep.BuildAnswer;
|
||||
export type BuiltArtifact = buildStep.BuiltArtifact;
|
||||
export type BuiltArtifactResolvedItem = buildStep.BuiltArtifactResolvedItem;
|
||||
export type Capacity = buildStep.Capacity;
|
||||
export type CheckAnswer = buildStep.CheckAnswer;
|
||||
export type ComposeAnswer = buildStep.ComposeAnswer;
|
||||
@@ -107,6 +128,7 @@ export type DropInput = buildStep.DropInput;
|
||||
export type Fetch = buildStep.Fetch;
|
||||
export type FetchAnswer = buildStep.FetchAnswer;
|
||||
export type FetchBesideItem = buildStep.FetchBesideItem;
|
||||
export type FetchBuildsItem = buildStep.FetchBuildsItem;
|
||||
export type FetchPackage = buildStep.FetchPackage;
|
||||
export type FetchRefusal = buildStep.FetchRefusal;
|
||||
export type KeptArtifactAnswer = buildStep.KeptArtifactAnswer;
|
||||
@@ -139,6 +161,8 @@ export type StepAskCompose = buildStep.StepAskCompose;
|
||||
export type StepEnded = buildStep.StepEnded;
|
||||
export type StepEndedEvent = buildStep.StepEndedEvent;
|
||||
export type StepNames = buildStep.StepNames;
|
||||
export type StepProgress = buildStep.StepProgress;
|
||||
export type StepProgressEvent = buildStep.StepProgressEvent;
|
||||
export type StepResult = buildStep.StepResult;
|
||||
export type StepRun = buildStep.StepRun;
|
||||
export type StepsAnswer = buildStep.StepsAnswer;
|
||||
@@ -146,6 +170,8 @@ export type StepsInput = buildStep.StepsInput;
|
||||
export type TakenVersion = buildStep.TakenVersion;
|
||||
export type TestCounts = buildStep.TestCounts;
|
||||
export type Toolchain = buildStep.Toolchain;
|
||||
export type ToolchainReported = buildStep.ToolchainReported;
|
||||
export type ToolchainReportedEvent = buildStep.ToolchainReportedEvent;
|
||||
export type ToolchainUsed = buildStep.ToolchainUsed;
|
||||
|
||||
/** is the body of delivery. */
|
||||
@@ -166,6 +192,9 @@ export type RecheckAccept = StepAsk;
|
||||
/** is the body of check.run. */
|
||||
export type CheckRunAccept = StepAsk;
|
||||
|
||||
/** is one value of the state steps under the key <asker>.<node>.<id>. */
|
||||
export type StepsByAskerNodeIDValue = Step;
|
||||
|
||||
/** is one value of the state steps under the key <node>.<id>. */
|
||||
export type StepsByNodeIDValue = Step;
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@ export const accepts = {
|
||||
|
||||
export const events = {
|
||||
stepEnded: "step-ended",
|
||||
toolchainReported: "toolchain-reported",
|
||||
stepProgress: "step-progress",
|
||||
};
|
||||
|
||||
export const family = { name: "node-build-go.events", maxAgeDays: 7 };
|
||||
@@ -36,6 +38,22 @@ export function stepEndedSubject(node) {
|
||||
|
||||
export const stepEndedSubjectAll = "mesh.seat.node-build-go.event.step-ended.*";
|
||||
|
||||
export const toolchainReportedStream = "SEAT_node-build-go_toolchain-reported";
|
||||
|
||||
export function toolchainReportedSubject(node) {
|
||||
return "mesh.seat.node-build-go.event.toolchain-reported." + node;
|
||||
}
|
||||
|
||||
export const toolchainReportedSubjectAll = "mesh.seat.node-build-go.event.toolchain-reported.*";
|
||||
|
||||
export const stepProgressStream = "SEAT_node-build-go_step-progress";
|
||||
|
||||
export function stepProgressSubject(node) {
|
||||
return "mesh.seat.node-build-go.event.step-progress." + node;
|
||||
}
|
||||
|
||||
export const stepProgressSubjectAll = "mesh.seat.node-build-go.event.step-progress.*";
|
||||
|
||||
export const deliverySubject = "mesh.seat.node-build-go.accept.delivery";
|
||||
|
||||
export const checkSubject = "mesh.seat.node-build-go.accept.check";
|
||||
@@ -55,6 +73,29 @@ export const states = {
|
||||
detectedSteps: "detected-steps",
|
||||
};
|
||||
|
||||
/** The key of one value of the state steps under the form "<asker>.<node>.<id>". */
|
||||
export function stepsByAskerNodeIDKey(asker, node, id) {
|
||||
return asker + "." + node + "." + id;
|
||||
}
|
||||
|
||||
/** The parts of a key of the form "<asker>.<node>.<id>", or undefined for a key of another form. */
|
||||
export function parseStepsByAskerNodeIDKey(_key) {
|
||||
let _rest = _key;
|
||||
const _at0 = _rest.indexOf(".");
|
||||
if (_at0 <= 0) return undefined;
|
||||
const asker = _rest.slice(0, _at0);
|
||||
_rest = _rest.slice(_at0 + 1);
|
||||
const _at2 = _rest.indexOf(".");
|
||||
if (_at2 <= 0) return undefined;
|
||||
const node = _rest.slice(0, _at2);
|
||||
_rest = _rest.slice(_at2 + 1);
|
||||
if (_rest === "") return undefined;
|
||||
const id = _rest;
|
||||
_rest = "";
|
||||
if (_rest !== "") return undefined;
|
||||
return { asker, node, id };
|
||||
}
|
||||
|
||||
/** The key of one value of the state steps under the form "<node>.<id>". */
|
||||
export function stepsByNodeIDKey(node, id) {
|
||||
return node + "." + id;
|
||||
|
||||
+60
-4
@@ -13,6 +13,8 @@ export declare const verbs: {
|
||||
|
||||
export declare const events: {
|
||||
readonly stepEnded: "step-ended";
|
||||
readonly toolchainReported: "toolchain-reported";
|
||||
readonly stepProgress: "step-progress";
|
||||
};
|
||||
|
||||
/** one artifact of a build ask: built from the fetched tree, or copied from an upstream pin */
|
||||
@@ -32,11 +34,20 @@ export interface BuildAnswer {
|
||||
"toolchain"?: ToolchainUsed;
|
||||
}
|
||||
|
||||
export interface BuiltArtifactResolvedItem {
|
||||
/** sha256: of the package's bytes as the build fetched them */
|
||||
"digest": string;
|
||||
"package": string;
|
||||
"version": string;
|
||||
}
|
||||
|
||||
/** one artifact a build made: its digest and the reference it was published under */
|
||||
export interface BuiltArtifact {
|
||||
"artifact": string;
|
||||
"digest": string;
|
||||
"reference"?: string;
|
||||
/** one per mesh package the build resolved (the catalog graph entry's packages, the edges of kind 1) */
|
||||
"resolved"?: BuiltArtifactResolvedItem[];
|
||||
}
|
||||
|
||||
/** how many steps the node runs at once */
|
||||
@@ -100,6 +111,15 @@ export interface FetchBesideItem {
|
||||
"repository": string;
|
||||
}
|
||||
|
||||
export interface FetchBuildsItem {
|
||||
"artifact": string;
|
||||
/** the member's build digest, sha256: */
|
||||
"digest": string;
|
||||
"module": string;
|
||||
/** the name in its ecosystem (the Go module path, the npm name) */
|
||||
"package": string;
|
||||
}
|
||||
|
||||
/** the TypeScript module's group package: the group's tagged version of the SDK ({name, version}), placed from the registry in each member whatever its lock names, or the group's package member ({repository, commit, ref}), packed by the fetch and served to the members in place of the registry's */
|
||||
export interface FetchPackage {
|
||||
"commit"?: string;
|
||||
@@ -115,6 +135,8 @@ export interface Fetch {
|
||||
"base"?: string;
|
||||
/** the sibling repositories placed beside the fetched tree */
|
||||
"beside"?: FetchBesideItem[];
|
||||
/** a group run's package members, each by its build's digest: the build module reads each from the artifact store at artifact-store://<module>/<artifact>/blobs/<digest>, checks the bytes against the digest and lays them as the group package; a package named here is named in neither packages nor package */
|
||||
"builds"?: FetchBuildsItem[];
|
||||
/** the full commit */
|
||||
"commit": string;
|
||||
/** the module's directory in the repository ("." for its root), with paths */
|
||||
@@ -416,6 +438,14 @@ export interface StepNames {
|
||||
"repositories": string[];
|
||||
}
|
||||
|
||||
/** a running ask's progress, said once a minute while the holder runs it (design 47 G section 496) */
|
||||
export interface StepProgress {
|
||||
"ask": string;
|
||||
"asker": string;
|
||||
"at": string;
|
||||
"phase": "fetch" | "build" | "run" | "report";
|
||||
}
|
||||
|
||||
/** what a step answered, by the kind of ask: a build's artifacts, toolchain, imports, out-of-step modules and the artifact it kept; a check's fetch and levels; a compose's built, key and verdict; a run's built, key, exit, output and said; a declined ask's declined, needs and lacks. The fields of the kinds do not meet, so one layout holds the five */
|
||||
export interface StepResult {
|
||||
"artifacts"?: BuiltArtifact[];
|
||||
@@ -502,8 +532,20 @@ export interface Toolchain {
|
||||
"version": string;
|
||||
}
|
||||
|
||||
/** a node's toolchain of a language, said at the build module's start and each time its toolchains key of the language changes (design 47 G section 492) */
|
||||
export interface ToolchainReported {
|
||||
/** the digest of the program of the provision the build module requires for that language's toolchain, never the build module's own */
|
||||
"digest": string;
|
||||
"language": string;
|
||||
"node": string;
|
||||
"recipe": string;
|
||||
"version": string;
|
||||
}
|
||||
|
||||
/** the toolchain a build was made with: the language, the installed version and the build module's recipe */
|
||||
export interface ToolchainUsed {
|
||||
/** the building node's digest of its toolchain program (kind 3's entry), never the build module's own program */
|
||||
"digest"?: string;
|
||||
"language": string;
|
||||
"recipe": string;
|
||||
"version": string;
|
||||
@@ -579,22 +621,36 @@ export interface KeptArtifactAnswer {
|
||||
export interface StepsInput {
|
||||
/** find them anew, not from the kept answer */
|
||||
"again"?: boolean;
|
||||
/** the artifact reference, <module>:<artifact> */
|
||||
/** the key of detected-steps, <module>.<artifact> */
|
||||
"artifact": string;
|
||||
"fetch": Fetch;
|
||||
/** delete the artifact's kept key instead of detecting; needs why */
|
||||
"drop"?: boolean;
|
||||
"fetch"?: Fetch;
|
||||
"fingerprint"?: string;
|
||||
"module": string;
|
||||
/** with drop, why: a non-empty text, written in the hand-act log with the caller the bus proved */
|
||||
"why"?: string;
|
||||
}
|
||||
|
||||
/** is what steps answers. */
|
||||
export interface StepsAnswer {
|
||||
"artifact": string;
|
||||
"artifact"?: string;
|
||||
/** the artifact whose key a drop deleted */
|
||||
"dropped"?: string;
|
||||
/** why a drop deleted nothing, naming the hand-act log when it refused */
|
||||
"error"?: string;
|
||||
"fingerprint"?: string;
|
||||
/** answered from the kept answer */
|
||||
"kept"?: boolean;
|
||||
"steps": DetectedStep[];
|
||||
"steps"?: DetectedStep[];
|
||||
}
|
||||
|
||||
/** is the body of the event step-ended. */
|
||||
export type StepEndedEvent = StepEnded;
|
||||
|
||||
/** is the body of the event toolchain-reported. */
|
||||
export type ToolchainReportedEvent = ToolchainReported;
|
||||
|
||||
/** is the body of the event step-progress. */
|
||||
export type StepProgressEvent = StepProgress;
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ export const verbs = {
|
||||
|
||||
export const events = {
|
||||
stepEnded: "step-ended",
|
||||
toolchainReported: "toolchain-reported",
|
||||
stepProgress: "step-progress",
|
||||
};
|
||||
|
||||
export {};
|
||||
|
||||
+29
@@ -25,6 +25,8 @@ export declare const accepts: {
|
||||
|
||||
export declare const events: {
|
||||
readonly stepEnded: "step-ended";
|
||||
readonly toolchainReported: "toolchain-reported";
|
||||
readonly stepProgress: "step-progress";
|
||||
};
|
||||
|
||||
export declare const family: { readonly name: "node-build-image.events"; readonly maxAgeDays: 7 };
|
||||
@@ -35,6 +37,18 @@ export declare function stepEndedSubject(node: string): string;
|
||||
|
||||
export declare const stepEndedSubjectAll: "mesh.seat.node-build-image.event.step-ended.*";
|
||||
|
||||
export declare const toolchainReportedStream: "SEAT_node-build-image_toolchain-reported";
|
||||
|
||||
export declare function toolchainReportedSubject(node: string): string;
|
||||
|
||||
export declare const toolchainReportedSubjectAll: "mesh.seat.node-build-image.event.toolchain-reported.*";
|
||||
|
||||
export declare const stepProgressStream: "SEAT_node-build-image_step-progress";
|
||||
|
||||
export declare function stepProgressSubject(node: string): string;
|
||||
|
||||
export declare const stepProgressSubjectAll: "mesh.seat.node-build-image.event.step-progress.*";
|
||||
|
||||
export declare const deliverySubject: "mesh.seat.node-build-image.accept.delivery";
|
||||
|
||||
export declare const checkSubject: "mesh.seat.node-build-image.accept.check";
|
||||
@@ -52,6 +66,12 @@ export declare const states: {
|
||||
readonly detectedSteps: "detected-steps";
|
||||
};
|
||||
|
||||
/** The key of one value of the state steps under the form "<asker>.<node>.<id>". */
|
||||
export declare function stepsByAskerNodeIDKey(asker: string, node: string, id: string): string;
|
||||
|
||||
/** The parts of a key of the form "<asker>.<node>.<id>", or undefined for a key of another form. */
|
||||
export declare function parseStepsByAskerNodeIDKey(_key: string): { asker: string; node: string; id: string } | undefined;
|
||||
|
||||
/** The key of one value of the state steps under the form "<node>.<id>". */
|
||||
export declare function stepsByNodeIDKey(node: string, id: string): string;
|
||||
|
||||
@@ -91,6 +111,7 @@ export declare function parseDetectedStepsByModuleArtifactKey(_key: string): { m
|
||||
export type AskArtifact = buildStep.AskArtifact;
|
||||
export type BuildAnswer = buildStep.BuildAnswer;
|
||||
export type BuiltArtifact = buildStep.BuiltArtifact;
|
||||
export type BuiltArtifactResolvedItem = buildStep.BuiltArtifactResolvedItem;
|
||||
export type Capacity = buildStep.Capacity;
|
||||
export type CheckAnswer = buildStep.CheckAnswer;
|
||||
export type ComposeAnswer = buildStep.ComposeAnswer;
|
||||
@@ -104,6 +125,7 @@ export type DropInput = buildStep.DropInput;
|
||||
export type Fetch = buildStep.Fetch;
|
||||
export type FetchAnswer = buildStep.FetchAnswer;
|
||||
export type FetchBesideItem = buildStep.FetchBesideItem;
|
||||
export type FetchBuildsItem = buildStep.FetchBuildsItem;
|
||||
export type FetchPackage = buildStep.FetchPackage;
|
||||
export type FetchRefusal = buildStep.FetchRefusal;
|
||||
export type KeptArtifactAnswer = buildStep.KeptArtifactAnswer;
|
||||
@@ -136,6 +158,8 @@ export type StepAskCompose = buildStep.StepAskCompose;
|
||||
export type StepEnded = buildStep.StepEnded;
|
||||
export type StepEndedEvent = buildStep.StepEndedEvent;
|
||||
export type StepNames = buildStep.StepNames;
|
||||
export type StepProgress = buildStep.StepProgress;
|
||||
export type StepProgressEvent = buildStep.StepProgressEvent;
|
||||
export type StepResult = buildStep.StepResult;
|
||||
export type StepRun = buildStep.StepRun;
|
||||
export type StepsAnswer = buildStep.StepsAnswer;
|
||||
@@ -143,6 +167,8 @@ export type StepsInput = buildStep.StepsInput;
|
||||
export type TakenVersion = buildStep.TakenVersion;
|
||||
export type TestCounts = buildStep.TestCounts;
|
||||
export type Toolchain = buildStep.Toolchain;
|
||||
export type ToolchainReported = buildStep.ToolchainReported;
|
||||
export type ToolchainReportedEvent = buildStep.ToolchainReportedEvent;
|
||||
export type ToolchainUsed = buildStep.ToolchainUsed;
|
||||
|
||||
/** is the body of delivery. */
|
||||
@@ -160,6 +186,9 @@ export type DeliveryStepAccept = StepAsk;
|
||||
/** is the body of recheck. */
|
||||
export type RecheckAccept = StepAsk;
|
||||
|
||||
/** is one value of the state steps under the key <asker>.<node>.<id>. */
|
||||
export type StepsByAskerNodeIDValue = Step;
|
||||
|
||||
/** is one value of the state steps under the key <node>.<id>. */
|
||||
export type StepsByNodeIDValue = Step;
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ export const accepts = {
|
||||
|
||||
export const events = {
|
||||
stepEnded: "step-ended",
|
||||
toolchainReported: "toolchain-reported",
|
||||
stepProgress: "step-progress",
|
||||
};
|
||||
|
||||
export const family = { name: "node-build-image.events", maxAgeDays: 7 };
|
||||
@@ -35,6 +37,22 @@ export function stepEndedSubject(node) {
|
||||
|
||||
export const stepEndedSubjectAll = "mesh.seat.node-build-image.event.step-ended.*";
|
||||
|
||||
export const toolchainReportedStream = "SEAT_node-build-image_toolchain-reported";
|
||||
|
||||
export function toolchainReportedSubject(node) {
|
||||
return "mesh.seat.node-build-image.event.toolchain-reported." + node;
|
||||
}
|
||||
|
||||
export const toolchainReportedSubjectAll = "mesh.seat.node-build-image.event.toolchain-reported.*";
|
||||
|
||||
export const stepProgressStream = "SEAT_node-build-image_step-progress";
|
||||
|
||||
export function stepProgressSubject(node) {
|
||||
return "mesh.seat.node-build-image.event.step-progress." + node;
|
||||
}
|
||||
|
||||
export const stepProgressSubjectAll = "mesh.seat.node-build-image.event.step-progress.*";
|
||||
|
||||
export const deliverySubject = "mesh.seat.node-build-image.accept.delivery";
|
||||
|
||||
export const checkSubject = "mesh.seat.node-build-image.accept.check";
|
||||
@@ -52,6 +70,29 @@ export const states = {
|
||||
detectedSteps: "detected-steps",
|
||||
};
|
||||
|
||||
/** The key of one value of the state steps under the form "<asker>.<node>.<id>". */
|
||||
export function stepsByAskerNodeIDKey(asker, node, id) {
|
||||
return asker + "." + node + "." + id;
|
||||
}
|
||||
|
||||
/** The parts of a key of the form "<asker>.<node>.<id>", or undefined for a key of another form. */
|
||||
export function parseStepsByAskerNodeIDKey(_key) {
|
||||
let _rest = _key;
|
||||
const _at0 = _rest.indexOf(".");
|
||||
if (_at0 <= 0) return undefined;
|
||||
const asker = _rest.slice(0, _at0);
|
||||
_rest = _rest.slice(_at0 + 1);
|
||||
const _at2 = _rest.indexOf(".");
|
||||
if (_at2 <= 0) return undefined;
|
||||
const node = _rest.slice(0, _at2);
|
||||
_rest = _rest.slice(_at2 + 1);
|
||||
if (_rest === "") return undefined;
|
||||
const id = _rest;
|
||||
_rest = "";
|
||||
if (_rest !== "") return undefined;
|
||||
return { asker, node, id };
|
||||
}
|
||||
|
||||
/** The key of one value of the state steps under the form "<node>.<id>". */
|
||||
export function stepsByNodeIDKey(node, id) {
|
||||
return node + "." + id;
|
||||
|
||||
+60
-4
@@ -13,6 +13,8 @@ export declare const verbs: {
|
||||
|
||||
export declare const events: {
|
||||
readonly stepEnded: "step-ended";
|
||||
readonly toolchainReported: "toolchain-reported";
|
||||
readonly stepProgress: "step-progress";
|
||||
};
|
||||
|
||||
/** one artifact of a build ask: built from the fetched tree, or copied from an upstream pin */
|
||||
@@ -32,11 +34,20 @@ export interface BuildAnswer {
|
||||
"toolchain"?: ToolchainUsed;
|
||||
}
|
||||
|
||||
export interface BuiltArtifactResolvedItem {
|
||||
/** sha256: of the package's bytes as the build fetched them */
|
||||
"digest": string;
|
||||
"package": string;
|
||||
"version": string;
|
||||
}
|
||||
|
||||
/** one artifact a build made: its digest and the reference it was published under */
|
||||
export interface BuiltArtifact {
|
||||
"artifact": string;
|
||||
"digest": string;
|
||||
"reference"?: string;
|
||||
/** one per mesh package the build resolved (the catalog graph entry's packages, the edges of kind 1) */
|
||||
"resolved"?: BuiltArtifactResolvedItem[];
|
||||
}
|
||||
|
||||
/** how many steps the node runs at once */
|
||||
@@ -100,6 +111,15 @@ export interface FetchBesideItem {
|
||||
"repository": string;
|
||||
}
|
||||
|
||||
export interface FetchBuildsItem {
|
||||
"artifact": string;
|
||||
/** the member's build digest, sha256: */
|
||||
"digest": string;
|
||||
"module": string;
|
||||
/** the name in its ecosystem (the Go module path, the npm name) */
|
||||
"package": string;
|
||||
}
|
||||
|
||||
/** the TypeScript module's group package: the group's tagged version of the SDK ({name, version}), placed from the registry in each member whatever its lock names, or the group's package member ({repository, commit, ref}), packed by the fetch and served to the members in place of the registry's */
|
||||
export interface FetchPackage {
|
||||
"commit"?: string;
|
||||
@@ -115,6 +135,8 @@ export interface Fetch {
|
||||
"base"?: string;
|
||||
/** the sibling repositories placed beside the fetched tree */
|
||||
"beside"?: FetchBesideItem[];
|
||||
/** a group run's package members, each by its build's digest: the build module reads each from the artifact store at artifact-store://<module>/<artifact>/blobs/<digest>, checks the bytes against the digest and lays them as the group package; a package named here is named in neither packages nor package */
|
||||
"builds"?: FetchBuildsItem[];
|
||||
/** the full commit */
|
||||
"commit": string;
|
||||
/** the module's directory in the repository ("." for its root), with paths */
|
||||
@@ -416,6 +438,14 @@ export interface StepNames {
|
||||
"repositories": string[];
|
||||
}
|
||||
|
||||
/** a running ask's progress, said once a minute while the holder runs it (design 47 G section 496) */
|
||||
export interface StepProgress {
|
||||
"ask": string;
|
||||
"asker": string;
|
||||
"at": string;
|
||||
"phase": "fetch" | "build" | "run" | "report";
|
||||
}
|
||||
|
||||
/** what a step answered, by the kind of ask: a build's artifacts, toolchain, imports, out-of-step modules and the artifact it kept; a check's fetch and levels; a compose's built, key and verdict; a run's built, key, exit, output and said; a declined ask's declined, needs and lacks. The fields of the kinds do not meet, so one layout holds the five */
|
||||
export interface StepResult {
|
||||
"artifacts"?: BuiltArtifact[];
|
||||
@@ -502,8 +532,20 @@ export interface Toolchain {
|
||||
"version": string;
|
||||
}
|
||||
|
||||
/** a node's toolchain of a language, said at the build module's start and each time its toolchains key of the language changes (design 47 G section 492) */
|
||||
export interface ToolchainReported {
|
||||
/** the digest of the program of the provision the build module requires for that language's toolchain, never the build module's own */
|
||||
"digest": string;
|
||||
"language": string;
|
||||
"node": string;
|
||||
"recipe": string;
|
||||
"version": string;
|
||||
}
|
||||
|
||||
/** the toolchain a build was made with: the language, the installed version and the build module's recipe */
|
||||
export interface ToolchainUsed {
|
||||
/** the building node's digest of its toolchain program (kind 3's entry), never the build module's own program */
|
||||
"digest"?: string;
|
||||
"language": string;
|
||||
"recipe": string;
|
||||
"version": string;
|
||||
@@ -579,22 +621,36 @@ export interface KeptArtifactAnswer {
|
||||
export interface StepsInput {
|
||||
/** find them anew, not from the kept answer */
|
||||
"again"?: boolean;
|
||||
/** the artifact reference, <module>:<artifact> */
|
||||
/** the key of detected-steps, <module>.<artifact> */
|
||||
"artifact": string;
|
||||
"fetch": Fetch;
|
||||
/** delete the artifact's kept key instead of detecting; needs why */
|
||||
"drop"?: boolean;
|
||||
"fetch"?: Fetch;
|
||||
"fingerprint"?: string;
|
||||
"module": string;
|
||||
/** with drop, why: a non-empty text, written in the hand-act log with the caller the bus proved */
|
||||
"why"?: string;
|
||||
}
|
||||
|
||||
/** is what steps answers. */
|
||||
export interface StepsAnswer {
|
||||
"artifact": string;
|
||||
"artifact"?: string;
|
||||
/** the artifact whose key a drop deleted */
|
||||
"dropped"?: string;
|
||||
/** why a drop deleted nothing, naming the hand-act log when it refused */
|
||||
"error"?: string;
|
||||
"fingerprint"?: string;
|
||||
/** answered from the kept answer */
|
||||
"kept"?: boolean;
|
||||
"steps": DetectedStep[];
|
||||
"steps"?: DetectedStep[];
|
||||
}
|
||||
|
||||
/** is the body of the event step-ended. */
|
||||
export type StepEndedEvent = StepEnded;
|
||||
|
||||
/** is the body of the event toolchain-reported. */
|
||||
export type ToolchainReportedEvent = ToolchainReported;
|
||||
|
||||
/** is the body of the event step-progress. */
|
||||
export type StepProgressEvent = StepProgress;
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ export const verbs = {
|
||||
|
||||
export const events = {
|
||||
stepEnded: "step-ended",
|
||||
toolchainReported: "toolchain-reported",
|
||||
stepProgress: "step-progress",
|
||||
};
|
||||
|
||||
export {};
|
||||
|
||||
+29
@@ -25,6 +25,8 @@ export declare const accepts: {
|
||||
|
||||
export declare const events: {
|
||||
readonly stepEnded: "step-ended";
|
||||
readonly toolchainReported: "toolchain-reported";
|
||||
readonly stepProgress: "step-progress";
|
||||
};
|
||||
|
||||
export declare const family: { readonly name: "node-build-node.events"; readonly maxAgeDays: 7 };
|
||||
@@ -35,6 +37,18 @@ export declare function stepEndedSubject(node: string): string;
|
||||
|
||||
export declare const stepEndedSubjectAll: "mesh.seat.node-build-node.event.step-ended.*";
|
||||
|
||||
export declare const toolchainReportedStream: "SEAT_node-build-node_toolchain-reported";
|
||||
|
||||
export declare function toolchainReportedSubject(node: string): string;
|
||||
|
||||
export declare const toolchainReportedSubjectAll: "mesh.seat.node-build-node.event.toolchain-reported.*";
|
||||
|
||||
export declare const stepProgressStream: "SEAT_node-build-node_step-progress";
|
||||
|
||||
export declare function stepProgressSubject(node: string): string;
|
||||
|
||||
export declare const stepProgressSubjectAll: "mesh.seat.node-build-node.event.step-progress.*";
|
||||
|
||||
export declare const deliverySubject: "mesh.seat.node-build-node.accept.delivery";
|
||||
|
||||
export declare const checkSubject: "mesh.seat.node-build-node.accept.check";
|
||||
@@ -52,6 +66,12 @@ export declare const states: {
|
||||
readonly detectedSteps: "detected-steps";
|
||||
};
|
||||
|
||||
/** The key of one value of the state steps under the form "<asker>.<node>.<id>". */
|
||||
export declare function stepsByAskerNodeIDKey(asker: string, node: string, id: string): string;
|
||||
|
||||
/** The parts of a key of the form "<asker>.<node>.<id>", or undefined for a key of another form. */
|
||||
export declare function parseStepsByAskerNodeIDKey(_key: string): { asker: string; node: string; id: string } | undefined;
|
||||
|
||||
/** The key of one value of the state steps under the form "<node>.<id>". */
|
||||
export declare function stepsByNodeIDKey(node: string, id: string): string;
|
||||
|
||||
@@ -91,6 +111,7 @@ export declare function parseDetectedStepsByModuleArtifactKey(_key: string): { m
|
||||
export type AskArtifact = buildStep.AskArtifact;
|
||||
export type BuildAnswer = buildStep.BuildAnswer;
|
||||
export type BuiltArtifact = buildStep.BuiltArtifact;
|
||||
export type BuiltArtifactResolvedItem = buildStep.BuiltArtifactResolvedItem;
|
||||
export type Capacity = buildStep.Capacity;
|
||||
export type CheckAnswer = buildStep.CheckAnswer;
|
||||
export type ComposeAnswer = buildStep.ComposeAnswer;
|
||||
@@ -104,6 +125,7 @@ export type DropInput = buildStep.DropInput;
|
||||
export type Fetch = buildStep.Fetch;
|
||||
export type FetchAnswer = buildStep.FetchAnswer;
|
||||
export type FetchBesideItem = buildStep.FetchBesideItem;
|
||||
export type FetchBuildsItem = buildStep.FetchBuildsItem;
|
||||
export type FetchPackage = buildStep.FetchPackage;
|
||||
export type FetchRefusal = buildStep.FetchRefusal;
|
||||
export type KeptArtifactAnswer = buildStep.KeptArtifactAnswer;
|
||||
@@ -136,6 +158,8 @@ export type StepAskCompose = buildStep.StepAskCompose;
|
||||
export type StepEnded = buildStep.StepEnded;
|
||||
export type StepEndedEvent = buildStep.StepEndedEvent;
|
||||
export type StepNames = buildStep.StepNames;
|
||||
export type StepProgress = buildStep.StepProgress;
|
||||
export type StepProgressEvent = buildStep.StepProgressEvent;
|
||||
export type StepResult = buildStep.StepResult;
|
||||
export type StepRun = buildStep.StepRun;
|
||||
export type StepsAnswer = buildStep.StepsAnswer;
|
||||
@@ -143,6 +167,8 @@ export type StepsInput = buildStep.StepsInput;
|
||||
export type TakenVersion = buildStep.TakenVersion;
|
||||
export type TestCounts = buildStep.TestCounts;
|
||||
export type Toolchain = buildStep.Toolchain;
|
||||
export type ToolchainReported = buildStep.ToolchainReported;
|
||||
export type ToolchainReportedEvent = buildStep.ToolchainReportedEvent;
|
||||
export type ToolchainUsed = buildStep.ToolchainUsed;
|
||||
|
||||
/** is the body of delivery. */
|
||||
@@ -160,6 +186,9 @@ export type DeliveryStepAccept = StepAsk;
|
||||
/** is the body of recheck. */
|
||||
export type RecheckAccept = StepAsk;
|
||||
|
||||
/** is one value of the state steps under the key <asker>.<node>.<id>. */
|
||||
export type StepsByAskerNodeIDValue = Step;
|
||||
|
||||
/** is one value of the state steps under the key <node>.<id>. */
|
||||
export type StepsByNodeIDValue = Step;
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ export const accepts = {
|
||||
|
||||
export const events = {
|
||||
stepEnded: "step-ended",
|
||||
toolchainReported: "toolchain-reported",
|
||||
stepProgress: "step-progress",
|
||||
};
|
||||
|
||||
export const family = { name: "node-build-node.events", maxAgeDays: 7 };
|
||||
@@ -35,6 +37,22 @@ export function stepEndedSubject(node) {
|
||||
|
||||
export const stepEndedSubjectAll = "mesh.seat.node-build-node.event.step-ended.*";
|
||||
|
||||
export const toolchainReportedStream = "SEAT_node-build-node_toolchain-reported";
|
||||
|
||||
export function toolchainReportedSubject(node) {
|
||||
return "mesh.seat.node-build-node.event.toolchain-reported." + node;
|
||||
}
|
||||
|
||||
export const toolchainReportedSubjectAll = "mesh.seat.node-build-node.event.toolchain-reported.*";
|
||||
|
||||
export const stepProgressStream = "SEAT_node-build-node_step-progress";
|
||||
|
||||
export function stepProgressSubject(node) {
|
||||
return "mesh.seat.node-build-node.event.step-progress." + node;
|
||||
}
|
||||
|
||||
export const stepProgressSubjectAll = "mesh.seat.node-build-node.event.step-progress.*";
|
||||
|
||||
export const deliverySubject = "mesh.seat.node-build-node.accept.delivery";
|
||||
|
||||
export const checkSubject = "mesh.seat.node-build-node.accept.check";
|
||||
@@ -52,6 +70,29 @@ export const states = {
|
||||
detectedSteps: "detected-steps",
|
||||
};
|
||||
|
||||
/** The key of one value of the state steps under the form "<asker>.<node>.<id>". */
|
||||
export function stepsByAskerNodeIDKey(asker, node, id) {
|
||||
return asker + "." + node + "." + id;
|
||||
}
|
||||
|
||||
/** The parts of a key of the form "<asker>.<node>.<id>", or undefined for a key of another form. */
|
||||
export function parseStepsByAskerNodeIDKey(_key) {
|
||||
let _rest = _key;
|
||||
const _at0 = _rest.indexOf(".");
|
||||
if (_at0 <= 0) return undefined;
|
||||
const asker = _rest.slice(0, _at0);
|
||||
_rest = _rest.slice(_at0 + 1);
|
||||
const _at2 = _rest.indexOf(".");
|
||||
if (_at2 <= 0) return undefined;
|
||||
const node = _rest.slice(0, _at2);
|
||||
_rest = _rest.slice(_at2 + 1);
|
||||
if (_rest === "") return undefined;
|
||||
const id = _rest;
|
||||
_rest = "";
|
||||
if (_rest !== "") return undefined;
|
||||
return { asker, node, id };
|
||||
}
|
||||
|
||||
/** The key of one value of the state steps under the form "<node>.<id>". */
|
||||
export function stepsByNodeIDKey(node, id) {
|
||||
return node + "." + id;
|
||||
|
||||
+60
-4
@@ -13,6 +13,8 @@ export declare const verbs: {
|
||||
|
||||
export declare const events: {
|
||||
readonly stepEnded: "step-ended";
|
||||
readonly toolchainReported: "toolchain-reported";
|
||||
readonly stepProgress: "step-progress";
|
||||
};
|
||||
|
||||
/** one artifact of a build ask: built from the fetched tree, or copied from an upstream pin */
|
||||
@@ -32,11 +34,20 @@ export interface BuildAnswer {
|
||||
"toolchain"?: ToolchainUsed;
|
||||
}
|
||||
|
||||
export interface BuiltArtifactResolvedItem {
|
||||
/** sha256: of the package's bytes as the build fetched them */
|
||||
"digest": string;
|
||||
"package": string;
|
||||
"version": string;
|
||||
}
|
||||
|
||||
/** one artifact a build made: its digest and the reference it was published under */
|
||||
export interface BuiltArtifact {
|
||||
"artifact": string;
|
||||
"digest": string;
|
||||
"reference"?: string;
|
||||
/** one per mesh package the build resolved (the catalog graph entry's packages, the edges of kind 1) */
|
||||
"resolved"?: BuiltArtifactResolvedItem[];
|
||||
}
|
||||
|
||||
/** how many steps the node runs at once */
|
||||
@@ -100,6 +111,15 @@ export interface FetchBesideItem {
|
||||
"repository": string;
|
||||
}
|
||||
|
||||
export interface FetchBuildsItem {
|
||||
"artifact": string;
|
||||
/** the member's build digest, sha256: */
|
||||
"digest": string;
|
||||
"module": string;
|
||||
/** the name in its ecosystem (the Go module path, the npm name) */
|
||||
"package": string;
|
||||
}
|
||||
|
||||
/** the TypeScript module's group package: the group's tagged version of the SDK ({name, version}), placed from the registry in each member whatever its lock names, or the group's package member ({repository, commit, ref}), packed by the fetch and served to the members in place of the registry's */
|
||||
export interface FetchPackage {
|
||||
"commit"?: string;
|
||||
@@ -115,6 +135,8 @@ export interface Fetch {
|
||||
"base"?: string;
|
||||
/** the sibling repositories placed beside the fetched tree */
|
||||
"beside"?: FetchBesideItem[];
|
||||
/** a group run's package members, each by its build's digest: the build module reads each from the artifact store at artifact-store://<module>/<artifact>/blobs/<digest>, checks the bytes against the digest and lays them as the group package; a package named here is named in neither packages nor package */
|
||||
"builds"?: FetchBuildsItem[];
|
||||
/** the full commit */
|
||||
"commit": string;
|
||||
/** the module's directory in the repository ("." for its root), with paths */
|
||||
@@ -416,6 +438,14 @@ export interface StepNames {
|
||||
"repositories": string[];
|
||||
}
|
||||
|
||||
/** a running ask's progress, said once a minute while the holder runs it (design 47 G section 496) */
|
||||
export interface StepProgress {
|
||||
"ask": string;
|
||||
"asker": string;
|
||||
"at": string;
|
||||
"phase": "fetch" | "build" | "run" | "report";
|
||||
}
|
||||
|
||||
/** what a step answered, by the kind of ask: a build's artifacts, toolchain, imports, out-of-step modules and the artifact it kept; a check's fetch and levels; a compose's built, key and verdict; a run's built, key, exit, output and said; a declined ask's declined, needs and lacks. The fields of the kinds do not meet, so one layout holds the five */
|
||||
export interface StepResult {
|
||||
"artifacts"?: BuiltArtifact[];
|
||||
@@ -502,8 +532,20 @@ export interface Toolchain {
|
||||
"version": string;
|
||||
}
|
||||
|
||||
/** a node's toolchain of a language, said at the build module's start and each time its toolchains key of the language changes (design 47 G section 492) */
|
||||
export interface ToolchainReported {
|
||||
/** the digest of the program of the provision the build module requires for that language's toolchain, never the build module's own */
|
||||
"digest": string;
|
||||
"language": string;
|
||||
"node": string;
|
||||
"recipe": string;
|
||||
"version": string;
|
||||
}
|
||||
|
||||
/** the toolchain a build was made with: the language, the installed version and the build module's recipe */
|
||||
export interface ToolchainUsed {
|
||||
/** the building node's digest of its toolchain program (kind 3's entry), never the build module's own program */
|
||||
"digest"?: string;
|
||||
"language": string;
|
||||
"recipe": string;
|
||||
"version": string;
|
||||
@@ -579,22 +621,36 @@ export interface KeptArtifactAnswer {
|
||||
export interface StepsInput {
|
||||
/** find them anew, not from the kept answer */
|
||||
"again"?: boolean;
|
||||
/** the artifact reference, <module>:<artifact> */
|
||||
/** the key of detected-steps, <module>.<artifact> */
|
||||
"artifact": string;
|
||||
"fetch": Fetch;
|
||||
/** delete the artifact's kept key instead of detecting; needs why */
|
||||
"drop"?: boolean;
|
||||
"fetch"?: Fetch;
|
||||
"fingerprint"?: string;
|
||||
"module": string;
|
||||
/** with drop, why: a non-empty text, written in the hand-act log with the caller the bus proved */
|
||||
"why"?: string;
|
||||
}
|
||||
|
||||
/** is what steps answers. */
|
||||
export interface StepsAnswer {
|
||||
"artifact": string;
|
||||
"artifact"?: string;
|
||||
/** the artifact whose key a drop deleted */
|
||||
"dropped"?: string;
|
||||
/** why a drop deleted nothing, naming the hand-act log when it refused */
|
||||
"error"?: string;
|
||||
"fingerprint"?: string;
|
||||
/** answered from the kept answer */
|
||||
"kept"?: boolean;
|
||||
"steps": DetectedStep[];
|
||||
"steps"?: DetectedStep[];
|
||||
}
|
||||
|
||||
/** is the body of the event step-ended. */
|
||||
export type StepEndedEvent = StepEnded;
|
||||
|
||||
/** is the body of the event toolchain-reported. */
|
||||
export type ToolchainReportedEvent = ToolchainReported;
|
||||
|
||||
/** is the body of the event step-progress. */
|
||||
export type StepProgressEvent = StepProgress;
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ export const verbs = {
|
||||
|
||||
export const events = {
|
||||
stepEnded: "step-ended",
|
||||
toolchainReported: "toolchain-reported",
|
||||
stepProgress: "step-progress",
|
||||
};
|
||||
|
||||
export {};
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"mesh-delivery-view/internal/view"
|
||||
)
|
||||
|
||||
// catalogWithin is how long a call waits for the seat mesh-catalog's first view before it says there is none.
|
||||
var catalogWithin = 30 * time.Second
|
||||
|
||||
// catalogRead is the modules the view's tools answer, read from the catalog half through the seat mesh-catalog's verb
|
||||
// `revision` (design 47, Two graphs): the trunk's newest revision, asked once per call and in pages, each entry in the
|
||||
// form of the value the tools always answered. With no view the read says so; it never answers no modules.
|
||||
func catalogRead(ask func(key string, body any) (json.RawMessage, error)) view.StateRead {
|
||||
var (
|
||||
once sync.Once
|
||||
values map[string]json.RawMessage
|
||||
rev uint64
|
||||
failed error
|
||||
)
|
||||
load := func() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), catalogWithin)
|
||||
defer cancel()
|
||||
c, err := meshcatalogclient.NewCatalog(ask, "main")
|
||||
if err != nil {
|
||||
failed = err
|
||||
return
|
||||
}
|
||||
if err := c.Start(ctx); err != nil {
|
||||
failed = fmt.Errorf("the catalog half has no first view yet (%s): %w", meshcatalogclient.RevisionVerb, err)
|
||||
return
|
||||
}
|
||||
half, ok := c.View()
|
||||
if !ok {
|
||||
failed = fmt.Errorf("the catalog half has no first view yet (%s)", meshcatalogclient.RevisionVerb)
|
||||
return
|
||||
}
|
||||
rev, values = uint64(half.Revision.Revision), map[string]json.RawMessage{}
|
||||
for module, e := range half.Entries {
|
||||
raw, _ := json.Marshal(e)
|
||||
v, err := view.ModuleValue(raw)
|
||||
if err != nil {
|
||||
failed = fmt.Errorf("the catalog entry of %s: %w", module, err)
|
||||
return
|
||||
}
|
||||
values[module] = v
|
||||
}
|
||||
}
|
||||
read := func() (map[string]json.RawMessage, uint64, error) {
|
||||
once.Do(load)
|
||||
return values, rev, failed
|
||||
}
|
||||
return view.StateRead{
|
||||
Keys: func() ([]string, error) {
|
||||
v, _, err := read()
|
||||
keys := make([]string, 0, len(v))
|
||||
for k := range v {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return keys, err
|
||||
},
|
||||
Get: func(key string) (json.RawMessage, error) {
|
||||
v, _, err := read()
|
||||
return v[key], err
|
||||
},
|
||||
GetEntry: func(key string) (json.RawMessage, uint64, error) {
|
||||
v, r, err := read()
|
||||
return v[key], r, err
|
||||
},
|
||||
All: func() (map[string]json.RawMessage, error) {
|
||||
v, _, err := read()
|
||||
return v, err
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -396,7 +396,7 @@ func ownersRead(ask func(key string, body any) (json.RawMessage, error)) view.Re
|
||||
// Each owner's read tool as the view's invokes names it.
|
||||
indexer := func(tool string) view.StateRead { return view.OwnerRead(ask, view.IndexerModule+"."+tool) }
|
||||
assigner := func(tool string) view.StateRead { return view.OwnerRead(ask, view.AssignerModule+"."+tool) }
|
||||
return view.Reader{State: indexer("indexer_read_modules"), CandidatesState: indexer("indexer_read_candidates"),
|
||||
return view.Reader{State: catalogRead(ask), CandidatesState: indexer("indexer_read_candidates"),
|
||||
WantsState: assigner("assigner_read_wants"), DifferencesState: assigner("assigner_read_differences"),
|
||||
HeardState: indexer("indexer_read_heard"),
|
||||
PredefinedState: indexer("indexer_read_predefined"),
|
||||
|
||||
@@ -3,12 +3,14 @@ package main
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient"
|
||||
"mesh-delivery-view/internal/view"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"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/views"
|
||||
)
|
||||
@@ -51,7 +53,6 @@ func ownersAnswerTheToolsAsBefore(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
states := map[string]source{
|
||||
view.IndexerModule + ".modules": {"a": withFP2, "b": withNone},
|
||||
view.IndexerModule + ".candidates": {"feat/x": cand, "fix": `{"v":1,"group":"fix","base":3,"heads":"h","pulls":[{"modules":["netcheck"]}]}`},
|
||||
view.AssignerModule + ".wants": {"want.ace.zsh": `{"fp":"` + fp2A + `"}`, "want.novox.zsh": `{"fp":"` + fp2B + `"}`,
|
||||
"held." + keyB: `{"node":"ace","words":"failed"}`},
|
||||
@@ -67,14 +68,12 @@ func ownersAnswerTheToolsAsBefore(t *testing.T) {
|
||||
const heard1 = `{"v":1,"subject":"s","sequence":41,"repository":"novox/mesh-catalog","commit":"9cdac09a"}`
|
||||
states[view.IndexerModule+".heard"] = source{"forge": heard1}
|
||||
histories := map[string]map[string][]views.Revision{
|
||||
view.IndexerModule + ".modules": {"a": {{Revision: 7, At: at, Op: "put", Value: json.RawMessage(withNone)},
|
||||
{Revision: 9, At: at.Add(time.Minute), Op: "put", Value: json.RawMessage(withFP2)}}},
|
||||
view.IndexerModule + ".heard": {"forge": {{Revision: 3, At: at, Op: "put", Value: json.RawMessage(heard1)}}},
|
||||
view.AssignerModule + ".wants": {"want.ace.zsh": {{Revision: 12, At: at, Op: "put", Value: json.RawMessage(`{"fp":"` + fp2A + `"}`)}}},
|
||||
}
|
||||
// The direct reading, as the tools read the states before (the reads the manifest no longer has).
|
||||
st := func(name string) view.StateRead { return fake(states[name]).reader().State }
|
||||
direct := view.Reader{State: st(view.IndexerModule + ".modules"), CandidatesState: st(view.IndexerModule + ".candidates"),
|
||||
direct := view.Reader{CandidatesState: st(view.IndexerModule + ".candidates"),
|
||||
WantsState: st(view.AssignerModule + ".wants"), DifferencesState: st(view.AssignerModule + ".differences"),
|
||||
PredefinedState: st(view.IndexerModule + "." + view.IndexerPredefinedState),
|
||||
ConfigCandidatesState: st(view.IndexerModule + "." + view.IndexerConfigCandidatesState),
|
||||
@@ -89,6 +88,10 @@ func ownersAnswerTheToolsAsBefore(t *testing.T) {
|
||||
}
|
||||
asked := map[string]int{}
|
||||
ask := func(key string, body any) (json.RawMessage, error) {
|
||||
if key == meshcatalogclient.RevisionVerb {
|
||||
asked[key]++
|
||||
return json.Marshal(catalogAnswer)
|
||||
}
|
||||
name, ok := tools[key]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no tool %s", key)
|
||||
@@ -118,10 +121,6 @@ func ownersAnswerTheToolsAsBefore(t *testing.T) {
|
||||
tool string
|
||||
args map[string]any
|
||||
}{
|
||||
{view.ModuleReadTool, map[string]any{"module": "a"}},
|
||||
{view.ModuleReadTool, map[string]any{"module": "gone"}},
|
||||
{view.ModulesReadTool, map[string]any{}},
|
||||
{view.ModulesReadTool, map[string]any{"none2": true}},
|
||||
{view.CandidateReadTool, map[string]any{"group": "feat/x"}},
|
||||
{view.CandidatesReadTool, map[string]any{}},
|
||||
{view.CandidatesReadTool, map[string]any{"module": "zsh"}},
|
||||
@@ -143,12 +142,24 @@ func ownersAnswerTheToolsAsBefore(t *testing.T) {
|
||||
t.Errorf("%s %v through the owner's read tool = %s, %v; reading the state it was %s, %v", row.tool, row.args, gj, gerr, wj, werr)
|
||||
}
|
||||
}
|
||||
// A key's history is asked of the owner's read tool and answered from its kept values (A2j finding 1, J4).
|
||||
out, err := run(t, owned, view.ModuleHistoryTool, map[string]any{"module": "a"})
|
||||
if h, _ := out.(view.ModuleHistory); err != nil || len(h.Values) != 2 || h.Values[0].Revision != 9 || h.Values[1].FP2 != "" || !h.Values[1].At.Equal(at) {
|
||||
t.Errorf("indexer_module_history a through the indexer's read tool = %+v, %v", out, err)
|
||||
// The modules are the catalog half's, asked of the seat mesh-catalog's verb revision (W16.1): a module is its entry in
|
||||
// the form of the value the tool always answered, the modules without a fingerprint say why, and the module's history
|
||||
// is not a thing the catalog half keeps.
|
||||
a, err := run(t, owned, view.ModuleReadTool, map[string]any{"module": "a"})
|
||||
if raw, _ := json.Marshal(a); err != nil || !strings.Contains(string(raw), `"fp2":"`+fp2A+`"`) || !strings.Contains(string(raw), `"commit":"c1"`) || !strings.Contains(string(raw), `"src":7`) {
|
||||
t.Errorf("indexer_module a = %s, %v", raw, err)
|
||||
}
|
||||
out, err = run(t, owned, view.HeardTool, map[string]any{})
|
||||
if _, err := run(t, owned, view.ModuleReadTool, map[string]any{"module": "gone"}); err == nil {
|
||||
t.Errorf("indexer_module gone answered a module the catalog half does not hold")
|
||||
}
|
||||
all, err := run(t, owned, view.ModulesReadTool, map[string]any{"none2": true})
|
||||
if raw, _ := json.Marshal(all); err != nil || !strings.Contains(string(raw), `"b"`) || strings.Contains(string(raw), `"a"`) || !strings.Contains(string(raw), "builder's fingerprint not yet known") {
|
||||
t.Errorf("indexer_modules none2 = %s, %v", raw, err)
|
||||
}
|
||||
if _, err := run(t, owned, view.ModuleHistoryTool, map[string]any{"module": "a"}); err == nil {
|
||||
t.Errorf("indexer_module_history answered a history the catalog half does not keep")
|
||||
}
|
||||
out, err := run(t, owned, view.HeardTool, map[string]any{})
|
||||
if rows, _ := out.(view.HeardAnswer); err != nil || len(rows.Heard) != 1 || rows.Heard[0].Revision != 3 || string(rows.Heard[0].Heard) != heard1 {
|
||||
t.Errorf("indexer_heard through the indexer's read tool = %+v, %v", out, err)
|
||||
}
|
||||
@@ -156,10 +167,29 @@ func ownersAnswerTheToolsAsBefore(t *testing.T) {
|
||||
if revs, err := owned.WantsState.History("want.ace.zsh"); err != nil || len(revs) != 1 || !revs[0].At.Equal(at) || revs[0].Revision != 12 {
|
||||
t.Errorf("the want's history through the assigner's read tool = %+v, %v", revs, err)
|
||||
}
|
||||
// With no view of the catalog half the modules are not read: the tools say so, never that no module is held.
|
||||
catalogWithin = 20 * time.Millisecond
|
||||
none := ownersRead(func(key string, body any) (json.RawMessage, error) { return nil, fmt.Errorf("the holder is away") })
|
||||
if out, err := run(t, none, view.ModulesReadTool, map[string]any{}); err == nil || !strings.Contains(err.Error(), "no first view") {
|
||||
t.Errorf("indexer_modules with no view = %v, %v, want an error naming it", out, err)
|
||||
}
|
||||
// Each owner state the tools read was asked of its owner's read tool.
|
||||
for tool, name := range tools {
|
||||
if name != view.IndexerModule+"."+graph.State && asked[tool] == 0 {
|
||||
t.Errorf("%s was never asked", tool)
|
||||
}
|
||||
}
|
||||
if asked[meshcatalogclient.RevisionVerb] == 0 {
|
||||
t.Errorf("%s was never asked", meshcatalogclient.RevisionVerb)
|
||||
}
|
||||
}
|
||||
|
||||
// catalogAnswer is the seat mesh-catalog's newest trunk revision: a with a fingerprint and the source revision it moved at,
|
||||
// b with none and the reason.
|
||||
var catalogAnswer = meshcatalog.RevisionAnswer{
|
||||
Revision: meshcatalog.CatalogRevision{Revision: 4, Kind: meshcatalogclient.KindTrunk, Source: 7},
|
||||
Entries: []meshcatalog.CatalogEntry{
|
||||
{Module: "a", Repository: "novox/r", Commit: "c1", Path: ".", Manifest: map[string]any{"module": "a"}, Fp: fp2A, Inputs: &meshcatalog.EntryInputs{Src: 7}},
|
||||
{Module: "b", Repository: "novox/r", Commit: "c2", Path: "modules/b", Manifest: map[string]any{"module": "b"}, None: "builder's fingerprint not yet known"},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -47,6 +48,67 @@ func serveOwners(t *testing.T, ctx context.Context, js jetstream.JetStream, nc *
|
||||
}
|
||||
}
|
||||
|
||||
// catalogHolder is the seat mesh-catalog's holder as the view's process needs it (W16.1): its verb revision answering the
|
||||
// trunk's newest revision whole, and its event catalog-revision said on the shared events stream at each change, which the
|
||||
// view's durable consumer hears.
|
||||
type catalogHolder struct {
|
||||
t testing.TB
|
||||
js jetstream.JetStream
|
||||
mu sync.Mutex
|
||||
// rev is the revision's number; entries are its entries by module.
|
||||
rev int64
|
||||
entries map[string]meshcatalog.CatalogEntry
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func serveCatalog(t testing.TB, ctx context.Context, js jetstream.JetStream, nc *nats.Conn) *catalogHolder {
|
||||
t.Helper()
|
||||
h := &catalogHolder{t: t, js: js, ctx: ctx, rev: 1, entries: map[string]meshcatalog.CatalogEntry{}}
|
||||
events, err := js.Stream(ctx, process.EventsStream)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
info := events.CachedInfo().Config
|
||||
if !slices.Contains(info.Subjects, "mesh.seat.*.event.>") {
|
||||
info.Subjects = append(info.Subjects, "mesh.seat.*.event.>")
|
||||
if _, err := js.UpdateStream(ctx, info); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
answer := func(m *nats.Msg) {
|
||||
h.mu.Lock()
|
||||
a := meshcatalog.RevisionAnswer{Revision: meshcatalog.CatalogRevision{Revision: h.rev, Kind: meshcatalogclient.KindTrunk}}
|
||||
for _, e := range h.entries {
|
||||
a.Entries = append(a.Entries, e)
|
||||
}
|
||||
h.mu.Unlock()
|
||||
raw, _ := json.Marshal(map[string]any{"result": a, "node": "n0"})
|
||||
_ = m.Respond(raw)
|
||||
}
|
||||
for _, subject := range []string{"mesh.seat.mesh-catalog.call.revision.>", "mesh.seat.mesh-catalog.call.revision"} {
|
||||
sub, err := nc.Subscribe(subject, answer)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { _ = sub.Unsubscribe() })
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
// put is the holder's next trunk revision with the entry of one module changed, said as catalog-revision.
|
||||
func (h *catalogHolder) put(e meshcatalog.CatalogEntry) {
|
||||
h.t.Helper()
|
||||
h.mu.Lock()
|
||||
h.rev++
|
||||
h.entries[e.Module] = e
|
||||
rev := h.rev
|
||||
h.mu.Unlock()
|
||||
body, _ := json.Marshal(meshcatalog.CatalogRevisionEvent{Kind: meshcatalogclient.KindTrunk, Branch: "main", Revision: rev, Changed: []string{e.Module}})
|
||||
if _, err := h.js.Publish(h.ctx, "mesh.seat.mesh-catalog.event."+meshcatalog.EventCatalogRevision, body); err != nil {
|
||||
h.t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestProcessWholeSeesTheBuckets starts the program the way the node does (its entry, run, with its environment and a
|
||||
// broker file) against an embedded bus holding the three buckets, and asks its socket.
|
||||
func TestProcessWholeSeesTheBuckets(t *testing.T) { // 1134/AC8
|
||||
@@ -126,6 +188,7 @@ func TestProcessWholeSeesTheBuckets(t *testing.T) { // 1134/AC8
|
||||
runCtx, stop := context.WithCancel(ctx)
|
||||
done := make(chan error, 1)
|
||||
serveOwners(t, ctx, js, nc)
|
||||
serveCatalog(t, ctx, js, nc)
|
||||
go func() { done <- run(runCtx, func(k string) string { return env[k] }, io.Discard) }()
|
||||
t.Cleanup(func() { stop(); <-done })
|
||||
|
||||
@@ -226,6 +289,7 @@ func TestProcessWholeShowsWhatTheWatchmanSays(t *testing.T) { // 1168/AC5
|
||||
runCtx, stop := context.WithCancel(ctx)
|
||||
done := make(chan error, 1)
|
||||
serveOwners(t, ctx, js, nc)
|
||||
serveCatalog(t, ctx, js, nc)
|
||||
go func() { done <- run(runCtx, func(k string) string { return env[k] }, io.Discard) }()
|
||||
t.Cleanup(func() { stop(); <-done })
|
||||
|
||||
@@ -293,6 +357,7 @@ func TestProcessWholeShowsTheIndexersBucketsOnceTheyExist(t *testing.T) { // 124
|
||||
runCtx, stop := context.WithCancel(ctx)
|
||||
done := make(chan error, 1)
|
||||
serveOwners(t, ctx, js, nc)
|
||||
catalog := serveCatalog(t, ctx, js, nc)
|
||||
go func() { done <- run(runCtx, func(k string) string { return env[k] }, io.Discard) }()
|
||||
t.Cleanup(func() { stop(); <-done })
|
||||
|
||||
@@ -306,26 +371,16 @@ func TestProcessWholeShowsTheIndexersBucketsOnceTheyExist(t *testing.T) { // 124
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mods, err := js.KeyValue(ctx, view.ModulesBucket)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
const head = "0123456789abcdef0123456789abcdef01234567"
|
||||
hash, _ := meshdeliveryclient.HeadsHash([]meshdeliveryclient.Head{{Repository: "novox/mesh-delivery", Number: 12, Head: head}})
|
||||
cand, err := json.Marshal(meshcatalog.CandidatesByGroupValue{Group: "feat/x", Heads: hash, Pulls: []meshcatalog.CandidatesByGroupValuePullsItem{{Repository: "novox/mesh-delivery", Number: 12, Head: head, Modules: []string{"a"}}}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mod, err := meshcatalogclient.EncodeModule(meshcatalog.ModulesByModuleValue{None: "no tree", Repository: "novox/mesh-delivery", Commit: head})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := cands.Put(ctx, "feat/x", cand); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := mods.Put(ctx, "a", mod); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
catalog.put(meshcatalog.CatalogEntry{Module: "a", None: "no tree", Repository: "novox/mesh-delivery", Commit: head, Manifest: map[string]any{"module": "a"}})
|
||||
var last view.Snapshot
|
||||
for deadline := time.Now().Add(15 * time.Second); time.Now().Before(deadline); time.Sleep(100 * time.Millisecond) {
|
||||
if s, err := view.Ask(socket); err == nil {
|
||||
@@ -380,6 +435,7 @@ func TestProcessWholeShowsTheOpenAsksAndDropsTheAnsweredOnes(t *testing.T) { //
|
||||
runCtx, stop := context.WithCancel(ctx)
|
||||
done := make(chan error, 1)
|
||||
serveOwners(t, ctx, js, nc)
|
||||
serveCatalog(t, ctx, js, nc)
|
||||
go func() { done <- run(runCtx, func(k string) string { return env[k] }, io.Discard) }()
|
||||
t.Cleanup(func() { stop(); <-done })
|
||||
|
||||
@@ -471,6 +527,7 @@ func TestProcessWholeListsEveryDifference(t *testing.T) { // 1297/B8 AC
|
||||
runCtx, stop := context.WithCancel(ctx)
|
||||
done := make(chan error, 1)
|
||||
serveOwners(t, ctx, js, nc)
|
||||
serveCatalog(t, ctx, js, nc)
|
||||
go func() { done <- run(runCtx, func(k string) string { return env[k] }, io.Discard) }()
|
||||
t.Cleanup(func() { stop(); <-done })
|
||||
|
||||
@@ -569,6 +626,7 @@ func TestProcessWholeWritesTheAnswerToAnAskNeedingNoProof(t *testing.T) { // 129
|
||||
runCtx, stop := context.WithCancel(ctx)
|
||||
done := make(chan error, 1)
|
||||
serveOwners(t, ctx, js, nc)
|
||||
serveCatalog(t, ctx, js, nc)
|
||||
go func() { done <- run(runCtx, func(k string) string { return env[k] }, io.Discard) }()
|
||||
t.Cleanup(func() { stop(); <-done })
|
||||
|
||||
@@ -721,6 +779,7 @@ func TestProcessWholeShowsTheBacklogOfBuildAsks(t *testing.T) { // 1887/A8a AC
|
||||
runCtx, stop := context.WithCancel(ctx)
|
||||
done := make(chan error, 1)
|
||||
serveOwners(t, ctx, js, nc)
|
||||
serveCatalog(t, ctx, js, nc)
|
||||
go func() { done <- run(runCtx, func(k string) string { return env[k] }, io.Discard) }()
|
||||
t.Cleanup(func() { stop(); <-done })
|
||||
|
||||
@@ -768,7 +827,7 @@ func TestProcessWholeSaysTheSourceRevisionIsDelivered(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
kvs := map[string]jetstream.KeyValue{}
|
||||
for _, name := range []string{view.SourceBucket, view.ModulesBucket, view.AssignerWantsBucket, view.HaveBucket} {
|
||||
for _, name := range []string{view.SourceBucket, view.AssignerWantsBucket, view.HaveBucket} {
|
||||
kv, err := js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: name, History: 5})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -789,20 +848,16 @@ func TestProcessWholeSaysTheSourceRevisionIsDelivered(t *testing.T) {
|
||||
runCtx, stop := context.WithCancel(ctx)
|
||||
done := make(chan error, 1)
|
||||
serveOwners(t, ctx, js, nc)
|
||||
catalog := serveCatalog(t, ctx, js, nc)
|
||||
go func() { done <- run(runCtx, func(k string) string { return env[k] }, io.Discard) }()
|
||||
t.Cleanup(func() { stop(); <-done })
|
||||
|
||||
fp := "src2:" + strings.Repeat("2", 64)
|
||||
mod, _ := meshcatalogclient.EncodeModule(meshcatalog.ModulesByModuleValue{Fp2: fp, Manifest: strings.Repeat("a", 64), Repository: "novox/web", Commit: strings.Repeat("1", 40), Path: meshcatalogclient.RootPath})
|
||||
want, _ := meshwantsclient.EncodeWant(meshwantsclient.Want{Fp: fp, Composed: "sha256:" + strings.Repeat("c", 64)})
|
||||
failed, _ := nodeengineclient.EncodeHave(nodeengine.HaveByNodeModuleValue{Fp: fp, Phase: nodeengineclient.PhaseRunning, Health: nodeengineclient.HealthFailed})
|
||||
good, _ := nodeengineclient.EncodeHave(nodeengine.HaveByNodeModuleValue{Fp: fp, Phase: nodeengineclient.PhaseRunning, Health: nodeengineclient.HealthOK})
|
||||
src, _ := meshdeliveryclient.EncodeSource(meshdelivery.SourceByBranchValue{Group: "feat/x", Heads: map[string]string{"novox/web": strings.Repeat("1", 40)}})
|
||||
for bucket, kv := range map[string][]byte{view.ModulesBucket: mod} {
|
||||
if _, err := kvs[bucket].Put(ctx, "web", kv); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
catalog.put(meshcatalog.CatalogEntry{Module: "web", Fp: fp, Manifest: map[string]any{"module": "a"}, Repository: "novox/web", Commit: strings.Repeat("1", 40), Path: meshcatalogclient.RootPath})
|
||||
if _, err := kvs[view.AssignerWantsBucket].Put(ctx, "want.ace.web", want); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -886,6 +941,7 @@ func TestProcessWholeSaysModulesInMotion(t *testing.T) {
|
||||
runCtx, stop := context.WithCancel(ctx)
|
||||
done := make(chan error, 1)
|
||||
serveOwners(t, ctx, js, nc)
|
||||
serveCatalog(t, ctx, js, nc)
|
||||
go func() { done <- run(runCtx, func(k string) string { return env[k] }, io.Discard) }()
|
||||
t.Cleanup(func() { stop(); <-done })
|
||||
time.Sleep(2 * time.Second) // the view has read what stood: nothing
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient"
|
||||
"io"
|
||||
"log"
|
||||
"mesh-delivery-view/internal/view"
|
||||
@@ -126,15 +127,21 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
|
||||
}
|
||||
}
|
||||
open := buckets{js: js, reader: reader}
|
||||
for _, bucket := range []string{view.HaveBucket, view.AssignmentsBucket, view.PresenceBucket, view.StuckBucket, view.CandidatesBucket, view.ModulesBucket, view.AsksBucket, view.AnswersBucket, builds.BuilderBuildsBucket, view.AssignerWantsBucket, view.SourceBucket, view.AssignerAsksBucket, viewstate.ViewAnswersBucket, view.TakenBucket} {
|
||||
for _, bucket := range []string{view.HaveBucket, view.AssignmentsBucket, view.PresenceBucket, view.StuckBucket, view.CandidatesBucket, view.AsksBucket, view.AnswersBucket, builds.BuilderBuildsBucket, view.AssignerWantsBucket, view.SourceBucket, view.AssignerAsksBucket, viewstate.ViewAnswersBucket, view.TakenBucket} {
|
||||
go follow(ctx, open, bucket, false, model, logger)
|
||||
}
|
||||
// The modules' and the source's history give each fingerprint's first commit and each head's source revision (ADR 0556).
|
||||
// The modules' picture holds no history: from its first view on, each change it takes is the history (finding of DLV-M4).
|
||||
for _, bucket := range []string{view.ModulesBucket, view.SourceBucket} {
|
||||
go follow(ctx, open, bucket, true, model, logger)
|
||||
// The source's history gives each head's source revision (ADR 0556); the modules' history is each revision of the
|
||||
// catalog half the view takes, in its order (below).
|
||||
go follow(ctx, open, view.SourceBucket, true, model, logger)
|
||||
statusBucket := process.AwaitBucket(ctx, js, conn, firstViewsBucket, "view_first_views reads nothing and view_first_view_again is not heard", logger.Printf)
|
||||
// The modules are the catalog half's, adopted whole by the seat's client: its first view, its status, its hand asks and
|
||||
// its event catalog-revision (on the module's durable consumer) are the reader's.
|
||||
catalog, _, err := meshcatalogclient.WireCatalogProcess(reader, calls.Call, "main", catalogChanged(model, logger))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
reader.Start(ctx, js, cred, process.AwaitBucket(ctx, js, conn, firstViewsBucket, "view_first_views reads nothing and view_first_view_again is not heard", logger.Printf))
|
||||
reader.Start(ctx, js, cred, statusBucket)
|
||||
catalogHeld(ctx, catalog, model)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case err := <-webUp:
|
||||
@@ -189,7 +196,6 @@ var ownersFollowed = []struct{ owner, state, tool string }{
|
||||
{view.AssignerModule, "wants", view.AssignerModule + ".assigner_read_wants"},
|
||||
{view.AssignerModule, "asks", view.AssignerModule + ".assigner_read_asks"},
|
||||
{view.IndexerModule, "candidates", view.IndexerModule + ".indexer_read_candidates"},
|
||||
{view.IndexerModule, "modules", view.IndexerModule + ".indexer_read_modules"},
|
||||
{view.IndexerModule, "taken", view.IndexerModule + ".indexer_read_taken"},
|
||||
}
|
||||
|
||||
@@ -277,3 +283,35 @@ func emitEvent(ctx context.Context, js jetstream.JetStream, cred process.Account
|
||||
_, err = js.PublishMsg(pub, msg, jetstream.WithMsgID(id))
|
||||
return err
|
||||
}
|
||||
|
||||
// catalogChanged hands one module of a revision the view took to the model, in the form of its value; the modules'
|
||||
// history is these changes in the order of the revisions.
|
||||
func catalogChanged(model *view.Model, logger *log.Logger) func(string, json.RawMessage, bool, time.Time) {
|
||||
return func(module string, entry json.RawMessage, gone bool, at time.Time) {
|
||||
e := view.Entry{Bucket: view.ModulesBucket, Key: module, At: at, Deleted: gone}
|
||||
if !gone {
|
||||
v, err := view.ModuleValue(entry)
|
||||
if err != nil {
|
||||
logger.Printf("the catalog entry of %s is not taken: %v", module, err)
|
||||
return
|
||||
}
|
||||
e.Value = v
|
||||
}
|
||||
model.Apply(e)
|
||||
e.History = true
|
||||
model.Apply(e)
|
||||
}
|
||||
}
|
||||
|
||||
// catalogHeld makes the model's pictures of the modules initial once the catalog half's first view is held: the process
|
||||
// starts at once and waits for the seat's holder, saying so.
|
||||
func catalogHeld(ctx context.Context, cat *meshcatalogclient.Catalog, model *view.Model) {
|
||||
go func() {
|
||||
select {
|
||||
case <-cat.Follower.Ready():
|
||||
model.Initial(view.ModulesBucket)
|
||||
model.Initial(view.ReachHistory(view.ModulesBucket))
|
||||
case <-ctx.Done():
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package view
|
||||
|
||||
// The view reads the catalog half of the delivery through the seat mesh-catalog (design 47, Two graphs; ADR 0656 §6):
|
||||
// a first view of its verb `revision`, then each trunk revision its event `catalog-revision` says, never the indexer's
|
||||
// state `modules`. The model keeps the picture it always kept (one value per module, its history for the reach), so each
|
||||
// entry of a revision is handed to it in the form of that value.
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient"
|
||||
"git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog"
|
||||
)
|
||||
|
||||
// CatalogSeat is the seat whose verb `revision` and event `catalog-revision` the view reads the modules from, as its
|
||||
// manifest's `invokes` and `consumes` name them.
|
||||
const CatalogSeat = meshcatalog.Seat
|
||||
|
||||
// ModulesBucket names the model's picture of the catalog half's modules (and its history): the model's label, no
|
||||
// bucket of anybody's.
|
||||
const ModulesBucket = CatalogSeat + "_modules"
|
||||
|
||||
// ModuleValue is the value of one module of the model, as the model decodes it, made from the catalog entry (its JSON):
|
||||
// the fingerprint is the entry's `fp` (the only form there is), the source revisions are the entry's inputs'. A manifest
|
||||
// held for unknown fields has no verdict here: the model reads the fingerprint and the commit.
|
||||
func ModuleValue(entry json.RawMessage) (json.RawMessage, error) {
|
||||
var e meshcatalog.CatalogEntry
|
||||
if err := json.Unmarshal(entry, &e); err != nil || e.Module == "" {
|
||||
return nil, fmt.Errorf("a catalog entry does not read: %v", err)
|
||||
}
|
||||
v := meshcatalog.ModulesByModuleValue{Commit: e.Commit, Fp2: e.Fp, None2: e.None, Path: e.Path, Repository: e.Repository, Packages: e.Packages}
|
||||
if len(e.Manifest) > 0 {
|
||||
// The entry holds its manifest whole; the model keeps the digest of its bytes, as the value it always kept does.
|
||||
raw, _ := json.Marshal(e.Manifest)
|
||||
v.Manifest = meshcatalogclient.ManifestDigest(raw)
|
||||
}
|
||||
if e.Verdict == meshcatalogclient.VerdictAccepted || e.Verdict == meshcatalogclient.VerdictRefused {
|
||||
v.Verdict = e.Verdict
|
||||
}
|
||||
if in := e.Inputs; in != nil {
|
||||
v.Src, v.FpSrc, v.Toolchains, v.Paths, v.Warnings = in.Src, in.FpSrc, in.Toolchains, in.Paths, in.Warnings
|
||||
if f := in.From; f != nil && v.Manifest != "" {
|
||||
v.Inputs2 = &meshcatalog.ModulesByModuleValueInputs2{Bases: f.Bases, Builder: f.Builder, Contexts: f.Contexts,
|
||||
ImportForm: f.ImportForm, Imports: f.Imports, Locks: f.Locks, Packages: f.Packages, Paths: f.Paths, Tree: f.Tree}
|
||||
}
|
||||
if v.Verdict == meshcatalogclient.VerdictRefused {
|
||||
v.Problems = in.Problems
|
||||
}
|
||||
}
|
||||
if v.Verdict == meshcatalogclient.VerdictRefused && len(v.Problems) == 0 {
|
||||
v.Verdict = ""
|
||||
}
|
||||
if v.Verdict != meshcatalogclient.VerdictRefused {
|
||||
v.Warnings = nil
|
||||
}
|
||||
return meshcatalogclient.EncodeModule(v)
|
||||
}
|
||||
@@ -24,7 +24,6 @@ const (
|
||||
EngineModule = "node-engine"
|
||||
|
||||
// The indexer's states the view reads.
|
||||
ModulesBucket = IndexerModule + "_modules"
|
||||
CandidatesBucket = IndexerModule + "_candidates"
|
||||
TakenBucket = IndexerModule + "_taken"
|
||||
PackagesState = "packages"
|
||||
|
||||
@@ -3,6 +3,7 @@ package manifest
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient"
|
||||
"io/fs"
|
||||
"mesh-delivery-view/internal/view"
|
||||
"os"
|
||||
@@ -119,8 +120,9 @@ func TestManifestIsTakenAndReadsOnly(t *testing.T) { // 1134/AC7
|
||||
// follows then heard on their views families (ADR 0615 §1, §5; DLV-M4).
|
||||
if want := []string{"mesh-delivery-assigner.assigner_read_asks", "mesh-delivery-assigner.assigner_read_differences", "mesh-delivery-assigner.assigner_read_assignments",
|
||||
"mesh-delivery-assigner.assigner_read_wants", "mesh-delivery-indexer.indexer_read_candidates", "mesh-delivery-indexer.indexer_read_config_candidates",
|
||||
"mesh-delivery-indexer.indexer_read_graph", "mesh-delivery-indexer.indexer_read_heard", "mesh-delivery-indexer.indexer_pending", "mesh-delivery-indexer.indexer_read_modules",
|
||||
"mesh-delivery-indexer.indexer_read_graph", "mesh-delivery-indexer.indexer_read_heard", "mesh-delivery-indexer.indexer_pending",
|
||||
"mesh-delivery-indexer.indexer_read_packages", "mesh-delivery-indexer.indexer_read_predefined", "mesh-delivery-indexer.indexer_read_taken",
|
||||
"seat:mesh-catalog.revision", // ADR 0663: the modules are the catalog half's, read through the seat's verb revision, never the indexer's
|
||||
"seat:mesh-hand-acts.hand-act", // the join: its first view asked again by hand is a hand act (views.StatusTools)
|
||||
"seat:node-engine.manifest-reads"}; !reflect.DeepEqual(m.Invokes, want) { // ADR 0618 §3: node_manifest_reads asks the node-engine seat's verb of every node; 1585/T24: view_tiers asks indexer_pending, a verb edge (ADR 0614 §2)
|
||||
t.Errorf("invokes = %v, want %v", m.Invokes, want)
|
||||
@@ -133,7 +135,7 @@ func TestManifestIsTakenAndReadsOnly(t *testing.T) { // 1134/AC7
|
||||
t.Errorf("version = %q, want 0.1", m.Version)
|
||||
}
|
||||
if want := []string{"mesh-conditions.findings-asked", "mesh-delivery-assigner.views.asks", "mesh-delivery-assigner.views.assignments", "mesh-delivery-assigner.views.wants",
|
||||
"mesh-delivery-indexer.views.candidates", "mesh-delivery-indexer.views.modules", "mesh-delivery-indexer.views.taken"}; !reflect.DeepEqual(m.Consumes, want) {
|
||||
"mesh-catalog.catalog-revision", "mesh-delivery-indexer.views.candidates", "mesh-delivery-indexer.views.taken"}; !reflect.DeepEqual(m.Consumes, want) {
|
||||
t.Errorf("consumes = %v, want %v", m.Consumes, want)
|
||||
}
|
||||
// The one event the view says: a source revision is delivered (1906/D9, ADR 0539 §17), derived by it from the wants and the haves.
|
||||
@@ -180,7 +182,7 @@ func TestManifestIsTakenAndReadsOnly(t *testing.T) { // 1134/AC7
|
||||
if !slices.Contains(m.Reads, view.SignersRead) {
|
||||
t.Errorf("the view reads %s, its manifest's reads are %v", view.SignersRead, m.Reads)
|
||||
}
|
||||
followed := map[string]string{view.ModulesBucket: view.IndexerModule + ".views.modules", view.CandidatesBucket: view.IndexerModule + ".views.candidates",
|
||||
followed := map[string]string{view.CandidatesBucket: view.IndexerModule + ".views.candidates",
|
||||
view.TakenBucket: view.IndexerModule + ".views.taken", view.AssignmentsBucket: view.AssignerModule + ".views.assignments",
|
||||
view.AssignerWantsBucket: view.AssignerModule + ".views.wants", view.AssignerAsksBucket: view.AssignerModule + ".views.asks"}
|
||||
for bucket, family := range followed {
|
||||
@@ -191,11 +193,20 @@ func TestManifestIsTakenAndReadsOnly(t *testing.T) { // 1134/AC7
|
||||
t.Errorf("the view follows %s, its manifest consumes %v", family, m.Consumes)
|
||||
}
|
||||
}
|
||||
for _, tool := range []string{view.IndexerModule + ".indexer_read_modules", view.IndexerModule + ".indexer_read_candidates", view.IndexerModule + ".indexer_pending", view.AssignerModule + ".assigner_read_wants"} {
|
||||
for _, tool := range []string{meshcatalogclient.RevisionVerb, view.IndexerModule + ".indexer_read_candidates", view.IndexerModule + ".indexer_pending", view.AssignerModule + ".assigner_read_wants"} {
|
||||
if !slices.Contains(m.Invokes, tool) {
|
||||
t.Errorf("the view asks %s, its manifest invokes %v", tool, m.Invokes)
|
||||
}
|
||||
}
|
||||
// The modules are the catalog half's: the seat's verb is invoked, its event consumed, and the indexer's state modules is neither.
|
||||
if !slices.Contains(m.Consumes, view.CatalogSeat+"."+meshcatalog.EventCatalogRevision) {
|
||||
t.Errorf("the view hears the seat's %s, its manifest consumes %v", meshcatalog.EventCatalogRevision, m.Consumes)
|
||||
}
|
||||
for _, name := range append(slices.Clone(m.Invokes), m.Consumes...) {
|
||||
if strings.Contains(name, "indexer_read_modules") || strings.Contains(name, "views.modules") {
|
||||
t.Errorf("the manifest names %s: the modules are the catalog half's (ADR 0663)", name)
|
||||
}
|
||||
}
|
||||
if viewstate.ViewModule != m.Module || viewstate.ViewAnswersBucket != m.Module+"_answers" || viewstate.ReachBucket != m.Module+"_reach" {
|
||||
t.Errorf("the view names itself %s (%s, %s), its manifest %s", viewstate.ViewModule, viewstate.ViewAnswersBucket, viewstate.ReachBucket, m.Module)
|
||||
}
|
||||
|
||||
@@ -46,8 +46,8 @@
|
||||
"mesh-delivery-assigner.views.asks",
|
||||
"mesh-delivery-assigner.views.assignments",
|
||||
"mesh-delivery-assigner.views.wants",
|
||||
"mesh-catalog.catalog-revision",
|
||||
"mesh-delivery-indexer.views.candidates",
|
||||
"mesh-delivery-indexer.views.modules",
|
||||
"mesh-delivery-indexer.views.taken"
|
||||
],
|
||||
"streams": [
|
||||
@@ -68,10 +68,10 @@
|
||||
"mesh-delivery-indexer.indexer_read_graph",
|
||||
"mesh-delivery-indexer.indexer_read_heard",
|
||||
"mesh-delivery-indexer.indexer_pending",
|
||||
"mesh-delivery-indexer.indexer_read_modules",
|
||||
"mesh-delivery-indexer.indexer_read_packages",
|
||||
"mesh-delivery-indexer.indexer_read_predefined",
|
||||
"mesh-delivery-indexer.indexer_read_taken",
|
||||
"seat:mesh-catalog.revision",
|
||||
"seat:mesh-hand-acts.hand-act",
|
||||
"seat:node-engine.manifest-reads"
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user