Skip to content

Commit

Permalink
feat: separate plugins.yml file (#3184)
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

* address review

* testdata

Co-authored-by: Jerónimo Albi <[email protected]>
  • Loading branch information
Alex Johnson and jeronimoalbi authored Nov 30, 2022
1 parent 1d2517f commit 1317723
Show file tree
Hide file tree
Showing 16 changed files with 267 additions and 69 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

- [#3184](https://github.com/ignite/cli/pull/3184) Separate `plugins.yml` config file.
- [#3038](https://github.com/ignite/cli/pull/3038) Addition of Plugin Hooks in Plugin System
- [#3056](https://github.com/ignite/cli/pull/3056) Add `--genesis-config` flag option to `ignite network chain publish`
- [#2892](https://github.com/ignite/cli/pull/2982/) Add `ignite scaffold react` command.
Expand Down
15 changes: 14 additions & 1 deletion ignite/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const (
flagYes = "yes"
flagClearCache = "clear-cache"
flagSkipProto = "skip-proto"
flagPlugins = "plugins"

checkVersionTimeout = time.Millisecond * 600
cacheFileName = "ignite_cache.db"
Expand Down Expand Up @@ -81,6 +82,7 @@ To get started, create a blockchain:
c.AddCommand(NewVersion())
c.AddCommand(NewPlugin())
c.AddCommand(deprecated()...)
c.PersistentFlags().AddFlagSet(flagSetPlugins())

return c
}
Expand Down Expand Up @@ -121,7 +123,7 @@ func getHome(cmd *cobra.Command) (home string) {

func flagSetConfig() *flag.FlagSet {
fs := flag.NewFlagSet("", flag.ContinueOnError)
fs.StringP(flagConfig, "c", "", "Ignite config file (default: ./config.yml)")
fs.StringP(flagConfig, "c", "", "path to Ignite config file (default: ./config.yml)")
return fs
}

Expand All @@ -130,6 +132,17 @@ func getConfig(cmd *cobra.Command) (config string) {
return
}

func flagSetPlugins() *flag.FlagSet {
fs := flag.NewFlagSet("", flag.ContinueOnError)
fs.StringP(flagPlugins, "x", "", "path to Ignite plugins config file (default: ./plugins.yml)")
return fs
}

func getPlugins(cmd *cobra.Command) (config string) {
config, _ = cmd.Flags().GetString(flagPlugins)
return
}

func flagSetYes() *flag.FlagSet {
fs := flag.NewFlagSet("", flag.ContinueOnError)
fs.BoolP(flagYes, "y", false, "answers interactive yes/no questions with yes")
Expand Down
25 changes: 21 additions & 4 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"

pluginsconfig "github.com/ignite/cli/ignite/config/plugins"
"github.com/ignite/cli/ignite/pkg/cliui"
"github.com/ignite/cli/ignite/pkg/xgit"
"github.com/ignite/cli/ignite/services/plugin"
Expand All @@ -26,19 +27,35 @@ const (
// LoadPlugins tries to load all the plugins found in configuration.
// 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)
cfg, err := parseLocalPlugins(rootCmd)
if err != nil {
// Binary is run outside of an chain app, plugins can't be loaded
// if binary is run where there is no plugins.yml, don't load
return nil
}
plugins, err = plugin.Load(ctx, chain)

// TODO: parse global config

plugins, err = plugin.Load(ctx, cfg)
if err != nil {
return err
}
return loadPlugins(rootCmd, plugins)
}

func parseLocalPlugins(rootCmd *cobra.Command) (cfg *pluginsconfig.Config, err error) {
appPath := flagGetPath(rootCmd)
pluginsPath := getPlugins(rootCmd)
if pluginsPath == "" {
if pluginsPath, err = pluginsconfig.LocateDefault(appPath); err != nil {
return cfg, err
}
}

cfg, err = pluginsconfig.ParseFile(pluginsPath)

return cfg, err
}

func loadPlugins(rootCmd *cobra.Command, plugins []*plugin.Plugin) error {
// Link plugins to related commands
var loadErrors []string
Expand Down
6 changes: 3 additions & 3 deletions ignite/cmd/plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"

"github.com/ignite/cli/ignite/config"
pluginsconfig "github.com/ignite/cli/ignite/config/plugins"
"github.com/ignite/cli/ignite/services/plugin"
"github.com/ignite/cli/ignite/services/plugin/mocks"
)
Expand Down Expand Up @@ -308,7 +308,7 @@ ignite
assert := assert.New(t)
pi := mocks.NewPluginInterface(t)
p := &plugin.Plugin{
Plugin: config.Plugin{
Plugin: pluginsconfig.Plugin{
Path: "foo",
With: pluginParams,
},
Expand Down Expand Up @@ -537,7 +537,7 @@ func TestLinkPluginHooks(t *testing.T) {
// assert := assert.New(t)
pi := mocks.NewPluginInterface(t)
p := &plugin.Plugin{
Plugin: config.Plugin{
Plugin: pluginsconfig.Plugin{
Path: "foo",
With: pluginParams,
},
Expand Down
24 changes: 0 additions & 24 deletions ignite/config/chain/v1/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,30 +23,6 @@ type Config struct {
base.Config `yaml:",inline"`

Validators []Validator `yaml:"validators"`
Plugins []Plugin `yaml:"plugins,omitempty"`
}

// Plugin keeps plugin name and location.
type Plugin struct {
// Path holds the location of the plugin.
// A path can be local, in that case it must start with a `/`.
// A remote path on the other hand, is an URL to a public remote git
// repository. For example:
//
// path: github.com/foo/bar
//
// It can contain a path inside that repository, if for instance the repo
// contains multiple plugins, For example:
//
// path: github.com/foo/bar/plugin1
//
// It can also specify a tag or a branch, by adding a `@` and the branch/tag
// name at the end of the path. For example:
//
// path: github.com/foo/bar/plugin1@v42
Path string `yaml:"path"`
// With holds arguments passed to the plugin interface
With map[string]string `yaml:"with"`
}

func (c *Config) SetDefaults() error {
Expand Down
9 changes: 0 additions & 9 deletions ignite/config/chain/v1/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,6 @@ func TestConfigDecode(t *testing.T) {
},
},
}},
Plugins: []v1.Plugin{
{
Path: "/path/to/plugin1",
},
{
Path: "/path/to/plugin2",
With: map[string]string{"foo": "bar", "bar": "baz"},
},
},
}
assert.Equal(expected, cfg)
}
Expand Down
76 changes: 76 additions & 0 deletions ignite/config/plugins/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package plugins

import (
"io"
"os"
"path/filepath"

"github.com/imdario/mergo"
"gopkg.in/yaml.v2"
)

// PluginsConfigFilenames is a list of recognized names as Ignite's plugins config file.
var PluginsConfigFilenames = []string{"plugins.yml", "plugins.yaml"}

// DefaultConfig returns a config with default values.
func DefaultConfig() *Config {
c := Config{}
return &c
}

// LocateDefault locates the default path for the config file.
// Returns ErrConfigNotFound when no config file found.
func LocateDefault(root string) (path string, err error) {
for _, name := range PluginsConfigFilenames {
path = filepath.Join(root, name)
if _, err := os.Stat(path); err == nil {
return path, nil
} else if !os.IsNotExist(err) {
return "", err
}
}

return "", ErrConfigNotFound
}

type Config struct {
Plugins []Plugin `yaml:"plugins,omitempty"`
}

// Plugin keeps plugin name and location.
type Plugin struct {
// Path holds the location of the plugin.
// A path can be local, in that case it must start with a `/`.
// A remote path on the other hand, is an URL to a public remote git
// repository. For example:
//
// path: github.com/foo/bar
//
// It can contain a path inside that repository, if for instance the repo
// contains multiple plugins, For example:
//
// path: github.com/foo/bar/plugin1
//
// It can also specify a tag or a branch, by adding a `@` and the branch/tag
// name at the end of the path. For example:
//
// path: github.com/foo/bar/plugin1@v42
Path string `yaml:"path"`
// With holds arguments passed to the plugin interface
With map[string]string `yaml:"with"`
}

// Clone returns an identical copy of the instance
func (c *Config) Clone() (*Config, error) {
copy := Config{}
if err := mergo.Merge(&copy, c, mergo.WithAppendSlice); err != nil {
return nil, err
}

return &copy, nil
}

// Decode decodes the config file values from YAML.
func (c *Config) Decode(r io.Reader) error {
return yaml.NewDecoder(r).Decode(c)
}
36 changes: 36 additions & 0 deletions ignite/config/plugins/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package plugins_test

import (
"os"
"testing"

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

"github.com/ignite/cli/ignite/config/plugins"
)

func TestConfigDecode(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
f, err := os.Open("testdata/plugins.yml")
require.NoError(err)
defer f.Close()
var cfg plugins.Config

err = cfg.Decode(f)

require.NoError(err)
expected := plugins.Config{
Plugins: []plugins.Plugin{
{
Path: "/path/to/plugin1",
},
{
Path: "/path/to/plugin2",
With: map[string]string{"foo": "bar", "bar": "baz"},
},
},
}
assert.Equal(expected, cfg)
}
6 changes: 6 additions & 0 deletions ignite/config/plugins/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package plugins

import "errors"

// ErrConfigNotFound indicates that the plugins.yml can't be found.
var ErrConfigNotFound = errors.New("could not locate a plugins.yml")
33 changes: 33 additions & 0 deletions ignite/config/plugins/parse.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package plugins

import (
"io"
"os"

"gopkg.in/yaml.v2"
)

// ParseFile parses a plugins config.
func ParseFile(path string) (*Config, error) {
file, err := os.Open(path)
if err != nil {
return DefaultConfig(), err
}

defer file.Close()

return Parse(file)
}

// Parse reads a config file for ignite binary plugins
func Parse(configFile io.Reader) (*Config, error) {
return parse(configFile)
}

func parse(configFile io.Reader) (*Config, error) {
var c Config

err := yaml.NewDecoder(configFile).Decode(&c)

return &c, err
}
25 changes: 25 additions & 0 deletions ignite/config/plugins/parse_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package plugins_test

import (
"bytes"
"testing"

"github.com/stretchr/testify/require"

pluginsconfig "github.com/ignite/cli/ignite/config/plugins"
"github.com/ignite/cli/ignite/config/plugins/testdata"
)

func TestParse(t *testing.T) {
// Arrange: Initialize a reader with the previous version
r := bytes.NewReader(testdata.ConfigYAML)

// Act
cfg, err := pluginsconfig.Parse(r)

// Assert
require.NoError(t, err)

// Assert: Parse must return the latest version
require.Equal(t, testdata.GetConfig(t), cfg)
}
8 changes: 8 additions & 0 deletions ignite/config/plugins/testdata/plugins.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
plugins:
- name: plugin1
path: /path/to/plugin1
- name: plugin2
path: /path/to/plugin2
with:
foo: bar
bar: baz
24 changes: 24 additions & 0 deletions ignite/config/plugins/testdata/testdata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package testdata

import (
"bytes"
_ "embed"
"testing"

"github.com/stretchr/testify/require"
"gopkg.in/yaml.v2"

pluginsconfig "github.com/ignite/cli/ignite/config/plugins"
)

//go:embed plugins.yml
var ConfigYAML []byte

func GetConfig(t *testing.T) *pluginsconfig.Config {
c := &pluginsconfig.Config{}

err := yaml.NewDecoder(bytes.NewReader(ConfigYAML)).Decode(c)
require.NoError(t, err)

return c
}
6 changes: 0 additions & 6 deletions ignite/config/pluginsconfig.go

This file was deleted.

Loading

0 comments on commit 1317723

Please sign in to comment.