Skip to content

Commit

Permalink
feat(plugin): support command's flags (ignite#3060)
Browse files Browse the repository at this point in the history
* wip

* wip: implements gob decoder/encoder

* flags data are passing the network

* hide flags field and expose Flags() func like cobra.Command

* Remove cobra.Command field from plugin.Command

The field was passed to give access to the flags, but that wasb;t
working because cobra.Command is not fully serializable (it has a lot of
unexported fields).

* comments

* add int64 flag type

* test: add flags

* test: plugin use current ignite/cli

* chore: upgrade plugin CLI dep

* fix: InitAndCommit doesn't create repo if already exists

* chore: improve plugin default code

* add CL entry

* chore: update plugin CLI dep

* fix after merge

* dont remove band now

* remove replace in plugin gomod and use latest version

* fix: plugin.Hooks can return an error

* update plugin dep version

* add spinner during plugin scaffolding

* fix: populate hook.with with plugin config

* test: execute commands on linkPluginCmds

* test: assert args in TestLinkPluginCmds

* test: global args in TestLinkPluginHooks

* refac(plugin): merge Commands and Hooks into Manifest

Also stop using the same type for definition and execution. Now Manifest
returns definition with type Command and Hook, while Execute* methods
takes ExecutedCommand and ExecutedHook as parameters.

* fix lint error

* improve plugin error handling

* feat(plugin): support more flag types

* update plugin cli version and other fixes

* test: try to fix

* docs: comment

* docs: more comments

* docs: more comments

* docs: comments

* remove slack private lnk

* remove private slack link

* run make format

* docs: add code comments

* fix: ExecuteCommand embedding

ExecuteCommand cannot implement gob decoder/encoder and at the same time
being embeded in ExecutedHook.

Co-authored-by: Alex Johnson <[email protected]>
  • Loading branch information
tbruyelle and Alex Johnson authored Nov 15, 2022
1 parent ecb65c9 commit 728bb50
Show file tree
Hide file tree
Showing 25 changed files with 1,025 additions and 511 deletions.
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- [#2892](https://github.com/ignite/cli/pull/2982/) Add `ignite generate hooks` command.
- [#2955](https://github.com/ignite/cli/pull/2955/) Add `ignite network request add-account` command.
- [#2877](https://github.com/ignite/cli/pull/2877) Plugin system
- [#3060](https://github.com/ignite/cli/pull/3060) Plugin system flag support
- [#2995](https://github.com/ignite/cli/pull/2995/) Add `ignite network request remove-validator` command.
- [#2999](https://github.com/ignite/cli/pull/2999/) Add `ignite network request remove-account` command.

Expand Down
2 changes: 1 addition & 1 deletion ignite/cmd/chain_build.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ func chainBuildHandler(cmd *cobra.Command, _ []string) error {
chainOption = append(chainOption, chain.CheckDependencies())
}

c, err := NewChainWithHomeFlags(cmd, chainOption...)
c, err := newChainWithHomeFlags(cmd, chainOption...)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion ignite/cmd/chain_faucet.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func chainFaucetHandler(cmd *cobra.Command, args []string) error {
chain.CollectEvents(session.EventBus()),
}

c, err := NewChainWithHomeFlags(cmd, chainOption...)
c, err := newChainWithHomeFlags(cmd, chainOption...)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion ignite/cmd/chain_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func chainInitHandler(cmd *cobra.Command, _ []string) error {
chainOption = append(chainOption, chain.CheckDependencies())
}

c, err := NewChainWithHomeFlags(cmd, chainOption...)
c, err := newChainWithHomeFlags(cmd, chainOption...)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion ignite/cmd/chain_serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func chainServeHandler(cmd *cobra.Command, args []string) error {
}

// create the chain
c, err := NewChainWithHomeFlags(cmd, chainOption...)
c, err := newChainWithHomeFlags(cmd, chainOption...)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion ignite/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ func flagGetClearCache(cmd *cobra.Command) bool {
return clearCache
}

func NewChainWithHomeFlags(cmd *cobra.Command, chainOption ...chain.Option) (*chain.Chain, error) {
func newChainWithHomeFlags(cmd *cobra.Command, chainOption ...chain.Option) (*chain.Chain, error) {
// Check if custom home is provided
if home := getHome(cmd); home != "" {
chainOption = append(chainOption, chain.HomePath(home))
Expand Down
2 changes: 1 addition & 1 deletion ignite/cmd/generate_composables.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func generateComposablesHandler(cmd *cobra.Command, _ []string) error {
session := cliui.New(cliui.StartSpinnerWithText(statusGenerating))
defer session.End()

c, err := NewChainWithHomeFlags(
c, err := newChainWithHomeFlags(
cmd,
chain.WithOutputer(session),
chain.CollectEvents(session.EventBus()),
Expand Down
2 changes: 1 addition & 1 deletion ignite/cmd/generate_go.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func generateGoHandler(cmd *cobra.Command, _ []string) error {
session := cliui.New(cliui.StartSpinnerWithText(statusGenerating))
defer session.End()

c, err := NewChainWithHomeFlags(
c, err := newChainWithHomeFlags(
cmd,
chain.WithOutputer(session),
chain.CollectEvents(session.EventBus()),
Expand Down
2 changes: 1 addition & 1 deletion ignite/cmd/generate_hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func generateHooksHandler(cmd *cobra.Command, _ []string) error {
session := cliui.New(cliui.StartSpinnerWithText(statusGenerating))
defer session.End()

c, err := NewChainWithHomeFlags(
c, err := newChainWithHomeFlags(
cmd,
chain.WithOutputer(session),
chain.CollectEvents(session.EventBus()),
Expand Down
2 changes: 1 addition & 1 deletion ignite/cmd/generate_openapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func generateOpenAPIHandler(cmd *cobra.Command, _ []string) error {
session := cliui.New(cliui.StartSpinnerWithText(statusGenerating))
defer session.End()

c, err := NewChainWithHomeFlags(
c, err := newChainWithHomeFlags(
cmd,
chain.WithOutputer(session),
chain.CollectEvents(session.EventBus()),
Expand Down
2 changes: 1 addition & 1 deletion ignite/cmd/generate_typescript_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func generateTSClientHandler(cmd *cobra.Command, _ []string) error {
session := cliui.New(cliui.StartSpinnerWithText(statusGenerating))
defer session.End()

c, err := NewChainWithHomeFlags(
c, err := newChainWithHomeFlags(
cmd,
chain.WithOutputer(session),
chain.CollectEvents(session.EventBus()),
Expand Down
2 changes: 1 addition & 1 deletion ignite/cmd/generate_vuex.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func generateVuexHandler(cmd *cobra.Command, _ []string) error {
session := cliui.New(cliui.StartSpinnerWithText(statusGenerating))
defer session.End()

c, err := NewChainWithHomeFlags(
c, err := newChainWithHomeFlags(
cmd,
chain.WithOutputer(session),
chain.CollectEvents(session.EventBus()),
Expand Down
107 changes: 83 additions & 24 deletions ignite/cmd/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/pkg/errors"
"github.com/spf13/cobra"

"github.com/ignite/cli/ignite/pkg/cliui"
"github.com/ignite/cli/ignite/pkg/cliui/entrywriter"
"github.com/ignite/cli/ignite/pkg/xgit"
"github.com/ignite/cli/ignite/services/plugin"
Expand All @@ -27,7 +28,7 @@ const (
// If no configuration found, it returns w/o error.
func LoadPlugins(ctx context.Context, rootCmd *cobra.Command) error {
// NOTE(tb) Not sure if it's the right place to load this.
chain, err := NewChainWithHomeFlags(rootCmd)
chain, err := newChainWithHomeFlags(rootCmd)
if err != nil {
// Binary is run outside of an chain app, plugins can't be loaded
return nil
Expand All @@ -36,16 +37,31 @@ func LoadPlugins(ctx context.Context, rootCmd *cobra.Command) error {
if err != nil {
return err
}
return loadPlugins(rootCmd, plugins)
}

func loadPlugins(rootCmd *cobra.Command, plugins []*plugin.Plugin) error {
// Link plugins to related commands
var loadErrors []string
for _, p := range plugins {
linkPluginHooks(rootCmd, p)
if p.Error != nil {
loadErrors = append(loadErrors, p.Path)
continue
}
manifest, err := p.Interface.Manifest()
if err != nil {
p.Error = fmt.Errorf("Manifest() error: %w", err)
continue
}
linkPluginHooks(rootCmd, p, manifest.Hooks)
if p.Error != nil {
loadErrors = append(loadErrors, p.Path)
continue
}
linkPluginCmds(rootCmd, p)
linkPluginCmds(rootCmd, p, manifest.Commands)
if p.Error != nil {
loadErrors = append(loadErrors, p.Path)
continue
}
}
if len(loadErrors) > 0 {
Expand All @@ -65,12 +81,11 @@ func UnloadPlugins() {
}
}

func linkPluginHooks(rootCmd *cobra.Command, p *plugin.Plugin) {
func linkPluginHooks(rootCmd *cobra.Command, p *plugin.Plugin, hooks []plugin.Hook) {
if p.Error != nil {
return
}

for _, hook := range p.Interface.Hooks() {
for _, hook := range hooks {
linkPluginHook(rootCmd, p, hook)
}
}
Expand Down Expand Up @@ -98,6 +113,20 @@ func linkPluginHook(rootCmd *cobra.Command, p *plugin.Plugin, hook plugin.Hook)
return
}

newExecutedHook := func(hook plugin.Hook, cmd *cobra.Command, args []string) plugin.ExecutedHook {
execHook := plugin.ExecutedHook{
Hook: hook,
ExecutedCommand: plugin.ExecutedCommand{
Use: cmd.Use,
Path: cmd.CommandPath(),
Args: args,
With: p.With,
},
}
execHook.ExecutedCommand.SetFlags(cmd.Flags())
return execHook
}

preRun := cmd.PreRunE
cmd.PreRunE = func(cmd *cobra.Command, args []string) error {
if preRun != nil {
Expand All @@ -106,8 +135,11 @@ func linkPluginHook(rootCmd *cobra.Command, p *plugin.Plugin, hook plugin.Hook)
return err
}
}

return p.Interface.ExecuteHookPre(hook, args)
err := p.Interface.ExecuteHookPre(newExecutedHook(hook, cmd, args))
if err != nil {
return fmt.Errorf("plugin %q ExecuteHookPre() error: %w", p.Path, err)
}
return nil
}

runCmd := cmd.RunE
Expand All @@ -117,9 +149,11 @@ func linkPluginHook(rootCmd *cobra.Command, p *plugin.Plugin, hook plugin.Hook)
err := runCmd(cmd, args)
// if the command has failed the `PostRun` will not execute. here we execute the cleanup step before returnning.
if err != nil {
p.Interface.ExecuteHookCleanUp(hook, args)
err := p.Interface.ExecuteHookCleanUp(newExecutedHook(hook, cmd, args))
if err != nil {
fmt.Printf("plugin %q ExecuteHookCleanUp() error: %v", p.Path, err)
}
}

return err
}

Expand All @@ -129,7 +163,14 @@ func linkPluginHook(rootCmd *cobra.Command, p *plugin.Plugin, hook plugin.Hook)

postCmd := cmd.PostRunE
cmd.PostRunE = func(cmd *cobra.Command, args []string) error {
defer p.Interface.ExecuteHookCleanUp(hook, args)
execHook := newExecutedHook(hook, cmd, args)

defer func() {
err := p.Interface.ExecuteHookCleanUp(execHook)
if err != nil {
fmt.Printf("plugin %q ExecuteHookCleanUp() error: %v", p.Path, err)
}
}()

if preRun != nil {
err := postCmd(cmd, args)
Expand All @@ -139,17 +180,21 @@ func linkPluginHook(rootCmd *cobra.Command, p *plugin.Plugin, hook plugin.Hook)
}
}

return p.Interface.ExecuteHookPost(hook, args)
err := p.Interface.ExecuteHookPost(execHook)
if err != nil {
return fmt.Errorf("plugin %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) {
func linkPluginCmds(rootCmd *cobra.Command, p *plugin.Plugin, pluginCmds []plugin.Command) {
if p.Error != nil {
return
}
for _, pluginCmd := range p.Interface.Commands() {
for _, pluginCmd := range pluginCmds {
linkPluginCmd(rootCmd, p, pluginCmd)
if p.Error != nil {
return
Expand Down Expand Up @@ -186,21 +231,34 @@ func linkPluginCmd(rootCmd *cobra.Command, p *plugin.Plugin, pluginCmd plugin.Co
Short: pluginCmd.Short,
Long: pluginCmd.Long,
}
for _, f := range pluginCmd.Flags {
err := f.FeedFlagSet(newCmd.Flags())
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 {
// Pass config parameters
pluginCmd.With = p.With
// Pass cobra cmd
pluginCmd.CobraCmd = cmd
execCmd := plugin.ExecutedCommand{
Use: cmd.Use,
Path: cmd.CommandPath(),
Args: args,
With: p.With,
}
execCmd.SetFlags(cmd.Flags())
// Call the plugin Execute
err := p.Interface.Execute(pluginCmd, args)
err := p.Interface.Execute(execCmd)
// NOTE(tb): This pause gives enough time for go-plugin to sync the
// output from stdout/stderr of the plugin. Without that pause, this
// output can be discarded and not printed in the user console.
time.Sleep(100 * time.Millisecond)
return err
if err != nil {
return fmt.Errorf("plugin %q Execute() error : %w", p.Path, err)
}
return nil
}
} else {
for _, pluginCmd := range pluginCmd.Commands {
Expand Down Expand Up @@ -286,6 +344,9 @@ func NewPluginScaffold() *cobra.Command {
Short: "Scaffold a new plugin",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
session := cliui.New(cliui.StartSpinnerWithText(statusScaffolding))
defer session.End()

wd, err := os.Getwd()
if err != nil {
return err
Expand All @@ -307,11 +368,9 @@ func NewPluginScaffold() *cobra.Command {
plugins:
- path: %[2]s
👉 once the plugin is pushed to a repository, the config becomes:
plugins:
- path: %[1]s
👉 once the plugin is pushed to a repository, replace the local path by the repository path.
`
fmt.Printf(message, moduleName, path)
session.Printf(message, moduleName, path)
return nil
},
}
Expand Down
Loading

0 comments on commit 728bb50

Please sign in to comment.