mesh/merge-gate head de5292323f33 merged into trunk a47976be4e69 — pass: builds mesh-controller → novox; no bus step; every machine composes with the chang…
mesh/repo-check head de5292323f33 merged into trunk a47976be4e69 — pass: its merge-check.sh passed
mesh/delivery delivered
A recheck rebuilds the pull request without its head branch name, so the sibling lookup found no group and said nothing: mesh-host#114's recheck was judged without its sibling. With no branch name, the group is the one whose member_details hold the pull request; an older mesh-delivery says the siblings could not be learned. TestReplay867Recheck replays it. Co-Authored-By: Claude <noreply@anthropic.com>
141 lines
5.7 KiB
Go
141 lines
5.7 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"regexp"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/novox/mesh-controller/internal/inventory"
|
|
"github.com/novox/mesh-controller/internal/link"
|
|
)
|
|
|
|
// **A delivery group's member is checked beside the other members' heads** (novox/hq ADR 0382, issue 867).
|
|
//
|
|
// A mesh-host change and a mesh-controller change that need each other could not both pass their own merge checks:
|
|
// each was read beside the other's repository at the commit the mesh runs, which holds neither change. So the
|
|
// controller asks mesh-delivery's `groups` once for every pull request's check it asks (§1), and names, in the ask,
|
|
// the group's other open pull requests into a base of the same name, in a repository the build seat reads beside the
|
|
// check — mesh-lab never — each with its whole head. The build seat reads those repositories at the sibling's merge (§2).
|
|
// A pull request for which mesh-delivery answered and named no sibling is asked exactly as before (§7).
|
|
|
|
// wholeCommit is a commit as the forge says a head: forty hexadecimal characters.
|
|
var wholeCommit = regexp.MustCompile(`^[0-9a-f]{40}$`)
|
|
|
|
// besideRepositories are the repositories (lower case, owner/repo) a check reads beside its own, by the directory each
|
|
// is found under — the core's, as besideOf clones them, and mesh-sdk beside the controller; never mesh-lab, which is
|
|
// always read at its trunk.
|
|
func besideRepositories(entries []inventory.Entry) map[string]string {
|
|
read := map[string]string{}
|
|
for _, e := range entries {
|
|
dir, core := coreModules[e.Manifest.Module]
|
|
if !core || e.Provided || e.Source.Repository == "" {
|
|
continue
|
|
}
|
|
read[nameOf(e.Source.Repository)] = dir
|
|
if dir == "mesh-controller" && e.Source.Seat != "" {
|
|
read[nameOf(siblingOf(e.Source.Repository, "mesh-sdk"))] = "mesh-sdk"
|
|
}
|
|
}
|
|
delete(read, "")
|
|
return read
|
|
}
|
|
|
|
// nameOf is a recorded repository as owner/repo in lower case, empty when it names no owner.
|
|
func nameOf(repository string) string {
|
|
owner, repo := sourceOwner(repository), sourceName(repository)
|
|
if owner == "" || repo == "" {
|
|
return ""
|
|
}
|
|
return strings.ToLower(owner + "/" + repo)
|
|
}
|
|
|
|
// siblingsFor is the pull request's siblings, as mesh-delivery's groups say them (ADR 0382 §1): the other members of
|
|
// the group its head branch names that are open, unmerged and into a base of its own base's name, in a repository the
|
|
// check reads beside it (beside: the directories the ask already names). The second answer is why they could not be
|
|
// learned, empty when they could: the group's members carry no head or base — a mesh-delivery from before the rule.
|
|
func siblingsFor(p link.PullUpdated, entries []inventory.Entry, beside map[string]link.CheckedOut,
|
|
lines []groupsLine) ([]link.CheckSibling, string) {
|
|
own := strings.ToLower(p.Owner + "/" + p.Repo)
|
|
var group *groupsLine
|
|
if p.Head == "" {
|
|
// A recheck (mesh-delivery's `recheck` through `delivery check`) carries no head branch name: the group is the one
|
|
// whose members hold this very pull request, by repository and number.
|
|
learnable := false
|
|
for i := range lines {
|
|
for _, m := range lines[i].Details {
|
|
learnable = true
|
|
if p.Number != 0 && m.Number == p.Number && strings.ToLower(m.Repository) == own {
|
|
group = &lines[i]
|
|
}
|
|
}
|
|
}
|
|
if group == nil {
|
|
for _, l := range lines {
|
|
if len(l.Details) == 0 && len(l.Members) > 0 && !learnable {
|
|
return nil, "mesh-delivery named the groups' members without their pull requests (a mesh-delivery from before novox/hq ADR 0382): " +
|
|
"the group of a pull request announced without its head branch cannot be found"
|
|
}
|
|
}
|
|
return nil, "" // a lone pull request
|
|
}
|
|
} else {
|
|
for i := range lines {
|
|
if lines[i].ID == p.Head {
|
|
group = &lines[i]
|
|
}
|
|
}
|
|
if group == nil {
|
|
return nil, ""
|
|
}
|
|
}
|
|
if len(group.Details) == 0 && len(group.Members) > 0 {
|
|
return nil, "mesh-delivery named the group's members without their heads and bases (a mesh-delivery from before novox/hq ADR 0382)"
|
|
}
|
|
read := besideRepositories(entries)
|
|
var siblings []link.CheckSibling
|
|
for _, m := range group.Details {
|
|
repository := strings.ToLower(m.Repository)
|
|
if repository == own {
|
|
continue
|
|
}
|
|
if !m.Open {
|
|
continue
|
|
}
|
|
if m.Head == "" || m.Base == "" {
|
|
return nil, "mesh-delivery named " + m.ID + " without its head or base (a mesh-delivery from before novox/hq ADR 0382)"
|
|
}
|
|
dir, readBeside := read[repository]
|
|
if m.Base != p.Base || !readBeside || dir == "mesh-lab" {
|
|
continue
|
|
}
|
|
if _, there := beside[dir]; !there {
|
|
continue
|
|
}
|
|
head := strings.ToLower(m.Head)
|
|
if !wholeCommit.MatchString(head) {
|
|
return nil, fmt.Sprintf("mesh-delivery named %s with a head that is not a whole commit (%q)", m.ID, m.Head)
|
|
}
|
|
siblings = append(siblings, link.CheckSibling{Repository: m.Repository, Number: m.Number, Head: head, Base: m.Base, Dir: dir})
|
|
}
|
|
sort.Slice(siblings, func(i, j int) bool { return siblings[i].Dir < siblings[j].Dir })
|
|
return siblings, ""
|
|
}
|
|
|
|
// withSiblings asks mesh-delivery's `groups` once and says in the request the pull request's siblings, or why they
|
|
// could not be learned (ADR 0382 §1): a check never waits on mesh-delivery past groupAskWithin and never fails for it,
|
|
// so a fix to mesh-delivery is still checked without it.
|
|
func withSiblings(ctx context.Context, request *link.BuildRequest, p link.PullUpdated, entries []inventory.Entry) {
|
|
if request.Check == nil {
|
|
return
|
|
}
|
|
lines, err := groupsAsked(ctx, deliverySeatHeld(entries))
|
|
if err != nil {
|
|
request.Check.SiblingsUnknown = fmt.Sprintf("mesh-delivery did not say the group's pull requests (%v): "+
|
|
"the check reads no siblings", err)
|
|
return
|
|
}
|
|
request.Check.Siblings, request.Check.SiblingsUnknown = siblingsFor(p, entries, request.Check.Beside, lines)
|
|
}
|