The checker's line says whether each candidate module's build is built, failed or waiting, read from the builder's keys (issue 1684)

This commit is contained in:
2026-10-21 21:29:13 +02:00
parent 66908d9bbc
commit 4b063252e7
6 changed files with 165 additions and 6 deletions
@@ -220,7 +220,7 @@ func startBed(t *testing.T) *bed {
for name, history := range map[string]int{delivery.CheckerVerdictsBucket: delivery.CheckerVerdictsHistory, delivery.CheckerKeptBucket: delivery.CheckerKeptHistory,
delivery.CheckerOwedBucket: delivery.CheckerOwedHistory, delivery.PreviewsBucket: delivery.PreviewsHistory, delivery.CandidatesBucket: delivery.CandidatesHistory,
delivery.ModulesBucket: delivery.ModulesHistory, delivery.ManifestsBucket: delivery.ManifestsHistory, delivery.SourceBucket: delivery.SourceHistory,
delivery.PresenceBucket: delivery.PresenceHistory, delivery.HaveBucket: delivery.HaveHistory} {
delivery.PresenceBucket: delivery.PresenceHistory, delivery.HaveBucket: delivery.HaveHistory, delivery.BuilderBuildsBucket: delivery.BuilderBuildsHistory} {
kv, err := js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: name, History: uint8(history)})
if err != nil {
t.Fatal(err)
@@ -526,3 +526,28 @@ func TestNoCheckStartsWhileADeliveryBuildsAndOneStartsWhenItEnds(t *testing.T) {
t.Errorf("verdict = %+v", v)
}
}
// TestTheLineSaysTheStateOfTheBuildsOfTheCandidatesModules: the builder wrote builds.avahi.<fp> done for the fingerprint the
// candidate carries for avahi; the status's line says avahi is built (ADR 0484 clause 7).
func TestTheLineSaysTheStateOfTheBuildsOfTheCandidatesModules(t *testing.T) { // N7/AC1
b := startBed(t)
fp := "src2:" + strings.Repeat("a", 64)
buildKey, _ := delivery.BuilderBuildKey("avahi", fp)
done, err := delivery.EncodeBuilderBuild(delivery.BuilderBuild{State: delivery.BuildDone, By: "n1.mesh-delivery-builder", Manifest: strings.Repeat("0a", 32),
Artifacts: []delivery.BuiltArtifact{{Name: "avahi", Kind: "bundle", Digest: "sha256:" + strings.Repeat("a", 64)}}})
if err != nil {
t.Fatal(err)
}
if _, err := b.buckets[delivery.BuilderBuildsBucket].Put(b.ctx, buildKey, done); err != nil {
t.Fatal(err)
}
p := pull(repo, 7, head1)
p.Modules = []string{"avahi"}
p.Fps = map[string]delivery.CandidateModule{"avahi": {FP2: fp, Manifest: strings.Repeat("0a", 32)}}
key := b.candidate(t, "feat/x", p)
b.start(t, nil)
b.verdict(t, key, ended)
if o := b.owed(t, 7, head1, "success"); !strings.Contains(o.Text, "builds: avahi built") {
t.Errorf("owed = %+v, want the builds in the line", o)
}
}
@@ -24,12 +24,13 @@ type judgeDeps struct {
previews check.KV
repos *repoModules
announce check.Announcer
builds check.BuildStates
logf func(format string, args ...any)
}
func newJudge(d judgeDeps) check.Judge {
return &check.Examiner{Steps: d.runner, Toolchains: d.tools, Presence: d.presence, Health: d.have, Places: d.places,
Kept: check.Kept{Store: d.kept}, Previews: d.previews, Modules: d.repos, Announce: d.announce, Trunks: d.trunks, Logf: d.logf}
Kept: check.Kept{Store: d.kept}, Previews: d.previews, Modules: d.repos, Builds: d.builds, Announce: d.announce, Trunks: d.trunks, Logf: d.logf}
}
// manifestKV reads a manifest by its digest.
@@ -110,3 +111,19 @@ func (r *repoModules) BuildModules(repository string) []string {
sort.Strings(out)
return out
}
// buildReader reads the builder's build of a module at a fingerprint (mesh-delivery-builder_builds).
type buildReader struct{ kv kvStore }
func (r buildReader) Build(ctx context.Context, module, fp2 string) (delivery.BuilderBuild, bool, error) {
key, err := delivery.BuilderBuildKey(module, fp2)
if err != nil {
return delivery.BuilderBuild{}, false, err
}
value, _, found, err := r.kv.Get(ctx, key)
if err != nil || !found {
return delivery.BuilderBuild{}, false, err
}
b, err := delivery.DecodeBuilderBuild(value)
return b, err == nil, err
}
@@ -198,6 +198,17 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
Link: link,
Logf: logger.Printf,
}
// The builder's builds say how the candidates' fingerprints stand (issue 1684); where its bucket is not there yet the line
// says nothing of the builds, until the checker starts again.
var builds check.BuildStates
switch kv, err := js.KeyValue(ctx, delivery.BuilderBuildsBucket); {
case errors.Is(err, jetstream.ErrBucketNotFound):
logger.Printf("the bucket %s does not exist yet: the line says nothing of the builds until the checker starts again", delivery.BuilderBuildsBucket)
case err != nil:
return fmt.Errorf("the bucket %s: %w", delivery.BuilderBuildsBucket, err)
default:
builds = buildReader{kvStore{kv}}
}
holds := newHoldWatch(ctx, js, ch.Reconsider, logger)
ch.Hold = holds.Held
announce := func(ctx context.Context, event, key string) {
@@ -210,7 +221,7 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
ch.Judge = newJudge(judgeDeps{
runner: runner, tools: tools, presence: presence, have: have, places: places, trunks: trunks,
kept: kvStore{buckets[delivery.CheckerKeptBucket]}, previews: kvStore{buckets[delivery.PreviewsBucket]},
repos: repos, logf: logger.Printf,
repos: repos, builds: builds, logf: logger.Printf,
announce: announce,
})
ch.Announce = announce