J3 (A3n finding 3, A2k finding 1): the builder's version.numbered is a contract, and a build's answer names its files out of step
delivery.VersionNumbered with VersionNumberedEvent and VersionNumberedSubject is the body the builder says when it numbers a version, so a build module consumes it from mesh-sdk instead of the builder's internal package. BuildAnswer gains out-of-step, so the builder decodes a trunk build's compare by its field, not by name. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
This commit is contained in:
@@ -105,8 +105,21 @@ func TestPublishedPackagesAndImportsRoundTripAndRefuse(t *testing.T) {
|
||||
}
|
||||
}
|
||||
var answer BuildAnswer
|
||||
if err := json.Unmarshal([]byte(`{"artifacts":[],"imports":["`+sdk+`"]}`), &answer); err != nil || len(answer.Imports) != 1 {
|
||||
t.Fatalf("build answer imports: %v, %v", err, answer.Imports)
|
||||
if err := json.Unmarshal([]byte(`{"artifacts":[],"imports":["`+sdk+`"],"out-of-step":["go/seats/x/contract.go"]}`), &answer); err != nil ||
|
||||
len(answer.Imports) != 1 || !reflect.DeepEqual(answer.OutOfStep, []string{"go/seats/x/contract.go"}) {
|
||||
t.Fatalf("build answer imports and files out of step: %v, %v, %v", err, answer.Imports, answer.OutOfStep)
|
||||
}
|
||||
// The builder's version.numbered, as a build module consumes it: the builder's own body whole, and its subject.
|
||||
numbered := VersionNumbered{Name: "mesh-sdk/go", Kind: "package", Version: "0.1.7", Module: "mesh-sdk", Fingerprint: "src2:x",
|
||||
Commit: commit, Digest: "sha256:d", Seat: "go-package-registry", Head: commit, Again: "2026-11-04T15:00:00Z"}
|
||||
if raw, err := EncodeVersionNumbered(numbered); err != nil {
|
||||
t.Errorf("numbered refused: %v", err)
|
||||
} else if back, err := DecodeVersionNumbered(raw); err != nil || back != numbered ||
|
||||
!strings.Contains(string(raw), `"fingerprint":"src2:x"`) || VersionNumberedSubject != "mesh.mod.mesh-delivery-builder.event.version.numbered" {
|
||||
t.Errorf("numbered round trip: %+v, %v, %s", back, err, raw)
|
||||
}
|
||||
if _, err := DecodeVersionNumbered([]byte(`{"name":"mesh-sdk/go","version":"0.1.7"}`)); err == nil {
|
||||
t.Error("a numbered version naming no build is read")
|
||||
}
|
||||
if err := CheckImports([]string{"fmt"}); err == nil {
|
||||
t.Error("CheckImports takes a standard package")
|
||||
|
||||
@@ -588,6 +588,10 @@ type BuildAnswer struct {
|
||||
// once (novox/hq ADR 0593 §2): a Go build module reports them, the builder carries them onto the build's end
|
||||
// (BuildLogEntry.Imports). Absent from a build module that reports none.
|
||||
Imports []string `json:"imports,omitempty"`
|
||||
// OutOfStep are the generated files the build's compare found out of step with what their generator writes (the test
|
||||
// step's `out-of-step`, answered by a trunk build's ask too): the builder carries them onto the build's end
|
||||
// (BuildLogEntry.OutOfStep). Absent when none is, or from a build module that compares none.
|
||||
OutOfStep []string `json:"out-of-step,omitempty"`
|
||||
}
|
||||
|
||||
// AskBuilt is one artifact a build ask built.
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package delivery
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// The builder's event version.numbered (design 51 *Events of the builder*, *The floor*): said once per version the builder
|
||||
// numbers, on its own family `version` (seven days), apart from its builds' ends, so a waiter on a build's end does not
|
||||
// wake on it. A build module consumes it to publish a build's version with the held digest, which stores nothing.
|
||||
const (
|
||||
// VersionNumberedEvent is the event's local name.
|
||||
VersionNumberedEvent = "version.numbered"
|
||||
// VersionNumberedSubject is its subject, as a consumer's filter names it.
|
||||
VersionNumberedSubject = "mesh.mod." + BuilderModule + ".event." + VersionNumberedEvent
|
||||
)
|
||||
|
||||
// VersionNumbered is the body of VersionNumberedEvent: a numbered version of a name, the build it was numbered for, and
|
||||
// what the build module needs to publish it.
|
||||
type VersionNumbered struct {
|
||||
// Name is the numbered name, as builder_versions lists it; Kind its artifact's kind.
|
||||
Name string `json:"name"`
|
||||
Kind string `json:"kind"`
|
||||
Version string `json:"version"`
|
||||
Tag string `json:"tag,omitempty"`
|
||||
// Module and Fingerprint are the build the version was numbered for; Commit its source commit.
|
||||
Module string `json:"module"`
|
||||
Fingerprint string `json:"fingerprint"`
|
||||
Commit string `json:"commit,omitempty"`
|
||||
// Digest is the built artifact's, when the build held one; Seat the registry seat it is published through.
|
||||
Digest string `json:"digest,omitempty"`
|
||||
Seat string `json:"seat,omitempty"`
|
||||
// Head is the head the version answers, when it answers a tag ask.
|
||||
Head string `json:"head,omitempty"`
|
||||
// Again is when builder_publish_again said it once more, so the bus's dedup does not drop it.
|
||||
Again string `json:"again,omitempty"`
|
||||
}
|
||||
|
||||
// EncodeVersionNumbered writes the body, refusing one that names no name, version, module or fingerprint.
|
||||
func EncodeVersionNumbered(n VersionNumbered) ([]byte, error) {
|
||||
if n.Name == "" || n.Version == "" || n.Module == "" || n.Fingerprint == "" {
|
||||
return nil, fmt.Errorf("delivery: %s names its name, version, module and fingerprint", VersionNumberedEvent)
|
||||
}
|
||||
return json.Marshal(n)
|
||||
}
|
||||
|
||||
// DecodeVersionNumbered reads the body, with the same refusal.
|
||||
func DecodeVersionNumbered(data []byte) (VersionNumbered, error) {
|
||||
var n VersionNumbered
|
||||
if err := json.Unmarshal(data, &n); err != nil {
|
||||
return VersionNumbered{}, fmt.Errorf("delivery: %s is not readable: %w", VersionNumberedEvent, err)
|
||||
}
|
||||
if _, err := EncodeVersionNumbered(n); err != nil {
|
||||
return VersionNumbered{}, err
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
Reference in New Issue
Block a user