Count a build outcome as the controller writes it: no state, no failure (issue 1281) #46
@@ -11,6 +11,7 @@ import "time"
|
|||||||
type BuildRecord struct {
|
type BuildRecord struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
State string `json:"state,omitempty"`
|
State string `json:"state,omitempty"`
|
||||||
|
Failed string `json:"failed,omitempty"`
|
||||||
Repository string `json:"repository"`
|
Repository string `json:"repository"`
|
||||||
Ref string `json:"ref,omitempty"`
|
Ref string `json:"ref,omitempty"`
|
||||||
Module string `json:"module,omitempty"`
|
Module string `json:"module,omitempty"`
|
||||||
|
|||||||
@@ -9,9 +9,11 @@ import (
|
|||||||
|
|
||||||
// The fixture's path is what the controller writes for a build request: strings.Trim(path, "/") at mesh-controller
|
// 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).
|
// 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
|
// The fixture started as 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
|
// 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.
|
// manifest-digest and one made artifact (shape: mesh-controller internal/chain/records.go:91-117 Build, json tags, at commit
|
||||||
|
// 1f708e2d8d72e2c27dbeaba02f14c128d700eada; an outcome has no state key and a build asked after a merge no ref, records.go:57-59), which a BuildRecord does not carry. It was then edited by hand
|
||||||
|
// to the controller's layout for an outcome: state and ref removed, the repository a clone URL (issue 1281).
|
||||||
func loadBuildFixture(t *testing.T) []byte {
|
func loadBuildFixture(t *testing.T) []byte {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
raw, err := os.ReadFile("testdata/build-record-build-agent.json")
|
raw, err := os.ReadFile("testdata/build-record-build-agent.json")
|
||||||
@@ -29,10 +31,11 @@ func TestBuildRecordDecodes(t *testing.T) { // 1223/AC1
|
|||||||
for _, c := range []struct{ name, got, want string }{
|
for _, c := range []struct{ name, got, want string }{
|
||||||
{"id", r.ID, "build-1792395447088936520"},
|
{"id", r.ID, "build-1792395447088936520"},
|
||||||
{"module", r.Module, "build-agent"},
|
{"module", r.Module, "build-agent"},
|
||||||
{"repository", r.Repository, "novox/mesh-controller"},
|
{"repository", r.Repository, "http://novox.internal:20000/novox/mesh-controller.git"},
|
||||||
{"ref", r.Ref, "main"},
|
{"ref", r.Ref, ""},
|
||||||
{"commit", r.Commit, "7a74cee83f88e6585a5511c3cc53bdf0f5303d20"},
|
{"commit", r.Commit, "7a74cee83f88e6585a5511c3cc53bdf0f5303d20"},
|
||||||
{"state", r.State, "built"},
|
{"state", r.State, ""},
|
||||||
|
{"failed", r.Failed, ""},
|
||||||
{"source-fingerprint", r.SourceFingerprint, "src1:e846efe73716b78e80860baaac96153de2cb76551325753838ca245d2160a3d7"},
|
{"source-fingerprint", r.SourceFingerprint, "src1:e846efe73716b78e80860baaac96153de2cb76551325753838ca245d2160a3d7"},
|
||||||
} {
|
} {
|
||||||
if c.got != c.want {
|
if c.got != c.want {
|
||||||
@@ -42,6 +45,9 @@ func TestBuildRecordDecodes(t *testing.T) { // 1223/AC1
|
|||||||
if want := time.Date(2026, 10, 19, 9, 38, 0, 0, time.UTC); !r.At.Equal(want) {
|
if want := time.Date(2026, 10, 19, 9, 38, 0, 0, time.UTC); !r.At.Equal(want) {
|
||||||
t.Errorf("at = %v, want %v", r.At, want)
|
t.Errorf("at = %v, want %v", r.At, want)
|
||||||
}
|
}
|
||||||
|
if !r.IsTrunkBuild() { // 1281/AC7
|
||||||
|
t.Error("the fixture, a record as the controller writes it, is not a trunk build")
|
||||||
|
}
|
||||||
if r.SourceInputs == nil {
|
if r.SourceInputs == nil {
|
||||||
t.Fatal("source-inputs missing")
|
t.Fatal("source-inputs missing")
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
{"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"}
|
{"id":"build-1792395447088936520","repository":"http://novox.internal:20000/novox/mesh-controller.git","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"}
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ func SameRepository(a, b string) bool {
|
|||||||
return a != "" && strings.EqualFold(a, b)
|
return a != "" && strings.EqualFold(a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsTrunkBuild is whether the record is a successful build of the trunk that recorded its source inputs: state built, and
|
// IsTrunkBuild is whether the record is a successful build of the trunk that recorded its source inputs: an outcome
|
||||||
// ref "main" or none. A build a delivery plan asked after a merge, or rebuild or build --behind, follows the branch
|
// (the controller writes no state on one; state asked is a request, records.go:57-59), not failed, and ref "main" or none. A build a delivery plan asked after a merge, or rebuild or build --behind, follows the branch
|
||||||
// without naming it, so it carries no ref; a build of any other branch or of a commit names it (issue 1281).
|
// without naming it, so it carries no ref; a build of any other branch or of a commit names it (issue 1281).
|
||||||
func (r BuildRecord) IsTrunkBuild() bool {
|
func (r BuildRecord) IsTrunkBuild() bool {
|
||||||
return r.State == "built" && r.SourceInputs != nil && (r.Ref == "" || r.Ref == "main")
|
return r.State == "" && r.Failed == "" && r.SourceInputs != nil && (r.Ref == "" || r.Ref == "main")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,16 +42,17 @@ func TestIsTrunkBuild(t *testing.T) { // 1281/AC2
|
|||||||
r BuildRecord
|
r BuildRecord
|
||||||
want bool
|
want bool
|
||||||
}{
|
}{
|
||||||
{"built on main", BuildRecord{State: "built", Ref: "main", SourceInputs: inputs}, true},
|
{"built on main", BuildRecord{Ref: "main", SourceInputs: inputs}, true},
|
||||||
// a build a delivery plan asked after a merge, or rebuild / build --behind, carries no ref (live builds,
|
// a build a delivery plan asked after a merge, or rebuild / build --behind, carries no ref (live builds,
|
||||||
// 2026-10-19: zsh build-1792417485811374131, mesh-delivery build-1792435539606203626)
|
// 2026-10-19: zsh build-1792417485811374131, mesh-delivery build-1792435539606203626)
|
||||||
{"built with no ref", BuildRecord{State: "built", SourceInputs: inputs}, true},
|
{"built with no ref", BuildRecord{SourceInputs: inputs}, true},
|
||||||
{"another branch", BuildRecord{State: "built", Ref: "feat/x", SourceInputs: inputs}, false},
|
{"another branch", BuildRecord{Ref: "feat/x", SourceInputs: inputs}, false},
|
||||||
{"a commit", BuildRecord{State: "built", Ref: "9efdd87a", SourceInputs: inputs}, false},
|
{"a commit", BuildRecord{Ref: "9efdd87a", SourceInputs: inputs}, false},
|
||||||
{"failed", BuildRecord{State: "failed", Ref: "main", SourceInputs: inputs}, false},
|
{"asked", BuildRecord{State: "asked", SourceInputs: inputs}, false},
|
||||||
{"no state", BuildRecord{Ref: "main", SourceInputs: inputs}, false},
|
{"answered, state answered, no source inputs", BuildRecord{State: "answered"}, false},
|
||||||
{"no source inputs", BuildRecord{State: "built", Ref: "main"}, false},
|
{"failed", BuildRecord{Failed: "boom", Ref: "main", SourceInputs: inputs}, false},
|
||||||
{"no ref and no source inputs", BuildRecord{State: "built"}, false},
|
{"no source inputs", BuildRecord{Ref: "main"}, false},
|
||||||
|
{"no ref and no source inputs", BuildRecord{}, false},
|
||||||
} {
|
} {
|
||||||
if got := c.r.IsTrunkBuild(); got != c.want {
|
if got := c.r.IsTrunkBuild(); got != c.want {
|
||||||
t.Errorf("%s: IsTrunkBuild = %v, want %v", c.name, got, c.want)
|
t.Errorf("%s: IsTrunkBuild = %v, want %v", c.name, got, c.want)
|
||||||
@@ -59,7 +60,7 @@ func TestIsTrunkBuild(t *testing.T) { // 1281/AC2
|
|||||||
}
|
}
|
||||||
// decoded as the controller writes it: the key "ref" is absent
|
// decoded as the controller writes it: the key "ref" is absent
|
||||||
var r BuildRecord
|
var r BuildRecord
|
||||||
raw := `{"id":"build-1792417485811374131","state":"built","repository":"http://novox.internal:20000/novox/mesh-catalog.git","module":"zsh","source-inputs":{"Module":"zsh","Tree":"t"}}`
|
raw := `{"id":"build-1792417485811374131","repository":"http://novox.internal:20000/novox/mesh-catalog.git","module":"zsh","source-inputs":{"Module":"zsh","Tree":"t"}}`
|
||||||
if err := json.Unmarshal([]byte(raw), &r); err != nil || !r.IsTrunkBuild() {
|
if err := json.Unmarshal([]byte(raw), &r); err != nil || !r.IsTrunkBuild() {
|
||||||
t.Errorf("a decoded record with no ref: trunk = %v, err %v", r.IsTrunkBuild(), err)
|
t.Errorf("a decoded record with no ref: trunk = %v, err %v", r.IsTrunkBuild(), err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user