Order a branch's plans by its merges, and build the newest commit (hq issue 349)
A merge acted on late by the catch-up made its plan after the plan of the merge that followed it, superseded it by creation time, and folded its unbuilt modules into a plan at the older commit: on 2026-10-09 the forge's security fix (a082615b) was superseded by 8ff8197a. A plan now keeps its merge time (migration 0086), supersession follows it, and a merge older than an open plan of its branch is planned at that plan's commit, which contains it.
This commit is contained in:
@@ -188,11 +188,13 @@ func planOfMerge(m link.SourceMoved, moved []string, edges []inventory.Edge) inv
|
||||
for _, name := range set {
|
||||
modules[name] = &inventory.PlanModule{}
|
||||
}
|
||||
merged, _ := time.Parse(time.RFC3339, m.MergedAt)
|
||||
return inventory.Plan{
|
||||
ID: fmt.Sprintf("plan-%d", time.Now().UnixNano()),
|
||||
Repository: m.Owner + "/" + m.Repo,
|
||||
Branch: m.Base,
|
||||
Commit: m.Commit,
|
||||
Merged: merged.UTC(),
|
||||
Created: time.Now().UTC(),
|
||||
State: inventory.PlanBuilding,
|
||||
Tiers: tiers,
|
||||
@@ -220,12 +222,20 @@ func planOfMerge(m link.SourceMoved, moved []string, edges []inventory.Edge) inv
|
||||
//
|
||||
// A plan with no branch recorded is from before branches were kept, and is superseded by the next
|
||||
// plan of its repository: what it had not built is folded in, so nothing is lost by it.
|
||||
//
|
||||
// **Newer is the branch's order, not the plans'** (novox/hq issue 349). A merge the bus did not hand
|
||||
// over is acted on late, by the catch-up, so its plan is made after the plan of a merge that came after
|
||||
// it — and that later-made plan of the earlier commit superseded the later merge's, and built what it
|
||||
// folded in from the commit before the later merge: twice on 2026-10-09, once a security fix. So where
|
||||
// both plans know when their merge was made, that decides; only where one does not do the plans' own
|
||||
// times. A merge older than an open plan of its branch never reaches here at its own commit: it is
|
||||
// planned at the newest open plan's commit, which contains it (newestOnTheBranch).
|
||||
func supersededBy(newer inventory.Plan, open []inventory.Plan, rollsOut func(string) bool) ([]string, []inventory.Plan) {
|
||||
folded := map[string]bool{}
|
||||
var closed []inventory.Plan
|
||||
for _, old := range open {
|
||||
if old.ID == newer.ID || !old.Open() || !strings.EqualFold(old.Repository, newer.Repository) ||
|
||||
(old.Branch != "" && old.Branch != newer.Branch) || !old.Created.Before(newer.Created) {
|
||||
(old.Branch != "" && old.Branch != newer.Branch) || !earlierOnTheBranch(old, newer) {
|
||||
continue
|
||||
}
|
||||
var took []string
|
||||
@@ -254,6 +264,39 @@ func supersededBy(newer inventory.Plan, open []inventory.Plan, rollsOut func(str
|
||||
return out, closed
|
||||
}
|
||||
|
||||
// earlierOnTheBranch says plan a answers a merge made before b's: by when the forge made each merge where
|
||||
// both are known, else by when each plan was made. A merge's own plan made again at the same commit
|
||||
// (the same merge time) is ordered by when it was made. Pure.
|
||||
func earlierOnTheBranch(a, b inventory.Plan) bool {
|
||||
if !a.Merged.IsZero() && !b.Merged.IsZero() && !a.Merged.Equal(b.Merged) {
|
||||
return a.Merged.Before(b.Merged)
|
||||
}
|
||||
return a.Created.Before(b.Created)
|
||||
}
|
||||
|
||||
// newestOnTheBranch is the open plan of m's repository and branch whose merge was made after m's, the
|
||||
// newest of them; false when none was. Merges into one branch are made one on the other, so that plan's
|
||||
// commit contains m's change: m is planned there, and the newest commit of the branch is what is built
|
||||
// (novox/hq issue 349). Pure.
|
||||
func newestOnTheBranch(m link.SourceMoved, open []inventory.Plan) (inventory.Plan, bool) {
|
||||
merged, err := time.Parse(time.RFC3339, m.MergedAt)
|
||||
if err != nil {
|
||||
return inventory.Plan{}, false
|
||||
}
|
||||
var newest inventory.Plan
|
||||
found := false
|
||||
for _, p := range open {
|
||||
if !p.Open() || p.Release != nil || p.Merged.IsZero() || !strings.EqualFold(p.Repository, m.Owner+"/"+m.Repo) ||
|
||||
p.Branch != m.Base || !p.Merged.After(merged) {
|
||||
continue
|
||||
}
|
||||
if !found || p.Merged.After(newest.Merged) {
|
||||
newest, found = p, true
|
||||
}
|
||||
}
|
||||
return newest, found
|
||||
}
|
||||
|
||||
// gates is what the next tier needs running from this one: a module of the tier that a later
|
||||
// tier is built by — the runtime dependency — and whose policy rolls it out, must be applied by
|
||||
// the machines running it before the next tier is asked. A base an image stands on need only be
|
||||
|
||||
Reference in New Issue
Block a user