plans --what-if: the plan a merge would produce, read before merging
Given a repository and the files a branch changes (or the modules by name), plans answers with the tiers
the merge handler would produce — the modules those files touch, what packages their source, everything
reachable along the dependency relation — and what each tier does: built and sent to its machines, or
built and left because its policy records. Saved nowhere. On the seat as plans {repository, paths|modules}.
This commit is contained in:
@@ -576,6 +576,9 @@ func openPlans(plans []inventory.Plan) ([]inventory.Plan, int) {
|
||||
func plansCommand(ctx context.Context, args []string) error {
|
||||
set := flag.NewFlagSet("plans", flag.ContinueOnError)
|
||||
limit := set.Int("n", 10, "how many to show")
|
||||
whatIf := set.String("what-if", "", "owner/repository: the plan a merge there would produce, saving nothing — with --paths or --modules")
|
||||
paths := set.String("paths", "", "the files the merge would change, comma-separated, from the repository's root")
|
||||
modules := set.String("modules", "", "or the modules it would change, comma-separated")
|
||||
positionals, err := parseAround(set, args)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -616,6 +619,9 @@ func plansCommand(ctx context.Context, args []string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if *whatIf != "" {
|
||||
return planWhatIf(ctx, inv, *whatIf, splitList(*paths), splitList(*modules))
|
||||
}
|
||||
if len(positionals) == 2 && positionals[0] == "stop" {
|
||||
p, err := inv.PlanByID(ctx, positionals[1])
|
||||
if err != nil {
|
||||
@@ -646,3 +652,91 @@ func plansCommand(ctx context.Context, args []string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// planWhatIf is the plan a merge would produce, computed the way the merge handler computes one
|
||||
// and saved nowhere: the modules the repository's changed files touch (or the modules named), what
|
||||
// packages their source, everything reachable from them, in tiers. For reading before merging.
|
||||
func planWhatIf(ctx context.Context, inv *inventory.Inventory, repository string, paths, modules []string) error {
|
||||
owner, repo, found := strings.Cut(repository, "/")
|
||||
if !found {
|
||||
return fmt.Errorf("--what-if takes owner/repository, not %q", repository)
|
||||
}
|
||||
m := link.SourceMoved{Owner: owner, Repo: repo, Base: "main", Commit: "what-if", Paths: paths}
|
||||
entries, err := inv.Catalogued(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
read, err := inv.ReadRepositories(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var from, packaging []inventory.Entry
|
||||
named := map[string]bool{}
|
||||
for _, name := range modules {
|
||||
named[name] = true
|
||||
}
|
||||
for _, e := range entries {
|
||||
switch {
|
||||
case named[e.Manifest.Module]:
|
||||
from = append(from, e)
|
||||
case len(named) == 0 && sourceIs(e.Source, m):
|
||||
from = append(from, e)
|
||||
case readsFrom(read[e.Manifest.Module], m):
|
||||
packaging = append(packaging, e)
|
||||
}
|
||||
}
|
||||
if len(named) == 0 {
|
||||
from = whatTheMergeTouched(from, entries, m)
|
||||
}
|
||||
moved := append(append([]inventory.Entry{}, from...), packaging...)
|
||||
if len(moved) == 0 {
|
||||
fmt.Printf("a merge of %s changing %s would build nothing the mesh holds\n", repository,
|
||||
orNone(strings.Join(append(paths, modules...), ", ")))
|
||||
return nil
|
||||
}
|
||||
edges, err := inv.Dependencies(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var names []string
|
||||
for _, e := range moved {
|
||||
names = append(names, e.Manifest.Module)
|
||||
}
|
||||
p := planOfMerge(m, names, edges)
|
||||
fmt.Printf("a merge of %s would build %d module(s) in %d tier(s):\n", repository, len(p.Modules), len(p.Tiers))
|
||||
rolls := map[string]string{}
|
||||
for i, tier := range p.Tiers {
|
||||
fmt.Printf(" tier %d\n", i)
|
||||
for _, name := range tier {
|
||||
how := "built; its policy records, so nothing is sent"
|
||||
if u, err := inv.UpgradeOf(ctx, name); err == nil && u.RollOut {
|
||||
running, _ := inv.Running(ctx, name)
|
||||
how = "built, then sent to " + orNone(strings.Join(running, ", "))
|
||||
rolls[name] = how
|
||||
}
|
||||
fmt.Printf(" %-22s %s\n", name, how)
|
||||
}
|
||||
}
|
||||
if hasCycle(p.Tiers, edges) {
|
||||
fmt.Println(" the last tier depends on itself and would be built together, in no order")
|
||||
}
|
||||
if len(packaging) > 0 {
|
||||
var also []string
|
||||
for _, e := range packaging {
|
||||
also = append(also, e.Manifest.Module)
|
||||
}
|
||||
fmt.Printf(" %s package source from %s, so they are rebuilt without their own source moving\n",
|
||||
strings.Join(also, ", "), repository)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func splitList(s string) []string {
|
||||
var out []string
|
||||
for _, part := range strings.Split(s, ",") {
|
||||
if part = strings.TrimSpace(part); part != "" {
|
||||
out = append(out, part)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -70,6 +70,16 @@ func argvFor(verb string, args map[string]any) ([]string, error) {
|
||||
}
|
||||
return []string{"builds"}, nil
|
||||
case "plans":
|
||||
if r := str("repository"); r != "" {
|
||||
argv := []string{"plans", "--what-if", r}
|
||||
if p := str("paths"); p != "" {
|
||||
argv = append(argv, "--paths", p)
|
||||
}
|
||||
if m := str("modules"); m != "" {
|
||||
argv = append(argv, "--modules", m)
|
||||
}
|
||||
return argv, nil
|
||||
}
|
||||
if id := str("stop"); id != "" {
|
||||
return []string{"plans", "stop", id}, nil
|
||||
}
|
||||
|
||||
@@ -94,8 +94,11 @@ var ControllerVerbs = []Verb{
|
||||
{Name: "plans", Description: "What the last merges produced and where each stands (novox/hq ADR 0162): " +
|
||||
"the tiers, the tier a plan is at, what it waits for and since when; one plan whole, given its id.",
|
||||
Input: schema(map[string]string{
|
||||
"id": "a plan's id (as `plans` lists them): that plan, tier by tier",
|
||||
"stop": "a plan's id: stop it — what was asked still builds, nothing further is asked",
|
||||
"id": "a plan's id (as `plans` lists them): that plan, tier by tier",
|
||||
"stop": "a plan's id: stop it — what was asked still builds, nothing further is asked",
|
||||
"repository": "owner/repository: the plan a merge there would produce, saving nothing (what-if); with paths or modules",
|
||||
"paths": "with repository: the files the merge would change, comma-separated, from the repository's root",
|
||||
"modules": "with repository: or the modules it would change, comma-separated",
|
||||
}, nil)},
|
||||
{Name: "plan", Description: "What one machine would run, and why: the declaration the mesh would send it.",
|
||||
Input: schema(map[string]string{"node": "the machine's name"}, []string{"node"})},
|
||||
|
||||
Reference in New Issue
Block a user