Bring main into release 57's branch, conflicts resolved
mesh/merge-gate head 4b2eac702ab1 merged into trunk 221fe5a60d1f — pass: the change touches no module of the mesh's graph
mesh/repo-check head 4b2eac702ab1 merged into trunk 221fe5a60d1f — pass: THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh): feat/57-config-and-provisioning'…
mesh/delivery delivered
mesh/merge-gate head 4b2eac702ab1 merged into trunk 221fe5a60d1f — pass: the change touches no module of the mesh's graph
mesh/repo-check head 4b2eac702ab1 merged into trunk 221fe5a60d1f — pass: THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh): feat/57-config-and-provisioning'…
mesh/delivery delivered
So the release branch carries what main gained, and the store-tests drop check passes again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -164,6 +164,30 @@ 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"`
|
||||
// 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,
|
||||
// nothing built for a merge. Tests narrows what the repository's script runs to the tests a pattern names,
|
||||
// and StoreOnly to its store tests; a script that reads neither runs whole.
|
||||
Try bool `json:"try,omitempty"`
|
||||
Tests string `json:"tests,omitempty"`
|
||||
StoreOnly bool `json:"store-only,omitempty"`
|
||||
}
|
||||
|
||||
// StepTried is the step a build seat says a try's outcome under, in the build's log: one line, a Tried as JSON
|
||||
// (novox/hq issue 686).
|
||||
const StepTried = "tried"
|
||||
|
||||
// Tried is what came of a branch tried before its pull request (novox/hq issue 686): the verdict of the
|
||||
// repository's own check for the ref merged into the trunk, how long it took, and the lines that name what
|
||||
// failed, picked from the whole of its output (issue 460).
|
||||
type Tried struct {
|
||||
Verdict string `json:"verdict"`
|
||||
Summary string `json:"summary"`
|
||||
Took string `json:"took"`
|
||||
Head string `json:"head,omitempty"`
|
||||
Trunk string `json:"trunk,omitempty"`
|
||||
Failed []string `json:"failed,omitempty"`
|
||||
}
|
||||
|
||||
// CheckBuild is one module a check builds for its merge: from the merge it made when Merged — a module built from
|
||||
|
||||
@@ -96,8 +96,27 @@ type BuildStart struct {
|
||||
// an earlier taker left unfinished — most often a build agent replaced mid-build — and started
|
||||
// again from the beginning (novox/hq issue 594). 0 from a machine that predates it.
|
||||
Delivery int `json:"delivery,omitempty"`
|
||||
// Try marks the start of a branch tried before its pull request (novox/hq issue 686), which names no
|
||||
// repository and no ref (startOf).
|
||||
Try bool `json:"try,omitempty"`
|
||||
}
|
||||
|
||||
// startOf is what a build machine says of the ask it took. **A try's start names no repository and no commit**
|
||||
// (novox/hq issue 686): the forge's holder writes mesh/merge-gate pending on an open pull request whose head is
|
||||
// the commit a start names, and a try of that head is no check of the pull request — nothing on the forge moves
|
||||
// for a try. Its id, machine and time are all the queue reads of a start.
|
||||
func startOf(r BuildRequest, on string, delivery int, at time.Time) BuildStart {
|
||||
s := BuildStart{ID: r.ID, Repository: r.Repository, Path: r.Path, Ref: r.Ref, On: on,
|
||||
At: at.UTC().Format(time.RFC3339Nano), Delivery: delivery}
|
||||
if IsTry(r) {
|
||||
s.Repository, s.Path, s.Ref, s.Try = "", "", "", true
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// IsTry says an ask is a branch tried before its pull request (novox/hq issue 686).
|
||||
func IsTry(r BuildRequest) bool { return r.Check != nil && r.Check.Try }
|
||||
|
||||
// Restarted is how a build handed out again says so (novox/hq issue 594), in its log and in its check's
|
||||
// report, so a second run never reads as the first. Neutral on why: a holder replaced mid-build, a paused
|
||||
// holder handing an ask back as it arrived, and a bus that held its acknowledgements past the wait all
|
||||
|
||||
@@ -401,10 +401,7 @@ func (b *natsBuild) Done() error { return b.msg.Ack() }
|
||||
// Began publishes that this machine has taken the build, into the stream like the outcome, so a
|
||||
// reader that asks afterwards sees when it started as well as how it ended.
|
||||
func (b *natsBuild) Began(ctx context.Context) error {
|
||||
body, err := json.Marshal(BuildStart{
|
||||
ID: b.request.ID, Repository: b.request.Repository, Path: b.request.Path,
|
||||
Ref: b.request.Ref, On: b.on, At: time.Now().UTC().Format(time.RFC3339Nano), Delivery: b.delivery,
|
||||
})
|
||||
body, err := json.Marshal(startOf(b.request, b.on, b.delivery, time.Now()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -427,6 +427,9 @@ func ReadCheckBuilds(ctx context.Context, js *broker.JetStream, seat string, pul
|
||||
// safeKey is an id that can be a key, and a subject token, as it is: a build id, never a pattern.
|
||||
var safeKey = regexp.MustCompile(`^[A-Za-z0-9_-]+$`)
|
||||
|
||||
// SafeBuildID says an id can be a subject token as it is: a build's id, never a pattern.
|
||||
func SafeBuildID(id string) bool { return safeKey.MatchString(id) }
|
||||
|
||||
// Cancelled is what the controller writes for an ask it cancelled.
|
||||
type Cancelled struct {
|
||||
At string `json:"at"`
|
||||
|
||||
@@ -26,6 +26,8 @@ import (
|
||||
// - a check that has waited CheckWaitsAtMost goes before every delivery asked after it, so no check
|
||||
// waits for ever behind a stream of deliveries;
|
||||
// - within a kind, first come, first served, by when it was asked;
|
||||
// - a branch tried before its pull request goes after everything else, however long it has waited: it is
|
||||
// never promoted ahead of a delivery, and never ahead of a pull request's check (novox/hq issue 686);
|
||||
// - an ask whose kind cannot be told counts as a check: a delivery taken late breaks a budget, a check
|
||||
// taken late breaks nothing.
|
||||
//
|
||||
@@ -64,8 +66,12 @@ func (a QueuedAsk) askedOrStored() time.Time {
|
||||
// QueueOrder is the order the waiting asks are to be handed out in, now: the deliveries and every check
|
||||
// that has waited CheckWaitsAtMost, by when each was asked, then the other checks, by when each was asked.
|
||||
func QueueOrder(waiting []QueuedAsk, now time.Time) []QueuedAsk {
|
||||
var first, then []QueuedAsk
|
||||
var first, then, tries []QueuedAsk
|
||||
for _, a := range waiting {
|
||||
if IsTry(a.Request) {
|
||||
tries = append(tries, a)
|
||||
continue
|
||||
}
|
||||
if KindOf(a.Request) == KindCheck && now.Sub(a.askedOrStored()) < CheckWaitsAtMost {
|
||||
then = append(then, a)
|
||||
continue
|
||||
@@ -82,7 +88,8 @@ func QueueOrder(waiting []QueuedAsk, now time.Time) []QueuedAsk {
|
||||
}
|
||||
byAsking(first)
|
||||
byAsking(then)
|
||||
return append(first, then...)
|
||||
byAsking(tries)
|
||||
return append(append(first, then...), tries...)
|
||||
}
|
||||
|
||||
// toMove is the asks to move to the tail, in this order, for the queue to stand as order says: everything
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package link
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// The start of a try names no repository and no commit (novox/hq issue 686): the forge's holder writes
|
||||
// mesh/merge-gate pending on the open pull request whose head a start names, and nothing on the forge moves
|
||||
// for a try. A pull request's check still names both.
|
||||
func TestATrysStartNamesNoCommitTheForgeCouldMatch(t *testing.T) {
|
||||
sha := strings.Repeat("c0ffee", 7)[:40]
|
||||
at := time.Date(2026, 10, 14, 0, 0, 0, 0, time.UTC)
|
||||
try := BuildRequest{ID: "build-1", Repository: "https://forge/novox/a.git", Ref: sha,
|
||||
Check: &CheckRequest{Owner: "novox", Repo: "a", Try: true}}
|
||||
body, _ := json.Marshal(startOf(try, "ace", 1, at))
|
||||
if strings.Contains(string(body), sha) || strings.Contains(string(body), "novox/a") {
|
||||
t.Fatalf("a try's start names what the forge matches a pull request by: %s", body)
|
||||
}
|
||||
if s := startOf(try, "ace", 2, at); s.ID != "build-1" || s.On != "ace" || !s.Try || s.Delivery != 2 || s.At == "" {
|
||||
t.Fatalf("a try's start lost what the queue reads: %+v", s)
|
||||
}
|
||||
pull := try
|
||||
pull.Check = &CheckRequest{Owner: "novox", Repo: "a", Number: 7}
|
||||
if s := startOf(pull, "ace", 1, at); s.Ref != sha || s.Repository == "" || s.Try {
|
||||
t.Fatalf("a pull request's check's start: %+v", s)
|
||||
}
|
||||
}
|
||||
|
||||
// A try stays behind every delivery and every pull request's check, however long it has waited: the ten
|
||||
// minutes that put a check ahead of later deliveries never promote a try (novox/hq issue 686).
|
||||
func TestATryIsNeverHandedOutBeforeADeliveryOrAPullRequestsCheck(t *testing.T) {
|
||||
now := time.Date(2026, 10, 12, 12, 0, 0, 0, time.UTC)
|
||||
try := func(seq uint64, id string, asked time.Time) QueuedAsk {
|
||||
a := anAsk(seq, id, asked, true)
|
||||
a.Request.Check.Try = true
|
||||
return a
|
||||
}
|
||||
waiting := []QueuedAsk{
|
||||
try(1, "try-old", now.Add(-40*time.Minute)),
|
||||
try(2, "try-young", now.Add(-time.Minute)),
|
||||
anAsk(3, "check-young", now.Add(-2*time.Minute), true),
|
||||
anAsk(4, "delivery", now.Add(-time.Minute), false),
|
||||
}
|
||||
want := []string{"delivery", "check-young", "try-old", "try-young"}
|
||||
if got := idsOf(QueueOrder(waiting, now)); !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("handed out as %v, want %v", got, want)
|
||||
}
|
||||
if !SafeBuildID("build-20261014T000000-1a2b") || SafeBuildID("x.>") || SafeBuildID("*") || SafeBuildID("") {
|
||||
t.Fatal("an id that is a pattern passes for a build's id")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user