Both reads go in R4g with build-agent. A path a key names is never overwritten, and no fingerprint changes.
91 lines
2.9 KiB
Go
91 lines
2.9 KiB
Go
package index
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"sync"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
)
|
|
|
|
// Builds holds the controller's build records (mesh-controller_builds) for the two things the indexer still reads from
|
|
// them until release R4g, when build-agent goes (the lead's decision of R4e, 2026-10-22):
|
|
// - the Builder input of the builder's own module, which build-agent builds (TrunkOf; novox/hq ADR 0453 clause 5,
|
|
// issue 1727);
|
|
// - the directory of a module whose modules key has none (PathOf; issue 1614), filled once and changing no fingerprint.
|
|
//
|
|
// It lists no module and maps no directory to a module: the indexer's modules are those its own messages and bucket name
|
|
// (ADR 0501 clause 6, issue 1851). Safe for use from a watch and from the message handler at once.
|
|
type Builds struct {
|
|
mu sync.Mutex
|
|
records map[string]delivery.BuildRecord // by bucket key
|
|
}
|
|
|
|
// NewBuilds is an empty Builds.
|
|
func NewBuilds() *Builds { return &Builds{records: map[string]delivery.BuildRecord{}} }
|
|
|
|
// Put takes a key of the builds bucket and its value. Only a build record of a module (key `<module>.build.<id>`,
|
|
// delivery.BuildKey) counts; the newest-pointer keys and requests that knew no module are not records of one.
|
|
func (b *Builds) Put(key string, value []byte) {
|
|
if !strings.Contains(key, ".build.") {
|
|
return
|
|
}
|
|
var r delivery.BuildRecord
|
|
if err := json.Unmarshal(value, &r); err != nil || r.Module == "" || r.Repository == "" {
|
|
return
|
|
}
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
b.records[key] = r
|
|
}
|
|
|
|
// Delete takes a key out.
|
|
func (b *Builds) Delete(key string) {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
delete(b.records, key)
|
|
}
|
|
|
|
func newer(a, b delivery.BuildRecord) bool {
|
|
return a.At.After(b.At) || (a.At.Equal(b.At) && a.ID > b.ID)
|
|
}
|
|
|
|
// TrunkOf is the newest successful trunk build record of a module, in whatever repository it was built from; nil when
|
|
// there is none (issue 1727).
|
|
func (b *Builds) TrunkOf(module string) *delivery.BuildRecord {
|
|
var best *delivery.BuildRecord
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
for _, r := range b.records {
|
|
if r.Module == module && r.IsTrunkBuild() && (best == nil || newer(r, *best)) {
|
|
c := r
|
|
best = &c
|
|
}
|
|
}
|
|
return best
|
|
}
|
|
|
|
// PathOf is the directory of a module of the repository as its newest build record of that repository names it, its
|
|
// newest successful trunk build's first ("." for the repository's root), and false when no record of the repository
|
|
// names the module (issue 1614).
|
|
func (b *Builds) PathOf(module, repository string) (string, bool) {
|
|
var best *delivery.BuildRecord
|
|
trunk := false
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
for _, r := range b.records {
|
|
if r.Module != module || !delivery.SameRepository(r.Repository, repository) {
|
|
continue
|
|
}
|
|
t := r.IsTrunkBuild()
|
|
if best == nil || (t && !trunk) || (t == trunk && newer(r, *best)) {
|
|
c := r
|
|
best, trunk = &c, t
|
|
}
|
|
}
|
|
if best == nil {
|
|
return "", false
|
|
}
|
|
return pathOf(strings.Trim(best.Path, "/")), true
|
|
}
|