mesh-issues: a fix whose pull request has no record learns its merge from the delivery or the forge, so a delivered event can cover it (issue 1950)

This commit is contained in:
2026-10-27 01:24:57 +01:00
parent 64530a8dcb
commit b51f03c4ff
8 changed files with 402 additions and 67 deletions
+30 -33
View File
@@ -454,9 +454,22 @@ func (t *Tracker) OnDelivered(e stdio.Envelope) error {
at = t.now()
}
var changed []uint64
learned := 0
for n, is := range all {
for _, pr := range liveFixes(is) {
f := fixOf(is, pr)
if f == nil && learned < MostSweepAsks {
// A pull request named and kept no record for (merged before the tracker heard deliveries): it learns its
// merge now, so this very event can cover it (issue 1950).
learned++
if ok, err := t.learnFix(n, pr); err != nil {
t.Logf("[mesh-issues] the merge of %s (issue %d) could not be learned (asked again at the next event): %v", pr, n, err)
} else if ok {
if fresh, _, err := read(t.Store, n); err == nil {
is, f = fresh, fixOf(fresh, pr)
}
}
}
if f != nil && f.Merged && !contains(is.Delivered, pr) && f.MergedAs == "" {
t.fillMerge(n, f, pr)
}
@@ -624,6 +637,10 @@ type Delivery struct {
Repository string
Group string
MergedAs string
// Pulled says the answer carried the forge's pull request (merged, closed, the commit its merge landed as); Closed that it
// was closed without merging.
Pulled bool
Closed bool
}
// heard is what a delivery answered, as a fix keeps it.
@@ -662,16 +679,18 @@ func busDeliveryState(owner string) DeliveryState {
Group string `json:"group"`
MergedAs string `json:"merged_as"`
} `json:"delivery"`
Pull struct {
Merged bool `json:"merged"`
} `json:"pull"`
Pull *pullFacts `json:"pull"`
}
if err := json.Unmarshal(raw, &out); err != nil {
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,
d := Delivery{State: out.Delivery.State, Why: out.Delivery.Waits, Since: out.Delivery.Since.UTC(),
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
Repository: strings.ToLower(owner + "/" + repository), Group: out.Delivery.Group, MergedAs: out.Delivery.MergedAs}
if out.Pull != nil {
d.fromPull(*out.Pull, owner+"/"+repository)
}
return d, nil
}
}
@@ -695,34 +714,12 @@ func (t *Tracker) OnMoved(e stdio.Envelope) error {
if t.Deliveries != nil {
// Each ask up to the bus's 30 seconds, under the runtime's two minutes; one that fails, or more left to
// ask, has the event offered again, so a delivery never asked ends in max-deliveries, not in silence.
asked := 0
for _, pr := range fixPullRequests(is) {
if fixOf(is, pr) != nil {
continue
}
if asked == MostDeliveryAsks {
return fmt.Errorf("issue %d names more pull requests than are asked at once; the rest at the next offer", body.Number)
}
asked++
repo, number, _ := strings.Cut(pr, "#")
d, err := t.Deliveries(repo, number)
if err != nil {
return fmt.Errorf("the delivery of %s (issue %d) could not be asked: %v", pr, body.Number, err)
}
state := d.State
if state == "" {
// The delivery knows nothing of it yet: not kept, so it is asked again at the next move, and its own
// events say it when they come.
continue
}
// The delivery's own time, so a transition stamped the same moment is not taken as older.
since := d.Since
if since.IsZero() {
since = t.now()
}
if _, err := t.recordFix(pr, d.heard(since), body.Number); err != nil {
return err
}
asked, _, err := t.learnUnheard(body.Number, is, MostDeliveryAsks)
if err != nil {
return err
}
if asked == MostDeliveryAsks && len(unheardOf(is)) > asked {
return fmt.Errorf("issue %d names more pull requests than are asked at once; the rest at the next offer", body.Number)
}
}
return t.afterFix(body.Number)