Skip to content

Commit

Permalink
feat(config): global plugins config (#3214)
Browse files Browse the repository at this point in the history
* base

* base refactor

* refactor

* rename

* rename

* fix comment

* rename file

* format

* refactor base

* format

* refactor some imports

* imports refactor

* fix

* fix import

* refactor:

* changelog

* changelog

* Update ignite/services/network/networkchain/init.go

Co-authored-by: Jerónimo Albi <[email protected]>

* base -> baseconfig

* refactoring for clarity

* v12 -> v1

* fix tests

* fix integration

* fix name

* fix integration

* format

* Update ignite/cmd/cmd.go

Co-authored-by: Jerónimo Albi <[email protected]>

* imports

* LocateDefault logic

* lint fix

* refactor and test

* finish refactor

* revert

* move

* move

* rename

* fix tests

* fix test

* rename

* add global

* functionality

* format

* fix error statement for global plugins

* typo

* chainconfig

* imports

* fix imports

* address review

* testdata

* better import

* fix test

* changelog

* refactor

* simplify

* RemoveDuplicates

* refactor LoadPlugins

* lint

* add global flag

* add info

* add error prints

* simplify return

* fix test

* fix global parse

* add integration test

* modify test

* fix returns

* use ignite example plugin

* refactor global flag

* Fix plugin integration test

Add more assertion and use an alternate `.ignite` folder to avoid
conflicts with user home.

Also fix `plugin add -g` when `.ignite/plugins` doesn't exist.

* add removed TODO by mistake

* move flags

* return err

* Update ignite/cmd/plugin.go

Co-authored-by: Thomas Bruyelle <[email protected]>

* format

* fix test

* remove yaml tag

* Update ignite/cmd/plugin.go

Co-authored-by: Thomas Bruyelle <[email protected]>

* fix test

* test: add key/value pairs to plugin add

* add detailed plugin

Co-authored-by: Jerónimo Albi <[email protected]>
Co-authored-by: Thomas Bruyelle <[email protected]>
Co-authored-by: Thomas Bruyelle <[email protected]>
  • Loading branch information
4 people authored Dec 14, 2022
1 parent bdcd0b7 commit 304e5d5
Show file tree
Hide file tree
Showing 12 changed files with 395 additions and 71 deletions.
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

### Features

- [#3214](https://github.com/ignite/cli/pull/3214) Global plugins config.
- [#3142](https://github.com/ignite/cli/pull/3142) Add `ignite network request param-change` command.
- [#3181](https://github.com/ignite/cli/pull/3181) Addition of `add` `remove` commands for `plugins`
- [#3184](https://github.com/ignite/cli/pull/3184) Separate `plugins.yml` config file.
Expand Down
127 changes: 106 additions & 21 deletions ignite/cmd/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/pkg/errors"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"

pluginsconfig "github.com/ignite/cli/ignite/config/plugins"
"github.com/ignite/cli/ignite/pkg/clictx"
Expand All @@ -19,29 +20,44 @@ import (
"github.com/ignite/cli/ignite/services/plugin"
)

const (
igniteCmdPrefix = "ignite "
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

const (
igniteCmdPrefix = "ignite "
)

// LoadPlugins tries to load all the plugins found in configuration.
// If no configuration found, it returns w/o error.
// LoadPlugins tries to load all the plugins found in configurations.
// If no configurations found, it returns w/o error.
func LoadPlugins(ctx context.Context, rootCmd *cobra.Command) error {
cfg, err := parseLocalPlugins(rootCmd)
if err != nil {
// if binary is run where there is no plugins.yml, don't load
return nil
pluginsConfigs := make([]pluginsconfig.Plugin, 0)

localCfg, err := parseLocalPlugins(rootCmd)
if err != nil && !errors.As(err, &cosmosanalysis.ErrPathNotChain{}) {
return err
} else if err == nil {
pluginsConfigs = append(pluginsConfigs, localCfg.Plugins...)
}

globalCfg, err := parseGlobalPlugins()
if err == nil {
pluginsConfigs = append(pluginsConfigs, globalCfg.Plugins...)
}

// TODO: parse global config
if len(pluginsConfigs) == 0 {
return nil
}

plugins, err = plugin.Load(ctx, cfg)
uniquePlugins := plugin.RemoveDuplicates(pluginsConfigs)
plugins, err = plugin.Load(ctx, uniquePlugins)
if err != nil {
return err
} else if len(plugins) == 0 {
return nil
}

return loadPlugins(rootCmd, plugins)
}

Expand All @@ -62,6 +78,25 @@ func parseLocalPlugins(cmd *cobra.Command) (*pluginsconfig.Config, error) {
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.Plugins {
cfg.Plugins[i].Global = true
}
return
}

func loadPlugins(rootCmd *cobra.Command, plugins []*plugin.Plugin) error {
// Link plugins to related commands
var loadErrors []string
Expand Down Expand Up @@ -373,8 +408,8 @@ func NewPluginUpdate() *cobra.Command {
func NewPluginAdd() *cobra.Command {
cmdPluginAdd := &cobra.Command{
Use: "add [path] [key=value]...",
Short: "Adds a plugin declaration to a chain's plugin configuration",
Long: `Adds a plugin declaration to a chain's plugin configuration.
Short: "Adds a plugin declaration to a plugin configuration",
Long: `Adds a plugin declaration to a plugin configuration.
Respects key value pairs declared after the plugin path to be added to the
generated configuration definition.
Example:
Expand All @@ -384,7 +419,17 @@ Example:
session := cliui.New(cliui.WithStdout(os.Stdout))
defer session.End()

conf, err := parseLocalPlugins(cmd)
var (
conf *pluginsconfig.Config
err error
)

global := flagGetPluginsGlobal(cmd)
if global {
conf, err = parseGlobalPlugins()
} else {
conf, err = parseLocalPlugins(cmd)
}
if err != nil {
return err
}
Expand All @@ -396,8 +441,9 @@ Example:
}

p := pluginsconfig.Plugin{
Path: args[0],
With: make(map[string]string),
Path: args[0],
With: make(map[string]string),
Global: global,
}

var pluginArgs []string
Expand Down Expand Up @@ -432,6 +478,9 @@ Example:
return nil
},
}

cmdPluginAdd.Flags().AddFlagSet(flagSetPluginsGlobal())

return cmdPluginAdd
}

Expand All @@ -444,7 +493,17 @@ func NewPluginRemove() *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
s := cliui.New(cliui.WithStdout(os.Stdout))

conf, err := parseLocalPlugins(cmd)
var (
conf *pluginsconfig.Config
err error
)

global := flagGetPluginsGlobal(cmd)
if global {
conf, err = parseGlobalPlugins()
} else {
conf, err = parseLocalPlugins(cmd)
}
if err != nil {
return err
}
Expand All @@ -461,10 +520,14 @@ func NewPluginRemove() *cobra.Command {
}

s.Printf("%s %s removed\n", icons.OK, args[0])
s.Printf("\t%s updated\n", conf.Path())

return nil
},
}

cmdPluginRemove.Flags().AddFlagSet(flagSetPluginsGlobal())

return cmdPluginRemove
}

Expand Down Expand Up @@ -552,12 +615,22 @@ func printPlugins(session *cliui.Session) error {
hookCount = len(manifest.Hooks)
cmdCount = len(manifest.Commands)
)
return fmt.Sprintf("%s Loaded 🪝%d 💻%d", icons.OK, hookCount, cmdCount)

return fmt.Sprintf("%s Loaded: 🪝%d 💻%d", icons.OK, hookCount, cmdCount)
}

installedStatus := func(p *plugin.Plugin) string {
installed := "local"
if p.IsGlobal() {
installed = "global"
}
return installed
}

for _, p := range plugins {
entries = append(entries, []string{p.Path, buildStatus(p)})
entries = append(entries, []string{p.Path, buildStatus(p), installedStatus(p)})
}
if err := session.PrintTable([]string{"Path", "Status"}, entries...); err != nil {
if err := session.PrintTable([]string{"Path", "Status", "Config"}, entries...); err != nil {
return fmt.Errorf("error while printing plugins: %w", err)
}
return nil
Expand Down Expand Up @@ -613,3 +686,15 @@ func printPluginHooks(hooks []plugin.Hook, session *cliui.Session) error {
}
return nil
}

func flagSetPluginsGlobal() *flag.FlagSet {
fs := flag.NewFlagSet("", flag.ContinueOnError)
fs.BoolP(flagPluginsGlobal, "g", false, "use global plugins configuration"+
" ($HOME/.ignite/plugins/plugins.yml)")
return fs
}

func flagGetPluginsGlobal(cmd *cobra.Command) bool {
global, _ := cmd.Flags().GetBool(flagPluginsGlobal)
return global
}
15 changes: 2 additions & 13 deletions ignite/config/config.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,9 @@
package config

import (
"os"

"github.com/ignite/cli/ignite/pkg/env"
"github.com/ignite/cli/ignite/pkg/xfilepath"
)

// DirPath returns the path of configuration directory of Ignite.
var DirPath = xfilepath.JoinFromHome(xfilepath.Path(".ignite"))

// CreateConfigDir creates config directory if it is not created yet.
func CreateConfigDir() error {
path, err := DirPath()
if err != nil {
return err
}

return os.MkdirAll(path, 0o755)
}
var DirPath = xfilepath.Mkdir(env.ConfigDir())
3 changes: 3 additions & 0 deletions ignite/config/plugins/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ type Plugin struct {
Path string `yaml:"path"`
// With holds arguments passed to the plugin interface
With map[string]string `yaml:"with,omitempty"`
// Global holds whether the plugin is installed globally
// (default: $HOME/.ignite/plugins/plugins.yml) or locally for a chain.
Global bool `yaml:"-"`
}

// Path return the path of the config file.
Expand Down
30 changes: 28 additions & 2 deletions ignite/pkg/env/env.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,37 @@
package env

import "os"
import (
"fmt"
"os"
"path"

"github.com/ignite/cli/ignite/pkg/xfilepath"
)

const (
debug = "IGNT_DEBUG"
debug = "IGNT_DEBUG"
configDir = "IGNT_CONFIG_DIR"
)

func DebugEnabled() bool {
return os.Getenv(debug) == "1"
}

func ConfigDir() xfilepath.PathRetriever {
return func() (string, error) {
if dir := os.Getenv(configDir); dir != "" {
if !path.IsAbs(dir) {
panic(fmt.Sprintf("%s must be an absolute path", configDir))
}
return dir, nil
}
return xfilepath.JoinFromHome(xfilepath.Path(".ignite"))()
}
}

func SetConfigDir(dir string) {
err := os.Setenv(configDir, dir)
if err != nil {
panic(fmt.Sprintf("set config dir env: %v", err))
}
}
54 changes: 32 additions & 22 deletions ignite/pkg/xfilepath/xfilepath.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,18 @@ func PathWithError(path string, err error) PathRetriever {
// The returned path retriever eventually returns the error from the first provided path retrievers
// that returns a non-nil error.
func Join(paths ...PathRetriever) PathRetriever {
var components []string
var err error
for _, path := range paths {
var component string
component, err = path()
if err != nil {
break
}
components = append(components, component)
}
path := filepath.Join(components...)

return func() (string, error) {
var components []string
var err error
for _, path := range paths {
var component string
component, err = path()
if err != nil {
break
}
components = append(components, component)
}
path := filepath.Join(components...)
return path, err
}
}
Expand All @@ -52,18 +51,29 @@ func JoinFromHome(paths ...PathRetriever) PathRetriever {
// List returns a paths retriever from a list of path retrievers.
// The returned paths retriever eventually returns the error from the first provided path retrievers that returns a non-nil error.
func List(paths ...PathRetriever) PathsRetriever {
var list []string
var err error
for _, path := range paths {
var resolved string
resolved, err = path()
if err != nil {
break
return func() ([]string, error) {
var list []string
var err error
for _, path := range paths {
var resolved string
resolved, err = path()
if err != nil {
break
}
list = append(list, resolved)
}
list = append(list, resolved)
}

return func() ([]string, error) {
return list, err
}
}

// Mkdir ensure path exists before returning it.
func Mkdir(path PathRetriever) PathRetriever {
return func() (string, error) {
p, err := path()
if err != nil {
return "", err
}
return p, os.MkdirAll(p, 0o755)
}
}
14 changes: 14 additions & 0 deletions ignite/pkg/xfilepath/xfilepath_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package xfilepath_test
import (
"errors"
"os"
"path"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/ignite/cli/ignite/pkg/xfilepath"
Expand Down Expand Up @@ -88,3 +90,15 @@ func TestList(t *testing.T) {
_, err = retriever()
require.Error(t, err)
}

func TestMkdir(t *testing.T) {
require := require.New(t)
assert := assert.New(t)
newdir := path.Join(t.TempDir(), "hey")

dir, err := xfilepath.Mkdir(xfilepath.Path(newdir))()

require.NoError(err)
assert.Equal(newdir, dir)
assert.DirExists(dir)
}
Loading

0 comments on commit 304e5d5

Please sign in to comment.