Keep a check build whose key is unchanged instead of building it again
mesh/merge-gate head 1666747a2246 merged into trunk 04730b54a8f2 — pass: builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step; every …
mesh/repo-check head 1666747a2246 merged into trunk 04730b54a8f2 — pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request

A fresh check asked when only the trunk moved rebuilt every image, though
the reuse key said nothing it is built from changed: a controller check
took about 12 minutes instead of 5.5. The check now keeps a build kept
under its key when a verdict of the build seat names that key and every
digest, names it as the build's own check so a walk takes it, and the
cleanup keeps it while any check names it (novox/hq issue 703).
This commit is contained in:
2026-10-14 09:41:39 +02:00
parent 04730b54a8
commit 1666747a22
11 changed files with 328 additions and 22 deletions
+7
View File
@@ -164,6 +164,10 @@ type CheckRequest struct {
// each, from the merge it judged, into the scratch namespace once it has judged and not failed, for the walk to
// take. A build seat from before it builds nothing, and the walk builds as before.
Builds []CheckBuild `json:"builds,omitempty"`
// Named are the builds the seat's verdicts name for those modules, of any pull request, within the week check
// builds are kept (novox/hq issue 703): the check keeps a build kept under a module's unchanged key, instead of
// building it again, only when one of these names its key and every digest. A build seat from before it builds.
Named []CheckedBuild `json:"named,omitempty"`
// Try marks a branch tried before its pull request (novox/hq issue 686): the repository's own check for a
// pushed branch or commit, asked by a builder through the controller's `try` verb. Its outcome is read from
// the build's log (StepTried) and said to nobody: no status on the forge, nothing recorded or registered,
@@ -212,6 +216,9 @@ type CheckBuilt struct {
Made []MadeArtifact `json:"made,omitempty"`
NotBuilt string `json:"not-built,omitempty"`
Took string `json:"took,omitempty"`
// Kept names the check whose build this check kept instead of building, its key unchanged (novox/hq issue 703):
// a walk takes it as that check's build. Empty when this check built it.
Kept string `json:"kept,omitempty"`
}
// GroupMember is one other head of a delivery group's composed check.
+30
View File
@@ -37,3 +37,33 @@ func TestNatsTheBuildsAWalkMayTakeAreTheChecksOutcomesOfItsPullRequests(t *testi
t.Fatalf("read %+v", got)
}
}
// A check reads the builds every pull request's checks made of the modules it builds, and a kept build is named as
// the check's that built it (novox/hq issue 703).
func TestNatsACheckReadsTheBuildsOfItsModulesAndAKeptBuildIsTheBuildersCheck(t *testing.T) {
js := aBus(t)
since := time.Now().Add(-time.Minute)
say := func(r BuildResult) {
body, _ := json.Marshal(r)
if _, err := js.Context().Publish(BuildOutcomeOf(TheBuildMachine), body); err != nil {
t.Fatal(err)
}
}
app := CheckBuilt{Module: "app", Key: "r1:1", Made: []MadeArtifact{{Name: "server", Reference: "s/app/server@sha256:a"}}}
web := CheckBuilt{Module: "web", Key: "r1:2", Made: []MadeArtifact{{Name: "server", Reference: "s/web/server@sha256:b"}}}
say(BuildResult{ID: "build-1", Checked: &CheckRequest{Owner: "novox", Repo: "shop", Number: 7},
Check: &CheckOutcome{Verdict: "pass", Built: []CheckBuilt{app, web}}})
kept := app
kept.Kept = "build-1"
say(BuildResult{ID: "build-2", Checked: &CheckRequest{Owner: "novox", Repo: "till", Number: 3},
Check: &CheckOutcome{Verdict: "pass", Built: []CheckBuilt{kept}}})
got, err := ReadCheckBuildsOf(t.Context(), js, TheBuildMachine, []string{"app"}, since)
if err != nil {
t.Fatal(err)
}
if len(got) != 2 || got[0].Check != "build-1" || got[0].Pull.Repo != "till" || got[1].Check != "build-1" ||
got[1].Built.Module != "app" {
t.Fatalf("read %+v", got)
}
}
+26 -5
View File
@@ -375,6 +375,26 @@ func ReadCheckBuilds(ctx context.Context, js *broker.JetStream, seat string, pul
for _, p := range pulls {
wanted[p] = true
}
return readCheckBuilds(ctx, js, seat, since, func(p PullRef, _ CheckBuilt) bool { return wanted[p] })
}
// ReadCheckBuildsOf is every build the seat's checks of any pull request said they made, or kept, of those modules
// since a moment (novox/hq issue 703): what a check may keep instead of building. Newest first.
func ReadCheckBuildsOf(ctx context.Context, js *broker.JetStream, seat string, modules []string, since time.Time) ([]CheckedBuild, error) {
if len(modules) == 0 {
return nil, nil
}
wanted := map[string]bool{}
for _, m := range modules {
wanted[m] = true
}
return readCheckBuilds(ctx, js, seat, since, func(_ PullRef, b CheckBuilt) bool { return wanted[b.Module] })
}
// readCheckBuilds is every build the seat's checks said they made, or kept, that wanted takes. A kept build is named
// as the check's that built it (novox/hq issue 703): the record under its key is that check's.
func readCheckBuilds(ctx context.Context, js *broker.JetStream, seat string, since time.Time,
wanted func(PullRef, CheckBuilt) bool) ([]CheckedBuild, error) {
subject := BuildOutcomeOf(seat)
sub, err := js.Context().PullSubscribe(subject, "",
nats.BindStream(broker.EventsStream), nats.StartTime(since), nats.AckNone())
@@ -402,12 +422,13 @@ func ReadCheckBuilds(ctx context.Context, js *broker.JetStream, seat string, pul
continue
}
pull := PullRef{Owner: r.Checked.Owner, Repo: r.Checked.Repo, Number: r.Checked.Number}
if !wanted[pull] {
continue
}
for _, b := range r.Check.Built {
if b.NotBuilt == "" && b.Key != "" {
out = append(out, CheckedBuild{Pull: pull, Check: r.ID, Built: b})
if b.NotBuilt == "" && b.Key != "" && wanted(pull, b) {
check := r.ID
if b.Kept != "" {
check = b.Kept
}
out = append(out, CheckedBuild{Pull: pull, Check: check, Built: b})
}
}
}