Gate: seed source.main with the trunk head of a repository it names none of (issue 1422)
source.main moved only at a merge, so a repository never merged through the gate had no trunk head and the checker could never judge its pull requests. The gate already hears main's head on each gitea.pull.updated (base_sha); it now adds it for a repository source.main lacks, from the newest open pull request into main, in one compare-and-set write that never replaces a head. A pull request into another base seeds nothing (ADR 0484 §11).
This commit is contained in:
@@ -57,6 +57,8 @@ type Holder struct {
|
||||
|
||||
// catchUp asks the next look to weigh again the newest merge of each repository against source.main (SourceCatchUp).
|
||||
catchUp bool
|
||||
// seeded are the repositories source.main was found to name, or was seeded with, since this owner started (seedTrunks).
|
||||
seeded map[string]bool
|
||||
|
||||
mu sync.Mutex
|
||||
deliveries map[string]*Delivery
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -48,6 +49,36 @@ func TestStartingWritesTheNewestMergeOfEachRepositoryNotYetInSourceMain(t *testi
|
||||
t.Fatalf("a second catch-up moved source.main again: %d moves, then %d", n, len(w.source.moves))
|
||||
}
|
||||
|
||||
// Issue 1422: a repository never merged through the gate gets main's head as the forge announced its open pull request
|
||||
// with, once, beside the others; a pull request into another base, or one announced with no base head (the forge could
|
||||
// not read it), seeds nothing; a head source.main holds is never replaced.
|
||||
const never, trunkN, trunkM = "novox/never-merged", "eeee000000000000000000000000000000000001", "eeee000000000000000000000000000000000002"
|
||||
s := newWorld(t)
|
||||
s.source.main = &sdk.Source{V: sdk.Version, Group: "before", Heads: map[string]string{i1726Repo: i1726Merge1}}
|
||||
other := pr("novox/other-base", 6, "ffff000000000000000000000000000000000001", "fix/c", "a.go")
|
||||
other.Base, other.BaseSHA = "nox-mesh", trunkM
|
||||
s.h.PullUpdated(other)
|
||||
unread := pr("novox/unread", 2, "ffff000000000000000000000000000000000002", "fix/d", "a.go")
|
||||
s.h.PullUpdated(unread)
|
||||
seed := pr(never, 4, "ffff000000000000000000000000000000000003", "fix/e", "a.go")
|
||||
seed.BaseSHA = trunkN
|
||||
s.h.PullUpdated(seed)
|
||||
s.settleAll()
|
||||
if h := s.source.main.Heads; h[never] != trunkN || h[i1726Repo] != i1726Merge1 || h["novox/other-base"] != "" || h["novox/unread"] != "" || len(s.source.moves) != 1 {
|
||||
t.Fatalf("source.main is %v after %d moves; wanted %s seeded at %s, the catalogue kept and nothing else, in one move", h, len(s.source.moves), never, trunkN)
|
||||
}
|
||||
if !strings.Contains(s.logs(), "seeded with "+trunkN[:12]) {
|
||||
t.Errorf("the seed was not said: %s", s.logs())
|
||||
}
|
||||
again := pr(never, 5, "ffff000000000000000000000000000000000004", "fix/f", "a.go")
|
||||
again.BaseSHA = trunkM
|
||||
s.h.PullUpdated(again)
|
||||
s.h.SourceCatchUp()
|
||||
s.settleAll()
|
||||
if got := s.source.main.Heads[never]; got != trunkN || len(s.source.moves) != 1 {
|
||||
t.Fatalf("a seeded head was replaced by %s (%d moves)", got, len(s.source.moves))
|
||||
}
|
||||
|
||||
o := newWorld(t)
|
||||
o.source.main = &sdk.Source{V: sdk.Version, Group: "newer", Heads: map[string]string{i1726Repo: i1726Merge2}}
|
||||
o.h.PullUpdated(pr(i1726Repo, 471, head2, "fix/b", "modules/app/b.go"))
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -524,6 +525,66 @@ func (h *Holder) settleForgeMerges() []func() {
|
||||
return out
|
||||
}
|
||||
|
||||
// trunkHead is a full commit id as the forge names a branch's head.
|
||||
var trunkHead = regexp.MustCompile(`^[0-9a-f]{40}$`)
|
||||
|
||||
// seedTrunks gives source.main the trunk head of a repository it names none of (issue 1422): one never merged through the
|
||||
// gate, whose open pull requests the checker could otherwise never judge. The head is the base_sha the forge announced the
|
||||
// newest open pull request into main with (gitea.pull.updated; ADR 0323), the trunk's head when it was announced. It is
|
||||
// added in one compare-and-set write that never replaces a head source.main holds, so a merge always wins; the first merge
|
||||
// through the gate moves it as any other. source.main is per repository and names main's head only (ADR 0484 §11, MergeBranch):
|
||||
// a pull request into another base seeds nothing. A repository is weighed once per start after it was found or seeded.
|
||||
// Under the lock; the writing happens outside it.
|
||||
func (h *Holder) seedTrunks() []func() {
|
||||
newest := map[string]*Delivery{}
|
||||
for _, d := range h.deliveries {
|
||||
if h.seeded[d.Repository] || d.Base != MergeBranch || !trunkHead.MatchString(d.BaseSHA) || d.State.Final() || d.MergedAs != "" {
|
||||
continue
|
||||
}
|
||||
if o := newest[d.Repository]; o == nil || d.Created.After(o.Created) {
|
||||
newest[d.Repository] = d
|
||||
}
|
||||
}
|
||||
var out []func()
|
||||
for repository, d := range newest {
|
||||
id, head, group := d.ID, d.BaseSHA, firstOf(d.Branch, d.Repository)
|
||||
out = append(out, func() {
|
||||
seeded := false
|
||||
err := h.Source.Move(func(prev *sdk.Source) (sdk.Source, bool) {
|
||||
seeded = false
|
||||
next := sdk.Source{Group: group, Heads: map[string]string{}}
|
||||
if prev != nil {
|
||||
if prev.Heads[repository] != "" {
|
||||
return *prev, false // named already: never replaced
|
||||
}
|
||||
next.Group = prev.Group
|
||||
for r, held := range prev.Heads {
|
||||
next.Heads[r] = held
|
||||
}
|
||||
}
|
||||
next.Heads[repository] = head
|
||||
seeded = true
|
||||
return next, true
|
||||
})
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
if err != nil {
|
||||
h.Logf("[mesh-delivery] %s: source.main names no trunk head of %s, and it cannot be seeded yet: %v; asked again in %s", id, repository, err, askAgainAfter)
|
||||
h.again("source.main, seeded with the trunk head of a repository it names none of")
|
||||
return
|
||||
}
|
||||
if h.seeded == nil {
|
||||
h.seeded = map[string]bool{}
|
||||
}
|
||||
h.seeded[repository] = true
|
||||
if seeded {
|
||||
h.Logf("[mesh-delivery] source.main named no trunk head of %s: seeded with %s, main's head as the forge announced %s with, so its pull requests are judged (issue 1422)", repository, shortOf(head, 12), id)
|
||||
}
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// replayLate is how long after its merge a merge heard only from the forge's word is a replay: the forge announces a merge
|
||||
// within seconds, so one heard this late was replayed from the bus's history, and the source already holds what came after.
|
||||
const replayLate = 15 * time.Minute
|
||||
|
||||
@@ -294,6 +294,7 @@ func (h *Holder) readyAsks(now time.Time) []func() {
|
||||
asks = append(asks, h.turnWrites()...) // the merge turns, after the holds of this look (ADR 0548 §3)
|
||||
asks = append(asks, h.settleMerges()...)
|
||||
asks = append(asks, h.settleForgeMerges()...)
|
||||
asks = append(asks, h.seedTrunks()...)
|
||||
asks = append(asks, h.withdrawnKeys()...)
|
||||
asks = append(h.cancels, asks...)
|
||||
h.cancels = nil
|
||||
|
||||
Reference in New Issue
Block a user