Some checks are pending
Docs Deploy / build_and_deploy (push) Waiting to run
Generate Docs / cli (push) Waiting to run
Generate Config Doc / cli (push) Waiting to run
Go formatting / go-formatting (push) Waiting to run
Check links / markdown-link-check (push) Waiting to run
Integration / pre-test (push) Waiting to run
Integration / test on (push) Blocked by required conditions
Integration / status (push) Blocked by required conditions
Lint / Lint Go code (push) Waiting to run
Test / test (ubuntu-latest) (push) Waiting to run
763 lines
19 KiB
Go
763 lines
19 KiB
Go
package ignitecmd
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/spf13/cobra"
|
|
flag "github.com/spf13/pflag"
|
|
|
|
pluginsconfig "github.com/ignite/cli/v29/ignite/config/plugins"
|
|
"github.com/ignite/cli/v29/ignite/pkg/clictx"
|
|
"github.com/ignite/cli/v29/ignite/pkg/cliui"
|
|
"github.com/ignite/cli/v29/ignite/pkg/cliui/icons"
|
|
"github.com/ignite/cli/v29/ignite/pkg/cosmosanalysis"
|
|
"github.com/ignite/cli/v29/ignite/pkg/errors"
|
|
"github.com/ignite/cli/v29/ignite/pkg/gomodule"
|
|
"github.com/ignite/cli/v29/ignite/pkg/xfilepath"
|
|
"github.com/ignite/cli/v29/ignite/pkg/xgit"
|
|
"github.com/ignite/cli/v29/ignite/services/chain"
|
|
"github.com/ignite/cli/v29/ignite/services/plugin"
|
|
)
|
|
|
|
const (
|
|
flagPluginsGlobal = "global"
|
|
)
|
|
|
|
// plugins hold the list of plugin declared in the config.
|
|
// A global variable is used so the list is accessible to the plugin commands.
|
|
var plugins []*plugin.Plugin
|
|
|
|
// LoadPlugins tries to load all the plugins found in configurations.
|
|
// If no configurations found, it returns w/o error.
|
|
func LoadPlugins(ctx context.Context, cmd *cobra.Command, session *cliui.Session) error {
|
|
var pluginsConfigs []pluginsconfig.Plugin
|
|
localCfg, err := parseLocalPlugins()
|
|
if err != nil && !errors.As(err, &cosmosanalysis.ErrPathNotChain{}) {
|
|
return err
|
|
} else if err == nil {
|
|
pluginsConfigs = append(pluginsConfigs, localCfg.Apps...)
|
|
}
|
|
|
|
globalCfg, err := parseGlobalPlugins()
|
|
if err == nil {
|
|
pluginsConfigs = append(pluginsConfigs, globalCfg.Apps...)
|
|
}
|
|
ensureDefaultPlugins(cmd, globalCfg)
|
|
|
|
if len(pluginsConfigs) == 0 {
|
|
return nil
|
|
}
|
|
|
|
uniquePlugins := pluginsconfig.RemoveDuplicates(pluginsConfigs)
|
|
plugins, err = plugin.Load(ctx, uniquePlugins, plugin.CollectEvents(session.EventBus()))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(plugins) == 0 {
|
|
return nil
|
|
}
|
|
|
|
return linkPlugins(ctx, cmd.Root(), plugins)
|
|
}
|
|
|
|
func parseLocalPlugins() (*pluginsconfig.Config, error) {
|
|
wd, err := os.Getwd()
|
|
if err != nil {
|
|
return nil, errors.Errorf("parse local apps: %w", err)
|
|
}
|
|
|
|
if err := cosmosanalysis.IsChainPath(wd); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return pluginsconfig.ParseDir(wd)
|
|
}
|
|
|
|
func parseGlobalPlugins() (cfg *pluginsconfig.Config, err error) {
|
|
globalDir, err := plugin.PluginsPath()
|
|
if err != nil {
|
|
return cfg, err
|
|
}
|
|
|
|
cfg, err = pluginsconfig.ParseDir(globalDir)
|
|
// if there is error parsing, return empty config and continue execution to load
|
|
// local plugins if they exist.
|
|
if err != nil {
|
|
return &pluginsconfig.Config{}, nil
|
|
}
|
|
|
|
for i := range cfg.Apps {
|
|
cfg.Apps[i].Global = true
|
|
}
|
|
return
|
|
}
|
|
|
|
func linkPlugins(ctx context.Context, rootCmd *cobra.Command, plugins []*plugin.Plugin) error {
|
|
// Link plugins to related commands
|
|
var linkErrors []*plugin.Plugin
|
|
for _, p := range plugins {
|
|
if p.Error != nil {
|
|
linkErrors = append(linkErrors, p)
|
|
continue
|
|
}
|
|
|
|
manifest, err := p.Interface.Manifest(ctx)
|
|
if err != nil {
|
|
p.Error = err
|
|
linkErrors = append(linkErrors, p)
|
|
continue
|
|
}
|
|
|
|
linkPluginHooks(rootCmd, p, manifest.Hooks)
|
|
if p.Error != nil {
|
|
linkErrors = append(linkErrors, p)
|
|
continue
|
|
}
|
|
|
|
linkPluginCmds(rootCmd, p, manifest.Commands)
|
|
if p.Error != nil {
|
|
linkErrors = append(linkErrors, p)
|
|
continue
|
|
}
|
|
}
|
|
|
|
if len(linkErrors) > 0 {
|
|
// unload any plugin that could have been loaded
|
|
defer UnloadPlugins()
|
|
|
|
if err := printPlugins(ctx, cliui.New(cliui.WithStdout(os.Stdout))); err != nil {
|
|
// content of loadErrors is more important than a print error, so we don't
|
|
// return here, just print the error.
|
|
fmt.Printf("fail to print: %v\n", err)
|
|
}
|
|
|
|
var s strings.Builder
|
|
for _, p := range linkErrors {
|
|
fmt.Fprintf(&s, "%s: %v", p.Path, p.Error)
|
|
}
|
|
return errors.Errorf("fail to link: %v", s.String())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// UnloadPlugins releases any loaded plugins, which is basically killing the
|
|
// plugin server instance.
|
|
func UnloadPlugins() {
|
|
for _, p := range plugins {
|
|
p.KillClient()
|
|
}
|
|
}
|
|
|
|
func linkPluginHooks(rootCmd *cobra.Command, p *plugin.Plugin, hooks []*plugin.Hook) {
|
|
if p.Error != nil {
|
|
return
|
|
}
|
|
for _, hook := range hooks {
|
|
linkPluginHook(rootCmd, p, hook)
|
|
}
|
|
}
|
|
|
|
func linkPluginHook(rootCmd *cobra.Command, p *plugin.Plugin, hook *plugin.Hook) {
|
|
cmdPath := hook.CommandPath()
|
|
cmd := findCommandByPath(rootCmd, cmdPath)
|
|
if cmd == nil {
|
|
p.Error = errors.Errorf("unable to find command path %q for app hook %q", cmdPath, hook.Name)
|
|
return
|
|
}
|
|
if !cmd.Runnable() {
|
|
p.Error = errors.Errorf("can't attach app hook %q to non executable command %q", hook.Name, hook.PlaceHookOn)
|
|
return
|
|
}
|
|
|
|
newExecutedHook := func(hook *plugin.Hook, cmd *cobra.Command, args []string) *plugin.ExecutedHook {
|
|
hook.ImportFlags(cmd)
|
|
execHook := &plugin.ExecutedHook{
|
|
Hook: hook,
|
|
ExecutedCommand: &plugin.ExecutedCommand{
|
|
Use: cmd.Use,
|
|
Path: cmd.CommandPath(),
|
|
Args: args,
|
|
OsArgs: os.Args,
|
|
With: p.With,
|
|
Flags: hook.Flags,
|
|
},
|
|
}
|
|
execHook.ExecutedCommand.ImportFlags(cmd)
|
|
return execHook
|
|
}
|
|
|
|
for _, f := range hook.Flags {
|
|
var fs *flag.FlagSet
|
|
if f.Persistent {
|
|
fs = cmd.PersistentFlags()
|
|
} else {
|
|
fs = cmd.Flags()
|
|
}
|
|
|
|
if err := f.ExportToFlagSet(fs); err != nil {
|
|
p.Error = errors.Errorf("can't attach hook flags %q to command %q", hook.Flags, hook.PlaceHookOn)
|
|
return
|
|
}
|
|
}
|
|
|
|
preRun := cmd.PreRunE
|
|
cmd.PreRunE = func(cmd *cobra.Command, args []string) error {
|
|
if preRun != nil {
|
|
err := preRun(cmd, args)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
api, err := newAppClientAPI(cmd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx := cmd.Context()
|
|
execHook := newExecutedHook(hook, cmd, args)
|
|
err = p.Interface.ExecuteHookPre(ctx, execHook, api)
|
|
if err != nil {
|
|
return errors.Errorf("app %q ExecuteHookPre() error: %w", p.Path, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
runCmd := cmd.RunE
|
|
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
|
if runCmd != nil {
|
|
err := runCmd(cmd, args)
|
|
// if the command has failed the `PostRun` will not execute. here we execute the cleanup step before returning.
|
|
if err != nil {
|
|
api, err := newAppClientAPI(cmd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx := cmd.Context()
|
|
execHook := newExecutedHook(hook, cmd, args)
|
|
err = p.Interface.ExecuteHookCleanUp(ctx, execHook, api)
|
|
if err != nil {
|
|
cmd.Printf("app %q ExecuteHookCleanUp() error: %v", p.Path, err)
|
|
}
|
|
}
|
|
return err
|
|
}
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
return nil
|
|
}
|
|
|
|
postCmd := cmd.PostRunE
|
|
cmd.PostRunE = func(cmd *cobra.Command, args []string) error {
|
|
api, err := newAppClientAPI(cmd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx := cmd.Context()
|
|
execHook := newExecutedHook(hook, cmd, args)
|
|
|
|
defer func() {
|
|
err := p.Interface.ExecuteHookCleanUp(ctx, execHook, api)
|
|
if err != nil {
|
|
cmd.Printf("app %q ExecuteHookCleanUp() error: %v", p.Path, err)
|
|
}
|
|
}()
|
|
|
|
if postCmd != nil {
|
|
err := postCmd(cmd, args)
|
|
if err != nil {
|
|
// dont return the error, log it and let execution continue to `Run`
|
|
return err
|
|
}
|
|
}
|
|
|
|
err = p.Interface.ExecuteHookPost(ctx, execHook, api)
|
|
if err != nil {
|
|
return errors.Errorf("app %q ExecuteHookPost() error : %w", p.Path, err)
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// linkPluginCmds tries to add the plugin commands to the legacy ignite
|
|
// commands.
|
|
func linkPluginCmds(rootCmd *cobra.Command, p *plugin.Plugin, pluginCmds []*plugin.Command) {
|
|
if p.Error != nil {
|
|
return
|
|
}
|
|
for _, pluginCmd := range pluginCmds {
|
|
linkPluginCmd(rootCmd, p, pluginCmd)
|
|
if p.Error != nil {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func linkPluginCmd(rootCmd *cobra.Command, p *plugin.Plugin, pluginCmd *plugin.Command) {
|
|
cmdPath := pluginCmd.Path()
|
|
cmd := findCommandByPath(rootCmd, cmdPath)
|
|
if cmd == nil {
|
|
p.Error = errors.Errorf("unable to find command path %q for app %q", cmdPath, p.Path)
|
|
return
|
|
}
|
|
if cmd.Runnable() {
|
|
p.Error = errors.Errorf("can't attach app command %q to runnable command %q", pluginCmd.Use, cmd.CommandPath())
|
|
return
|
|
}
|
|
|
|
// Check for existing commands
|
|
// pluginCmd.Use can be like `command [args]` so we need to remove those
|
|
// extra args if any.
|
|
pluginCmdName := strings.Split(pluginCmd.Use, " ")[0]
|
|
for _, cmd := range cmd.Commands() {
|
|
if cmd.Name() == pluginCmdName {
|
|
p.Error = errors.Errorf("app command %q already exists in Ignite's commands", pluginCmdName)
|
|
return
|
|
}
|
|
}
|
|
|
|
newCmd, err := pluginCmd.ToCobraCommand()
|
|
if err != nil {
|
|
p.Error = err
|
|
return
|
|
}
|
|
cmd.AddCommand(newCmd)
|
|
|
|
if len(pluginCmd.Commands) == 0 {
|
|
// pluginCmd has no sub commands, so it's runnable
|
|
newCmd.RunE = func(cmd *cobra.Command, args []string) error {
|
|
ctx := cmd.Context()
|
|
return clictx.Do(ctx, func() error {
|
|
api, err := newAppClientAPI(cmd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Call the plugin Execute
|
|
execCmd := &plugin.ExecutedCommand{
|
|
Use: cmd.Use,
|
|
Path: cmd.CommandPath(),
|
|
Args: args,
|
|
OsArgs: os.Args,
|
|
With: p.With,
|
|
}
|
|
execCmd.ImportFlags(cmd)
|
|
err = p.Interface.Execute(ctx, execCmd, api)
|
|
|
|
return err
|
|
})
|
|
}
|
|
} else {
|
|
for _, pluginCmd := range pluginCmd.Commands {
|
|
pluginCmd.PlaceCommandUnder = newCmd.CommandPath()
|
|
linkPluginCmd(newCmd, p, pluginCmd)
|
|
if p.Error != nil {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func findCommandByPath(cmd *cobra.Command, cmdPath string) *cobra.Command {
|
|
if cmd.CommandPath() == cmdPath {
|
|
return cmd
|
|
}
|
|
for _, cmd := range cmd.Commands() {
|
|
if cmd := findCommandByPath(cmd, cmdPath); cmd != nil {
|
|
return cmd
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// NewApp returns a command that groups Ignite App related sub commands.
|
|
func NewApp() *cobra.Command {
|
|
c := &cobra.Command{
|
|
Use: "app [command]",
|
|
Short: "Create and manage Ignite Apps",
|
|
}
|
|
|
|
c.AddCommand(
|
|
NewAppList(),
|
|
NewAppUpdate(),
|
|
NewAppScaffold(),
|
|
NewAppDescribe(),
|
|
NewAppInstall(),
|
|
NewAppUninstall(),
|
|
)
|
|
|
|
return c
|
|
}
|
|
|
|
func NewAppList() *cobra.Command {
|
|
lstCmd := &cobra.Command{
|
|
Use: "list",
|
|
Short: "List installed apps",
|
|
Long: "Prints status and information of all installed Ignite Apps.",
|
|
RunE: func(cmd *cobra.Command, _ []string) error {
|
|
s := cliui.New(cliui.WithStdout(os.Stdout))
|
|
return printPlugins(cmd.Context(), s)
|
|
},
|
|
}
|
|
return lstCmd
|
|
}
|
|
|
|
func NewAppUpdate() *cobra.Command {
|
|
return &cobra.Command{
|
|
Use: "update [path]",
|
|
Short: "Update app",
|
|
Long: `Updates an Ignite App specified by path.
|
|
|
|
If no path is specified all declared apps are updated.`,
|
|
Example: "ignite app update github.com/org/my-app/",
|
|
Args: cobra.MaximumNArgs(1),
|
|
RunE: func(_ *cobra.Command, args []string) error {
|
|
if len(args) == 0 {
|
|
// update all plugins
|
|
return plugin.Update(plugins...)
|
|
}
|
|
pluginPath, err := getAppPath(args[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// find the plugin to update
|
|
for _, p := range plugins {
|
|
if p.HasPath(pluginPath) {
|
|
return plugin.Update(p)
|
|
}
|
|
}
|
|
return errors.Errorf("App %q not found", pluginPath)
|
|
},
|
|
}
|
|
}
|
|
|
|
func NewAppInstall() *cobra.Command {
|
|
cmdPluginAdd := &cobra.Command{
|
|
Use: "install [path] [key=value]...",
|
|
Short: "Install app",
|
|
Long: `Installs an Ignite App.
|
|
|
|
Respects key value pairs declared after the app path to be added to the generated configuration definition.`,
|
|
Example: "ignite app install github.com/org/my-app/ foo=bar baz=qux",
|
|
Args: cobra.MinimumNArgs(1),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
session := cliui.New(
|
|
cliui.WithStdout(os.Stdout),
|
|
cliui.WithoutUserInteraction(getYes(cmd)),
|
|
)
|
|
defer session.End()
|
|
|
|
var (
|
|
conf *pluginsconfig.Config
|
|
err error
|
|
)
|
|
|
|
global := flagGetPluginsGlobal(cmd)
|
|
if global {
|
|
conf, err = parseGlobalPlugins()
|
|
} else {
|
|
conf, err = parseLocalPlugins()
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
pluginPath, err := getAppPath(args[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, p := range conf.Apps {
|
|
if p.HasPath(pluginPath) {
|
|
return errors.Errorf("app %s is already installed", pluginPath)
|
|
}
|
|
}
|
|
|
|
p := pluginsconfig.Plugin{
|
|
Path: pluginPath,
|
|
With: make(map[string]string),
|
|
Global: global,
|
|
}
|
|
|
|
pluginsOptions := []plugin.Option{
|
|
plugin.CollectEvents(session.EventBus()),
|
|
}
|
|
|
|
var pluginArgs []string
|
|
if len(args) > 1 {
|
|
pluginArgs = args[1:]
|
|
}
|
|
|
|
for _, pa := range pluginArgs {
|
|
kv := strings.Split(pa, "=")
|
|
if len(kv) != 2 {
|
|
return errors.Errorf("malformed key=value arg: %s", pa)
|
|
}
|
|
p.With[kv[0]] = kv[1]
|
|
}
|
|
|
|
plugins, err := plugin.Load(cmd.Context(), []pluginsconfig.Plugin{p}, pluginsOptions...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer plugins[0].KillClient()
|
|
|
|
if err := plugins[0].Error; err != nil {
|
|
if strings.Contains(err.Error(), "go.mod file not found in current directory") {
|
|
return errors.Errorf("unable to find an App at the root of this repository (%s). Please ensure your repository URL is correct. If you're trying to install an App under a subfolder, include the path at the end of your repository URL, e.g., github.com/ignite/apps/appregistry", pluginPath)
|
|
}
|
|
|
|
return errors.Errorf("error while loading app %q: %w", pluginPath, plugins[0].Error)
|
|
}
|
|
session.Println(icons.OK, "Done loading apps")
|
|
conf.Apps = append(conf.Apps, p)
|
|
|
|
if err := conf.Save(); err != nil {
|
|
return err
|
|
}
|
|
|
|
session.Printf("%s Installed %s\n", icons.Tada, pluginPath)
|
|
return nil
|
|
},
|
|
}
|
|
|
|
cmdPluginAdd.Flags().AddFlagSet(flagSetPluginsGlobal())
|
|
|
|
return cmdPluginAdd
|
|
}
|
|
|
|
func NewAppUninstall() *cobra.Command {
|
|
cmdPluginRemove := &cobra.Command{
|
|
Use: "uninstall [path]",
|
|
Aliases: []string{"rm"},
|
|
Short: "Uninstall app",
|
|
Long: "Uninstalls an Ignite App specified by path.",
|
|
Example: "ignite app uninstall github.com/org/my-app/",
|
|
Args: cobra.ExactArgs(1),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
s := cliui.New(cliui.WithStdout(os.Stdout))
|
|
|
|
var (
|
|
conf *pluginsconfig.Config
|
|
err error
|
|
)
|
|
|
|
global := flagGetPluginsGlobal(cmd)
|
|
if global {
|
|
conf, err = parseGlobalPlugins()
|
|
} else {
|
|
conf, err = parseLocalPlugins()
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
pluginPath, err := getAppPath(args[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
removed := false
|
|
for i, cp := range conf.Apps {
|
|
if cp.HasPath(pluginPath) {
|
|
conf.Apps = append(conf.Apps[:i], conf.Apps[i+1:]...)
|
|
removed = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !removed {
|
|
// return if no matching plugin path found
|
|
return errors.Errorf("app %s not found", pluginPath)
|
|
}
|
|
|
|
if err := conf.Save(); err != nil {
|
|
return err
|
|
}
|
|
|
|
s.Printf("%s %s uninstalled\n", icons.OK, pluginPath)
|
|
s.Printf("\t%s updated\n", conf.Path())
|
|
|
|
return nil
|
|
},
|
|
}
|
|
|
|
cmdPluginRemove.Flags().AddFlagSet(flagSetPluginsGlobal())
|
|
|
|
return cmdPluginRemove
|
|
}
|
|
|
|
func NewAppScaffold() *cobra.Command {
|
|
return &cobra.Command{
|
|
Use: "scaffold [name]",
|
|
Short: "Scaffold a new Ignite App",
|
|
Long: `Scaffolds a new Ignite App in the current directory.
|
|
|
|
A git repository will be created with the given module name, unless the current directory is already a git repository.`,
|
|
Example: "ignite app scaffold github.com/org/my-app/",
|
|
Args: cobra.ExactArgs(1),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
session := cliui.New(
|
|
cliui.StartSpinnerWithText(statusScaffolding),
|
|
cliui.WithoutUserInteraction(getYes(cmd)),
|
|
)
|
|
defer session.End()
|
|
|
|
wd, err := os.Getwd()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
moduleName := args[0]
|
|
path, err := plugin.Scaffold(cmd.Context(), session, wd, moduleName, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := xgit.InitAndCommit(path); err != nil {
|
|
return err
|
|
}
|
|
|
|
message := `⭐️ Successfully created a new Ignite App '%[1]s'.
|
|
|
|
👉 Update app code at '%[2]s/main.go'
|
|
|
|
👉 Test Ignite App integration by installing the app within the chain directory:
|
|
|
|
ignite app install %[2]s
|
|
|
|
Or globally:
|
|
|
|
ignite app install -g %[2]s
|
|
|
|
👉 Once the app is pushed to a repository, replace the local path by the repository path.
|
|
`
|
|
session.Printf(message, moduleName, path)
|
|
return nil
|
|
},
|
|
}
|
|
}
|
|
|
|
func NewAppDescribe() *cobra.Command {
|
|
return &cobra.Command{
|
|
Use: "describe [path]",
|
|
Short: "Print information about installed apps",
|
|
Long: "Print information about an installed Ignite App commands and hooks.",
|
|
Example: "ignite app describe github.com/org/my-app/",
|
|
Args: cobra.ExactArgs(1),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
var (
|
|
s = cliui.New(cliui.WithStdout(os.Stdout))
|
|
ctx = cmd.Context()
|
|
)
|
|
|
|
pluginPath, err := getAppPath(args[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, p := range plugins {
|
|
if p.HasPath(pluginPath) {
|
|
manifest, err := p.Interface.Manifest(ctx)
|
|
if err != nil {
|
|
return errors.Errorf("error while loading app manifest: %w", err)
|
|
}
|
|
|
|
if len(manifest.Commands) > 0 {
|
|
s.Println("Commands:")
|
|
for i, c := range manifest.Commands {
|
|
cmdPath := fmt.Sprintf("%s %s", c.Path(), c.Use)
|
|
s.Printf(" %d) %s\n", i+1, cmdPath)
|
|
}
|
|
}
|
|
|
|
if len(manifest.Hooks) > 0 {
|
|
s.Println("Hooks:")
|
|
for i, h := range manifest.Hooks {
|
|
s.Printf(" %d) '%s' on command '%s'\n", i+1, h.Name, h.CommandPath())
|
|
}
|
|
}
|
|
|
|
break
|
|
}
|
|
}
|
|
|
|
return nil
|
|
},
|
|
}
|
|
}
|
|
|
|
func getPluginLocationName(p *plugin.Plugin) string {
|
|
if p.IsGlobal() {
|
|
return "global"
|
|
}
|
|
return "local"
|
|
}
|
|
|
|
func getPluginStatus(ctx context.Context, p *plugin.Plugin) string {
|
|
if p.Error != nil {
|
|
return fmt.Sprintf("%s Error: %v", icons.NotOK, p.Error)
|
|
}
|
|
|
|
_, err := p.Interface.Manifest(ctx)
|
|
if err != nil {
|
|
return fmt.Sprintf("%s Error: Manifest() returned %v", icons.NotOK, err)
|
|
}
|
|
|
|
return fmt.Sprintf("%s Loaded", icons.OK)
|
|
}
|
|
|
|
func printPlugins(ctx context.Context, session *cliui.Session) error {
|
|
var entries [][]string
|
|
for _, p := range plugins {
|
|
entries = append(entries, []string{p.Path, getPluginLocationName(p), getPluginStatus(ctx, p)})
|
|
}
|
|
|
|
if err := session.PrintTable([]string{"Path", "Config", "Status"}, entries...); err != nil {
|
|
return errors.Errorf("error while printing apps: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func newAppClientAPI(cmd *cobra.Command) (plugin.ClientAPI, error) {
|
|
// Get chain when the plugin runs inside an blockchain app
|
|
c, err := chain.NewWithHomeFlags(cmd)
|
|
if err != nil && !errors.Is(err, gomodule.ErrGoModNotFound) {
|
|
return nil, err
|
|
}
|
|
|
|
var options []plugin.APIOption
|
|
if c != nil {
|
|
options = append(options, plugin.WithChain(c))
|
|
}
|
|
|
|
return plugin.NewClientAPI(options...), nil
|
|
}
|
|
|
|
func flagSetPluginsGlobal() *flag.FlagSet {
|
|
fs := flag.NewFlagSet("", flag.ContinueOnError)
|
|
fs.BoolP(flagPluginsGlobal, "g", false, "use global plugins configuration ($HOME/.ignite/apps/igniteapps.yml)")
|
|
return fs
|
|
}
|
|
|
|
func flagGetPluginsGlobal(cmd *cobra.Command) bool {
|
|
global, _ := cmd.Flags().GetBool(flagPluginsGlobal)
|
|
return global
|
|
}
|
|
|
|
func getAppPath(path string) (string, error) {
|
|
if xfilepath.IsDir(path) {
|
|
// if directory is relative, make it absolute
|
|
pluginPathAbs, err := xfilepath.MustAbs(path)
|
|
if err != nil {
|
|
return "", errors.Wrapf(err, "failed to get absolute path of %s", path)
|
|
}
|
|
path = pluginPathAbs
|
|
}
|
|
return path, nil
|
|
}
|