Find the teller's warrant by the heads its Approve is bound to, and judge a gap as hearMerge does (ADR 0457, issue 1336, review round 1)
This commit is contained in:
+102
-42
@@ -205,8 +205,8 @@ var readMergeEvent = func(ctx context.Context, owner, repo, branch string, tm tr
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// mergeAskPrefix is what the id of a merge ask of the delivery begins with when it names a pull request and its head: the
|
||||
// number and the head's first twelve digits (mesh-delivery's `merge-<number>-<head>-<try>`).
|
||||
// mergeAskPrefix is what the id of a merge ask made by the retired module mesh-delivery begins with: the pull request's number
|
||||
// and the head's first twelve digits (`merge-<number>-<head>-<try>`). The teller's ids name neither (see findMergeWarrant).
|
||||
func mergeAskPrefix(number int, head string) string {
|
||||
if len(head) > 12 {
|
||||
head = head[:12]
|
||||
@@ -221,8 +221,8 @@ func askersOfMerges() []string { return []string{sdkdelivery.TellerModule, theDe
|
||||
|
||||
// caughtUpWarrant judges a chosen merge ask of the router as the warrant of a merge: the ask is the operator's, the warrant
|
||||
// answers that very ask as it was published, and it is the operator's Approve proved by the channel it came through (ADR 0437
|
||||
// §6, as movedOnApprove judges an announced one). What it cannot re-prove is the heads the Approve was bound to: the
|
||||
// router's record does not carry them, and the ask's id names the pull request and the head merged.
|
||||
// §6, as movedOnApprove judges an announced one). The heads the Approve was bound to are checked by the caller, where it can
|
||||
// name them.
|
||||
func caughtUpWarrant(asker string, ask *asks.Ask, w *asks.Warrant) (string, error) {
|
||||
if err := askerAccepted(asker); err != nil {
|
||||
return "", err
|
||||
@@ -281,33 +281,107 @@ var routerAskIDs = func(ctx context.Context, inv catalogueReader, asker string)
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
// findMergeWarrant finds the warrant of a merge: the router's chosen merge ask whose id names the pull request and its head,
|
||||
// under an asker the controller takes a warrant from. Why it holds; or found false with why not, for a person to read; or an
|
||||
// error when the router could not be read, which is no answer. A variable so a test needs no bus.
|
||||
var findMergeWarrant = func(ctx context.Context, inv catalogueReader, number int, head string) (why string, found bool, reason string, err error) {
|
||||
if number <= 0 {
|
||||
return "", false, "the merge commit names no pull request, so no ask of the router can name it", nil
|
||||
// closedRecord is a router record of an ask that ended: it never changes, so it is read once.
|
||||
type closedRecord struct {
|
||||
ask *asks.Ask
|
||||
w *asks.Warrant
|
||||
}
|
||||
|
||||
var closedRecords sync.Map // asker + "." + id -> closedRecord
|
||||
|
||||
// closedRecordOf is the router's record of an ask, kept once it is closed. A nil ask is an ask still open, ended without a
|
||||
// choice, or not there.
|
||||
func closedRecordOf(ctx context.Context, inv catalogueReader, asker, id string) (*asks.Ask, *asks.Warrant, error) {
|
||||
if v, ok := closedRecords.Load(asker + "." + id); ok {
|
||||
r := v.(closedRecord)
|
||||
return r.ask, r.w, nil
|
||||
}
|
||||
prefix := mergeAskPrefix(number, head)
|
||||
reason = fmt.Sprintf("the router holds no merge ask %s* of %s", prefix, strings.Join(askersOfMerges(), " or "))
|
||||
ask, w, err := routerAskOf(ctx, inv, asker, id)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if ask != nil && w != nil {
|
||||
closedRecords.Store(asker+"."+id, closedRecord{ask, w})
|
||||
}
|
||||
return ask, w, nil
|
||||
}
|
||||
|
||||
// deliveryIDOf is the id the gate gives a delivery, and so the key of its head in the heads an Approve binds: the repository as
|
||||
// owner/name, lower-cased, `@` and the first twelve characters of the head.
|
||||
func deliveryIDOf(owner, repo, head string) string {
|
||||
if len(head) > 12 {
|
||||
head = head[:12]
|
||||
}
|
||||
return strings.ToLower(owner+"/"+repo) + "@" + head
|
||||
}
|
||||
|
||||
// coversHead says an ask the operator approved is for this merge: its Approve is bound to the heads of the pull request alone
|
||||
// (mergeAct over {delivery id: head}, which is how a pull request not in a group is asked), or, for a group's ask, whose other
|
||||
// members' heads the router's record does not carry in full, the ask as published names this pull request at this head in its
|
||||
// details ("<repo>#<number> ... at <head>:"), and the digest the warrant answers covers those words.
|
||||
func coversHead(ask *asks.Ask, owner, repo string, number int, head string) (how string, ok bool) {
|
||||
option, found := ask.Option(optionApprove)
|
||||
if !found {
|
||||
return "", false
|
||||
}
|
||||
heads := map[string]string{deliveryIDOf(owner, repo, head): head}
|
||||
if option.Performs(mergeAct(ask.ID, optionApprove, heads)) == nil {
|
||||
return "bound to the head of this pull request", true
|
||||
}
|
||||
short := head
|
||||
if len(short) > 12 {
|
||||
short = short[:12]
|
||||
}
|
||||
line := regexp.MustCompile(`(?m)^` + regexp.QuoteMeta(repo) + `#` + strconv.Itoa(number) + ` .* at ` + regexp.QuoteMeta(short) + `:`)
|
||||
if line.MatchString(ask.Details) {
|
||||
return "a group's ask naming this pull request at this head", true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// findMergeWarrant finds the warrant of a merge, with no read the controller does not make today: the router's chosen merge
|
||||
// ask for it. Under the teller (releases 3e on) the id is `merge-<group>-<heads hash>-<tail>`, which names neither the pull
|
||||
// request nor the head, so the teller's closed asks are read and the one the operator approved whose heads cover the merge's
|
||||
// second parent is the warrant (coversHead). Under mesh-delivery, which made the earlier merges, the id names the pull request
|
||||
// and the head and is found by them. Why it holds; or found false with why not, for a person to read; or an error when the
|
||||
// router could not be read, which is no answer. A variable so a test needs no bus.
|
||||
var findMergeWarrant = func(ctx context.Context, inv catalogueReader, owner, repo string, number int, head string) (why string, found bool, reason string, err error) {
|
||||
reason = "the router holds no chosen merge ask of " + strings.Join(askersOfMerges(), " or ") + " that covers the head " + head[:min(12, len(head))]
|
||||
for _, asker := range askersOfMerges() {
|
||||
ids, err := routerAskIDs(ctx, inv, asker)
|
||||
if err != nil {
|
||||
return "", false, "", err
|
||||
}
|
||||
for _, id := range ids {
|
||||
if !strings.HasPrefix(id, prefix) {
|
||||
named := number > 0 && strings.HasPrefix(id, mergeAskPrefix(number, head))
|
||||
if asker == sdkdelivery.TellerModule && !strings.HasPrefix(id, sdkdelivery.AskKindMerge+"-") {
|
||||
continue
|
||||
}
|
||||
ask, w, err := routerAskOf(ctx, inv, asker, id)
|
||||
if asker == theDelivery && !named {
|
||||
continue
|
||||
}
|
||||
ask, w, err := closedRecordOf(ctx, inv, asker, id)
|
||||
if err != nil {
|
||||
return "", false, "", err
|
||||
}
|
||||
if why, err := caughtUpWarrant(asker, ask, w); err == nil {
|
||||
return why, true, "", nil
|
||||
} else {
|
||||
reason = fmt.Sprintf("%s's ask %s holds no operator's Approve: %v", asker, id, err)
|
||||
if ask == nil || w == nil {
|
||||
continue
|
||||
}
|
||||
warrant, err := caughtUpWarrant(asker, ask, w)
|
||||
if err != nil {
|
||||
if named {
|
||||
reason = fmt.Sprintf("%s's ask %s holds no operator's Approve: %v", asker, id, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
how, covers := coversHead(ask, owner, repo, number, head)
|
||||
if !covers && !named {
|
||||
continue
|
||||
}
|
||||
if !covers {
|
||||
how = "the ask's id names this pull request and head"
|
||||
}
|
||||
return warrant + ", " + how, true, "", nil
|
||||
}
|
||||
}
|
||||
return "", false, reason, nil
|
||||
@@ -413,11 +487,19 @@ func (f following) catchUpTrunk(ctx context.Context, t caughtTrunk) (later bool)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
if !wouldBePlanned(ctx, inv, m, entries, read) {
|
||||
facts, err := factsOfMerge(ctx, inv, m, time.Now().UTC(), entries, read)
|
||||
if err != nil {
|
||||
fmt.Printf("catch-up: %s %.8s cannot be judged now: %v\n", repository, tm.Commit, err)
|
||||
later = true
|
||||
continue
|
||||
}
|
||||
// The very test hearMerge makes: a merge it would plan, or take as a first build, is a gap; any other it would say
|
||||
// nothing reads, and is no gap for a person to decide.
|
||||
if !facts.plans() && !facts.addsModuleToBuilds {
|
||||
settledMerges.Store(repository+"@"+tm.Commit, true)
|
||||
continue
|
||||
}
|
||||
why, found, reason, err := findMergeWarrant(ctx, inv, m.Number, tm.Head)
|
||||
why, found, reason, err := findMergeWarrant(ctx, inv, t.Owner, t.Repo, m.Number, tm.Head)
|
||||
if err != nil {
|
||||
fmt.Printf("catch-up: the router cannot be read, so %s %.8s is not caught up now: %v\n", repository, tm.Commit, err)
|
||||
later = true
|
||||
@@ -448,28 +530,6 @@ func (f following) catchUpTrunk(ctx context.Context, t caughtTrunk) (later bool)
|
||||
return later
|
||||
}
|
||||
|
||||
// wouldBePlanned says hearing the merge would plan something: it touches a module the mesh holds and is not history for every
|
||||
// one of them (or is owed, as hearMerge judges). A merge that moves nothing is no gap for a person to decide.
|
||||
func wouldBePlanned(ctx context.Context, inv *inventory.Inventory, m link.SourceMoved, entries []inventory.Entry,
|
||||
read map[string][]inventory.ReadRepository) bool {
|
||||
raw := m
|
||||
raw.MergedAt = ""
|
||||
touches := wouldMove(raw, entries, read)
|
||||
if len(touches) == 0 {
|
||||
return false
|
||||
}
|
||||
from, packaging, _ := mergeCandidates(m, entries, read)
|
||||
if len(from) > 0 || len(packaging) > 0 {
|
||||
return true
|
||||
}
|
||||
merged, err := time.Parse(time.RFC3339, m.MergedAt)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
owed, err := owedLate(ctx, inv, m, merged, touches)
|
||||
return err != nil || owed
|
||||
}
|
||||
|
||||
// raiseMergeNotHeard keeps open, and observes again, the condition of a merge with no warrant to plan it. One for each
|
||||
// repository and commit, however many catch-ups find it.
|
||||
func (f following) raiseMergeNotHeard(ctx context.Context, repository, branch string, tm trunkMerge, m link.SourceMoved, reason string) {
|
||||
|
||||
Reference in New Issue
Block a user