Say a build's directory and a pull request's module directories in the SDK (issue 1233) #39
@@ -4,7 +4,9 @@ import "time"
|
||||
|
||||
// BuildRecord is the layout mesh-controller internal/chain/records.go Build writes (line 91), the fields a reader outside
|
||||
// the controller reads (novox/hq ADR 0437 §4 §5, issue 1223). The controller's record carries more; a decode ignores
|
||||
// what is not here. SourceInputs has no json keys of its own: it is written under its Go field names, as the controller
|
||||
// what is not here. Path is the module's directory in its repository, as the controller writes it (Build.Path, records.go:100,
|
||||
// set from the build request trimmed of slashes at internal/inventory/chain_requests.go:19); empty for a module whose
|
||||
// manifest is the repository's root. SourceInputs has no json keys of its own: it is written under its Go field names, as the controller
|
||||
// writes it.
|
||||
type BuildRecord struct {
|
||||
ID string `json:"id"`
|
||||
@@ -12,6 +14,7 @@ type BuildRecord struct {
|
||||
Repository string `json:"repository"`
|
||||
Ref string `json:"ref,omitempty"`
|
||||
Module string `json:"module,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Commit string `json:"commit,omitempty"`
|
||||
At time.Time `json:"at,omitzero"`
|
||||
SourceFingerprint string `json:"source-fingerprint,omitempty"`
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// The fixture's path is what the controller writes for a build request: strings.Trim(path, "/") at mesh-controller
|
||||
// internal/inventory/chain_requests.go:19, the module's directory in its repository (Build.Path, internal/chain/records.go:100).
|
||||
// The fixture is a record the controller's own encoder wrote (json.Marshal of a chain.Build, mesh-controller
|
||||
// internal/chain, a throwaway test run once and deleted) with build-agent's build-1792395447088936520 values, plus a
|
||||
// manifest-digest and one made artifact, which a BuildRecord does not carry.
|
||||
@@ -49,6 +51,41 @@ func TestBuildRecordDecodes(t *testing.T) { // 1223/AC1
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildRecordPath(t *testing.T) { // 1233/AC1
|
||||
var r BuildRecord
|
||||
if err := json.Unmarshal(loadBuildFixture(t), &r); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if r.Path != "modules/build-agent" {
|
||||
t.Errorf("path = %q, want modules/build-agent", r.Path)
|
||||
}
|
||||
// mesh-controller's manifest is its repository's root: the controller writes no path (omitempty), so the path is "".
|
||||
var m map[string]json.RawMessage
|
||||
if err := json.Unmarshal(loadBuildFixture(t), &m); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
delete(m, "path")
|
||||
raw, _ := json.Marshal(m)
|
||||
var root BuildRecord
|
||||
if err := json.Unmarshal(raw, &root); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if root.Path != "" {
|
||||
t.Errorf("path of a root module = %q, want empty", root.Path)
|
||||
}
|
||||
// Every field 1223/AC1 checks is still equal in both.
|
||||
if root.ID != r.ID || root.Module != r.Module || root.Repository != r.Repository || root.Ref != r.Ref ||
|
||||
root.Commit != r.Commit || root.State != r.State || root.SourceFingerprint != r.SourceFingerprint || !root.At.Equal(r.At) {
|
||||
t.Errorf("fields differ: %+v vs %+v", root, r)
|
||||
}
|
||||
// And the path survives a round trip through the SDK's own type.
|
||||
back, _ := json.Marshal(r)
|
||||
var again BuildRecord
|
||||
if err := json.Unmarshal(back, &again); err != nil || again.Path != r.Path {
|
||||
t.Errorf("round trip path = %q (%v)", again.Path, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildRecordWithoutInputs(t *testing.T) { // 1223/AC2
|
||||
var m map[string]json.RawMessage
|
||||
if err := json.Unmarshal(loadBuildFixture(t), &m); err != nil {
|
||||
|
||||
+14
-1
@@ -241,7 +241,9 @@ func (m Module) check() error {
|
||||
// Forge is a message of the forge bridge's stream, trimmed to repository, branch, pull request and head. A pull
|
||||
// message says Action; a merged message carries the merge commit, the tree of every directory holding a
|
||||
// module.json and the repository's root tree; a refused message names the owed act it answers and the forge's own
|
||||
// words.
|
||||
// words. A pull message may also carry the module directories its head changes (Dirs, in the order the forge said) and
|
||||
// DirsKnown, true when the forge could say them: known and empty is DirsKnown with no Dirs, not known is neither
|
||||
// (the forge's module_dirs and module_dirs_said, mesh-catalog modules/gitea/trunk.ts). Other messages carry neither.
|
||||
type Forge struct {
|
||||
V int `json:"v"`
|
||||
Kind string `json:"kind"`
|
||||
@@ -250,6 +252,8 @@ type Forge struct {
|
||||
Number int `json:"number,omitempty"`
|
||||
Head string `json:"head,omitempty"`
|
||||
Action string `json:"action,omitempty"`
|
||||
Dirs []string `json:"dirs,omitempty"`
|
||||
DirsKnown bool `json:"dirs-known,omitempty"`
|
||||
Merge string `json:"merge,omitempty"`
|
||||
Trees map[string]string `json:"trees,omitempty"`
|
||||
Root string `json:"root,omitempty"`
|
||||
@@ -277,7 +281,13 @@ func (f Forge) check() error {
|
||||
default:
|
||||
return fmt.Errorf("delivery: the pull action %q is not opened, updated or closed", f.Action)
|
||||
}
|
||||
if len(f.Dirs) > 0 && !f.DirsKnown {
|
||||
return fmt.Errorf("delivery: a pull message with module directories must say it knows them (dirs-known)")
|
||||
}
|
||||
case ForgeMerged:
|
||||
if len(f.Dirs) > 0 || f.DirsKnown {
|
||||
return fmt.Errorf("delivery: a merged message carries no module directories")
|
||||
}
|
||||
if f.Number <= 0 {
|
||||
return fmt.Errorf("delivery: a merged message needs its pull request number")
|
||||
}
|
||||
@@ -299,6 +309,9 @@ func (f Forge) check() error {
|
||||
}
|
||||
}
|
||||
case ForgeRefused:
|
||||
if len(f.Dirs) > 0 || f.DirsKnown {
|
||||
return fmt.Errorf("delivery: a refused message carries no module directories")
|
||||
}
|
||||
if f.Act == "" {
|
||||
return fmt.Errorf("delivery: a refused message needs the act it answers")
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package delivery
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@@ -286,3 +287,54 @@ func TestSourceBuckets(t *testing.T) {
|
||||
t.Error("a bucket or history differs from the decided layout")
|
||||
}
|
||||
}
|
||||
|
||||
func TestForgeDirs(t *testing.T) {
|
||||
pull := func(dirs []string, known bool) Forge {
|
||||
return Forge{Kind: ForgePull, Repository: "novox/mesh-sdk", Number: 36, Head: "aaaa", Action: "updated", Dirs: dirs, DirsKnown: known}
|
||||
}
|
||||
round := func(f Forge) Forge {
|
||||
t.Helper()
|
||||
data, err := EncodeForge(f)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, err := DecodeForge(data)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return got
|
||||
}
|
||||
// 1233/AC2: the directories come back equal and in order.
|
||||
got := round(pull([]string{"modules/a", "modules/b"}, true))
|
||||
if !reflect.DeepEqual(got.Dirs, []string{"modules/a", "modules/b"}) || !got.DirsKnown {
|
||||
t.Errorf("dirs = %v known %v", got.Dirs, got.DirsKnown)
|
||||
}
|
||||
// 1233/AC3: a head changing no module directory is told apart from one whose directories could not be said.
|
||||
none, unknown := round(pull(nil, true)), round(pull(nil, false))
|
||||
if !none.DirsKnown || len(none.Dirs) != 0 {
|
||||
t.Errorf("known and empty: dirs %v known %v", none.Dirs, none.DirsKnown)
|
||||
}
|
||||
if unknown.DirsKnown || len(unknown.Dirs) != 0 {
|
||||
t.Errorf("not known: dirs %v known %v", unknown.Dirs, unknown.DirsKnown)
|
||||
}
|
||||
// 1233/AC4: a merged or a refused message carrying directories is refused.
|
||||
m := sampleForges()[1]
|
||||
m.Dirs, m.DirsKnown = []string{"modules/a"}, true
|
||||
if _, err := EncodeForge(m); err == nil {
|
||||
t.Error("merged message with dirs accepted")
|
||||
}
|
||||
r := sampleForges()[2]
|
||||
r.Dirs, r.DirsKnown = []string{"modules/a"}, true
|
||||
if _, err := EncodeForge(r); err == nil {
|
||||
t.Error("refused message with dirs accepted")
|
||||
}
|
||||
r = sampleForges()[2]
|
||||
r.DirsKnown = true
|
||||
if _, err := EncodeForge(r); err == nil {
|
||||
t.Error("refused message with dirs-known accepted")
|
||||
}
|
||||
// directories on a pull message that does not say it knows them are refused too.
|
||||
if _, err := EncodeForge(pull([]string{"modules/a"}, false)); err == nil {
|
||||
t.Error("dirs without dirs-known accepted")
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"id":"build-1792395447088936520","state":"built","repository":"novox/mesh-controller","ref":"main","module":"build-agent","commit":"7a74cee83f88e6585a5511c3cc53bdf0f5303d20","manifest-digest":"sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef","source-fingerprint":"src1:e846efe73716b78e80860baaac96153de2cb76551325753838ca245d2160a3d7","source-inputs":{"Module":"build-agent","Tree":"11c0b4f90cae0d6e4ef9cc48cbfdf9f6dbe52bca","Bases":["sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc","sha256:8ac98ca534ac3f51e1f420a1dd2c15e74c75cfa0f23f3ad27eb5d7236c349a0c"],"Contexts":{"server":"narrow:d51ca744fd11af59d616c858f76c27b3e58e16e11fbdc0c720eb9d7d19d55fac"},"Toolchains":null,"Unpinned":""},"made":[{"name":"server","kind":"image","reference":"registry.example/build-agent@sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc"}],"at":"2026-10-19T09:38:00Z"}
|
||||
{"id":"build-1792395447088936520","state":"built","repository":"novox/mesh-controller","ref":"main","module":"build-agent","commit":"7a74cee83f88e6585a5511c3cc53bdf0f5303d20","path":"modules/build-agent","manifest-digest":"sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef","source-fingerprint":"src1:e846efe73716b78e80860baaac96153de2cb76551325753838ca245d2160a3d7","source-inputs":{"Module":"build-agent","Tree":"11c0b4f90cae0d6e4ef9cc48cbfdf9f6dbe52bca","Bases":["sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc","sha256:8ac98ca534ac3f51e1f420a1dd2c15e74c75cfa0f23f3ad27eb5d7236c349a0c"],"Contexts":{"server":"narrow:d51ca744fd11af59d616c858f76c27b3e58e16e11fbdc0c720eb9d7d19d55fac"},"Toolchains":null,"Unpinned":""},"made":[{"name":"server","kind":"image","reference":"registry.example/build-agent@sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc"}],"at":"2026-10-19T09:38:00Z"}
|
||||
|
||||
Reference in New Issue
Block a user