mukan-ignite/ignite/config/plugins/config.go
Mukan Erkin Törük 26b204bd04
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
feat: fork Ignite CLI v29 as Mukan Ignite — remove cosmos-sdk restrictions
2026-05-11 03:31:37 +03:00

154 lines
4 KiB
Go

package plugins
import (
"os"
"slices"
"strings"
"gopkg.in/yaml.v3"
"github.com/ignite/cli/v29/ignite/pkg/errors"
"github.com/ignite/cli/v29/ignite/pkg/gomodule"
"github.com/ignite/cli/v29/ignite/pkg/xfilepath"
)
type Config struct {
path string
// Apps holds the list of installed Ignite Apps.
// Ignite Apps are implemented as plugins.
Apps []Plugin `yaml:"apps"`
}
// 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,omitempty"`
// Global holds whether the plugin is installed globally
// (default: $HOME/.ignite/apps/igniteapps.yml) or locally for a chain.
Global bool `yaml:"-"`
}
// RemoveDuplicates takes a list of Plugins and returns a new list with only unique values.
// Local plugins take precedence over global plugins if duplicate paths exist.
// Duplicates are compared regardless of version.
func RemoveDuplicates(plugins []Plugin) (unique []Plugin) {
// struct to track plugin configs
type check struct {
hasPath bool
global bool
prevIndex int
}
keys := make(map[string]check)
for i, plugin := range plugins {
c := keys[plugin.CanonicalPath()]
if !c.hasPath {
keys[plugin.CanonicalPath()] = check{
hasPath: true,
global: plugin.Global,
prevIndex: i,
}
unique = append(unique, plugin)
} else if c.hasPath && !plugin.Global && c.global { // overwrite global plugin if local duplicate exists
unique[c.prevIndex] = plugin
}
}
return unique
}
// IsGlobal returns whether the plugin is installed globally or locally for a chain.
func (p Plugin) IsGlobal() bool {
return p.Global
}
// IsLocalPath returns true if the plugin path is a local directory.
func (p Plugin) IsLocalPath() bool {
return xfilepath.IsDir(p.Path)
}
// HasPath verifies if a plugin has the given path regardless of version.
// For example github.com/foo/bar@v1 and github.com/foo/bar@v2 have the
// same path so "true" will be returned.
func (p Plugin) HasPath(path string) bool {
if path == "" {
return false
}
if p.Path == path {
return true
}
pluginPath := p.CanonicalPath()
path = strings.Split(path, "@")[0]
return pluginPath == path
}
// CanonicalPath returns the canonical path of a plugin (excludes version ref).
func (p Plugin) CanonicalPath() string {
return strings.Split(p.Path, "@")[0]
}
// Path return the path of the config file.
func (c Config) Path() string {
return c.path
}
// Save persists a config yaml to a specified path on disk.
// Must be writable.
func (c *Config) Save() error {
errf := func(err error) error {
return errors.Errorf("plugin config save: %w", err)
}
if c.path == "" {
return errf(errors.New("empty path"))
}
file, err := os.Create(c.path)
if err != nil {
return errf(err)
}
defer file.Close()
if err := yaml.NewEncoder(file).Encode(c); err != nil {
return errf(err)
}
return nil
}
// HasPlugin returns true if c contains a plugin with given path.
// Returns also true if there's a local plugin with the module name equal to
// that path.
func (c Config) HasPlugin(path string) bool {
return slices.ContainsFunc(c.Apps, func(cp Plugin) bool {
if cp.HasPath(path) {
return true
}
if cp.IsLocalPath() {
// check local plugin go.mod to see if module name match plugin path
gm, err := gomodule.ParseAt(cp.Path)
if err != nil {
// Skip if we can't parse gomod
return false
}
return Plugin{Path: gm.Module.Mod.Path}.HasPath(path)
}
return false
})
}