Merge pull request 'Say the class of what a change plan moves, for a fix's budget (issue 403, hq ADR 0313)' (#265) from fix/403-fix-time into main
This commit was merged in pull request #265.
This commit is contained in:
@@ -1109,6 +1109,16 @@ const resolverSeat = "mesh-dns-resolver"
|
|||||||
// classOf is a walk's class (novox/hq ADR 0282 decision 1): core when it walks a module on the controller's own
|
// classOf is a walk's class (novox/hq ADR 0282 decision 1): core when it walks a module on the controller's own
|
||||||
// path or one holding the mesh's resolver, leaf otherwise.
|
// path or one holding the mesh's resolver, leaf otherwise.
|
||||||
func classOf(walk inventory.Plan, entries []inventory.Entry) string {
|
func classOf(walk inventory.Plan, entries []inventory.Entry) string {
|
||||||
|
modules := make([]string, 0, len(walk.Modules))
|
||||||
|
for m := range walk.Modules {
|
||||||
|
modules = append(modules, m)
|
||||||
|
}
|
||||||
|
return classOfModules(modules, entries)
|
||||||
|
}
|
||||||
|
|
||||||
|
// classOfModules is the class of modules moved together: core when one is on the controller's own path or holds
|
||||||
|
// the mesh's resolver, leaf otherwise (ADR 0282 decision 1).
|
||||||
|
func classOfModules(modules []string, entries []inventory.Entry) string {
|
||||||
resolvers := map[string]bool{}
|
resolvers := map[string]bool{}
|
||||||
for _, e := range entries {
|
for _, e := range entries {
|
||||||
for _, c := range e.Manifest.Claims {
|
for _, c := range e.Manifest.Claims {
|
||||||
@@ -1117,7 +1127,7 @@ func classOf(walk inventory.Plan, entries []inventory.Entry) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for m := range walk.Modules {
|
for _, m := range modules {
|
||||||
if _, own := onTheControllersPath[m]; own || resolvers[m] {
|
if _, own := onTheControllersPath[m]; own || resolvers[m] {
|
||||||
return inventory.ClassCore
|
return inventory.ClassCore
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path"
|
||||||
"slices"
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -105,9 +106,32 @@ func changePlanOf(repository, base, head string, r mergeReach, entries []invento
|
|||||||
p.Machines = append(p.Machines, *machines[name])
|
p.Machines = append(p.Machines, *machines[name])
|
||||||
}
|
}
|
||||||
p.Summary = summaryOf(p)
|
p.Summary = summaryOf(p)
|
||||||
|
p.Class = changeClassOf(p, entries)
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// classNone is the class of a change that moves no module: nothing for a walk to move (novox/hq ADR 0313 §2).
|
||||||
|
const classNone = "none"
|
||||||
|
|
||||||
|
// changeClassOf is a change plan's class (novox/hq ADR 0313 §2): that of the slowest-budgeted module it moves,
|
||||||
|
// itself or as a dependent — core when one is on the controller's own path or holds the mesh's resolver, leaf
|
||||||
|
// otherwise — and none when it moves nothing. A module it adds a directory for counts by its own class: the
|
||||||
|
// catalogue names a module's directory by the module, so the directory's last element is its name. The graph holds
|
||||||
|
// no manifest of it yet, so a new module is core by its name alone (on the controller's own path), never by a
|
||||||
|
// resolver claim its manifest makes.
|
||||||
|
func changeClassOf(p link.ChangePlan, entries []inventory.Entry) string {
|
||||||
|
if len(p.Moved) == 0 && len(p.Dependents) == 0 && len(p.New) == 0 {
|
||||||
|
return classNone
|
||||||
|
}
|
||||||
|
modules := append(append([]string{}, p.Moved...), p.Dependents...)
|
||||||
|
for _, dir := range p.New {
|
||||||
|
if name := path.Base(strings.Trim(dir, "/")); name != "." && name != "/" && name != "" {
|
||||||
|
modules = append(modules, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return classOfModules(modules, entries)
|
||||||
|
}
|
||||||
|
|
||||||
// grantsStepWord names the grants step (novox/hq issue 490): the bus's user list sent alone to the machine
|
// grantsStepWord names the grants step (novox/hq issue 490): the bus's user list sent alone to the machine
|
||||||
// holding the bus ahead of a walk's gate. Not the bus step, which replaces the bus itself.
|
// holding the bus ahead of a walk's gate. Not the bus step, which replaces the bus itself.
|
||||||
const grantsStepWord = "grants step"
|
const grantsStepWord = "grants step"
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/novox/mesh-controller/internal/catalogue"
|
||||||
|
"github.com/novox/mesh-controller/internal/inventory"
|
||||||
|
"github.com/novox/mesh-controller/internal/link"
|
||||||
|
)
|
||||||
|
|
||||||
|
// **A change plan says its class** (novox/hq ADR 0313 §2): the issue tracker holds a fix to the budget of the
|
||||||
|
// slowest-budgeted module the fix moves, and a fix that moves no module has no fix budget. The plan says core,
|
||||||
|
// leaf or none, so mesh-delivery can pass it on with the fix's delivery.
|
||||||
|
func TestAChangePlanSaysTheClassOfWhatItMoves(t *testing.T) {
|
||||||
|
const repo = "http://forge.internal:20000/novox/mesh-catalog.git"
|
||||||
|
entry := func(name, path string) inventory.Entry {
|
||||||
|
e := fromRepo(name, repo, path)
|
||||||
|
e.Source.BuiltFrom = "old"
|
||||||
|
e.On = []string{"anchor"}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
gitea := entry("gitea", "modules/gitea")
|
||||||
|
resolver := entry("dnsmasq", "modules/dnsmasq")
|
||||||
|
resolver.Manifest.Claims = []catalogue.Claim{{Name: resolverSeat}}
|
||||||
|
delivery := entry(catalogue.DeliverySeat, "modules/mesh-delivery")
|
||||||
|
webApp := entry("photos", "modules/photos")
|
||||||
|
entries := []inventory.Entry{gitea, resolver, delivery, webApp}
|
||||||
|
// photos stands on gitea: a change to gitea moves photos as its dependent.
|
||||||
|
edges := []inventory.Edge{{From: "photos", To: "gitea", Kind: inventory.EdgeStandsOn}}
|
||||||
|
policy := func(string) (inventory.Upgrade, bool) { return inventory.Upgrade{RollOut: true}, true }
|
||||||
|
plan := func(paths ...string) link.ChangePlan {
|
||||||
|
m := link.SourceMoved{Owner: "novox", Repo: "mesh-catalog", Base: "main", Commit: "head", Paths: paths}
|
||||||
|
return changePlanOf("novox/mesh-catalog", "main", "head", reachOfMerge(m, entries, nil, edges), entries, policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range []struct {
|
||||||
|
why string
|
||||||
|
paths []string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{"a leaf module and its dependent", []string{"modules/gitea/index.ts"}, inventory.ClassLeaf},
|
||||||
|
{"the delivery's owner, on the controller's own path", []string{"modules/mesh-delivery/cmd/x.go"}, inventory.ClassCore},
|
||||||
|
{"the mesh's resolver", []string{"modules/dnsmasq/module.json"}, inventory.ClassCore},
|
||||||
|
{"a leaf and a core module together: the slowest-budgeted", []string{"modules/gitea/index.ts", "modules/dnsmasq/module.json"}, inventory.ClassCore},
|
||||||
|
{"nothing any module's build reads", []string{"README.md"}, "none"},
|
||||||
|
{"a new leaf module alone", []string{"modules/weather/module.json"}, inventory.ClassLeaf},
|
||||||
|
{"a new module on the controller's own path, by its own class", []string{"next/mesh-delivery/module.json"}, inventory.ClassCore},
|
||||||
|
{"a new leaf module beside a core one moved", []string{"modules/weather/module.json", "modules/dnsmasq/module.json"}, inventory.ClassCore},
|
||||||
|
} {
|
||||||
|
if got := plan(c.paths...).Class; got != c.want {
|
||||||
|
t.Errorf("%s: class %q, want %q", c.why, got, c.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -325,6 +325,9 @@ type ChangePlan struct {
|
|||||||
Steps []string `json:"steps,omitempty"`
|
Steps []string `json:"steps,omitempty"`
|
||||||
// Summary is the plan in one line, for a commit status.
|
// Summary is the plan in one line, for a commit status.
|
||||||
Summary string `json:"summary"`
|
Summary string `json:"summary"`
|
||||||
|
// Class is the class of the slowest-budgeted module the change moves (novox/hq ADR 0282 §1, ADR 0313 §2):
|
||||||
|
// core, leaf, or none when it moves no module at all — a fix that moves nothing has no fix budget.
|
||||||
|
Class string `json:"class,omitempty"`
|
||||||
// Trunk is the trunk commit the head was merged into when it was judged (novox/hq ADR 0323 §3), twelve
|
// Trunk is the trunk commit the head was merged into when it was judged (novox/hq ADR 0323 §3), twelve
|
||||||
// characters: set on the plan a verdict carries, which mesh-delivery keeps with its other inputs.
|
// characters: set on the plan a verdict carries, which mesh-delivery keeps with its other inputs.
|
||||||
Trunk string `json:"trunk,omitempty"`
|
Trunk string `json:"trunk,omitempty"`
|
||||||
|
|||||||
Reference in New Issue
Block a user