From 1d09aa9d428362152f30a7a6d665bffe425e2e08 Mon Sep 17 00:00:00 2001 From: Igor Ignatyev Date: Wed, 18 Jun 2025 16:02:37 +0300 Subject: [PATCH] Move dependencies commands to bump plugin --- action.yaml => action.bump.yaml | 0 action.dependencies.yaml | 32 +++++++++ dependencies.go | 122 ++++++++++++++++++++++++++++++++ plugin.go | 43 +++++++++-- 4 files changed, 192 insertions(+), 5 deletions(-) rename action.yaml => action.bump.yaml (100%) create mode 100644 action.dependencies.yaml create mode 100644 dependencies.go diff --git a/action.yaml b/action.bump.yaml similarity index 100% rename from action.yaml rename to action.bump.yaml diff --git a/action.dependencies.yaml b/action.dependencies.yaml new file mode 100644 index 0000000..39391f3 --- /dev/null +++ b/action.dependencies.yaml @@ -0,0 +1,32 @@ +runtime: plugin +action: + title: Dependencies + description: "Shows dependencies and dependent resources of selected resource" + alias: + - deps + arguments: + - name: target + title: Target + description: Target resource + required: true + options: + - name: source + title: Source + description: Resources source dir + type: string + default: ".compose/build" + - name: mrn + title: MRN + description: Show MRN instead of paths + type: boolean + default: false + - name: tree + title: Tree + description: "Show dependencies in tree-like output" + type: boolean + default: false + - name: depth + title: Depth + description: "Limit recursion lookup depth" + type: integer + default: 99 diff --git a/dependencies.go b/dependencies.go new file mode 100644 index 0000000..36b5667 --- /dev/null +++ b/dependencies.go @@ -0,0 +1,122 @@ +package plasmactlbump + +import ( + "fmt" + "sort" + + "github.com/launchrctl/launchr" + "github.com/launchrctl/launchr/pkg/action" + "github.com/skilld-labs/plasmactl-bump/v2/pkg/sync" +) + +type dependenciesAction struct { + action.WithLogger + action.WithTerm +} + +func (a *dependenciesAction) run(target, source string, toPath, showTree bool, depth int8) error { + searchMrn := target + _, errConvert := sync.ConvertMRNtoPath(searchMrn) + + if errConvert != nil { + r := sync.BuildResourceFromPath(target, source) + if r == nil { + return fmt.Errorf("not valid resource %q", target) + } + + searchMrn = r.GetName() + } + + var header string + if toPath { + header, _ = sync.ConvertMRNtoPath(searchMrn) + } else { + header = searchMrn + } + + inv, err := sync.NewInventory(source, a.Log()) + if err != nil { + return err + } + parents := inv.GetRequiredByResources(searchMrn, depth) + if len(parents) > 0 { + a.Term().Info().Println("Dependent resources:") + if showTree { + var parentsTree forwardTree = inv.GetRequiredByMap() + parentsTree.print(a.Term(), header, "", 1, depth, searchMrn, toPath) + } else { + a.printList(parents, toPath) + } + } + + children := inv.GetDependsOnResources(searchMrn, depth) + if len(children) > 0 { + a.Term().Info().Println("Dependencies:") + if showTree { + var childrenTree forwardTree = inv.GetDependsOnMap() + childrenTree.print(a.Term(), header, "", 1, depth, searchMrn, toPath) + } else { + a.printList(children, toPath) + } + } + + return nil +} + +func (a *dependenciesAction) printList(items map[string]bool, toPath bool) { + keys := make([]string, 0, len(items)) + for k := range items { + keys = append(keys, k) + } + + sort.Strings(keys) + for _, item := range keys { + res := item + if toPath { + res, _ = sync.ConvertMRNtoPath(res) + } + + a.Term().Print(res + "\n") + } +} + +type forwardTree map[string]*sync.OrderedMap[bool] + +func (t forwardTree) print(printer *launchr.Terminal, header, indent string, depth, limit int8, parent string, toPath bool) { + if indent == "" { + printer.Printfln(header) + } + + if depth == limit { + return + } + + children, ok := t[parent] + if !ok { + return + } + + keys := children.Keys() + sort.Strings(keys) + + for i, node := range keys { + isLast := i == len(keys)-1 + var newIndent, edge string + + if isLast { + newIndent = indent + " " + edge = "└── " + } else { + newIndent = indent + "│ " + edge = "├── " + } + + value := node + if toPath { + value, _ = sync.ConvertMRNtoPath(value) + } + + printer.Printfln(indent + edge + value) + t.print(printer, "", newIndent, depth+1, limit, node, toPath) + } +} diff --git a/plugin.go b/plugin.go index 6c4c723..b076680 100644 --- a/plugin.go +++ b/plugin.go @@ -4,14 +4,19 @@ package plasmactlbump import ( "context" _ "embed" + "fmt" + "os" "github.com/launchrctl/keyring" "github.com/launchrctl/launchr" "github.com/launchrctl/launchr/pkg/action" ) -//go:embed action.yaml -var actionYaml []byte +//go:embed action.bump.yaml +var actionBumpYaml []byte + +//go:embed action.dependencies.yaml +var actionDependenciesYaml []byte func init() { launchr.RegisterPlugin(&Plugin{}) @@ -39,8 +44,8 @@ func (p *Plugin) OnAppInit(app launchr.App) error { // DiscoverActions implements [launchr.ActionDiscoveryPlugin] interface. func (p *Plugin) DiscoverActions(_ context.Context) ([]*action.Action, error) { - a := action.NewFromYAML("bump", actionYaml) - a.SetRuntime(action.NewFnRuntime(func(_ context.Context, a *action.Action) error { + ba := action.NewFromYAML("bump", actionBumpYaml) + ba.SetRuntime(action.NewFnRuntime(func(_ context.Context, a *action.Action) error { input := a.Input() doSync := input.Opt("sync").(bool) dryRun := input.Opt("dry-run").(bool) @@ -88,7 +93,35 @@ func (p *Plugin) DiscoverActions(_ context.Context) ([]*action.Action, error) { return nil })) - return []*action.Action{a}, nil + + da := action.NewFromYAML("dependencies", actionDependenciesYaml) + da.SetRuntime(action.NewFnRuntime(func(_ context.Context, a *action.Action) error { + log, _, _, term := getLogger(a) + + input := a.Input() + source := input.Opt("source").(string) + if _, err := os.Stat(source); os.IsNotExist(err) { + term.Warning().Printfln("%s doesn't exist, fallback to current dir", source) + source = "." + } else { + term.Info().Printfln("Selected source is %s", source) + } + + showPaths := input.Opt("mrn").(bool) + showTree := input.Opt("tree").(bool) + depth := int8(input.Opt("depth").(int)) //nolint:gosec + if depth == 0 { + return fmt.Errorf("depth value should not be zero") + } + + target := input.Arg("target").(string) + dependencies := &dependenciesAction{} + dependencies.SetLogger(log) + dependencies.SetTerm(term) + return dependencies.run(target, source, !showPaths, showTree, depth) + })) + + return []*action.Action{ba, da}, nil } func getLogger(a *action.Action) (*launchr.Logger, launchr.LogLevel, launchr.Streams, *launchr.Terminal) {