|
|
|
@@ -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
|
|
|
|
|
}
|
|
|
|
|