Match a fix to a delivered revision by what the gate says of its merge; keep #474's source catch-up at start (issues 1946, 1726)

This commit is contained in:
2026-10-24 18:13:40 +02:00
parent 4dee77df23
commit 05f849979c
9 changed files with 226 additions and 13 deletions
@@ -131,3 +131,45 @@ func TestANewerRevisionCoversTheOlderMergesOfItsRepositoryAsTheForgeSaysAncestry
t.Fatalf("another repository's head covered it: %q", v.Delivery)
}
}
// A fix kept with only its merged flag (heard through `checks`, or kept before release 6) is matched to a delivered revision
// by asking the delivery for its merge: the group and the commit it landed as.
func TestAFixKeptWithoutItsMergeIsMatchedByAskingTheDeliveryForIt(t *testing.T) {
h := newHarness(t)
n := fixIssue(t, h, "mesh-catalog#40")
// Kept as a fix the tracker heard merged and nothing more: no group, no commit, no repository.
if _, err := h.change(n, Act{Op: "delivery", By: Seat}, func(i *Issue) error {
at := time.Date(2026, 10, 24, 12, 0, 0, 0, time.UTC)
i.Fixes = append(i.Fixes, Fix{PullRequest: "mesh-catalog#40", State: "published", Merged: true, MergedAt: &at, Since: at})
return nil
}); err != nil {
t.Fatal(err)
}
asked := 0
h.Deliveries = func(repository, number string) (Delivery, error) {
asked++
return Delivery{State: "merged", Merged: true, Repository: "novox/mesh-catalog", Group: "feat/r6", MergedAs: "9999999999990000"}, nil
}
viewSays(t, h, 20, "feat/r6", nil)
if asked == 0 {
t.Fatal("the delivery was not asked for the merge of a fix kept without it")
}
if v, _ := h.View(n); v.Delivery != "delivered to every machine it should reach" {
t.Fatalf("%q", v.Delivery)
}
// What `checks` says is carried: group, commit and repository reach the fix when it is heard through the delivery.
h2 := newHarness(t)
m := h2.open(t, "x")
h2.Deliveries = func(string, string) (Delivery, error) {
return Delivery{State: "merged", Merged: true, Since: time.Date(2026, 10, 24, 12, 0, 0, 0, time.UTC), Repository: "novox/mesh-catalog",
Group: "feat/q", MergedAs: "8888888888880000"}, nil
}
fixedAtFixing(t, h2, m, "mesh-catalog#41")
if err := h2.OnMoved(stdio.Envelope{Body: []byte(`{"number":` + itoa(int(m)) + `}`)}); err != nil {
t.Fatal(err)
}
f := fixOf(get(t, h2, m), "mesh-catalog#41")
if f == nil || f.Group != "feat/q" || f.MergedAs != "8888888888880000" || f.Repository != "novox/mesh-catalog" {
t.Fatalf("heard through checks the fix keeps %+v", f)
}
}
@@ -453,6 +453,9 @@ func (t *Tracker) OnDelivered(e stdio.Envelope) error {
for n, is := range all {
for _, pr := range liveFixes(is) {
f := fixOf(is, pr)
if f != nil && f.Merged && !contains(is.Delivered, pr) && f.MergedAs == "" {
t.fillMerge(n, f, pr)
}
if f == nil || !f.Merged || contains(is.Delivered, pr) || !(deliveredBy(f, body.Group, body.Heads) || t.coveredBy(f, body.Heads)) {
continue
}
@@ -482,6 +485,40 @@ func (t *Tracker) OnDelivered(e stdio.Envelope) error {
return nil
}
// fillMerge learns what the gate says of the merge of a fix kept without it (heard through `checks`, or kept before release 6
// with only its merged flag): the repository, the group and the commit its merge landed as, asked of the delivery. A delivery
// that cannot be asked, or does not say, leaves the fix as it was; it is asked again at the next event. f is updated in place.
func (t *Tracker) fillMerge(n uint64, f *Fix, pr string) {
if t.Deliveries == nil {
return
}
repo, number, _ := strings.Cut(pr, "#")
d, err := t.Deliveries(repo, number)
if err != nil {
t.Logf("[mesh-issues] the merge of %s (issue %d) could not be asked of the delivery: %v", pr, n, err)
return
}
if d.MergedAs == "" {
return
}
if _, err := t.change(n, Act{Op: "delivery", By: Seat}, func(i *Issue) error {
if g := fixOf(i, pr); g != nil {
g.MergedAs, g.Group = d.MergedAs, d.Group
if d.Repository != "" {
g.Repository = d.Repository
}
}
return nil
}); err != nil {
t.Logf("[mesh-issues] the merge of %s (issue %d) could not be kept: %v", pr, n, err)
return
}
f.MergedAs, f.Group = d.MergedAs, d.Group
if d.Repository != "" {
f.Repository = d.Repository
}
}
// busContains asks the forge's gitea_contains over the bus: whether commit is an ancestor of in, or it.
func busContains(owner, repo, commit, in string) (bool, error) {
raw, err := stdio.Ask("gitea.gitea_contains", map[string]any{"owner": owner, "repo": repo, "commit": commit, "in": in})
@@ -568,11 +605,16 @@ type Delivery struct {
Class string
Opened *time.Time
MergedAt *time.Time
// Repository (owner/name), Group and MergedAs are what the gate says of the merge, which the view's `delivered` is matched by.
Repository string
Group string
MergedAs string
}
// heard is what a delivery answered, as a fix keeps it.
func (d Delivery) heard(since time.Time) heardFix {
return heardFix{State: d.State, Why: d.Why, Merged: d.Merged, At: since, Class: d.Class, Opened: d.Opened, MergedAt: d.MergedAt}
return heardFix{State: d.State, Why: d.Why, Merged: d.Merged, At: since, Class: d.Class, Opened: d.Opened, MergedAt: d.MergedAt,
Repository: d.Repository, Group: d.Group, MergedAs: d.MergedAs}
}
// nonZero is a moment said, or nil for one not said.
@@ -602,6 +644,8 @@ func busDeliveryState(owner string) DeliveryState {
Class string `json:"class"`
Opened *time.Time `json:"opened"`
MergedAt *time.Time `json:"merged_at"`
Group string `json:"group"`
MergedAs string `json:"merged_as"`
} `json:"delivery"`
Pull struct {
Merged bool `json:"merged"`
@@ -611,7 +655,8 @@ func busDeliveryState(owner string) DeliveryState {
return Delivery{}, fmt.Errorf("mesh-delivery's checks answered no delivery: %.200s", raw)
}
return Delivery{State: out.Delivery.State, Why: out.Delivery.Waits, Since: out.Delivery.Since.UTC(), Merged: out.Pull.Merged,
Class: out.Delivery.Class, Opened: nonZero(out.Delivery.Opened), MergedAt: nonZero(out.Delivery.MergedAt)}, nil
Class: out.Delivery.Class, Opened: nonZero(out.Delivery.Opened), MergedAt: nonZero(out.Delivery.MergedAt),
Repository: strings.ToLower(owner + "/" + repository), Group: out.Delivery.Group, MergedAs: out.Delivery.MergedAs}, nil
}
}