Recognise a trunk build by repository identity and an empty ref in the indexer and the view (issue 1281)
mesh/repo-check head 4b8e71370cc2 merged into trunk fa4be27c1e40 — pass: its merge-check.sh passed
mesh/merge-gate head 4b8e71370cc2 merged into trunk fa4be27c1e40 — pass: group fix/1281-trunk-builds-by-repository composed: every machine composes with th…
mesh/delivery delivered
mesh/delivery-group group fix/1281-trunk-builds-by-repository delivered: every member is delivered
mesh/repo-check head 4b8e71370cc2 merged into trunk fa4be27c1e40 — pass: its merge-check.sh passed
mesh/merge-gate head 4b8e71370cc2 merged into trunk fa4be27c1e40 — pass: group fix/1281-trunk-builds-by-repository composed: every machine composes with th…
mesh/delivery delivered
mesh/delivery-group group fix/1281-trunk-builds-by-repository delivered: every member is delivered
Build records name their repository by clone URL and a plan-asked build has no ref, so no module fingerprint was ever written or compared; both now use the SDK's tests.
This commit is contained in:
@@ -3,7 +3,7 @@ module mesh-delivery-indexer
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261019195003-4580d80a32d9
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261019203925-84a764372337
|
||||
github.com/nats-io/nats-server/v2 v2.11.17
|
||||
github.com/nats-io/nats.go v1.54.0
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261019195003-4580d80a32d9 h1:XL1dbtSleGAixg6FX4gEnSG/AJxRG/tWw6/7U1RuzHc=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261019195003-4580d80a32d9/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261019203925-84a764372337 h1:cZyFnvDiBpnNx8ifSUG9WA61OHsn7PC2ml2s4eD7AkA=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261019203925-84a764372337/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0=
|
||||
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI=
|
||||
github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=
|
||||
|
||||
@@ -57,7 +57,11 @@ func (b *Builds) Delete(key string) {
|
||||
}
|
||||
}
|
||||
|
||||
func dirKey(repository, dir string) string { return repository + "\x00" + strings.Trim(dir, "/") }
|
||||
// dirKey names a directory of a repository whichever way the repository is spelled: a build record names it by its clone
|
||||
// URL, a forge message by "owner/name" (issue 1281).
|
||||
func dirKey(repository, dir string) string {
|
||||
return strings.ToLower(delivery.RepositoryName(repository)) + "\x00" + strings.Trim(dir, "/")
|
||||
}
|
||||
|
||||
// Module is the module a build record of the repository names the directory as its path, if one does.
|
||||
func (b *Builds) Module(repository, dir string) (string, bool) {
|
||||
@@ -85,8 +89,8 @@ func (b *Builds) ModuleOf(repository, dir string) string {
|
||||
return path.Base(strings.Trim(dir, "/"))
|
||||
}
|
||||
|
||||
// Known is a module the builds know of in a repository: its directory there and its newest successful trunk build (ref
|
||||
// main, state built, with source inputs) if any. The newest key of a module is any state and ref, so it is not what
|
||||
// Known is a module the builds know of in a repository: its directory there and its newest successful trunk build
|
||||
// (delivery.BuildRecord.IsTrunkBuild) if any. The newest key of a module is any state and ref, so it is not what
|
||||
// finds this (mesh-sdk go/delivery/buildrecord.go NewestKey); the records are.
|
||||
type Known struct {
|
||||
Module string
|
||||
@@ -115,7 +119,7 @@ func (b *Builds) Modules(repository string) []Known {
|
||||
} else if newer(r, m.any) {
|
||||
m.any = r
|
||||
}
|
||||
if r.Ref == "main" && r.State == "built" && r.SourceInputs != nil && (m.trunk == nil || newer(r, *m.trunk)) {
|
||||
if r.IsTrunkBuild() && (m.trunk == nil || newer(r, *m.trunk)) {
|
||||
c := r
|
||||
m.trunk = &c
|
||||
}
|
||||
@@ -127,7 +131,7 @@ func (b *Builds) Modules(repository string) []Known {
|
||||
if m.trunk != nil {
|
||||
k.Repository, k.Path = m.trunk.Repository, strings.Trim(m.trunk.Path, "/")
|
||||
}
|
||||
if k.Repository == repository {
|
||||
if delivery.SameRepository(k.Repository, repository) {
|
||||
out = append(out, k)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
package index
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// The build records below are the live ones, as `mesh-controller.command builds` showed them on 2026-10-19 at about
|
||||
// 21:20 local (the last 100 builds): id, state, module, repository (the clone URL, as the record names it), commit (the
|
||||
// eight characters the listing shows), the time (the listing's local minute, as UTC), the source fingerprint, and the
|
||||
// ref only where the listing shows "at main"; a build a delivery plan asked after a merge shows none, so the record
|
||||
// carries none. No reading command shows a whole record, so two fields come from elsewhere: the path is the module's
|
||||
// directory in the catalogue (a clone of mesh-catalog and mesh-media-catalog, origin/main at the same time), and the
|
||||
// source-inputs are mailu's live inputs (build-1792391134620131370, captured in mesh-sdk go/delivery/fingerprint_test.go
|
||||
// TestFingerprintLiveBuilds) with the module's name put in; the indexer reads only their bases, contexts and toolchains.
|
||||
const (
|
||||
liveTree = "cfc6fa8ff5fc1e220132e51ade653b9d3f387b3e"
|
||||
liveInputsTail = `"Tree":"` + liveTree + `","Bases":["sha256:25f3cfeaceca14921366af4d1240b56457ef46273bdb508c7b0e8f469f6fd228"],"Contexts":null,` +
|
||||
`"Toolchains":{"code":"typescript sha256:4b81682df5665c1f461d3f720a3a0fd7593f4aa71e4fc272dbfdc4bbd235b489 20125bd590041690"},"Unpinned":""}`
|
||||
catalogURL = "http://novox.internal:20000/novox/mesh-catalog.git"
|
||||
mediaCatalogURL = "http://novox.internal:20000/novox/mesh-media-catalog.git"
|
||||
)
|
||||
|
||||
func liveRecord(id, module, repository, ref, commit, path, at, source string) [2]any {
|
||||
raw := `{"id":"` + id + `","state":"built","repository":"` + repository + `",`
|
||||
if ref != "" {
|
||||
raw += `"ref":"` + ref + `",`
|
||||
}
|
||||
raw += `"module":"` + module + `","commit":"` + commit + `","path":"` + path + `","at":"` + at + `","source-fingerprint":"` + source +
|
||||
`","source-inputs":{"Module":"` + module + `",` + liveInputsTail + `}`
|
||||
return [2]any{delivery.BuildKey(module, id), []byte(raw)}
|
||||
}
|
||||
|
||||
func liveBuilds() [][2]any {
|
||||
return [][2]any{
|
||||
// a plan-asked build after a merge: no ref
|
||||
liveRecord("build-1792417485811374131", "zsh", catalogURL, "", "e8c34487", "modules/zsh", "2026-10-19T13:57:00Z", "src1:7e600dde59f3d7f019af00741583304288883e7d3ed955f4c61da26474f3be63"),
|
||||
liveRecord("build-1792435539606203626", "mesh-delivery", catalogURL, "", "9efdd87a", "modules/mesh-delivery", "2026-10-19T18:45:00Z", "src1:f79e2eedc2689cf6b3591ebfdb81ec65899131122fb2fe3dd9e384091d8a2a6d"),
|
||||
// a build at main
|
||||
liveRecord("build-1792417463192745880", "mailu", catalogURL, "main", "e8c34487", "modules/mailu", "2026-10-19T13:50:00Z", "src1:98d35b17d3a3295e3a0efed2b8c4316c9a9b32ec155f8343824654661b7383a2"),
|
||||
// a module of another repository
|
||||
liveRecord("build-1792417481951691634", "sonarr", mediaCatalogURL, "main", "197abb06", "modules/sonarr", "2026-10-19T13:57:00Z", "src1:0fff753b8c66aa8a8ac1f38d210cea9512a78a8592dd2475bfe6ade77ff21b61"),
|
||||
}
|
||||
}
|
||||
|
||||
func liveIndexer(t *testing.T) (*Indexer, *memStore) {
|
||||
t.Helper()
|
||||
ix, mods := modulesIndexer(t, liveBuilds()...)
|
||||
return ix, mods
|
||||
}
|
||||
|
||||
func TestMergeWritesEveryModuleTheLiveRecordsNameInTheRepository(t *testing.T) { // 1281/AC3
|
||||
ix, mods := liveIndexer(t)
|
||||
msg := mergedMsg("novox/mesh-catalog", map[string]string{
|
||||
"modules/zsh": liveTree, "modules/mesh-delivery": "newtree", "modules/mailu": "newtree", "modules/sonarr": "newtree",
|
||||
})
|
||||
if err := ix.Apply(context.Background(), msg); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, name := range []string{"zsh", "mesh-delivery", "mailu"} {
|
||||
m, ok := moduleOf(t, mods, name)
|
||||
if !ok {
|
||||
t.Errorf("%s: no module key written", name)
|
||||
continue
|
||||
}
|
||||
if (m.FP == "") == (m.None == "") || m.Repository != "novox/mesh-catalog" || m.Commit != merge {
|
||||
t.Errorf("%s: module = %+v, want an fp or a none, repository novox/mesh-catalog, commit %s", name, m, merge)
|
||||
}
|
||||
}
|
||||
// the tree of zsh at the merge is the tree its inputs record, so its fingerprint is the one those inputs give
|
||||
var in delivery.SourceInputs
|
||||
in.Module, in.Tree = "zsh", liveTree
|
||||
in.Bases = []string{"sha256:25f3cfeaceca14921366af4d1240b56457ef46273bdb508c7b0e8f469f6fd228"}
|
||||
in.Toolchains = map[string]string{"code": "typescript sha256:4b81682df5665c1f461d3f720a3a0fd7593f4aa71e4fc272dbfdc4bbd235b489 20125bd590041690"}
|
||||
want, _ := delivery.SourceFingerprint(in)
|
||||
if m, _ := moduleOf(t, mods, "zsh"); m.FP != want {
|
||||
t.Errorf("zsh fp = %q, want %q", m.FP, want)
|
||||
}
|
||||
if _, ok := mods.vals["sonarr"]; ok {
|
||||
t.Error("the key of a module of mesh-media-catalog was written at a merge of mesh-catalog")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChangedDirectoryMapsToTheModuleItsLiveRecordNamesByPath(t *testing.T) { // 1281/AC4
|
||||
ix, _ := liveIndexer(t)
|
||||
for dir, want := range map[string]string{"modules/zsh": "zsh", "modules/mesh-delivery": "mesh-delivery", "modules/mailu/": "mailu"} {
|
||||
if got, ok := ix.Builds.Module("novox/mesh-catalog", dir); !ok || got != want {
|
||||
t.Errorf("Module(novox/mesh-catalog, %q) = %q, %v; want %q", dir, got, ok, want)
|
||||
}
|
||||
}
|
||||
if got, ok := ix.Builds.Module("novox/mesh-catalog", "modules/sonarr"); ok {
|
||||
t.Errorf("Module found %q for a directory of mesh-media-catalog under mesh-catalog", got)
|
||||
}
|
||||
if got := ix.Builds.ModuleOf("novox/mesh-media-catalog", "modules/sonarr"); got != "sonarr" {
|
||||
t.Errorf("ModuleOf(mesh-media-catalog, modules/sonarr) = %q, want sonarr", got)
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ module mesh-delivery-view
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261019124204-9ade199740e3
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261019203925-84a764372337
|
||||
github.com/nats-io/nats-server/v2 v2.11.17
|
||||
github.com/nats-io/nats.go v1.54.0
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261019124204-9ade199740e3 h1:Mu2QiGbL/sZFU5ya4lGUyQOB5OKXPy/KB40XfXTf2EU=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261019124204-9ade199740e3/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261019203925-84a764372337 h1:cZyFnvDiBpnNx8ifSUG9WA61OHsn7PC2ml2s4eD7AkA=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261019203925-84a764372337/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0=
|
||||
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI=
|
||||
github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=
|
||||
|
||||
@@ -55,7 +55,8 @@ func modEntry(t *testing.T, name string, m delivery.Module) Entry {
|
||||
func trunkBuild(module, id, ref, state, fp, at string) []Entry {
|
||||
rec := `{"id":"` + id + `","state":"` + state + `","repository":"novox/mesh-delivery","ref":"` + ref + `","module":"` + module + `","commit":"7a74cee83f88e6585a5511c3cc53bdf0f5303d20","path":"modules/` + module + `","at":"` + at + `"`
|
||||
if fp != "" {
|
||||
rec += `,"source-fingerprint":"` + fp + `"`
|
||||
// the controller writes the inputs whenever it writes the fingerprint (mesh-sdk testdata/build-record-build-agent.json)
|
||||
rec += `,"source-fingerprint":"` + fp + `","source-inputs":{"Module":"` + module + `","Tree":"t","Bases":null,"Contexts":null,"Toolchains":null,"Unpinned":""}`
|
||||
}
|
||||
rec += `}`
|
||||
return []Entry{
|
||||
@@ -188,3 +189,23 @@ func TestUnreadableIndexerValuesAreNamed(t *testing.T) { // never dropped silent
|
||||
t.Errorf("snapshot = %+v", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPlanAskedBuildWithNoRefIsATrunkBuild(t *testing.T) { // 1281/AC5
|
||||
// The build record of module mesh-delivery from mesh-catalog, build-1792435539606203626, as `mesh-controller.command
|
||||
// builds` showed it on 2026-10-19 at about 21:20 local: built on ace at 20:45 local, the repository the clone URL, the
|
||||
// source fingerprint below, and no ref (a delivery plan asked it after a merge; the listing shows no "at main").
|
||||
// No reading command shows the whole record: its path is the module's directory in the catalogue, and its
|
||||
// source-inputs are mailu's live ones (mesh-sdk go/delivery/fingerprint_test.go) with the module's name put in.
|
||||
const live = "src1:f79e2eedc2689cf6b3591ebfdb81ec65899131122fb2fe3dd9e384091d8a2a6d"
|
||||
const rec = `{"id":"build-1792435539606203626","state":"built","repository":"http://novox.internal:20000/novox/mesh-catalog.git",` +
|
||||
`"module":"mesh-delivery","commit":"9efdd87a","path":"modules/mesh-delivery","at":"2026-10-19T18:45:00Z","source-fingerprint":"` + live + `",` +
|
||||
`"source-inputs":{"Module":"mesh-delivery","Tree":"cfc6fa8ff5fc1e220132e51ade653b9d3f387b3e","Bases":["sha256:25f3cfeaceca14921366af4d1240b56457ef46273bdb508c7b0e8f469f6fd228"],"Contexts":null,"Toolchains":null,"Unpinned":""}}`
|
||||
m := New()
|
||||
fill(m,
|
||||
Entry{Bucket: BuildsBucket, Key: "mesh-delivery.build.build-1792435539606203626", Value: []byte(rec), At: t0},
|
||||
Entry{Bucket: BuildsBucket, Key: "mesh-delivery.newest", Value: []byte("build-1792435539606203626"), At: t0},
|
||||
modEntry(t, "mesh-delivery", delivery.Module{FP: live, Repository: "novox/mesh-catalog", Commit: hA}))
|
||||
if r := fpRow(t, m.Snapshot(), "mesh-delivery"); r.State != FPSame || r.Build != live {
|
||||
t.Errorf("mesh-delivery = %+v, want same as the no-ref build's fingerprint", r)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,12 +454,12 @@ func (m *Model) groupRows() []GroupRow {
|
||||
}
|
||||
|
||||
// fingerprintRows compare each module the indexer fingerprinted with the source fingerprint of the module's newest
|
||||
// successful trunk build (ref main, state built) that recorded one. The newest key is any state and ref, so it is not
|
||||
// successful trunk build (delivery.BuildRecord.IsTrunkBuild: built, on main or with no ref) that recorded one. The newest key is any state and ref, so it is not
|
||||
// used: the records are.
|
||||
func (m *Model) fingerprintRows() []FingerprintRow {
|
||||
newest := map[string]buildEntry{}
|
||||
for _, b := range m.builds {
|
||||
if b.rec.Module == "" || b.rec.Ref != "main" || b.rec.State != "built" || b.rec.SourceFingerprint == "" {
|
||||
if b.rec.Module == "" || !b.rec.IsTrunkBuild() || b.rec.SourceFingerprint == "" {
|
||||
continue
|
||||
}
|
||||
if old, ok := newest[b.rec.Module]; !ok || b.rec.At.After(old.rec.At) || (b.rec.At.Equal(old.rec.At) && b.rec.ID > old.rec.ID) {
|
||||
|
||||
Reference in New Issue
Block a user