From f2d9a5f2b0b41c15de1bbf2cf051534d2c25bf62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jer=C3=B3nimo=20Albi?= Date: Tue, 26 Sep 2023 09:45:46 +0200 Subject: [PATCH 1/9] docs: change docs to use "apps" instead of "plugins" (#3651) * docs: change docs to use "apps" instead of "plugins" * chore: correct plugin reference * chore: change app examples to use `app` as the type name * chore: restore apps config name references back to `plugins.yml` --- docs/docs/apps/01-using-apps.md | 42 +++++ .../02-developing-apps.md} | 146 +++++++++--------- docs/docs/{plugins => apps}/_category_.json | 2 +- docs/docs/plugins/01-using-plugins.md | 43 ------ 4 files changed, 118 insertions(+), 115 deletions(-) create mode 100644 docs/docs/apps/01-using-apps.md rename docs/docs/{plugins/02-dev-plugins.md => apps/02-developing-apps.md} (52%) rename docs/docs/{plugins => apps}/_category_.json (58%) delete mode 100644 docs/docs/plugins/01-using-plugins.md diff --git a/docs/docs/apps/01-using-apps.md b/docs/docs/apps/01-using-apps.md new file mode 100644 index 0000000000..1741496952 --- /dev/null +++ b/docs/docs/apps/01-using-apps.md @@ -0,0 +1,42 @@ +--- +description: Using and Developing Ignite Apps +--- + +# Using Ignite Apps + +Apps offer a way to extend the functionality of the Ignite CLI. There are two +core concepts within apps: `Commands` and `Hooks`. `Commands` extend the CLI's +functionality and `Hooks` extend existing CLI command functionality. + +Apps are registered in an Ignite scaffolded blockchain project through the +`plugins.yml`, or globally through `$HOME/.ignite/plugins/plugins.yml`. + +To use an app within your project execute the following command inside the +project directory: + +```sh +ignite app install github.com/project/cli-app +``` + +The app will be available only when running `ignite` inside the project +directory. + +To use an app globally on the other hand, execute the following command: + +```sh +ignite app install -g github.com/project/cli-app +``` + +The command will compile the app and make it immediately available to the +`ignite` command lists. + +## Listing installed apps + +When in an ignite scaffolded blockchain you can use the command `ignite app +list` to list all Ignite Apps and there statuses. + +## Updating apps + +When an app in a remote repository releases updates, running `ignite app +update ` will update an specific app declared in your +project's `config.yml`. diff --git a/docs/docs/plugins/02-dev-plugins.md b/docs/docs/apps/02-developing-apps.md similarity index 52% rename from docs/docs/plugins/02-dev-plugins.md rename to docs/docs/apps/02-developing-apps.md index 6cbc1b6426..1f5a13a508 100644 --- a/docs/docs/plugins/02-dev-plugins.md +++ b/docs/docs/apps/02-developing-apps.md @@ -1,50 +1,51 @@ --- -description: Using and Developing plugins +description: Using and Developing Ignite Apps --- -# Developing Plugins +# Developing Ignite Apps -It's easy to create a plugin and use it immediately in your project. First -choose a directory outside your project and run : +It's easy to create an app and use it immediately in your project. First +choose a directory outside your project and run: ```sh -$ ignite plugin scaffold my-plugin +$ ignite app scaffold my-app ``` -This will create a new directory `my-plugin` that contains the plugin's code, -and will output some instructions about how to use your plugin with the -`ignite` command. Indeed, a plugin path can be a local directory, which has -several benefits: +This will create a new directory `my-app` that contains the app's code +and will output some instructions about how to use your app with the +`ignite` command. An app path can be a local directory which has several +benefits: -- you don't need to use a git repository during the development of your plugin. -- the plugin is recompiled each time you run the `ignite` binary in your -project, if the source files are older than the plugin binary. +- You don't need to use a Git repository during the development of your app. +- The app is recompiled each time you run the `ignite` binary in your + project if the source files are older than the app binary. -Thus, the plugin development workflow is as simple as : +Thus, app development workflow is as simple as: -1. scaffold a plugin with `ignite plugin scaffold my-plugin` -2. add it to your config via `ignite plugin add -g /path/to/my-plugin` -3. update plugin code -4. run `ignite my-plugin` binary to compile and run the plugin. -5. go back to 3. +1. Scaffold an app with `ignite app scaffold my-app` +2. Add it to your config via `ignite app install -g /path/to/my-app` +3. Update app code +4. Run `ignite my-app` binary to compile and run the app +5. Go back to 3 -Once your plugin is ready, you can publish it to a git repository, and the -community can use it by calling `ignite plugin add github.com/foo/my-plugin`. +Once your app is ready you can publish it to a Git repository and the +community can use it by calling `ignite app install github.com/foo/my-app`. -Now let's detail how to update your plugin's code. +Now let's detail how to update your app's code. -## The plugin interface +## App interface -The `ignite` plugin system uses `github.com/hashicorp/go-plugin` under the hood, -which implies to implement a predefined interface: +Under the hood Ignite Apps are implemented using a plugin system based on +`github.com/hashicorp/go-plugin`. + +All apps must implement a predefined interface: ```go title=ignite/services/plugin/interface.go -// An ignite plugin must implements the Plugin interface. type Interface interface { - // Manifest declares the plugin's Command(s) and Hook(s). + // Manifest declares app's Command(s) and Hook(s). Manifest() (Manifest, error) - // Execute will be invoked by ignite when a plugin Command is executed. + // Execute will be invoked by ignite when an app Command is executed. // It is global for all commands declared in Manifest, if you have declared // multiple commands, use cmd.Path to distinguish them. Execute(cmd ExecutedCommand) error @@ -70,66 +71,68 @@ type Interface interface { } ``` -The code scaffolded already implements this interface, you just need to update -the methods' body. - +The scaffolded code already implements this interface, you just need to update +the method's body. -## Defining plugin's manifest +## Defining app's manifest -Here is the `Manifest` struct : +Here is the `Manifest` struct: ```go title=ignite/services/plugin/interface.go type Manifest struct { Name string + // Commands contains the commands that will be added to the list of ignite // commands. Each commands are independent, for nested commands use the // inner Commands field. Commands []Command + // Hooks contains the hooks that will be attached to the existing ignite // commands. Hooks []Hook - // SharedHost enables sharing a single plugin server across all running instances - // of a plugin. Useful if a plugin adds or extends long running commands + + // SharedHost enables sharing a single app server across all running instances + // of an app. Useful if an app adds or extends long running commands // - // Example: if a plugin defines a hook on `ignite chain serve`, a plugin server is instanciated + // Example: if an app defines a hook on `ignite chain serve`, a server is instanciated // when the command is run. Now if you want to interact with that instance from commands - // defined in that plugin, you need to enable `SharedHost`, or else the commands will just - // instantiate separate plugin servers. + // defined in that app, you need to enable `SharedHost`, or else the commands will just + // instantiate separate app servers. // - // When enabled, all plugins of the same `Path` loaded from the same configuration will - // attach it's rpc client to a an existing rpc server. + // When enabled, all apps of the same `Path` loaded from the same configuration will + // attach it's gRPC client to a an existing gRPC server. // - // If a plugin instance has no other running plugin servers, it will create one and it will be the host. + // If an app instance has no other running app servers, it will create one and it + // will be the host. SharedHost bool `yaml:"shared_host"` } ``` -In your plugin's code, the `Manifest` method already returns a predefined -`Manifest` struct as an example. Adapt it according to your need. +In your app's code the `Manifest` method already returns a predefined +`Manifest` struct as an example. You must adapt it according to your need. -If your plugin adds one or more new commands to `ignite`, feeds the `Commands` -field. +If your app adds one or more new commands to `ignite`, add them to the +`Commands` field. -If your plugin adds features to existing commands, feeds the `Hooks` field. +If your app adds features to existing commands, add them to the `Hooks` field. -Of course a plugin can declare `Commands` *and* `Hooks`. +Of course an app can declare both, `Commands` *and* `Hooks`. -A plugin may also share a host process by setting `SharedHost` to `true`. -`SharedHost` is desirable if a plugin hooks into, or declares long running commands. -Commands executed from the same plugin context interact with the same plugin server. +An app may also share a host process by setting `SharedHost` to `true`. +`SharedHost` is desirable if an app hooks into, or declares long running commands. +Commands executed from the same app context interact with the same app server. Allowing all executing commands to share the same server instance, giving shared execution context. -## Adding new command +## Adding new commands -Plugin commands are custom commands added to the ignite cli by a registered -plugin. Commands can be of any path not defined already by ignite. All plugin -commands will extend of the command root `ignite`. +App commands are custom commands added to Ignite CLI by an installed app. +Commands can use any path not defined already by the CLI. -For instance, let's say your plugin adds a new `oracle` command to `ignite -scaffold`, the `Manifest()` method will look like : +For instance, let's say your app adds a new `oracle` command to `ignite +scaffold`, then the `Manifest` method will look like : ```go -func (p) Manifest() (plugin.Manifest, error) { +func (app) Manifest() (plugin.Manifest, error) { return plugin.Manifest{ Name: "oracle", Commands: []plugin.Command{ @@ -149,11 +152,11 @@ func (p) Manifest() (plugin.Manifest, error) { } ``` -To update the plugin execution, you have to change the plugin `Execute` command, -for instance : +To update the app execution, you have to change the `Execute` command. For +example: ```go -func (p) Execute(cmd plugin.ExecutedCommand) error { +func (app) Execute(cmd plugin.ExecutedCommand) error { if len(cmd.Args) == 0 { return fmt.Errorf("oracle name missing") } @@ -171,23 +174,24 @@ func (p) Execute(cmd plugin.ExecutedCommand) error { } ``` -Then, run `ignite scaffold oracle` to execute the plugin. +Then, run `ignite scaffold oracle` to execute the app. ## Adding hooks -Plugin `Hooks` allow existing ignite commands to be extended with new +App `Hooks` allow existing CLI commands to be extended with new functionality. Hooks are useful when you want to streamline functionality without needing to run custom scripts after or before a command has been run. -this can streamline processes that where once error prone or forgotten all +This can streamline processes that where once error prone or forgotten all together. -The following are hooks defined which will run on a registered `ignite` commands +The following are hooks defined which will run on a registered `ignite` +command: | Name | Description | | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | Pre | Runs before a commands main functionality is invoked in the `PreRun` scope | | Post | Runs after a commands main functionality is invoked in the `PostRun` scope | -| Clean Up | Runs after a commands main functionality is invoked. if the command returns an error it will run before the error is returned to guarantee execution. | +| Clean Up | Runs after a commands main functionality is invoked. If the command returns an error it will run before the error is returned to guarantee execution. | *Note*: If a hook causes an error in the pre step the command will not run resulting in `post` and `clean up` not executing. @@ -195,7 +199,7 @@ resulting in `post` and `clean up` not executing. The following is an example of a `hook` definition. ```go -func (p) Manifest() (plugin.Manifest, error) { +func (app) Manifest() (plugin.Manifest, error) { return plugin.Manifest{ Name: "oracle", Hooks: []plugin.Hook{ @@ -207,7 +211,7 @@ func (p) Manifest() (plugin.Manifest, error) { }, nil } -func (p) ExecuteHookPre(hook plugin.ExecutedHook) error { +func (app) ExecuteHookPre(hook plugin.ExecutedHook) error { switch hook.Name { case "my-hook": fmt.Println("I'm executed before ignite chain build") @@ -217,7 +221,7 @@ func (p) ExecuteHookPre(hook plugin.ExecutedHook) error { return nil } -func (p) ExecuteHookPost(hook plugin.ExecutedHook) error { +func (app) ExecuteHookPost(hook plugin.ExecutedHook) error { switch hook.Name { case "my-hook": fmt.Println("I'm executed after ignite chain build (if no error)") @@ -227,7 +231,7 @@ func (p) ExecuteHookPost(hook plugin.ExecutedHook) error { return nil } -func (p) ExecuteHookCleanUp(hook plugin.ExecutedHook) error { +func (app) ExecuteHookCleanUp(hook plugin.ExecutedHook) error { switch hook.Name { case "my-hook": fmt.Println("I'm executed after ignite chain build (regardless errors)") @@ -238,7 +242,7 @@ func (p) ExecuteHookCleanUp(hook plugin.ExecutedHook) error { } ``` -Above we can see a similar definition to `Command` where a hook has a `Name` and -a `PlaceHookOn`. You'll notice that the `Execute*` methods map directly to each -life cycle of the hook. All hooks defined within the plugin will invoke these +Above we can see a similar definition to `Command` where a hook has a `Name` +and a `PlaceHookOn`. You'll notice that the `Execute*` methods map directly to +each life cycle of the hook. All hooks defined within the app will invoke these methods. diff --git a/docs/docs/plugins/_category_.json b/docs/docs/apps/_category_.json similarity index 58% rename from docs/docs/plugins/_category_.json rename to docs/docs/apps/_category_.json index 27114924ce..5ce59790e7 100644 --- a/docs/docs/plugins/_category_.json +++ b/docs/docs/apps/_category_.json @@ -1,5 +1,5 @@ { - "label": "Plugins", + "label": "Ignite Apps", "position": 7, "link": null } diff --git a/docs/docs/plugins/01-using-plugins.md b/docs/docs/plugins/01-using-plugins.md deleted file mode 100644 index 9b830a2ef6..0000000000 --- a/docs/docs/plugins/01-using-plugins.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -description: Using and Developing plugins ---- - -# Using Plugins - -Ignite plugins offer a way to extend the functionality of the Ignite CLI. There -are two core concepts within plugins : `Commands` and `Hooks`. Where `Commands` -extend the cli's functionality, and `Hooks` extend existing command -functionality. - -Plugins are registered in an Ignite scaffolded Blockchain project through the -`plugins.yml`, or globally through `$HOME/.ignite/plugins/plugins.yml`. - -To use a plugin within your project, execute the following command inside the -project directory: - -```sh -ignite plugin add github.com/project/cli-plugin -``` - -The plugin will be available only when running `ignite` inside the project -directory. - -To use a plugin globally on the other hand, execute the following command: - -```sh -ignite plugin add -g github.com/project/cli-plugin -``` - -The command will compile the plugin and make it immediately available to the -`ignite` command lists. - -## Listing installed plugins - -When in an ignite scaffolded blockchain you can use the command `ignite plugin -list` to list all plugins and there statuses. - -## Updating plugins - -When a plugin in a remote repository releases updates, running `ignite plugin -update ` will update a specific plugin declared in your -project's `config.yml`. From b0f75fa6e72969a8130d7107503a17a99aaac645 Mon Sep 17 00:00:00 2001 From: Ehsan-saradar Date: Wed, 27 Sep 2023 00:43:18 +0300 Subject: [PATCH 2/9] feat: rename Marshaler to Codec (#3658) * Rename Marshaler to Codec * Upgrade golangci to latest version * Update changelog * Rename Marshaler field in other places * Revert "Upgrade golangci to latest version" This reverts commit 7e4f23a6596268f0adb8ba41a98a104a86e43b6e. --------- Co-authored-by: Danilo Pantani --- changelog.md | 1 + ignite/templates/app/files/app/encoding.go.plush | 6 +++--- ignite/templates/app/files/app/params/encoding.go.plush | 2 +- .../app/files/cmd/{{binaryNamePrefix}}d/cmd/root.go.plush | 2 +- .../testutil/files/testutil/network/network.go.plush | 4 ++-- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/changelog.md b/changelog.md index 3d4c238c88..d7039492ae 100644 --- a/changelog.md +++ b/changelog.md @@ -14,6 +14,7 @@ - [#3559](https://github.com/ignite/cli/pull/3559) Bump network plugin version to `v0.1.1` - [#3522](https://github.com/ignite/cli/pull/3522) Remove indentation from `chain serve` output - [#3601](https://github.com/ignite/cli/pull/3601) Update ts-relayer version to `0.10.0` +- [#3658](https://github.com/ignite/cli/pull/3658) Rename Marshaler to Codec in EncodingConfig - [#3653](https://github.com/ignite/cli/pull/3653) Add "app" extension to plugin binaries - [#3656](https://github.com/ignite/cli/pull/3656) Disable Go toolchain download diff --git a/ignite/templates/app/files/app/encoding.go.plush b/ignite/templates/app/files/app/encoding.go.plush index a376774363..ab4dc16f89 100644 --- a/ignite/templates/app/files/app/encoding.go.plush +++ b/ignite/templates/app/files/app/encoding.go.plush @@ -13,12 +13,12 @@ import ( func makeEncodingConfig() params.EncodingConfig { amino := codec.NewLegacyAmino() interfaceRegistry := types.NewInterfaceRegistry() - marshaler := codec.NewProtoCodec(interfaceRegistry) - txCfg := tx.NewTxConfig(marshaler, tx.DefaultSignModes) + cdc := codec.NewProtoCodec(interfaceRegistry) + txCfg := tx.NewTxConfig(cdc, tx.DefaultSignModes) return params.EncodingConfig{ InterfaceRegistry: interfaceRegistry, - Marshaler: marshaler, + Codec: cdc, TxConfig: txCfg, Amino: amino, } diff --git a/ignite/templates/app/files/app/params/encoding.go.plush b/ignite/templates/app/files/app/params/encoding.go.plush index 3d634abf16..8ff9ea04b3 100644 --- a/ignite/templates/app/files/app/params/encoding.go.plush +++ b/ignite/templates/app/files/app/params/encoding.go.plush @@ -10,7 +10,7 @@ import ( // This is provided for compatibility between protobuf and amino implementations. type EncodingConfig struct { InterfaceRegistry types.InterfaceRegistry - Marshaler codec.Codec + Codec codec.Codec TxConfig client.TxConfig Amino *codec.LegacyAmino } diff --git a/ignite/templates/app/files/cmd/{{binaryNamePrefix}}d/cmd/root.go.plush b/ignite/templates/app/files/cmd/{{binaryNamePrefix}}d/cmd/root.go.plush index c8183e85c9..8e4c0b9308 100644 --- a/ignite/templates/app/files/cmd/{{binaryNamePrefix}}d/cmd/root.go.plush +++ b/ignite/templates/app/files/cmd/{{binaryNamePrefix}}d/cmd/root.go.plush @@ -38,7 +38,7 @@ import ( func NewRootCmd() (*cobra.Command, appparams.EncodingConfig) { encodingConfig := app.MakeEncodingConfig() initClientCtx := client.Context{}. - WithCodec(encodingConfig.Marshaler). + WithCodec(encodingConfig.Codec). WithInterfaceRegistry(encodingConfig.InterfaceRegistry). WithTxConfig(encodingConfig.TxConfig). WithLegacyAmino(encodingConfig.Amino). diff --git a/ignite/templates/testutil/files/testutil/network/network.go.plush b/ignite/templates/testutil/files/testutil/network/network.go.plush index 1307ebbda4..2536d92ca6 100644 --- a/ignite/templates/testutil/files/testutil/network/network.go.plush +++ b/ignite/templates/testutil/files/testutil/network/network.go.plush @@ -54,7 +54,7 @@ func DefaultConfig() network.Config { chainID = "chain-" + tmrand.NewRand().Str(6) ) return network.Config{ - Codec: encoding.Marshaler, + Codec: encoding.Codec, TxConfig: encoding.TxConfig, LegacyAmino: encoding.Amino, InterfaceRegistry: encoding.InterfaceRegistry, @@ -71,7 +71,7 @@ func DefaultConfig() network.Config { baseapp.SetChainID(chainID), ) }, - GenesisState: app.ModuleBasics.DefaultGenesis(encoding.Marshaler), + GenesisState: app.ModuleBasics.DefaultGenesis(encoding.Codec), TimeoutCommit: 2 * time.Second, ChainID: chainID, NumValidators: 1, From f2108290c0014a043c9f08f4f4e9c461697d4498 Mon Sep 17 00:00:00 2001 From: Ehsan-saradar Date: Wed, 27 Sep 2023 10:46:16 +0300 Subject: [PATCH 3/9] Use go list cmd to find ignite cli package (#3666) Co-authored-by: Danilo Pantani --- go.sum | 2 -- ignite/pkg/gocmd/gocmd.go | 19 +++++++++++++++++++ ignite/pkg/gocmd/gocmd_test.go | 13 +++++++++++++ integration/env.go | 9 ++++++--- 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/go.sum b/go.sum index 9d3d5e374a..6baf08233d 100644 --- a/go.sum +++ b/go.sum @@ -1354,8 +1354,6 @@ golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= -golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/ignite/pkg/gocmd/gocmd.go b/ignite/pkg/gocmd/gocmd.go index 657d0490f9..f9a99c7330 100644 --- a/ignite/pkg/gocmd/gocmd.go +++ b/ignite/pkg/gocmd/gocmd.go @@ -39,6 +39,9 @@ const ( // CommandEnv represents go "env" command. CommandEnv = "env" + // CommandList represents go "list" command. + CommandList = "list" + // EnvGOARCH represents GOARCH variable. EnvGOARCH = "GOARCH" // EnvGOMOD represents GOMOD variable. @@ -168,6 +171,22 @@ func Get(ctx context.Context, path string, pkgs []string, options ...exec.Option return exec.Exec(ctx, command, append(options, exec.StepOption(step.Workdir(path)))...) } +// List returns the list of packages in path. +func List(ctx context.Context, path string, flags []string, options ...exec.Option) ([]string, error) { + command := []string{ + Name(), + CommandList, + } + command = append(command, flags...) + var b bytes.Buffer + err := exec.Exec(ctx, command, + append(options, exec.StepOption(step.Workdir(path)), exec.StepOption(step.Stdout(&b)))...) + if err != nil { + return nil, err + } + return strings.Fields(b.String()), nil +} + // Ldflags returns a combined ldflags set from flags. func Ldflags(flags ...string) string { return strings.Join(flags, " ") diff --git a/ignite/pkg/gocmd/gocmd_test.go b/ignite/pkg/gocmd/gocmd_test.go index 1f13e41f90..e1baae53a9 100644 --- a/ignite/pkg/gocmd/gocmd_test.go +++ b/ignite/pkg/gocmd/gocmd_test.go @@ -1,7 +1,9 @@ package gocmd_test import ( + "context" "errors" + "os" "testing" "github.com/stretchr/testify/assert" @@ -16,3 +18,14 @@ func TestIsInstallError(t *testing.T) { go get github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2`) assert.True(t, gocmd.IsInstallError(err)) } + +func TestList(t *testing.T) { + wd, err := os.Getwd() + assert.NoError(t, err) + + ctx := context.Background() + packages, err := gocmd.List(ctx, wd, []string{"-m", "-f={{.Path}}", "github.com/ignite/cli"}) + assert.NoError(t, err) + + assert.Contains(t, packages, "github.com/ignite/cli") +} diff --git a/integration/env.go b/integration/env.go index 95b241bdfa..394a818464 100644 --- a/integration/env.go +++ b/integration/env.go @@ -20,7 +20,6 @@ import ( "github.com/ignite/cli/ignite/pkg/cosmosfaucet" "github.com/ignite/cli/ignite/pkg/env" "github.com/ignite/cli/ignite/pkg/gocmd" - "github.com/ignite/cli/ignite/pkg/gomodulepath" "github.com/ignite/cli/ignite/pkg/httpstatuschecker" "github.com/ignite/cli/ignite/pkg/xurl" ) @@ -71,10 +70,14 @@ func compileBinary(ctx context.Context) { if err != nil { panic(fmt.Sprintf("unable to get working dir: %v", err)) } - _, appPath, err := gomodulepath.Find(wd) + pkgs, err := gocmd.List(ctx, wd, []string{"-m", "-f={{.Dir}}", "github.com/ignite/cli"}) if err != nil { - panic(fmt.Sprintf("unable to read go module path: %v", err)) + panic(fmt.Sprintf("unable to list ignite cli package: %v", err)) } + if len(pkgs) != 1 { + panic(fmt.Sprintf("expected only one package, got %d", len(pkgs))) + } + appPath := pkgs[0] var ( output, binary = filepath.Split(IgniteApp) path = path.Join(appPath, "ignite", "cmd", "ignite") From fa472206be95e1f3431ee7f1c17709da121c4159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jer=C3=B3nimo=20Albi?= Date: Wed, 27 Sep 2023 09:56:34 +0200 Subject: [PATCH 4/9] fix: find Cosmos SDK runtime app registered modules (#3661) * fix: find Cosmos SDK runtime app registered modules These modules are required to properly generate OpenAPI specs. * chore: update changelog * chore: remove empty test definition --- changelog.md | 1 + ignite/pkg/cosmosanalysis/app/app.go | 148 +++++++++++++++++- ignite/pkg/cosmosanalysis/app/app_test.go | 11 ++ .../modules/runtime_api_routes/app.go | 60 +++++++ .../modules/runtime_api_routes/go.mod | 37 +++++ ignite/pkg/goenv/goenv.go | 15 ++ ignite/pkg/goenv/goenv_test.go | 49 ++++++ 7 files changed, 318 insertions(+), 3 deletions(-) create mode 100644 ignite/pkg/cosmosanalysis/app/testdata/modules/runtime_api_routes/app.go create mode 100644 ignite/pkg/cosmosanalysis/app/testdata/modules/runtime_api_routes/go.mod create mode 100644 ignite/pkg/goenv/goenv_test.go diff --git a/changelog.md b/changelog.md index d7039492ae..0cc96bd824 100644 --- a/changelog.md +++ b/changelog.md @@ -25,6 +25,7 @@ - [#3610](https://github.com/ignite/cli/pull/3610) Fix overflow issue of cosmos faucet in `pkg/cosmosfaucet/transfer.go` and `pkg/cosmosfaucet/cosmosfaucet.go` - [#3618](https://github.com/ignite/cli/pull/3618) Fix TS client generation import path issue - [#3631](https://github.com/ignite/cli/pull/3631) Fix unnecessary vue import in hooks/composables template +- [#3661](https://github.com/ignite/cli/pull/3661) Change `pkg/cosmosanalysis` to find Cosmos SDK runtime app registered modules ## [`v0.27.0`](https://github.com/ignite/cli/releases/tag/v0.27.0) diff --git a/ignite/pkg/cosmosanalysis/app/app.go b/ignite/pkg/cosmosanalysis/app/app.go index 2a4bebdf43..b240fe86da 100644 --- a/ignite/pkg/cosmosanalysis/app/app.go +++ b/ignite/pkg/cosmosanalysis/app/app.go @@ -7,18 +7,23 @@ import ( "go/format" "go/parser" "go/token" + "os" "path/filepath" "github.com/pkg/errors" "github.com/ignite/cli/ignite/pkg/cosmosanalysis" + "github.com/ignite/cli/ignite/pkg/cosmosver" "github.com/ignite/cli/ignite/pkg/goanalysis" + "github.com/ignite/cli/ignite/pkg/goenv" + "github.com/ignite/cli/ignite/pkg/gomodule" "github.com/ignite/cli/ignite/pkg/xast" ) const ( - appWiringImport = "cosmossdk.io/depinject" - appWiringCallMethod = "Inject" + appWiringImport = "cosmossdk.io/depinject" + appWiringCallMethod = "Inject" + registerRoutesMethod = "RegisterAPIRoutes" ) // CheckKeeper checks for the existence of the keeper with the provided name in the app structure. @@ -96,6 +101,10 @@ func FindRegisteredModules(chainRoot string) (modules []string, err error) { return nil, err } + // The modules registered by Cosmos SDK `rumtime.App` are included + // when the app registers API modules though the `App` instance. + var includeRuntimeModules bool + // Loop on package's files for _, f := range appPkg.Files { fileImports := goanalysis.FormatImports(f) @@ -118,6 +127,11 @@ func FindRegisteredModules(chainRoot string) (modules []string, err error) { return xast.ErrStop } + // Check if Cosmos SDK runtime App is called to register API routes + if !includeRuntimeModules { + includeRuntimeModules = checkRuntimeAppCalled(n) + } + // Find modules in RegisterAPIRoutes declaration if pkgs := findRegisterAPIRoutesRegistrations(n); pkgs != nil { for _, p := range pkgs { @@ -127,6 +141,7 @@ func FindRegisteredModules(chainRoot string) (modules []string, err error) { } modules = append(modules, importModule) } + return xast.ErrStop } @@ -136,6 +151,19 @@ func FindRegisteredModules(chainRoot string) (modules []string, err error) { return nil, err } } + + // Try to find the modules registered in Cosmos SDK `runtime.App`. + // This is required to properly generate OpenAPI specs for these + // modules when `app.App.RegisterAPIRoutes` is called. + if includeRuntimeModules { + runtimeModules, err := findRuntimeRegisteredModules(chainRoot) + if err != nil { + return nil, err + } + + modules = append(modules, runtimeModules...) + } + return modules, nil } @@ -270,7 +298,7 @@ func findRegisterAPIRoutesRegistrations(n ast.Node) []string { return nil } - if funcLitType.Name.Name != "RegisterAPIRoutes" { + if funcLitType.Name.Name != registerRoutesMethod { return nil } @@ -306,3 +334,117 @@ func findRegisterAPIRoutesRegistrations(n ast.Node) []string { return packagesRegistered } + +func checkRuntimeAppCalled(n ast.Node) bool { + funcLitType, ok := n.(*ast.FuncDecl) + if !ok { + return false + } + + if funcLitType.Name.Name != registerRoutesMethod { + return false + } + + for _, stmt := range funcLitType.Body.List { + exprStmt, ok := stmt.(*ast.ExprStmt) + if !ok { + continue + } + + exprCall, ok := exprStmt.X.(*ast.CallExpr) + if !ok { + continue + } + + exprFun, ok := exprCall.Fun.(*ast.SelectorExpr) + if !ok || exprFun.Sel.Name != registerRoutesMethod { + continue + } + + exprSel, ok := exprFun.X.(*ast.SelectorExpr) + if !ok || exprSel.Sel.Name != "App" { + continue + } + + identType, ok := exprSel.X.(*ast.Ident) + if !ok || identType.Name != "app" { + continue + } + + return true + } + + return false +} + +func findRuntimeRegisteredModules(chainRoot string) ([]string, error) { + // Resolve the absolute path to the Cosmos SDK module + cosmosPath, err := resolveCosmosPackagePath(chainRoot) + if err != nil { + return nil, err + } + + var modules []string + + // When runtime package doesn't exists it means is an older Cosmos SDK version, + // so all the module API registrations are defined within user's app. + path := filepath.Join(cosmosPath, "runtime", "app.go") + if _, err := os.Stat(path); os.IsNotExist(err) { + return modules, nil + } + + f, _, err := xast.ParseFile(path) + if err != nil { + return nil, err + } + + imports := goanalysis.FormatImports(f) + err = xast.Inspect(f, func(n ast.Node) error { + if pkgs := findRegisterAPIRoutesRegistrations(n); pkgs != nil { + for _, p := range pkgs { + if m := imports[p]; m != "" { + modules = append(modules, m) + } + } + return xast.ErrStop + } + return nil + }) + + if err != nil { + return nil, err + } + return modules, nil +} + +func resolveCosmosPackagePath(chainRoot string) (string, error) { + modFile, err := gomodule.ParseAt(chainRoot) + if err != nil { + return "", err + } + + deps, err := gomodule.ResolveDependencies(modFile) + if err != nil { + return "", err + } + + var pkg string + for _, dep := range deps { + if dep.Path == cosmosver.CosmosModulePath { + pkg = dep.String() + break + } + } + + if pkg == "" { + return "", errors.New("Cosmos SDK package version not found") + } + + // Check path of the package directory within Go's module cache + path := filepath.Join(goenv.GoModCache(), pkg) + info, err := os.Stat(path) + if os.IsNotExist(err) || !info.IsDir() { + return "", errors.New("local path to Cosmos SDK package not found") + } + return path, nil +} diff --git a/ignite/pkg/cosmosanalysis/app/app_test.go b/ignite/pkg/cosmosanalysis/app/app_test.go index 03a4930683..808fcdae81 100644 --- a/ignite/pkg/cosmosanalysis/app/app_test.go +++ b/ignite/pkg/cosmosanalysis/app/app_test.go @@ -136,6 +136,17 @@ func TestFindRegisteredModules(t *testing.T) { "github.com/cosmos/cosmos-sdk/client/grpc/tmservice", ), }, + { + name: "with runtime api routes", + path: "testdata/modules/runtime_api_routes", + expectedModules: append( + basicModules, + "github.com/cosmos/cosmos-sdk/x/auth/tx", + "github.com/cosmos/cosmos-sdk/client/grpc/tmservice", + "github.com/username/test/x/foo", + "github.com/cosmos/cosmos-sdk/client/grpc/node", + ), + }, { name: "same file function", path: "testdata/modules/file_function", diff --git a/ignite/pkg/cosmosanalysis/app/testdata/modules/runtime_api_routes/app.go b/ignite/pkg/cosmosanalysis/app/testdata/modules/runtime_api_routes/app.go new file mode 100644 index 0000000000..42490e86fc --- /dev/null +++ b/ignite/pkg/cosmosanalysis/app/testdata/modules/runtime_api_routes/app.go @@ -0,0 +1,60 @@ +package app + +import ( + "cosmossdk.io/api/tendermint/abci" + "cosmossdk.io/client/v2/autocli" + "github.com/cosmos/cosmos-sdk/client" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/runtime" + "github.com/cosmos/cosmos-sdk/server/api" + "github.com/cosmos/cosmos-sdk/server/config" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/cosmos/cosmos-sdk/x/gov" + govclient "github.com/cosmos/cosmos-sdk/x/gov/client" + paramsclient "github.com/cosmos/cosmos-sdk/x/params/client" + "github.com/cosmos/cosmos-sdk/x/staking" + foomodule "github.com/username/test/x/foo" +) + +// App modules are defined as NewBasicManager arguments +var ModuleBasics = module.NewBasicManager( + auth.AppModuleBasic{}, + bank.AppModuleBasic{}, + staking.AppModuleBasic{}, + gov.NewAppModuleBasic([]govclient.ProposalHandler{ + paramsclient.ProposalHandler, + }), + foomodule.AppModuleBasic{}, +) + +type Foo struct { + *runtime.App +} + +func (Foo) Name() string { return "foo" } +func (Foo) InterfaceRegistry() codectypes.InterfaceRegistry { return nil } +func (Foo) TxConfig() client.TxConfig { return nil } +func (Foo) AutoCliOpts() autocli.AppOptions { return autocli.AppOptions{} } + +func (Foo) BeginBlocker(sdk.Context, abci.RequestBeginBlock) abci.ResponseBeginBlock { + return abci.ResponseBeginBlock{} +} + +func (Foo) EndBlocker(sdk.Context, abci.RequestEndBlock) abci.ResponseEndBlock { + return abci.ResponseEndBlock{} +} + +func (app *Foo) RegisterAPIRoutes(s *api.Server, cfg config.APIConfig) { + // This module should be discovered + foomodule.RegisterGRPCGatewayRoutes(s.ClientCtx, s.GRPCGatewayRouter) + // Runtime app modules for the current Cosmos SDK should be discovered too + app.App.RegisterAPIRoutes(apiSvr, apiConfig) +} + +func (Foo) GetKey(storeKey string) *storetypes.KVStoreKey { return nil } + +func (Foo) TxConfig() client.TxConfig { return nil } diff --git a/ignite/pkg/cosmosanalysis/app/testdata/modules/runtime_api_routes/go.mod b/ignite/pkg/cosmosanalysis/app/testdata/modules/runtime_api_routes/go.mod new file mode 100644 index 0000000000..e480ea031f --- /dev/null +++ b/ignite/pkg/cosmosanalysis/app/testdata/modules/runtime_api_routes/go.mod @@ -0,0 +1,37 @@ +module app + +go 1.20 + +require ( + cosmossdk.io/api v0.3.1 + cosmossdk.io/core v0.5.1 + cosmossdk.io/depinject v1.0.0-alpha.3 + cosmossdk.io/errors v1.0.0-beta.7 + cosmossdk.io/math v1.0.1 + github.com/bufbuild/buf v1.23.1 + github.com/cometbft/cometbft v0.37.2 + github.com/cometbft/cometbft-db v0.8.0 + github.com/cosmos/cosmos-proto v1.0.0-beta.2 + github.com/cosmos/cosmos-sdk v0.47.3 + github.com/cosmos/gogoproto v1.4.10 + github.com/cosmos/ibc-go/v7 v7.2.0 + github.com/golang/protobuf v1.5.3 + github.com/gorilla/mux v1.8.0 + github.com/grpc-ecosystem/grpc-gateway v1.16.0 + github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2 + github.com/spf13/cast v1.5.1 + github.com/spf13/cobra v1.7.0 + github.com/spf13/pflag v1.0.5 + github.com/stretchr/testify v1.8.4 + google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 + google.golang.org/grpc v1.55.0 + google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 + google.golang.org/protobuf v1.31.0 +) + +replace ( + // use cosmos fork of keyring + github.com/99designs/keyring => github.com/cosmos/keyring v1.2.0 + // replace broken goleveldb + github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 +) diff --git a/ignite/pkg/goenv/goenv.go b/ignite/pkg/goenv/goenv.go index 98bbadcc07..17bc304280 100644 --- a/ignite/pkg/goenv/goenv.go +++ b/ignite/pkg/goenv/goenv.go @@ -14,10 +14,14 @@ const ( // GOPATH is the env var for GOPATH. GOPATH = "GOPATH" + + // GOMODCACHE is the env var for GOMODCACHE. + GOMODCACHE = "GOMODCACHE" ) const ( binDir = "bin" + modDir = "pkg/mod" ) // Bin returns the path of where Go binaries are installed. @@ -40,3 +44,14 @@ func Path() string { func ConfigurePath() error { return os.Setenv("PATH", Path()) } + +// GoModCache returns the path to Go's module cache. +func GoModCache() string { + if path := os.Getenv(GOMODCACHE); path != "" { + return path + } + if path := os.Getenv(GOPATH); path != "" { + return filepath.Join(path, modDir) + } + return filepath.Join(build.Default.GOPATH, modDir) +} diff --git a/ignite/pkg/goenv/goenv_test.go b/ignite/pkg/goenv/goenv_test.go new file mode 100644 index 0000000000..c4c9429bed --- /dev/null +++ b/ignite/pkg/goenv/goenv_test.go @@ -0,0 +1,49 @@ +package goenv_test + +import ( + "go/build" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/ignite/cli/ignite/pkg/goenv" +) + +func TestGoModCache(t *testing.T) { + cases := []struct { + name, envKey, envValue, want string + }{ + { + name: "from go module cache", + envKey: "GOMODCACHE", + envValue: "/foo/cache/pkg/mod", + want: "/foo/cache/pkg/mod", + }, + { + name: "from go path", + envKey: "GOPATH", + envValue: "/foo/go", + want: "/foo/go/pkg/mod", + }, + { + name: "from default path", + want: filepath.Join(build.Default.GOPATH, "pkg/mod"), + }, + } + + for _, tt := range cases { + t.Run(tt.name, func(t *testing.T) { + // Arrange + if tt.envKey != "" { + t.Setenv(tt.envKey, tt.envValue) + } + + // Act + path := goenv.GoModCache() + + // Assert + require.Equal(t, tt.want, path) + }) + } +} From ced08ac1daf7a57a4213dd809e49059eed185df0 Mon Sep 17 00:00:00 2001 From: Danilo Pantani Date: Fri, 29 Sep 2023 15:03:03 +0200 Subject: [PATCH 5/9] add julien and ehsan to codeowners file (#3671) Co-authored-by: Pantani --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d1b0df20c9..dafc8525a7 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,7 +1,7 @@ # CODEOWNERS: https://help.github.com/articles/about-codeowners/ # Primary repo maintainers -* @ilgooz @jeronimoalbi @Pantani +* @ilgooz @jeronimoalbi @Pantani @julienrbrt @Ehsan-saradar # Docs *.md @ilgooz From b0e1babe3c0cf8783de3934a506fae00a820946c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jer=C3=B3nimo=20Albi?= Date: Mon, 2 Oct 2023 14:45:02 +0200 Subject: [PATCH 6/9] docs: fix broken cosmos SDK documentation links (#3677) --- docs/docs/02-guide/06-ibc.md | 2 +- docs/docs/02-guide/10-simapp.md | 4 ++-- docs/versioned_docs/version-v0.25.2/guide/02-hello.md | 6 +++--- .../version-v0.25.2/guide/04-nameservice/00-tutorial.md | 6 +++--- .../version-v0.25.2/guide/04-nameservice/04-keeper.md | 2 +- docs/versioned_docs/version-v0.25.2/guide/07-ibc.md | 2 +- docs/versioned_docs/version-v0.25.2/kb/02-serve.md | 2 +- docs/versioned_docs/version-v0.25.2/kb/11-simapp.md | 4 ++-- docs/versioned_docs/version-v0.26.1/02-guide/06-ibc.md | 2 +- docs/versioned_docs/version-v0.26.1/02-guide/10-simapp.md | 4 ++-- docs/versioned_docs/version-v0.27.1/02-guide/06-ibc.md | 2 +- docs/versioned_docs/version-v0.27.1/02-guide/10-simapp.md | 4 ++-- 12 files changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/docs/02-guide/06-ibc.md b/docs/docs/02-guide/06-ibc.md index 83efb4f760..9f1973adc5 100644 --- a/docs/docs/02-guide/06-ibc.md +++ b/docs/docs/02-guide/06-ibc.md @@ -291,7 +291,7 @@ acknowledgment. Append the type instance as `PostID` on receiving the packet: - The context `ctx` is an [immutable data - structure](https://docs.cosmos.network/main/core/context.html#go-context-package) + structure](https://docs.cosmos.network/main/core/context#go-context-package) that has header data from the transaction. See [how the context is initiated](https://github.com/cosmos/cosmos-sdk/blob/main/types/context.go#L71) - The identifier format that you defined earlier diff --git a/docs/docs/02-guide/10-simapp.md b/docs/docs/02-guide/10-simapp.md index fd2dcea96f..a6aff362b9 100644 --- a/docs/docs/02-guide/10-simapp.md +++ b/docs/docs/02-guide/10-simapp.md @@ -15,7 +15,7 @@ simulation methods for each scaffolded message. Every new module that is scaffolded with Ignite CLI implements the Cosmos SDK [Module -Simulation](https://docs.cosmos.network/main/building-modules/simulator.html). +Simulation](https://docs.cosmos.network/main/building-modules/simulator). - Each new message creates a file with the simulation methods required for the tests. @@ -105,7 +105,7 @@ according to call the function. ## Invariants Simulating a chain can help you prevent [chain invariants -errors](https://docs.cosmos.network/main/building-modules/invariants.html). An +errors](https://docs.cosmos.network/main/building-modules/invariants). An invariant is a function called by the chain to check if something broke, invalidating the chain data. To create a new invariant and check the chain integrity, you must create a method to validate the invariants and register all diff --git a/docs/versioned_docs/version-v0.25.2/guide/02-hello.md b/docs/versioned_docs/version-v0.25.2/guide/02-hello.md index be260add1d..3766995671 100644 --- a/docs/versioned_docs/version-v0.25.2/guide/02-hello.md +++ b/docs/versioned_docs/version-v0.25.2/guide/02-hello.md @@ -109,12 +109,12 @@ To get your blockchain to say `Hello! Ignite CLI`, you need to make these change Protocol buffer files contain proto rpc calls that define Cosmos SDK queries and message handlers, and proto messages that define Cosmos SDK types. The rpc calls are also responsible for exposing an HTTP API. -For each Cosmos SDK module, the [Keeper](https://docs.cosmos.network/main/building-modules/keeper.html) is an abstraction for modifying the state of the blockchain. Keeper functions let you query or write to the state. After you add the first query to your chain, the next step is to register the query. You only need to register a query once. +For each Cosmos SDK module, the [Keeper](https://docs.cosmos.network/main/building-modules/keeper) is an abstraction for modifying the state of the blockchain. Keeper functions let you query or write to the state. After you add the first query to your chain, the next step is to register the query. You only need to register a query once. A typical blockchain developer workflow looks something like this: -- Start with proto files to define Cosmos SDK [messages](https://docs.cosmos.network/main/building-modules/msg-services.html) -- Define and register [queries](https://docs.cosmos.network/main/building-modules/query-services.html) +- Start with proto files to define Cosmos SDK [messages](https://docs.cosmos.network/main/building-modules/msg-services) +- Define and register [queries](https://docs.cosmos.network/main/building-modules/query-services) - Define message handler logic - Finally, implement the logic of these queries and message handlers in keeper functions diff --git a/docs/versioned_docs/version-v0.25.2/guide/04-nameservice/00-tutorial.md b/docs/versioned_docs/version-v0.25.2/guide/04-nameservice/00-tutorial.md index fb6f993bcb..9898d973bb 100644 --- a/docs/versioned_docs/version-v0.25.2/guide/04-nameservice/00-tutorial.md +++ b/docs/versioned_docs/version-v0.25.2/guide/04-nameservice/00-tutorial.md @@ -29,7 +29,7 @@ This tutorial guides you through these steps to build a blockchain for a nameser - A supported version of [Ignite CLI](/). To install Ignite CLI, see [Install Ignite CLI](../01-install.md). * A text editor like [Visual Studio Code](https://code.visualstudio.com/download). * A web browser like [Chrome](https://www.google.com/chrome) or [Firefox](https://www.mozilla.org/en-US/firefox/new). -- Familiarity with [Cosmos SDK modules](https://docs.cosmos.network/main/building-modules/intro.html) +- Familiarity with [Cosmos SDK modules](https://docs.cosmos.network/main/building-modules/intro) ## Nameservice App Goals @@ -72,11 +72,11 @@ For your nameservice app, use one store to map a `name` key to its respective `w ## Messages -In the Cosmos SDK, [messages](https://docs.cosmos.network/main/building-modules/messages-and-queries.html#messages) are objects that are contained in transactions to trigger state transitions. Each Cosmos SDK module defines a list of messages and how to handle them. +In the Cosmos SDK, [messages](https://docs.cosmos.network/main/building-modules/messages-and-queries#messages) are objects that are contained in transactions to trigger state transitions. Each Cosmos SDK module defines a list of messages and how to handle them. You must create [messages for the nameservice module](./02-messages.md) that support this functionality: -- When a transaction that is included in a block reaches a Tendermint node, the transaction is passed to the application using the Application Blockchain Interface [(ABCI)](https://docs.cosmos.network/main/intro/sdk-app-architecture.html#abci) between Tendermint and your app. +- When a transaction that is included in a block reaches a Tendermint node, the transaction is passed to the application using the Application Blockchain Interface [(ABCI)](https://docs.cosmos.network/main/intro/sdk-app-architecture#abci) between Tendermint and your app. - The transaction is decoded to get the message. - The message is then routed to the appropriate module and handled according to the logic defined in the corresponding `Handler`. - If the state needs to be updated, the `Handler` calls the `Keeper` to perform the update. diff --git a/docs/versioned_docs/version-v0.25.2/guide/04-nameservice/04-keeper.md b/docs/versioned_docs/version-v0.25.2/guide/04-nameservice/04-keeper.md index 06af980dde..6c0fb0d7c4 100644 --- a/docs/versioned_docs/version-v0.25.2/guide/04-nameservice/04-keeper.md +++ b/docs/versioned_docs/version-v0.25.2/guide/04-nameservice/04-keeper.md @@ -5,7 +5,7 @@ description: Define keepers for the nameservice module. # Keeper -> The main core of a Cosmos SDK module is a piece called the keeper. The keeper handles interactions with the store, has references to other [keepers](https://docs.cosmos.network/main/building-modules/keeper.html) for cross-module interactions, and contains most of the core functionality of a module. +> The main core of a Cosmos SDK module is a piece called the keeper. The keeper handles interactions with the store, has references to other [keepers](https://docs.cosmos.network/main/building-modules/keeper) for cross-module interactions, and contains most of the core functionality of a module. ## Define Keepers for the Nameservice Module diff --git a/docs/versioned_docs/version-v0.25.2/guide/07-ibc.md b/docs/versioned_docs/version-v0.25.2/guide/07-ibc.md index 40944840c6..442d116e72 100644 --- a/docs/versioned_docs/version-v0.25.2/guide/07-ibc.md +++ b/docs/versioned_docs/version-v0.25.2/guide/07-ibc.md @@ -206,7 +206,7 @@ Finally, the Ignite CLI-generated AppendPost function returns the ID of the new Append the type instance as `PostID` on receiving the packet: -- The context `ctx` is an [immutable data structure](https://docs.cosmos.network/main/core/context.html#go-context-package) that has header data from the transaction. See [how the context is initiated](https://github.com/cosmos/cosmos-sdk/blob/master/types/context.go#L71) +- The context `ctx` is an [immutable data structure](https://docs.cosmos.network/main/core/context#go-context-package) that has header data from the transaction. See [how the context is initiated](https://github.com/cosmos/cosmos-sdk/blob/master/types/context.go#L71) - The identifier format that you defined earlier - The `title` is the Title of the blog post - The `content` is the Content of the blog post diff --git a/docs/versioned_docs/version-v0.25.2/kb/02-serve.md b/docs/versioned_docs/version-v0.25.2/kb/02-serve.md index 7b3e9005bc..6806c8252a 100644 --- a/docs/versioned_docs/version-v0.25.2/kb/02-serve.md +++ b/docs/versioned_docs/version-v0.25.2/kb/02-serve.md @@ -88,4 +88,4 @@ build: ldflags: [ "-X main.Env=prod", "-X main.Version=1.0.1" ] ``` -Learn more about how to use the binary to [run a chain in production](https://docs.cosmos.network/main/run-node/run-node.html). +Learn more about how to use the binary to [run a chain in production](https://docs.cosmos.network/main/run-node/run-node). diff --git a/docs/versioned_docs/version-v0.25.2/kb/11-simapp.md b/docs/versioned_docs/version-v0.25.2/kb/11-simapp.md index 8728fd4dd3..f70d8a24aa 100644 --- a/docs/versioned_docs/version-v0.25.2/kb/11-simapp.md +++ b/docs/versioned_docs/version-v0.25.2/kb/11-simapp.md @@ -10,7 +10,7 @@ The Ignite CLI chain simulator can help you to run your chain based in randomize ## Module simulation -Every new module that is scaffolded with Ignite CLI implements the Cosmos SDK [Module Simulation](https://docs.cosmos.network/main/building-modules/simulator.html). +Every new module that is scaffolded with Ignite CLI implements the Cosmos SDK [Module Simulation](https://docs.cosmos.network/main/building-modules/simulator). - Each new message creates a file with the simulation methods required for the tests. - Scaffolding a `CRUD` type like a `list` or `map` creates a simulation file with `create`, `update`, and `delete` simulation methods in the `x//simulation` folder and registers these methods in `x//module_simulation.go`. @@ -80,7 +80,7 @@ After the parameters are scaffolded, change the `x//module_simulation.go ## Invariants -Simulating a chain can help you prevent [chain invariants errors](https://docs.cosmos.network/main/building-modules/invariants.html). An invariant is a function called by the chain to check if something broke, invalidating the chain data. +Simulating a chain can help you prevent [chain invariants errors](https://docs.cosmos.network/main/building-modules/invariants). An invariant is a function called by the chain to check if something broke, invalidating the chain data. To create a new invariant and check the chain integrity, you must create a method to validate the invariants and register all invariants. For example, in `x/earth/keeper/invariants.go`: diff --git a/docs/versioned_docs/version-v0.26.1/02-guide/06-ibc.md b/docs/versioned_docs/version-v0.26.1/02-guide/06-ibc.md index 83efb4f760..9f1973adc5 100644 --- a/docs/versioned_docs/version-v0.26.1/02-guide/06-ibc.md +++ b/docs/versioned_docs/version-v0.26.1/02-guide/06-ibc.md @@ -291,7 +291,7 @@ acknowledgment. Append the type instance as `PostID` on receiving the packet: - The context `ctx` is an [immutable data - structure](https://docs.cosmos.network/main/core/context.html#go-context-package) + structure](https://docs.cosmos.network/main/core/context#go-context-package) that has header data from the transaction. See [how the context is initiated](https://github.com/cosmos/cosmos-sdk/blob/main/types/context.go#L71) - The identifier format that you defined earlier diff --git a/docs/versioned_docs/version-v0.26.1/02-guide/10-simapp.md b/docs/versioned_docs/version-v0.26.1/02-guide/10-simapp.md index fd2dcea96f..a6aff362b9 100644 --- a/docs/versioned_docs/version-v0.26.1/02-guide/10-simapp.md +++ b/docs/versioned_docs/version-v0.26.1/02-guide/10-simapp.md @@ -15,7 +15,7 @@ simulation methods for each scaffolded message. Every new module that is scaffolded with Ignite CLI implements the Cosmos SDK [Module -Simulation](https://docs.cosmos.network/main/building-modules/simulator.html). +Simulation](https://docs.cosmos.network/main/building-modules/simulator). - Each new message creates a file with the simulation methods required for the tests. @@ -105,7 +105,7 @@ according to call the function. ## Invariants Simulating a chain can help you prevent [chain invariants -errors](https://docs.cosmos.network/main/building-modules/invariants.html). An +errors](https://docs.cosmos.network/main/building-modules/invariants). An invariant is a function called by the chain to check if something broke, invalidating the chain data. To create a new invariant and check the chain integrity, you must create a method to validate the invariants and register all diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/06-ibc.md b/docs/versioned_docs/version-v0.27.1/02-guide/06-ibc.md index 83efb4f760..9f1973adc5 100644 --- a/docs/versioned_docs/version-v0.27.1/02-guide/06-ibc.md +++ b/docs/versioned_docs/version-v0.27.1/02-guide/06-ibc.md @@ -291,7 +291,7 @@ acknowledgment. Append the type instance as `PostID` on receiving the packet: - The context `ctx` is an [immutable data - structure](https://docs.cosmos.network/main/core/context.html#go-context-package) + structure](https://docs.cosmos.network/main/core/context#go-context-package) that has header data from the transaction. See [how the context is initiated](https://github.com/cosmos/cosmos-sdk/blob/main/types/context.go#L71) - The identifier format that you defined earlier diff --git a/docs/versioned_docs/version-v0.27.1/02-guide/10-simapp.md b/docs/versioned_docs/version-v0.27.1/02-guide/10-simapp.md index fd2dcea96f..a6aff362b9 100644 --- a/docs/versioned_docs/version-v0.27.1/02-guide/10-simapp.md +++ b/docs/versioned_docs/version-v0.27.1/02-guide/10-simapp.md @@ -15,7 +15,7 @@ simulation methods for each scaffolded message. Every new module that is scaffolded with Ignite CLI implements the Cosmos SDK [Module -Simulation](https://docs.cosmos.network/main/building-modules/simulator.html). +Simulation](https://docs.cosmos.network/main/building-modules/simulator). - Each new message creates a file with the simulation methods required for the tests. @@ -105,7 +105,7 @@ according to call the function. ## Invariants Simulating a chain can help you prevent [chain invariants -errors](https://docs.cosmos.network/main/building-modules/invariants.html). An +errors](https://docs.cosmos.network/main/building-modules/invariants). An invariant is a function called by the chain to check if something broke, invalidating the chain data. To create a new invariant and check the chain integrity, you must create a method to validate the invariants and register all From 35e7c7a200bcefd5276b8fbd795fe1e95a7723b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jer=C3=B3nimo=20Albi?= Date: Wed, 4 Oct 2023 15:34:42 +0200 Subject: [PATCH 7/9] refactor: rename `plugins` config file to `igniteapps` (#3669) * refactor: rename "plugins" config section to "apps" * refactor: change plugins config file name to `igniteapps.yaml` * refactor: rename global Ignite's "plugins" folder to "apps" * chore: update changelog * docs: update config file path in the documentation * fix: correct UI session to print unmarked bus events This allows printing events without having to stop the spinner. These type of events are not removed when the spinner is sropped. * feat: add text indenting support to event bus events * refactor: improve `doctor` command output indentation * feat: add plugins migration support to `doctor` command * docs: add migration docs for `v0.28.0` * fix: correct plugins config find function * docs: fix broken documentation links This fix is not related to this PR but it needs to be fixed so the CI passes. * chore: documentation typo * chore: restore app config filename variable name --- changelog.md | 1 + docs/docs/06-migration/v0.19.2.md | 2 +- docs/docs/06-migration/v0.26.0.md | 4 +- docs/docs/06-migration/v0.28.0.md | 17 ++ docs/docs/apps/01-using-apps.md | 2 +- .../contributing/02-technical-setup.md | 2 +- .../version-v0.25.2/migration/v0.19.2.md | 2 +- .../version-v0.26.1/06-migration/v0.19.2.md | 2 +- .../version-v0.26.1/06-migration/v0.26.0.md | 4 +- .../version-v0.27.1/06-migration/v0.19.2.md | 2 +- .../version-v0.27.1/06-migration/v0.26.0.md | 4 +- ignite/cmd/doctor.go | 4 + ignite/cmd/plugin.go | 19 +- ignite/cmd/plugin_default.go | 2 +- ignite/cmd/plugin_default_test.go | 2 +- ignite/config/chain/v1/testdata/config2.yaml | 2 +- ignite/config/plugins/config.go | 10 +- ignite/config/plugins/config_test.go | 22 +- ignite/config/plugins/parse.go | 5 +- ignite/config/plugins/parse_test.go | 12 +- .../{other/plugins.yaml => igniteapps.yml} | 2 +- .../invalid/{plugins.yml => igniteapps.yml} | 0 .../{plugins.yml => other/igniteapps.yaml} | 2 +- ignite/internal/tools/gen-cli-docs/main.go | 2 +- ignite/pkg/cliui/cliui.go | 1 + ignite/pkg/events/events.go | 23 +- ignite/services/doctor/doctor.go | 27 ++- ignite/services/doctor/doctor_plugins.go | 210 ++++++++++++++++++ ignite/services/plugin/plugin.go | 14 +- ignite/services/plugin/plugin_test.go | 26 +-- integration/plugin/plugin_test.go | 4 +- 31 files changed, 349 insertions(+), 82 deletions(-) create mode 100644 docs/docs/06-migration/v0.28.0.md rename ignite/config/plugins/testdata/{other/plugins.yaml => igniteapps.yml} (93%) rename ignite/config/plugins/testdata/invalid/{plugins.yml => igniteapps.yml} (100%) rename ignite/config/plugins/testdata/{plugins.yml => other/igniteapps.yaml} (93%) create mode 100644 ignite/services/doctor/doctor_plugins.go diff --git a/changelog.md b/changelog.md index 0cc96bd824..d93a62dae8 100644 --- a/changelog.md +++ b/changelog.md @@ -17,6 +17,7 @@ - [#3658](https://github.com/ignite/cli/pull/3658) Rename Marshaler to Codec in EncodingConfig - [#3653](https://github.com/ignite/cli/pull/3653) Add "app" extension to plugin binaries - [#3656](https://github.com/ignite/cli/pull/3656) Disable Go toolchain download +- [#3669](https://github.com/ignite/cli/pull/3669) Rename `plugins` config file to `igniteapps` ### Fixes diff --git a/docs/docs/06-migration/v0.19.2.md b/docs/docs/06-migration/v0.19.2.md index 5bd63a73c1..0ee04f20d3 100644 --- a/docs/docs/06-migration/v0.19.2.md +++ b/docs/docs/06-migration/v0.19.2.md @@ -14,7 +14,7 @@ official Ignite CLI repo which introduces breaking changes. To migrate your chain that was scaffolded with Ignite CLI versions lower than v0.19.2: 1. IBC upgrade: Use - the [IBC migration documents](https://github.com/cosmos/ibc-go/blob/main/docs/migrations/v1-to-v2.md) + the [IBC migration documents](https://github.com/cosmos/ibc-go/blob/v6.2.0/docs/migrations/v1-to-v2.md) 2. In your chain's `go.mod` file, remove `tendermint/spm` and add the v0.19.2 version of `tendermint/starport`. If your chain uses these packages, change the import paths as shown: diff --git a/docs/docs/06-migration/v0.26.0.md b/docs/docs/06-migration/v0.26.0.md index 61bf0c5dbb..8dae447d15 100644 --- a/docs/docs/06-migration/v0.26.0.md +++ b/docs/docs/06-migration/v0.26.0.md @@ -17,7 +17,7 @@ their tooling to the latest version. Chains that are newly scaffolded with Ignite CLI `v0.26.0` now use `ibc-go/v6` for ibc functionality. It is not necessary, but recommended to upgrade to the newest version of `ibc-go`. Most migrations can be done by following the -`ibc-go` [migration guide](https://github.com/cosmos/ibc-go/blob/main/docs/migrations/v5-to-v6.md), but there are some +`ibc-go` [migration guide](https://github.com/cosmos/ibc-go/blob/v6.2.0/docs/migrations/v5-to-v6.md), but there are some specific changes that will need to be followed for Ignite scaffolded chains. ### Removing `cosmosibckeeper` @@ -215,7 +215,7 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger { ### Remaining migration -After all uses of `cosmosibckeeper` have been removed, you can follow any remaining steps in the`ibc-go`[migration guide](https://github.com/cosmos/ibc-go/blob/main/docs/migrations/v5-to-v6.md). +After all uses of `cosmosibckeeper` have been removed, you can follow any remaining steps in the`ibc-go`[migration guide](https://github.com/cosmos/ibc-go/blob/v6.2.0/docs/migrations/v5-to-v6.md). ## Scaffolded Release Workflow diff --git a/docs/docs/06-migration/v0.28.0.md b/docs/docs/06-migration/v0.28.0.md new file mode 100644 index 0000000000..5484d9d482 --- /dev/null +++ b/docs/docs/06-migration/v0.28.0.md @@ -0,0 +1,17 @@ +--- +sidebar_position: 990 +title: v0.28.0 +description: For chains that were scaffolded with Ignite CLI versions lower than v0.28.0. changes are required to use Ignite CLI v0.28.0. +--- + +## Upgrading legacy plugins configuration files + +Ignite `v0.28.0` changes the plugin system which is now called Ignite Apps. This version includes changes +to the CLI command names and the plugin configuration file. + +The plugins configuration file is now called `igniteapps.yml` and the "plugins" section is now called "apps". + +The global plugins directory is now `$HOME/.ignite/apps` instead `$HOME/.ignite/plugins`. + +Updates can be automatically applied by running `ignite doctor` in your blockchain application directory. +Running the command outside your blockchain application directory will only update the global plugins. diff --git a/docs/docs/apps/01-using-apps.md b/docs/docs/apps/01-using-apps.md index 1741496952..459d6c7ea3 100644 --- a/docs/docs/apps/01-using-apps.md +++ b/docs/docs/apps/01-using-apps.md @@ -9,7 +9,7 @@ core concepts within apps: `Commands` and `Hooks`. `Commands` extend the CLI's functionality and `Hooks` extend existing CLI command functionality. Apps are registered in an Ignite scaffolded blockchain project through the -`plugins.yml`, or globally through `$HOME/.ignite/plugins/plugins.yml`. +`igniteapps.yml`, or globally through `$HOME/.ignite/apps/igniteapps.yml`. To use an app within your project execute the following command inside the project directory: diff --git a/docs/versioned_docs/version-v0.25.2/contributing/02-technical-setup.md b/docs/versioned_docs/version-v0.25.2/contributing/02-technical-setup.md index d295f14b76..849e194304 100644 --- a/docs/versioned_docs/version-v0.25.2/contributing/02-technical-setup.md +++ b/docs/versioned_docs/version-v0.25.2/contributing/02-technical-setup.md @@ -7,7 +7,7 @@ To ensure you have a successful experience working with our Developer Tutorials ## Setting up Visual Studio Code -1. Install [Visual Studio Code](https://vscode-docs.readthedocs.io/en/latest/editor/setup). +1. Install [Visual Studio Code](https://vscode-docs.readthedocs.io/en/latest/editor/setup/). 1. Click **Extensions** in the sidebar. 1. Install this extension: - Go for VS Code The official Go extension for Visual Studio Code diff --git a/docs/versioned_docs/version-v0.25.2/migration/v0.19.2.md b/docs/versioned_docs/version-v0.25.2/migration/v0.19.2.md index d661a0bf19..1b185f7767 100644 --- a/docs/versioned_docs/version-v0.25.2/migration/v0.19.2.md +++ b/docs/versioned_docs/version-v0.25.2/migration/v0.19.2.md @@ -12,7 +12,7 @@ With Ignite CLI v0.19.2, the contents of the deprecated Ignite CLI Modules `tend To migrate your chain that was scaffolded with Ignite CLI versions lower than v0.19.2: -1. IBC upgrade: Use the [IBC migration documents](https://github.com/cosmos/ibc-go/blob/main/docs/migrations/v1-to-v2.md) +1. IBC upgrade: Use the [IBC migration documents](https://github.com/cosmos/ibc-go/blob/v6.2.0/docs/migrations/v1-to-v2.md) 2. In your chain's `go.mod` file, remove `tendermint/spm` and add the v0.19.2 version of `tendermint/starport`. If your chain uses these packages, change the import paths as shown: diff --git a/docs/versioned_docs/version-v0.26.1/06-migration/v0.19.2.md b/docs/versioned_docs/version-v0.26.1/06-migration/v0.19.2.md index 5bd63a73c1..0ee04f20d3 100644 --- a/docs/versioned_docs/version-v0.26.1/06-migration/v0.19.2.md +++ b/docs/versioned_docs/version-v0.26.1/06-migration/v0.19.2.md @@ -14,7 +14,7 @@ official Ignite CLI repo which introduces breaking changes. To migrate your chain that was scaffolded with Ignite CLI versions lower than v0.19.2: 1. IBC upgrade: Use - the [IBC migration documents](https://github.com/cosmos/ibc-go/blob/main/docs/migrations/v1-to-v2.md) + the [IBC migration documents](https://github.com/cosmos/ibc-go/blob/v6.2.0/docs/migrations/v1-to-v2.md) 2. In your chain's `go.mod` file, remove `tendermint/spm` and add the v0.19.2 version of `tendermint/starport`. If your chain uses these packages, change the import paths as shown: diff --git a/docs/versioned_docs/version-v0.26.1/06-migration/v0.26.0.md b/docs/versioned_docs/version-v0.26.1/06-migration/v0.26.0.md index 61bf0c5dbb..8dae447d15 100644 --- a/docs/versioned_docs/version-v0.26.1/06-migration/v0.26.0.md +++ b/docs/versioned_docs/version-v0.26.1/06-migration/v0.26.0.md @@ -17,7 +17,7 @@ their tooling to the latest version. Chains that are newly scaffolded with Ignite CLI `v0.26.0` now use `ibc-go/v6` for ibc functionality. It is not necessary, but recommended to upgrade to the newest version of `ibc-go`. Most migrations can be done by following the -`ibc-go` [migration guide](https://github.com/cosmos/ibc-go/blob/main/docs/migrations/v5-to-v6.md), but there are some +`ibc-go` [migration guide](https://github.com/cosmos/ibc-go/blob/v6.2.0/docs/migrations/v5-to-v6.md), but there are some specific changes that will need to be followed for Ignite scaffolded chains. ### Removing `cosmosibckeeper` @@ -215,7 +215,7 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger { ### Remaining migration -After all uses of `cosmosibckeeper` have been removed, you can follow any remaining steps in the`ibc-go`[migration guide](https://github.com/cosmos/ibc-go/blob/main/docs/migrations/v5-to-v6.md). +After all uses of `cosmosibckeeper` have been removed, you can follow any remaining steps in the`ibc-go`[migration guide](https://github.com/cosmos/ibc-go/blob/v6.2.0/docs/migrations/v5-to-v6.md). ## Scaffolded Release Workflow diff --git a/docs/versioned_docs/version-v0.27.1/06-migration/v0.19.2.md b/docs/versioned_docs/version-v0.27.1/06-migration/v0.19.2.md index 5bd63a73c1..0ee04f20d3 100644 --- a/docs/versioned_docs/version-v0.27.1/06-migration/v0.19.2.md +++ b/docs/versioned_docs/version-v0.27.1/06-migration/v0.19.2.md @@ -14,7 +14,7 @@ official Ignite CLI repo which introduces breaking changes. To migrate your chain that was scaffolded with Ignite CLI versions lower than v0.19.2: 1. IBC upgrade: Use - the [IBC migration documents](https://github.com/cosmos/ibc-go/blob/main/docs/migrations/v1-to-v2.md) + the [IBC migration documents](https://github.com/cosmos/ibc-go/blob/v6.2.0/docs/migrations/v1-to-v2.md) 2. In your chain's `go.mod` file, remove `tendermint/spm` and add the v0.19.2 version of `tendermint/starport`. If your chain uses these packages, change the import paths as shown: diff --git a/docs/versioned_docs/version-v0.27.1/06-migration/v0.26.0.md b/docs/versioned_docs/version-v0.27.1/06-migration/v0.26.0.md index 61bf0c5dbb..8dae447d15 100644 --- a/docs/versioned_docs/version-v0.27.1/06-migration/v0.26.0.md +++ b/docs/versioned_docs/version-v0.27.1/06-migration/v0.26.0.md @@ -17,7 +17,7 @@ their tooling to the latest version. Chains that are newly scaffolded with Ignite CLI `v0.26.0` now use `ibc-go/v6` for ibc functionality. It is not necessary, but recommended to upgrade to the newest version of `ibc-go`. Most migrations can be done by following the -`ibc-go` [migration guide](https://github.com/cosmos/ibc-go/blob/main/docs/migrations/v5-to-v6.md), but there are some +`ibc-go` [migration guide](https://github.com/cosmos/ibc-go/blob/v6.2.0/docs/migrations/v5-to-v6.md), but there are some specific changes that will need to be followed for Ignite scaffolded chains. ### Removing `cosmosibckeeper` @@ -215,7 +215,7 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger { ### Remaining migration -After all uses of `cosmosibckeeper` have been removed, you can follow any remaining steps in the`ibc-go`[migration guide](https://github.com/cosmos/ibc-go/blob/main/docs/migrations/v5-to-v6.md). +After all uses of `cosmosibckeeper` have been removed, you can follow any remaining steps in the`ibc-go`[migration guide](https://github.com/cosmos/ibc-go/blob/v6.2.0/docs/migrations/v5-to-v6.md). ## Scaffolded Release Workflow diff --git a/ignite/cmd/doctor.go b/ignite/cmd/doctor.go index 2d0b9cc277..072b6d5edb 100644 --- a/ignite/cmd/doctor.go +++ b/ignite/cmd/doctor.go @@ -22,6 +22,10 @@ func NewDoctor() *cobra.Command { return err } + if err := doc.MigratePluginsConfig(); err != nil { + return err + } + return doc.FixDependencyTools(cmd.Context()) }, } diff --git a/ignite/cmd/plugin.go b/ignite/cmd/plugin.go index f93e7a39f8..64e77048fb 100644 --- a/ignite/cmd/plugin.go +++ b/ignite/cmd/plugin.go @@ -39,12 +39,12 @@ func LoadPlugins(ctx context.Context, cmd *cobra.Command) error { if err != nil && !errors.As(err, &cosmosanalysis.ErrPathNotChain{}) { return err } else if err == nil { - pluginsConfigs = append(pluginsConfigs, localCfg.Plugins...) + pluginsConfigs = append(pluginsConfigs, localCfg.Apps...) } globalCfg, err := parseGlobalPlugins() if err == nil { - pluginsConfigs = append(pluginsConfigs, globalCfg.Plugins...) + pluginsConfigs = append(pluginsConfigs, globalCfg.Apps...) } ensureDefaultPlugins(cmd, globalCfg) @@ -97,8 +97,8 @@ func parseGlobalPlugins() (cfg *pluginsconfig.Config, err error) { return &pluginsconfig.Config{}, nil } - for i := range cfg.Plugins { - cfg.Plugins[i].Global = true + for i := range cfg.Apps { + cfg.Apps[i].Global = true } return } @@ -436,7 +436,7 @@ Example: return err } - for _, p := range conf.Plugins { + for _, p := range conf.Apps { if p.Path == args[0] { return fmt.Errorf("cannot add duplicate plugin %s", args[0]) } @@ -476,7 +476,7 @@ Example: return fmt.Errorf("error while loading plugin %q: %w", args[0], plugins[0].Error) } session.Println("Done loading plugin") - conf.Plugins = append(conf.Plugins, p) + conf.Apps = append(conf.Apps, p) if err := conf.Save(); err != nil { return err @@ -517,9 +517,9 @@ func NewPluginRemove() *cobra.Command { } removed := false - for i, cp := range conf.Plugins { + for i, cp := range conf.Apps { if cp.Path == args[0] { - conf.Plugins = append(conf.Plugins[:i], conf.Plugins[i+1:]...) + conf.Apps = append(conf.Apps[:i], conf.Apps[i+1:]...) removed = true break } @@ -658,8 +658,7 @@ func printPlugins(session *cliui.Session) error { func flagSetPluginsGlobal() *flag.FlagSet { fs := flag.NewFlagSet("", flag.ContinueOnError) - fs.BoolP(flagPluginsGlobal, "g", false, "use global plugins configuration"+ - " ($HOME/.ignite/plugins/plugins.yml)") + fs.BoolP(flagPluginsGlobal, "g", false, "use global plugins configuration ($HOME/.ignite/apps/igniteapps.yml)") return fs } diff --git a/ignite/cmd/plugin_default.go b/ignite/cmd/plugin_default.go index 17b564be1d..a7a2424197 100644 --- a/ignite/cmd/plugin_default.go +++ b/ignite/cmd/plugin_default.go @@ -74,7 +74,7 @@ func newPluginInstallCmd(dp defaultPlugin) *cobra.Command { pluginCfg := pluginsconfig.Plugin{ Path: dp.path, } - cfg.Plugins = append(cfg.Plugins, pluginCfg) + cfg.Apps = append(cfg.Apps, pluginCfg) if err := cfg.Save(); err != nil { return err } diff --git a/ignite/cmd/plugin_default_test.go b/ignite/cmd/plugin_default_test.go index 04528f14ec..bf2d4a4c5e 100644 --- a/ignite/cmd/plugin_default_test.go +++ b/ignite/cmd/plugin_default_test.go @@ -23,7 +23,7 @@ func TestEnsureDefaultPlugins(t *testing.T) { { name: "should not add because already present in config", cfg: &pluginsconfig.Config{ - Plugins: []pluginsconfig.Plugin{{ + Apps: []pluginsconfig.Plugin{{ Path: "github.com/ignite/cli-plugin-network@v42", }}, }, diff --git a/ignite/config/chain/v1/testdata/config2.yaml b/ignite/config/chain/v1/testdata/config2.yaml index ea1b8d2822..38c2fe42c8 100644 --- a/ignite/config/chain/v1/testdata/config2.yaml +++ b/ignite/config/chain/v1/testdata/config2.yaml @@ -37,7 +37,7 @@ validators: address: 0.0.0.0:8545 ws-address: 0.0.0.0:8546 home: $HOME/.evmosd -plugins: +apps: - name: plugin1 path: /path/to/plugin1 - name: plugin2 diff --git a/ignite/config/plugins/config.go b/ignite/config/plugins/config.go index e2740eeb84..4c5bf2e05b 100644 --- a/ignite/config/plugins/config.go +++ b/ignite/config/plugins/config.go @@ -16,7 +16,9 @@ type Config struct { // path to the config file path string - Plugins []Plugin `yaml:"plugins"` + // Apps holds the list of installed Ignite Apps. + // Ignite Apps are implemented as plugins. + Apps []Plugin `yaml:"apps"` } // Plugin keeps plugin name and location. @@ -38,10 +40,12 @@ type Plugin struct { // // 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/plugins/plugins.yml) or locally for a chain. + // (default: $HOME/.ignite/apps/igniteapps.yml) or locally for a chain. Global bool `yaml:"-"` } @@ -134,7 +138,7 @@ func (c *Config) Save() error { // 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.Plugins, func(cp Plugin) bool { + return slices.ContainsFunc(c.Apps, func(cp Plugin) bool { if cp.HasPath(path) { return true } diff --git a/ignite/config/plugins/config_test.go b/ignite/config/plugins/config_test.go index b560b6f770..5986efe8ad 100644 --- a/ignite/config/plugins/config_test.go +++ b/ignite/config/plugins/config_test.go @@ -268,30 +268,30 @@ func TestConfigSave(t *testing.T) { require.NoError(t, err) return cfg }, - expectedContent: "plugins: []\n", + expectedContent: "apps: []\n", }, { name: "ok: config path is an existing file", buildConfig: func(t *testing.T) *pluginsconfig.Config { - // copy testdata/plugins.yml to tmp because it will be modified + // copy testdata/igniteapps.yml to tmp because it will be modified dir := t.TempDir() - bz, err := os.ReadFile("testdata/plugins.yml") + bz, err := os.ReadFile("testdata/igniteapps.yml") require.NoError(t, err) - err = os.WriteFile(path.Join(dir, "plugins.yml"), bz, 0o666) + err = os.WriteFile(path.Join(dir, "igniteapps.yml"), bz, 0o666) require.NoError(t, err) // load from tmp cfg, _ := pluginsconfig.ParseDir(dir) // add a new plugin - cfg.Plugins = append(cfg.Plugins, pluginsconfig.Plugin{ + cfg.Apps = append(cfg.Apps, pluginsconfig.Plugin{ Path: "/path/to/plugin3", With: map[string]string{"key": "val"}, }) // update a plugin - cfg.Plugins[1].Path = "/path/to/plugin22" - cfg.Plugins[1].With["key"] = "val" + cfg.Apps[1].Path = "/path/to/plugin22" + cfg.Apps[1].With["key"] = "val" return cfg }, - expectedContent: `plugins: + expectedContent: `apps: - path: /path/to/plugin1 - path: /path/to/plugin22 with: @@ -338,7 +338,7 @@ func TestConfigHasPlugin(t *testing.T) { { name: "not found in config", cfg: pluginsconfig.Config{ - Plugins: []pluginsconfig.Plugin{ + Apps: []pluginsconfig.Plugin{ {Path: "github.com/ignite/example2"}, }, }, @@ -347,7 +347,7 @@ func TestConfigHasPlugin(t *testing.T) { { name: "found in config", cfg: pluginsconfig.Config{ - Plugins: []pluginsconfig.Plugin{ + Apps: []pluginsconfig.Plugin{ {Path: "github.com/ignite/example2"}, {Path: "github.com/ignite/example@master"}, }, @@ -357,7 +357,7 @@ func TestConfigHasPlugin(t *testing.T) { { name: "found in config but from a local plugin", cfg: pluginsconfig.Config{ - Plugins: []pluginsconfig.Plugin{ + Apps: []pluginsconfig.Plugin{ {Path: "github.com/ignite/example2"}, {Path: path.Join(wd, "testdata", "localplugin", "example")}, }, diff --git a/ignite/config/plugins/parse.go b/ignite/config/plugins/parse.go index 589cc5c1d6..e823500e18 100644 --- a/ignite/config/plugins/parse.go +++ b/ignite/config/plugins/parse.go @@ -12,7 +12,7 @@ import ( // ParseDir expects to find a plugin config file in dir. If dir is not a folder, // an error is returned. -// The plugin config file format can be `plugins.yml` or `plugins.yaml`. If +// The plugin config file format can be `igniteapps.yml` or `igniteapps.yaml`. If // found, the file is parsed into a Config and returned. If no file from the // given names above are found, then an empty config is returned, w/o errors. func ParseDir(dir string) (*Config, error) { @@ -53,8 +53,7 @@ func ParseDir(dir string) (*Config, error) { } var ( - // filenames is a list of recognized names as Ignite's plugins config file. - filenames = []string{"plugins.yml", "plugins.yaml"} + filenames = []string{"igniteapps.yml", "igniteapps.yaml"} defaultFilename = filenames[0] ) diff --git a/ignite/config/plugins/parse_test.go b/ignite/config/plugins/parse_test.go index 230fe28fbd..abe7558e3a 100644 --- a/ignite/config/plugins/parse_test.go +++ b/ignite/config/plugins/parse_test.go @@ -18,8 +18,8 @@ func TestParseDir(t *testing.T) { }{ { name: "fail: path is not a dir", - path: "testdata/plugins.yml", - expectedError: "plugin config parse: path testdata/plugins.yml is not a dir", + path: "testdata/igniteapps.yml", + expectedError: "plugin config parse: path testdata/igniteapps.yml is not a dir", }, { name: "fail: path doesn't exists", @@ -30,7 +30,7 @@ func TestParseDir(t *testing.T) { name: "ok: path doesn't contain any config", path: "testdata/noconfig", expectedPlugins: nil, - expectedPath: "testdata/noconfig/plugins.yml", + expectedPath: "testdata/noconfig/igniteapps.yml", }, { name: "fail: path contains an invalid yml file", @@ -52,7 +52,7 @@ func TestParseDir(t *testing.T) { }, }, }, - expectedPath: "testdata/plugins.yml", + expectedPath: "testdata/igniteapps.yml", }, { name: "ok: path contains a plugin.yaml file", @@ -69,7 +69,7 @@ func TestParseDir(t *testing.T) { }, }, }, - expectedPath: "testdata/other/plugins.yaml", + expectedPath: "testdata/other/igniteapps.yaml", }, } for _, tt := range tests { @@ -83,7 +83,7 @@ func TestParseDir(t *testing.T) { return } require.NoError(err) - require.Equal(tt.expectedPlugins, cfg.Plugins) + require.Equal(tt.expectedPlugins, cfg.Apps) require.Equal(tt.expectedPath, cfg.Path()) }) } diff --git a/ignite/config/plugins/testdata/other/plugins.yaml b/ignite/config/plugins/testdata/igniteapps.yml similarity index 93% rename from ignite/config/plugins/testdata/other/plugins.yaml rename to ignite/config/plugins/testdata/igniteapps.yml index 9e268110e2..2d541a7553 100644 --- a/ignite/config/plugins/testdata/other/plugins.yaml +++ b/ignite/config/plugins/testdata/igniteapps.yml @@ -1,4 +1,4 @@ -plugins: +apps: - name: plugin1 path: /path/to/plugin1 - name: plugin2 diff --git a/ignite/config/plugins/testdata/invalid/plugins.yml b/ignite/config/plugins/testdata/invalid/igniteapps.yml similarity index 100% rename from ignite/config/plugins/testdata/invalid/plugins.yml rename to ignite/config/plugins/testdata/invalid/igniteapps.yml diff --git a/ignite/config/plugins/testdata/plugins.yml b/ignite/config/plugins/testdata/other/igniteapps.yaml similarity index 93% rename from ignite/config/plugins/testdata/plugins.yml rename to ignite/config/plugins/testdata/other/igniteapps.yaml index 9e268110e2..2d541a7553 100644 --- a/ignite/config/plugins/testdata/plugins.yml +++ b/ignite/config/plugins/testdata/other/igniteapps.yaml @@ -1,4 +1,4 @@ -plugins: +apps: - name: plugin1 path: /path/to/plugin1 - name: plugin2 diff --git a/ignite/internal/tools/gen-cli-docs/main.go b/ignite/internal/tools/gen-cli-docs/main.go index 56ead1c6df..b07cfd4502 100644 --- a/ignite/internal/tools/gen-cli-docs/main.go +++ b/ignite/internal/tools/gen-cli-docs/main.go @@ -58,7 +58,7 @@ func run(outPath string) error { if err != nil { return err } - cfg.Plugins = append(cfg.Plugins, pluginsconfig.Plugin{ + cfg.Apps = append(cfg.Apps, pluginsconfig.Plugin{ // Add network plugin Path: ignitecmd.PluginNetworkPath, }) diff --git a/ignite/pkg/cliui/cliui.go b/ignite/pkg/cliui/cliui.go index 089aad3f57..fbbc098f2e 100644 --- a/ignite/pkg/cliui/cliui.go +++ b/ignite/pkg/cliui/cliui.go @@ -289,6 +289,7 @@ func (s *Session) handleEvents() { s.StopSpinner() fmt.Fprintf(stdout, "%s\n", e) case events.IndicationNone: + fallthrough default: // The text printed here won't be removed when the spinner stops resume := s.PauseSpinner() diff --git a/ignite/pkg/events/events.go b/ignite/pkg/events/events.go index 8b0a7202a8..d5a47d79cf 100644 --- a/ignite/pkg/events/events.go +++ b/ignite/pkg/events/events.go @@ -2,7 +2,11 @@ // for others to consume and display to end users in meaningful ways. package events -import "fmt" +import ( + "fmt" + + "github.com/muesli/reflow/indent" +) // ProgressIndication enumerates possible states of progress indication for an Event. type ProgressIndication uint8 @@ -23,6 +27,7 @@ type ( Event struct { ProgressIndication ProgressIndication Icon string + Indent uint Message string Verbose bool Group string @@ -67,6 +72,13 @@ func Icon(icon string) Option { } } +// Indent sets the text indentation. +func Indent(indent uint) Option { + return func(e *Event) { + e.Indent = indent + } +} + // Group sets a group name for the event. func Group(name string) Option { return func(e *Event) { @@ -86,11 +98,16 @@ func New(message string, options ...Option) Event { } func (e Event) String() string { + s := e.Message if e.Icon != "" { - return fmt.Sprintf("%s %s", e.Icon, e.Message) + s = fmt.Sprintf("%s %s", e.Icon, s) + } + + if e.Indent > 0 { + s = indent.String(s, e.Indent) } - return e.Message + return s } // InProgress returns true when the event is in progress. diff --git a/ignite/services/doctor/doctor.go b/ignite/services/doctor/doctor.go index 51f791236a..95f873ea63 100644 --- a/ignite/services/doctor/doctor.go +++ b/ignite/services/doctor/doctor.go @@ -55,7 +55,7 @@ func (d *Doctor) MigrateConfig(_ context.Context) error { return fmt.Errorf("doctor migrate config: %w", err) } - d.ev.Send("Checking chain config file:", events.ProgressFinish()) + d.ev.Send("Checking chain config file:") configPath, err := chainconfig.LocateDefault(".") if err != nil { @@ -95,6 +95,7 @@ func (d *Doctor) MigrateConfig(_ context.Context) error { d.ev.Send( fmt.Sprintf("config file %s", colors.Success(status)), events.Icon(icons.OK), + events.Indent(1), events.ProgressFinish(), ) @@ -109,7 +110,7 @@ func (d *Doctor) FixDependencyTools(ctx context.Context) error { return fmt.Errorf("doctor fix dependency tools: %w", err) } - d.ev.Send("Checking dependency tools:", events.ProgressFinish()) + d.ev.Send("Checking dependency tools:") _, err := os.Stat(ToolsFile) @@ -118,7 +119,7 @@ func (d *Doctor) FixDependencyTools(ctx context.Context) error { d.ev.Send( fmt.Sprintf("%s %s", ToolsFile, colors.Success("exists")), events.Icon(icons.OK), - events.ProgressUpdate(), + events.Indent(1), ) updated, err := d.ensureDependencyImports(ToolsFile) @@ -134,6 +135,7 @@ func (d *Doctor) FixDependencyTools(ctx context.Context) error { d.ev.Send( fmt.Sprintf("tools file %s", colors.Success(status)), events.Icon(icons.OK), + events.Indent(1), events.ProgressFinish(), ) @@ -142,6 +144,12 @@ func (d *Doctor) FixDependencyTools(ctx context.Context) error { return errf(err) } + d.ev.Send( + fmt.Sprintf("tools file %s", colors.Success("created")), + events.Icon(icons.OK), + events.Indent(1), + ) + default: return errf(err) } @@ -177,10 +185,10 @@ func (d Doctor) createToolsFile(ctx context.Context, toolsFilename string) error d.ev.Send( fmt.Sprintf("%s %s", toolsFilename, colors.Success("created")), events.Icon(icons.OK), - events.ProgressFinish(), + events.Indent(1), ) - d.ev.Send("Installing dependency tools", events.ProgressStart()) + d.ev.Send("Installing dependency tools", events.ProgressUpdate()) if err := cosmosgen.InstallDepTools(ctx, "."); err != nil { return err } @@ -189,7 +197,7 @@ func (d Doctor) createToolsFile(ctx context.Context, toolsFilename string) error d.ev.Send( fmt.Sprintf("%s %s", path.Base(dep), colors.Success("installed")), events.Icon(icons.OK), - events.ProgressFinish(), + events.Indent(1), ) } @@ -225,5 +233,12 @@ func (d Doctor) ensureDependencyImports(toolsFilename string) (bool, error) { return false, err } + d.ev.Send( + fmt.Sprintf("tools dependencies %s", colors.Success("OK")), + events.Icon(icons.OK), + events.Indent(1), + events.ProgressFinish(), + ) + return true, nil } diff --git a/ignite/services/doctor/doctor_plugins.go b/ignite/services/doctor/doctor_plugins.go new file mode 100644 index 0000000000..fdc1ce4bc5 --- /dev/null +++ b/ignite/services/doctor/doctor_plugins.go @@ -0,0 +1,210 @@ +package doctor + +import ( + "bytes" + "errors" + "fmt" + "io" + "os" + "path/filepath" + + "gopkg.in/yaml.v2" + + "github.com/ignite/cli/ignite/config" + chainconfig "github.com/ignite/cli/ignite/config/chain" + "github.com/ignite/cli/ignite/pkg/cliui/colors" + "github.com/ignite/cli/ignite/pkg/cliui/icons" + "github.com/ignite/cli/ignite/pkg/events" +) + +// MigratePluginsConfig migrates plugins config to Ignite App config if required. +func (d Doctor) MigratePluginsConfig() error { + errf := func(err error) error { + return fmt.Errorf("doctor migrate plugins config: %w", err) + } + + d.ev.Send("Checking for legacy plugin config files:") + d.ev.Send("Searching global plugins config file", events.ProgressStart()) + + if err := d.migrateGlobalPluginConfig(); err != nil { + return errf(err) + } + + d.ev.Send("Searching local plugins config file", events.ProgressUpdate()) + + if err := d.migrateLocalPluginsConfig(); err != nil { + return errf(err) + } + + d.ev.Send( + fmt.Sprintf("plugin config files %s", colors.Success("OK")), + events.Icon(icons.OK), + events.Indent(1), + events.ProgressFinish(), + ) + + return nil +} + +func (d Doctor) migrateGlobalPluginConfig() error { + globalPath, err := config.DirPath() + if err != nil { + return err + } + + // Global apps directory is always available because it is + // created if it doesn't exists when any command is executed. + appsPath := filepath.Join(globalPath, "apps", "igniteapps.yml") + if _, err := os.Stat(appsPath); err == nil { + d.ev.Send( + fmt.Sprintf("%s %s", appsPath, colors.Success("exists")), + events.Icon(icons.OK), + events.Indent(1), + ) + + // Ignite apps config file exists in global directory + return nil + } + + legacyPath, err := findPluginsConfigPath(filepath.Join(globalPath, "plugins")) + if err != nil { + return err + } else if legacyPath == "" { + // Nothing to migrate when the legacy plugins config path doesn't exist + return nil + } + + if err := d.migratePluginsConfigFiles(legacyPath, appsPath); err != nil { + return err + } + + d.ev.SendInfo( + fmt.Sprintf("directory %s can safely be removed", filepath.Dir(legacyPath)), + events.Icon(icons.Info), + events.Indent(1), + ) + + return nil +} + +func (d Doctor) migrateLocalPluginsConfig() error { + localPath, err := chainconfig.LocateDefault(".") + if err != nil { + if errors.Is(err, chainconfig.ErrConfigNotFound) { + // When app config is not found it means the doctor + // command is not being run within a blockchain app, + // so there is not local config to migrate + return nil + } + + return err + } + + localPath, err = filepath.Abs(filepath.Dir(localPath)) + if err != nil { + return err + } + + appsPath := filepath.Join(localPath, "igniteapps.yml") + if _, err := os.Stat(appsPath); err == nil { + d.ev.Send( + fmt.Sprintf("%s %s", appsPath, colors.Success("exists")), + events.Icon(icons.OK), + events.Indent(1), + ) + + // Ignite apps config file exists in current directory + return nil + } + + legacyPath, err := findPluginsConfigPath(localPath) + if err != nil { + return err + } else if legacyPath == "" { + // Nothing to migrate when plugins config file is not found in current directory + return nil + } + + return d.migratePluginsConfigFiles(legacyPath, appsPath) +} + +func (d Doctor) migratePluginsConfigFiles(pluginsPath, appsPath string) error { + pluginsFile, err := os.Open(pluginsPath) + if err != nil { + return err + } + + defer pluginsFile.Close() + + appsFile, err := os.OpenFile(appsPath, os.O_WRONLY|os.O_CREATE, 0o644) + if err != nil { + return err + } + + defer appsFile.Close() + + if err = migratePluginsConfig(pluginsFile, appsFile); err != nil { + return err + } + + d.ev.Send( + fmt.Sprintf("migrated config file %s to %s", colors.Faint(pluginsPath), colors.Faint(appsPath)), + events.Icon(icons.OK), + events.Indent(1), + ) + d.ev.SendInfo( + fmt.Sprintf("file %s can safely be removed", pluginsPath), + events.Icon(icons.Info), + events.Indent(1), + ) + + return nil +} + +func migratePluginsConfig(r io.Reader, w io.Writer) error { + bz, err := updatePluginsConfig(r) + if err != nil { + return err + } + + _, err = w.Write(bz) + if err != nil { + return err + } + return nil +} + +func updatePluginsConfig(r io.Reader) ([]byte, error) { + var cfg map[string]any + err := yaml.NewDecoder(r).Decode(&cfg) + if err != nil && !errors.Is(err, io.EOF) { + return nil, err + } + + if apps, ok := cfg["plugins"]; ok { + cfg["apps"] = apps + delete(cfg, "plugins") + } + + var buf bytes.Buffer + if err = yaml.NewEncoder(&buf).Encode(cfg); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +func findPluginsConfigPath(dir string) (string, error) { + for _, ext := range []string{"yml", "yaml"} { + path := filepath.Join(dir, fmt.Sprintf("plugins.%s", ext)) + _, err := os.Stat(path) + if err == nil { + // File found + return path, nil + } + + if !os.IsNotExist(err) { + return "", err + } + } + return "", nil +} diff --git a/ignite/services/plugin/plugin.go b/ignite/services/plugin/plugin.go index d1b66d2dbb..28091675a6 100644 --- a/ignite/services/plugin/plugin.go +++ b/ignite/services/plugin/plugin.go @@ -31,7 +31,7 @@ import ( // PluginsPath holds the plugin cache directory. var PluginsPath = xfilepath.Mkdir(xfilepath.Join( config.DirPath, - xfilepath.Path("plugins"), + xfilepath.Path("apps"), )) // Plugin represents a ignite plugin. @@ -76,11 +76,11 @@ func CollectEvents(ev events.Bus) Option { // There's 2 kinds of plugins, local or remote. // Local plugins have their path starting with a `/`, while remote plugins don't. // Local plugins are useful for development purpose. -// Remote plugins require to be fetched first, in $HOME/.ignite/plugins folder, then they are loaded -// from there. +// Remote plugins require to be fetched first, in $HOME/.ignite/apps folder, +// then they are loaded from there. // -// If an error occurs during a plugin load, it's not returned but rather stored in the `Plugin.Error` -// field. This prevents the loading of other plugins to be interrupted. +// If an error occurs during a plugin load, it's not returned but rather stored in +// the `Plugin.Error` field. This prevents the loading of other plugins to be interrupted. func Load(ctx context.Context, plugins []pluginsconfig.Plugin, options ...Option) ([]*Plugin, error) { pluginsDir, err := PluginsPath() if err != nil { @@ -163,8 +163,8 @@ func newPlugin(pluginsDir string, cp pluginsconfig.Plugin, options ...Option) *P p.cloneDir = path.Join(pluginsDir, p.repoPath) } - // Plugin can have a subpath within its repository. For example, - // "github.com/ignite/plugins/plugin1" where "plugin1" is the subpath. + // Plugin can have a subpath within its repository. + // For example, "github.com/ignite/apps/app1" where "app1" is the subpath. repoSubPath := path.Join(parts[3:]...) p.srcPath = path.Join(p.cloneDir, repoSubPath) diff --git a/ignite/services/plugin/plugin_test.go b/ignite/services/plugin/plugin_test.go index 36583953a3..08d59f91b6 100644 --- a/ignite/services/plugin/plugin_test.go +++ b/ignite/services/plugin/plugin_test.go @@ -79,9 +79,9 @@ func TestNewPlugin(t *testing.T) { expectedPlugin: Plugin{ repoPath: "github.com/ignite/plugin", cloneURL: "https://github.com/ignite/plugin", - cloneDir: ".ignite/plugins/github.com/ignite/plugin", + cloneDir: ".ignite/apps/github.com/ignite/plugin", reference: "", - srcPath: ".ignite/plugins/github.com/ignite/plugin", + srcPath: ".ignite/apps/github.com/ignite/plugin", name: "plugin", }, }, @@ -91,9 +91,9 @@ func TestNewPlugin(t *testing.T) { expectedPlugin: Plugin{ repoPath: "github.com/ignite/plugin@develop", cloneURL: "https://github.com/ignite/plugin", - cloneDir: ".ignite/plugins/github.com/ignite/plugin-develop", + cloneDir: ".ignite/apps/github.com/ignite/plugin-develop", reference: "develop", - srcPath: ".ignite/plugins/github.com/ignite/plugin-develop", + srcPath: ".ignite/apps/github.com/ignite/plugin-develop", name: "plugin", }, }, @@ -103,9 +103,9 @@ func TestNewPlugin(t *testing.T) { expectedPlugin: Plugin{ repoPath: "github.com/ignite/plugin@package/v1.0.0", cloneURL: "https://github.com/ignite/plugin", - cloneDir: ".ignite/plugins/github.com/ignite/plugin-package-v1.0.0", + cloneDir: ".ignite/apps/github.com/ignite/plugin-package-v1.0.0", reference: "package/v1.0.0", - srcPath: ".ignite/plugins/github.com/ignite/plugin-package-v1.0.0", + srcPath: ".ignite/apps/github.com/ignite/plugin-package-v1.0.0", name: "plugin", }, }, @@ -115,9 +115,9 @@ func TestNewPlugin(t *testing.T) { expectedPlugin: Plugin{ repoPath: "github.com/ignite/plugin", cloneURL: "https://github.com/ignite/plugin", - cloneDir: ".ignite/plugins/github.com/ignite/plugin", + cloneDir: ".ignite/apps/github.com/ignite/plugin", reference: "", - srcPath: ".ignite/plugins/github.com/ignite/plugin/plugin1", + srcPath: ".ignite/apps/github.com/ignite/plugin/plugin1", name: "plugin1", }, }, @@ -127,9 +127,9 @@ func TestNewPlugin(t *testing.T) { expectedPlugin: Plugin{ repoPath: "github.com/ignite/plugin@develop", cloneURL: "https://github.com/ignite/plugin", - cloneDir: ".ignite/plugins/github.com/ignite/plugin-develop", + cloneDir: ".ignite/apps/github.com/ignite/plugin-develop", reference: "develop", - srcPath: ".ignite/plugins/github.com/ignite/plugin-develop/plugin1", + srcPath: ".ignite/apps/github.com/ignite/plugin-develop/plugin1", name: "plugin1", }, }, @@ -139,9 +139,9 @@ func TestNewPlugin(t *testing.T) { expectedPlugin: Plugin{ repoPath: "github.com/ignite/plugin@package/v1.0.0", cloneURL: "https://github.com/ignite/plugin", - cloneDir: ".ignite/plugins/github.com/ignite/plugin-package-v1.0.0", + cloneDir: ".ignite/apps/github.com/ignite/plugin-package-v1.0.0", reference: "package/v1.0.0", - srcPath: ".ignite/plugins/github.com/ignite/plugin-package-v1.0.0/plugin1", + srcPath: ".ignite/apps/github.com/ignite/plugin-package-v1.0.0/plugin1", name: "plugin1", }, }, @@ -150,7 +150,7 @@ func TestNewPlugin(t *testing.T) { t.Run(tt.name, func(t *testing.T) { tt.expectedPlugin.Plugin = tt.pluginCfg - p := newPlugin(".ignite/plugins", tt.pluginCfg) + p := newPlugin(".ignite/apps", tt.pluginCfg) assertPlugin(t, tt.expectedPlugin, *p) }) diff --git a/integration/plugin/plugin_test.go b/integration/plugin/plugin_test.go index fa67287c5d..551ce600ee 100644 --- a/integration/plugin/plugin_test.go +++ b/integration/plugin/plugin_test.go @@ -23,13 +23,13 @@ func TestAddRemovePlugin(t *testing.T) { assertPlugins = func(expectedLocalPlugins, expectedGlobalPlugins []pluginsconfig.Plugin) { localCfg, err := pluginsconfig.ParseDir(app.SourcePath()) require.NoError(err) - assert.ElementsMatch(expectedLocalPlugins, localCfg.Plugins, "unexpected local plugins") + assert.ElementsMatch(expectedLocalPlugins, localCfg.Apps, "unexpected local plugins") globalCfgPath, err := plugin.PluginsPath() require.NoError(err) globalCfg, err := pluginsconfig.ParseDir(globalCfgPath) require.NoError(err) - assert.ElementsMatch(expectedGlobalPlugins, globalCfg.Plugins, "unexpected global plugins") + assert.ElementsMatch(expectedGlobalPlugins, globalCfg.Apps, "unexpected global plugins") } ) From a9a3144f17b62bfeeae5c5a509c336dec343fa94 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 4 Oct 2023 16:13:27 +0200 Subject: [PATCH 8/9] chore: go formatting (#3622) Co-authored-by: jeronimoalbi --- ignite/pkg/cliui/view/accountview/account_test.go | 3 ++- ignite/pkg/cosmosfaucet/transfer.go | 1 + ignite/services/chain/faucet.go | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/ignite/pkg/cliui/view/accountview/account_test.go b/ignite/pkg/cliui/view/accountview/account_test.go index 218d9375cc..e5cb240357 100644 --- a/ignite/pkg/cliui/view/accountview/account_test.go +++ b/ignite/pkg/cliui/view/accountview/account_test.go @@ -3,8 +3,9 @@ package accountview_test import ( "testing" - "github.com/ignite/cli/ignite/pkg/cliui/view/accountview" "github.com/stretchr/testify/assert" + + "github.com/ignite/cli/ignite/pkg/cliui/view/accountview" ) func TestAccountString(t *testing.T) { diff --git a/ignite/pkg/cosmosfaucet/transfer.go b/ignite/pkg/cosmosfaucet/transfer.go index 37a437e648..4141977cc7 100644 --- a/ignite/pkg/cosmosfaucet/transfer.go +++ b/ignite/pkg/cosmosfaucet/transfer.go @@ -9,6 +9,7 @@ import ( sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" + chaincmdrunner "github.com/ignite/cli/ignite/pkg/chaincmd/runner" ) diff --git a/ignite/services/chain/faucet.go b/ignite/services/chain/faucet.go index 07cc1c7c02..1777780c1b 100644 --- a/ignite/services/chain/faucet.go +++ b/ignite/services/chain/faucet.go @@ -10,6 +10,7 @@ import ( "github.com/pkg/errors" sdkmath "cosmossdk.io/math" + chainconfig "github.com/ignite/cli/ignite/config/chain" chaincmdrunner "github.com/ignite/cli/ignite/pkg/chaincmd/runner" "github.com/ignite/cli/ignite/pkg/cosmosfaucet" From 88ffe6208f81e158b096f8aeb78b8778075c875b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jer=C3=B3nimo=20Albi?= Date: Thu, 5 Oct 2023 12:27:07 +0200 Subject: [PATCH 9/9] refactor: rename CLI "plugin" command to "app" (#3662) * refactor: rename "plugin" to "app" * refactor: simplify code * chore: update changelog * tests: fixed unit tests * tests: fix integration tests * chore: change plugin template to use `app` as the type name * chore: update plugin code documentation to use "app" instead * tests: fixed service unit tests * chore: update comments * chore: fix typo * chore: improve command description * chore: restore plugin to test case names * chore: remove commented unit test code * chore: change app commands to properly set example command Co-authored-by: Julien Robert * chore: remove redundant plugin path check --- changelog.md | 1 + ignite/cmd/cmd.go | 8 +- ignite/cmd/plugin.go | 217 ++++++++++-------- ignite/cmd/plugin_default.go | 8 - ignite/cmd/plugin_test.go | 35 +-- ignite/config/plugins/config.go | 6 +- ignite/pkg/cliui/icons/icon.go | 9 +- ignite/services/plugin/cache.go | 2 +- ignite/services/plugin/interface.go | 78 ++++--- ignite/services/plugin/plugin.go | 21 +- ignite/services/plugin/plugin_test.go | 106 ++++----- ignite/services/plugin/scaffold.go | 2 +- ignite/services/plugin/template/main.go.plush | 14 +- integration/plugin/plugin_test.go | 20 +- 14 files changed, 261 insertions(+), 266 deletions(-) diff --git a/changelog.md b/changelog.md index d93a62dae8..6dc419c068 100644 --- a/changelog.md +++ b/changelog.md @@ -17,6 +17,7 @@ - [#3658](https://github.com/ignite/cli/pull/3658) Rename Marshaler to Codec in EncodingConfig - [#3653](https://github.com/ignite/cli/pull/3653) Add "app" extension to plugin binaries - [#3656](https://github.com/ignite/cli/pull/3656) Disable Go toolchain download +- [#3662](https://github.com/ignite/cli/pull/3662) Refactor CLI "plugin" command to "app" - [#3669](https://github.com/ignite/cli/pull/3669) Rename `plugins` config file to `igniteapps` ### Fixes diff --git a/ignite/cmd/cmd.go b/ignite/cmd/cmd.go index 311f8d5ea1..7549336770 100644 --- a/ignite/cmd/cmd.go +++ b/ignite/cmd/cmd.go @@ -39,8 +39,8 @@ const ( ) // New creates a new root command for `Ignite CLI` with its sub commands. -// Returns the cobra.Command, a cleanUp function and an error. The cleanUp -// function must be invoked by the caller to clean eventual plugin instances. +// Returns the cobra.Command, a cleanup function and an error. The cleanup +// function must be invoked by the caller to clean eventual Ignite App instances. func New(ctx context.Context) (*cobra.Command, func(), error) { cobra.EnableCommandSorting = false @@ -78,14 +78,14 @@ To get started, create a blockchain: NewTools(), NewDocs(), NewVersion(), - NewPlugin(), + NewApp(), NewDoctor(), ) c.AddCommand(deprecated()...) // Load plugins if any if err := LoadPlugins(ctx, c); err != nil { - return nil, nil, fmt.Errorf("error while loading plugins: %w", err) + return nil, nil, fmt.Errorf("error while loading apps: %w", err) } return c, UnloadPlugins, nil } diff --git a/ignite/cmd/plugin.go b/ignite/cmd/plugin.go index 64e77048fb..9c114149b3 100644 --- a/ignite/cmd/plugin.go +++ b/ignite/cmd/plugin.go @@ -76,7 +76,7 @@ func parseLocalPlugins(cmd *cobra.Command) (*pluginsconfig.Config, error) { _ = cmd wd, err := os.Getwd() if err != nil { - return nil, fmt.Errorf("parse local plugins: %w", err) + return nil, fmt.Errorf("parse local apps: %w", err) } if err := cosmosanalysis.IsChainPath(wd); err != nil { return nil, err @@ -165,11 +165,11 @@ func linkPluginHook(rootCmd *cobra.Command, p *plugin.Plugin, hook plugin.Hook) cmdPath := hook.PlaceHookOnFull() cmd := findCommandByPath(rootCmd, cmdPath) if cmd == nil { - p.Error = errors.Errorf("unable to find commandPath %q for plugin hook %q", cmdPath, hook.Name) + 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 plugin hook %q to non executable command %q", hook.Name, hook.PlaceHookOn) + p.Error = errors.Errorf("can't attach app hook %q to non executable command %q", hook.Name, hook.PlaceHookOn) return } @@ -198,7 +198,7 @@ func linkPluginHook(rootCmd *cobra.Command, p *plugin.Plugin, hook plugin.Hook) } err := p.Interface.ExecuteHookPre(newExecutedHook(hook, cmd, args)) if err != nil { - return fmt.Errorf("plugin %q ExecuteHookPre() error: %w", p.Path, err) + return fmt.Errorf("app %q ExecuteHookPre() error: %w", p.Path, err) } return nil } @@ -212,7 +212,7 @@ func linkPluginHook(rootCmd *cobra.Command, p *plugin.Plugin, hook plugin.Hook) if err != nil { err := p.Interface.ExecuteHookCleanUp(newExecutedHook(hook, cmd, args)) if err != nil { - fmt.Printf("plugin %q ExecuteHookCleanUp() error: %v", p.Path, err) + fmt.Printf("app %q ExecuteHookCleanUp() error: %v", p.Path, err) } } return err @@ -229,7 +229,7 @@ func linkPluginHook(rootCmd *cobra.Command, p *plugin.Plugin, hook plugin.Hook) defer func() { err := p.Interface.ExecuteHookCleanUp(execHook) if err != nil { - fmt.Printf("plugin %q ExecuteHookCleanUp() error: %v", p.Path, err) + fmt.Printf("app %q ExecuteHookCleanUp() error: %v", p.Path, err) } }() @@ -243,7 +243,7 @@ func linkPluginHook(rootCmd *cobra.Command, p *plugin.Plugin, hook plugin.Hook) err := p.Interface.ExecuteHookPost(execHook) if err != nil { - return fmt.Errorf("plugin %q ExecuteHookPost() error : %w", p.Path, err) + return fmt.Errorf("app %q ExecuteHookPost() error : %w", p.Path, err) } return nil } @@ -267,11 +267,11 @@ func linkPluginCmd(rootCmd *cobra.Command, p *plugin.Plugin, pluginCmd plugin.Co cmdPath := pluginCmd.PlaceCommandUnderFull() cmd := findCommandByPath(rootCmd, cmdPath) if cmd == nil { - p.Error = errors.Errorf("unable to find commandPath %q for plugin %q", cmdPath, p.Path) + 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 plugin command %q to runnable command %q", pluginCmd.Use, cmd.CommandPath()) + p.Error = errors.Errorf("can't attach app command %q to runnable command %q", pluginCmd.Use, cmd.CommandPath()) return } @@ -281,7 +281,7 @@ func linkPluginCmd(rootCmd *cobra.Command, p *plugin.Plugin, pluginCmd plugin.Co pluginCmdName := strings.Split(pluginCmd.Use, " ")[0] for _, cmd := range cmd.Commands() { if cmd.Name() == pluginCmdName { - p.Error = errors.Errorf("plugin command %q already exists in ignite's commands", pluginCmdName) + p.Error = errors.Errorf("app command %q already exists in Ignite's commands", pluginCmdName) return } } @@ -343,30 +343,30 @@ func findCommandByPath(cmd *cobra.Command, cmdPath string) *cobra.Command { return nil } -// NewPlugin returns a command that groups plugin related sub commands. -func NewPlugin() *cobra.Command { +// NewApp returns a command that groups Ignite App related sub commands. +func NewApp() *cobra.Command { c := &cobra.Command{ - Use: "plugin [command]", - Short: "Handle plugins", + Use: "app [command]", + Short: "Create and manage Ignite Apps", } c.AddCommand( - NewPluginList(), - NewPluginUpdate(), - NewPluginScaffold(), - NewPluginDescribe(), - NewPluginAdd(), - NewPluginRemove(), + NewAppList(), + NewAppUpdate(), + NewAppScaffold(), + NewAppDescribe(), + NewAppInstall(), + NewAppUninstall(), ) return c } -func NewPluginList() *cobra.Command { +func NewAppList() *cobra.Command { lstCmd := &cobra.Command{ Use: "list", - Short: "List declared plugins and status", - Long: "Prints status and information of declared plugins", + Short: "List installed apps", + Long: "Prints status and information of all installed Ignite Apps.", RunE: func(cmd *cobra.Command, args []string) error { s := cliui.New(cliui.WithStdout(os.Stdout)) return printPlugins(s) @@ -375,12 +375,15 @@ func NewPluginList() *cobra.Command { return lstCmd } -func NewPluginUpdate() *cobra.Command { +func NewAppUpdate() *cobra.Command { return &cobra.Command{ Use: "update [path]", - Short: "Update plugins", - Long: "Updates a plugin specified by path. If no path is specified all declared plugins are updated", - Args: cobra.MaximumNArgs(1), + 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(cmd *cobra.Command, args []string) error { if len(args) == 0 { // update all plugins @@ -388,7 +391,7 @@ func NewPluginUpdate() *cobra.Command { if err != nil { return err } - fmt.Printf("All plugins updated.\n") + fmt.Printf("All apps updated.\n") return nil } // find the plugin to update @@ -398,25 +401,24 @@ func NewPluginUpdate() *cobra.Command { if err != nil { return err } - fmt.Printf("Plugin %q updated.\n", p.Path) + fmt.Printf("App %q updated.\n", p.Path) return nil } } - return errors.Errorf("Plugin %q not found", args[0]) + return errors.Errorf("App %q not found", args[0]) }, } } -func NewPluginAdd() *cobra.Command { +func NewAppInstall() *cobra.Command { cmdPluginAdd := &cobra.Command{ - Use: "add [path] [key=value]...", - 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: - ignite plugin add github.com/org/my-plugin/ foo=bar baz=qux`, - Args: cobra.MinimumNArgs(1), + 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)) defer session.End() @@ -438,7 +440,7 @@ Example: for _, p := range conf.Apps { if p.Path == args[0] { - return fmt.Errorf("cannot add duplicate plugin %s", args[0]) + return fmt.Errorf("app %s is already installed", args[0]) } } @@ -465,7 +467,7 @@ Example: p.With[kv[0]] = kv[1] } - session.StartSpinner("Loading plugin") + session.StartSpinner("Loading app") plugins, err := plugin.Load(cmd.Context(), []pluginsconfig.Plugin{p}, pluginsOptions...) if err != nil { return err @@ -473,16 +475,16 @@ Example: defer plugins[0].KillClient() if plugins[0].Error != nil { - return fmt.Errorf("error while loading plugin %q: %w", args[0], plugins[0].Error) + return fmt.Errorf("error while loading app %q: %w", args[0], plugins[0].Error) } - session.Println("Done loading plugin") + session.Println(icons.OK, "Done loading apps") conf.Apps = append(conf.Apps, p) if err := conf.Save(); err != nil { return err } - session.Printf("🎉 %s added \n", args[0]) + session.Printf("%s Installed %s\n", icons.Tada, args[0]) return nil }, } @@ -492,11 +494,13 @@ Example: return cmdPluginAdd } -func NewPluginRemove() *cobra.Command { +func NewAppUninstall() *cobra.Command { cmdPluginRemove := &cobra.Command{ - Use: "remove [path]", + Use: "uninstall [path]", Aliases: []string{"rm"}, - Short: "Removes a plugin declaration from a chain's plugin configuration", + 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)) @@ -527,14 +531,14 @@ func NewPluginRemove() *cobra.Command { if !removed { // return if no matching plugin path found - return fmt.Errorf("plugin %s not found", args[0]) + return fmt.Errorf("app %s not found", args[0]) } if err := conf.Save(); err != nil { return err } - s.Printf("%s %s removed\n", icons.OK, args[0]) + s.Printf("%s %s uninstalled\n", icons.OK, args[0]) s.Printf("\t%s updated\n", conf.Path()) return nil @@ -546,12 +550,15 @@ func NewPluginRemove() *cobra.Command { return cmdPluginRemove } -func NewPluginScaffold() *cobra.Command { +func NewAppScaffold() *cobra.Command { return &cobra.Command{ - Use: "scaffold [github.com/org/repo]", - Short: "Scaffold a new plugin", - Long: "Scaffolds a new plugin in the current directory with the given repository path configured. A git repository will be created with the given module name, unless the current directory is already a git repository.", - Args: cobra.ExactArgs(1), + 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)) defer session.End() @@ -569,19 +576,19 @@ func NewPluginScaffold() *cobra.Command { return err } - message := ` -⭐️ Successfully created a new plugin '%[1]s'. -👉 update plugin code at '%[2]s/main.go' + message := `⭐️ Successfully created a new Ignite App '%[1]s'. -👉 test plugin integration by adding the plugin to a chain's config: +👉 Update app code at '%[2]s/main.go' - ignite plugin add %[2]s +👉 Test Ignite App integration by installing the app within the chain directory: -Or to the global config: + ignite app install %[2]s - ignite plugin add -g %[2]s +Or globally: -👉 once the plugin is pushed to a repository, replace the local path by the repository path. + 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 @@ -589,12 +596,13 @@ Or to the global config: } } -func NewPluginDescribe() *cobra.Command { +func NewAppDescribe() *cobra.Command { return &cobra.Command{ - Use: "describe [path]", - Short: "Output information about the a registered plugin", - Long: "Output information about a registered plugins commands and hooks.", - Args: cobra.ExactArgs(1), + 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 { s := cliui.New(cliui.WithStdout(os.Stdout)) @@ -602,18 +610,24 @@ func NewPluginDescribe() *cobra.Command { if p.Path == args[0] { manifest, err := p.Interface.Manifest() if err != nil { - return fmt.Errorf("error while loading plugin manifest: %w", err) + return fmt.Errorf("error while loading app manifest: %w", err) } - s.Printf("Plugin '%s':\n", args[0]) - s.Printf("%s %d Command(s):\n", icons.Command, len(manifest.Commands)) - for i, c := range manifest.Commands { - cmdPath := fmt.Sprintf("%s %s", c.PlaceCommandUnderFull(), c.Use) - s.Printf("\t%d) '%s'\n", i+1, cmdPath) + + if len(manifest.Commands) > 0 { + s.Println("Commands:") + for i, c := range manifest.Commands { + cmdPath := fmt.Sprintf("%s %s", c.PlaceCommandUnderFull(), c.Use) + s.Printf(" %d) %s\n", i+1, cmdPath) + } } - s.Printf("%s %d Hook(s):\n", icons.Hook, len(manifest.Hooks)) - for i, h := range manifest.Hooks { - s.Printf("\t%d) '%s' on command '%s'\n", i+1, h.Name, h.PlaceHookOnFull()) + + 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.PlaceHookOnFull()) + } } + break } } @@ -623,35 +637,34 @@ func NewPluginDescribe() *cobra.Command { } } +func getPluginLocationName(p *plugin.Plugin) string { + if p.IsGlobal() { + return "global" + } + return "local" +} + +func getPluginStatus(p *plugin.Plugin) string { + if p.Error != nil { + return fmt.Sprintf("%s Error: %v", icons.NotOK, p.Error) + } + + _, err := p.Interface.Manifest() + if err != nil { + return fmt.Sprintf("%s Error: Manifest() returned %v", icons.NotOK, err) + } + + return fmt.Sprintf("%s Loaded", icons.OK) +} + func printPlugins(session *cliui.Session) error { - var ( - entries [][]string - buildStatus = func(p *plugin.Plugin) string { - if p.Error != nil { - return fmt.Sprintf("%s Error: %v", icons.NotOK, p.Error) - } - manifest, err := p.Interface.Manifest() - if err != nil { - return fmt.Sprintf("%s Error: Manifest() returned %v", icons.NotOK, err) - } - var ( - hookCount = len(manifest.Hooks) - cmdCount = len(manifest.Commands) - ) - return fmt.Sprintf("%s Loaded: %s %d %s%d ", icons.OK, icons.Command, cmdCount, icons.Hook, hookCount) - } - installedStatus = func(p *plugin.Plugin) string { - if p.IsGlobal() { - return "global" - } - return "local" - } - ) + var entries [][]string for _, p := range plugins { - entries = append(entries, []string{p.Path, buildStatus(p), installedStatus(p)}) + entries = append(entries, []string{p.Path, getPluginLocationName(p), getPluginStatus(p)}) } - if err := session.PrintTable([]string{"Path", "Status", "Config"}, entries...); err != nil { - return fmt.Errorf("error while printing plugins: %w", err) + + if err := session.PrintTable([]string{"Path", "Config", "Status"}, entries...); err != nil { + return fmt.Errorf("error while printing apps: %w", err) } return nil } diff --git a/ignite/cmd/plugin_default.go b/ignite/cmd/plugin_default.go index a7a2424197..6d9b995704 100644 --- a/ignite/cmd/plugin_default.go +++ b/ignite/cmd/plugin_default.go @@ -1,8 +1,6 @@ package ignitecmd import ( - "fmt" - "github.com/spf13/cobra" pluginsconfig "github.com/ignite/cli/ignite/config/plugins" @@ -63,12 +61,6 @@ func newPluginInstallCmd(dp defaultPlugin) *cobra.Command { if err != nil { return err } - if cfg.HasPlugin(dp.path) { - // plugin already declared in global plugins, this shouldn't happen - // because this is actually why this command has been added, so let's - // break violently - panic(fmt.Sprintf("plugin %q unexpected in global config", dp.path)) - } // add plugin to config pluginCfg := pluginsconfig.Plugin{ diff --git a/ignite/cmd/plugin_test.go b/ignite/cmd/plugin_test.go index 6fe44382a2..baded90f0a 100644 --- a/ignite/cmd/plugin_test.go +++ b/ignite/cmd/plugin_test.go @@ -167,7 +167,7 @@ ignite nil, ) }, - expectedError: `can't attach plugin command "foo" to runnable command "ignite scaffold chain"`, + expectedError: `can't attach app command "foo" to runnable command "ignite scaffold chain"`, }, { name: "fail: link to unknown command", @@ -183,7 +183,7 @@ ignite nil, ) }, - expectedError: `unable to find commandPath "ignite unknown" for plugin "foo"`, + expectedError: `unable to find command path "ignite unknown" for app "foo"`, }, { name: "fail: plugin name exists in legacy commands", @@ -198,7 +198,7 @@ ignite nil, ) }, - expectedError: `plugin command "scaffold" already exists in ignite's commands`, + expectedError: `app command "scaffold" already exists in Ignite's commands`, }, { name: "fail: plugin name with args exists in legacy commands", @@ -213,7 +213,7 @@ ignite nil, ) }, - expectedError: `plugin command "scaffold" already exists in ignite's commands`, + expectedError: `app command "scaffold" already exists in Ignite's commands`, }, { name: "fail: plugin name exists in legacy sub commands", @@ -229,7 +229,7 @@ ignite nil, ) }, - expectedError: `plugin command "chain" already exists in ignite's commands`, + expectedError: `app command "chain" already exists in Ignite's commands`, }, { name: "ok: link multiple at root", @@ -412,27 +412,6 @@ func TestLinkPluginHooks(t *testing.T) { expectedError string setup func(*testing.T, *mocks.PluginInterface) }{ - // TODO(tb): commented because linkPluginCmds is not invoked in this test, - // so it's not possible to assert that a hook can't be placed on a plugin - // command. - /* - { - name: "fail: hook plugin command", - setup: func(t *testing.T, p*mocks.PluginInterface) { - p.EXPECT().Manifest().Return(plugin.Manifest{Commands:[]plugin.Command{{Use: "test-plugin"}}, nil) - p.EXPECT().Manifest().Return(plugin.Manifest{ - []plugin.Hook{ - { - Name: "test-hook", - PlaceHookOn: "ignite test-plugin", - }, - }, - nil, - ) - }, - expectedError: `unable to find commandPath "ignite test-plugin" for plugin hook "test-hook"`, - }, - */ { name: "fail: command not runnable", setup: func(t *testing.T, p *mocks.PluginInterface) { @@ -447,7 +426,7 @@ func TestLinkPluginHooks(t *testing.T) { nil, ) }, - expectedError: `can't attach plugin hook "test-hook" to non executable command "ignite scaffold"`, + expectedError: `can't attach app hook "test-hook" to non executable command "ignite scaffold"`, }, { name: "fail: command doesn't exists", @@ -463,7 +442,7 @@ func TestLinkPluginHooks(t *testing.T) { nil, ) }, - expectedError: `unable to find commandPath "ignite chain" for plugin hook "test-hook"`, + expectedError: `unable to find command path "ignite chain" for app hook "test-hook"`, }, { name: "ok: single hook", diff --git a/ignite/config/plugins/config.go b/ignite/config/plugins/config.go index 4c5bf2e05b..8b386a2d9b 100644 --- a/ignite/config/plugins/config.go +++ b/ignite/config/plugins/config.go @@ -13,7 +13,6 @@ import ( ) type Config struct { - // path to the config file path string // Apps holds the list of installed Ignite Apps. @@ -89,9 +88,8 @@ func (p Plugin) IsLocalPath() bool { } // HasPath verifies if a plugin has the given path regardless of version. -// Example: -// github.com/foo/bar@v1 and github.com/foo/bar@v2 have the same path so "true" -// will be returned. +// 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 diff --git a/ignite/pkg/cliui/icons/icon.go b/ignite/pkg/cliui/icons/icon.go index 7a9d5373a9..75495e9753 100644 --- a/ignite/pkg/cliui/icons/icon.go +++ b/ignite/pkg/cliui/icons/icon.go @@ -5,11 +5,10 @@ import ( ) var ( - Earth = "🌍" - CD = "💿" - User = "👤" - Command = "❯⎯" - Hook = "🪝" + Earth = "🌍" + CD = "💿" + User = "👤" + Tada = "🎉" // OK is an OK mark. OK = colors.SprintFunc(colors.Green)("✔") diff --git a/ignite/services/plugin/cache.go b/ignite/services/plugin/cache.go index 6d38303d40..a0832da6ce 100644 --- a/ignite/services/plugin/cache.go +++ b/ignite/services/plugin/cache.go @@ -28,7 +28,7 @@ func writeConfigCache(pluginPath string, conf hplugin.ReattachConfig) error { return fmt.Errorf("provided path is invalid: %s", pluginPath) } if conf.Addr == nil { - return fmt.Errorf("plugin Address info cannot be empty") + return fmt.Errorf("app Address info cannot be empty") } cache, err := newCache() if err != nil { diff --git a/ignite/services/plugin/interface.go b/ignite/services/plugin/interface.go index c200f29cf6..b423f8485e 100644 --- a/ignite/services/plugin/interface.go +++ b/ignite/services/plugin/interface.go @@ -20,14 +20,14 @@ func init() { gob.Register(ExecutedHook{}) } -// An ignite plugin must implements the Plugin interface. +// Interface defines the interface that all Ignite App must implement. // //go:generate mockery --srcpkg . --name Interface --structname PluginInterface --filename interface.go --with-expecter type Interface interface { - // Manifest declares the plugin's Command(s) and Hook(s). + // Manifest declares the app's Command(s) and Hook(s). Manifest() (Manifest, error) - // Execute will be invoked by ignite when a plugin Command is executed. + // Execute will be invoked by ignite when an app Command is executed. // It is global for all commands declared in Manifest, if you have declared // multiple commands, use cmd.Path to distinguish them. Execute(cmd ExecutedCommand) error @@ -37,11 +37,13 @@ type Interface interface { // It is global for all hooks declared in Manifest, if you have declared // multiple hooks, use hook.Name to distinguish them. ExecuteHookPre(hook ExecutedHook) error + // ExecuteHookPost is invoked by ignite when a command specified by the hook // path is invoked. // It is global for all hooks declared in Manifest, if you have declared // multiple hooks, use hook.Name to distinguish them. ExecuteHookPost(hook ExecutedHook) error + // ExecuteHookCleanUp is invoked by ignite when a command specified by the // hook path is invoked. Unlike ExecuteHookPost, it is invoked regardless of // execution status of the command and hooks. @@ -50,28 +52,32 @@ type Interface interface { ExecuteHookCleanUp(hook ExecutedHook) error } -// Manifest represents the plugin behavior. +// Manifest represents the Ignite App behavior. type Manifest struct { Name string + // Commands contains the commands that will be added to the list of ignite // commands. Each commands are independent, for nested commands use the // inner Commands field. Commands []Command + // Hooks contains the hooks that will be attached to the existing ignite // commands. Hooks []Hook - // SharedHost enables sharing a single plugin server across all running instances - // of a plugin. Useful if a plugin adds or extends long running commands + + // SharedHost enables sharing a single app server across all running instances + // of an app. Useful if an app adds or extends long running commands // - // Example: if a plugin defines a hook on `ignite chain serve`, a plugin server is instanciated + // Example: if an app defines a hook on `ignite chain serve`, a server is instanciated // when the command is run. Now if you want to interact with that instance from commands - // defined in that plugin, you need to enable `SharedHost`, or else the commands will just - // instantiate separate plugin servers. + // defined in that app, you need to enable `SharedHost`, or else the commands will just + // instantiate separate app servers. // - // When enabled, all plugins of the same `Path` loaded from the same configuration will - // attach it's rpc client to a an existing rpc server. + // When enabled, all apps of the same `Path` loaded from the same configuration will + // attach it's RPC client to a an existing RPC server. // - // If a plugin instance has no other running plugin servers, it will create one and it will be the host. + // If an app instance has no other running app servers, it will create one and it + // will be the host. SharedHost bool `yaml:"shared_host"` } @@ -96,31 +102,38 @@ func convertCobraCommand(c *cobra.Command, placeCommandUnder string) Command { return cmd } -// Command represents a plugin command. +// Command represents an app command. type Command struct { // Same as cobra.Command.Use Use string + // Same as cobra.Command.Aliases Aliases []string + // Same as cobra.Command.Short Short string + // Same as cobra.Command.Long Long string + // Same as cobra.Command.Hidden Hidden bool + // Flags holds the list of command flags Flags []Flag + // PlaceCommandUnder indicates where the command should be placed. // For instance `ignite scaffold` will place the command at the // `scaffold` command. // An empty value is interpreted as `ignite` (==root). PlaceCommandUnder string + // List of sub commands Commands []Command } -// PlaceCommandUnderFull returns a normalized p.PlaceCommandUnder, by adding -// the `ignite ` prefix if not present. +// PlaceCommandUnderFull returns a normalized p.PlaceCommandUnder, +// by adding the `ignite ` prefix if not present. func (c Command) PlaceCommandUnderFull() string { return commandFull(c.PlaceCommandUnder) } @@ -133,8 +146,7 @@ func commandFull(cmdPath string) string { return strings.TrimSpace(cmdPath) } -// ToCobraCommand turns Command into a cobra.Command so it can be added to a -// parent command. +// ToCobraCommand turns Command into a cobra.Command so it can be added to a parent command. func (c Command) ToCobraCommand() (*cobra.Command, error) { cmd := &cobra.Command{ Use: c.Use, @@ -152,11 +164,12 @@ func (c Command) ToCobraCommand() (*cobra.Command, error) { return cmd, nil } -// Hook represents a user defined action within a plugin. +// Hook represents a user defined action within an app. type Hook struct { // Name identifies the hook for the client to invoke the correct hook // must be unique Name string + // PlaceHookOn indicates the command to register the hooks for PlaceHookOn string } @@ -167,24 +180,28 @@ func (h Hook) PlaceHookOnFull() string { return commandFull(h.PlaceHookOn) } -// ExecutedCommand represents a plugin command under execution. +// ExecutedCommand represents an app command under execution. type ExecutedCommand struct { // Use is copied from Command.Use Use string + // Path contains the command path, e.g. `ignite scaffold foo` Path string + // Args are the command arguments Args []string + // Full list of args taken from os.Args OSArgs []string - // With contains the plugin config parameters + + // With contains the app config parameters With map[string]string flags *pflag.FlagSet pflags *pflag.FlagSet } -// ExecutedHook represents a plugin hook under execution. +// ExecutedHook represents an app hook under execution. type ExecutedHook struct { // ExecutedCommand gives access to the command attached by the hook. ExecutedCommand ExecutedCommand @@ -210,7 +227,7 @@ func (c *ExecutedCommand) PersistentFlags() *pflag.FlagSet { } // SetFlags set the flags. -// As a plugin developer, you probably don't need to use it. +// As an app developer, you probably don't need to use it. func (c *ExecutedCommand) SetFlags(cmd *cobra.Command) { c.flags = cmd.Flags() c.pflags = cmd.PersistentFlags() @@ -224,8 +241,8 @@ type Flag struct { DefValue string // default value (as text); for usage message Type FlagType Value string - // Persistent indicates wether or not the flag is propagated on children - // commands + + // Persistent indicates wether or not the flag is propagated on children commands Persistent bool } @@ -234,7 +251,7 @@ type FlagType string const ( // NOTE(tb): we declare only the main used cobra flag types for simplicity - // If a plugin receives an unhandled type, it will output an error. + // If an app receives an unhandled type, it will output an error. FlagTypeString FlagType = "string" FlagTypeInt FlagType = "int" FlagTypeUint FlagType = "uint" @@ -365,8 +382,8 @@ func (c *ExecutedCommand) GobDecode(bz []byte) error { } // handshakeConfigs are used to just do a basic handshake between -// a plugin and host. If the handshake fails, a user friendly error is shown. -// This prevents users from executing bad plugins or executing a plugin +// an app and host. If the handshake fails, a user friendly error is shown. +// This prevents users from executing bad apps or executing an app // directory. It is a UX feature, not a security feature. var handshakeConfig = plugin.HandshakeConfig{ ProtocolVersion: 1, @@ -419,7 +436,6 @@ func (g *InterfaceRPC) ExecuteHookCleanUp(hook ExecutedHook) error { // InterfaceRPCServer is the RPC server that InterfaceRPC talks to, conforming to // the requirements of net/rpc. type InterfaceRPCServer struct { - // This is the real implementation Impl Interface } @@ -445,18 +461,14 @@ func (s *InterfaceRPCServer) ExecuteHookCleanUp(args map[string]interface{}, _ * return s.Impl.ExecuteHookCleanUp(args["executedHook"].(ExecutedHook)) } -// This is the implementation of plugin.Interface so we can serve/consume this +// InterfacePlugin is the implementation of Interface. // // This has two methods: Server must return an RPC server for this plugin // type. We construct a InterfaceRPCServer for this. // // Client must return an implementation of our interface that communicates // over an RPC client. We return InterfaceRPC for this. -// -// Ignore MuxBroker. That is used to create more multiplexed streams on our -// plugin connection and is a more advanced use case. type InterfacePlugin struct { - // Impl Injection Impl Interface } diff --git a/ignite/services/plugin/plugin.go b/ignite/services/plugin/plugin.go index 28091675a6..0081c381c1 100644 --- a/ignite/services/plugin/plugin.go +++ b/ignite/services/plugin/plugin.go @@ -20,6 +20,7 @@ import ( "github.com/ignite/cli/ignite/config" pluginsconfig "github.com/ignite/cli/ignite/config/plugins" + "github.com/ignite/cli/ignite/pkg/cliui/icons" "github.com/ignite/cli/ignite/pkg/env" "github.com/ignite/cli/ignite/pkg/events" "github.com/ignite/cli/ignite/pkg/gocmd" @@ -117,7 +118,7 @@ func newPlugin(pluginsDir string, cp pluginsconfig.Plugin, options ...Option) *P pluginPath = cp.Path ) if pluginPath == "" { - p.Error = errors.Errorf(`missing plugin property "path"`) + p.Error = errors.Errorf(`missing app property "path"`) return p } @@ -130,11 +131,11 @@ func newPlugin(pluginsDir string, cp pluginsconfig.Plugin, options ...Option) *P // This is a local plugin, check if the file exists st, err := os.Stat(pluginPath) if err != nil { - p.Error = errors.Wrapf(err, "local plugin path %q not found", pluginPath) + p.Error = errors.Wrapf(err, "local app path %q not found", pluginPath) return p } if !st.IsDir() { - p.Error = errors.Errorf("local plugin path %q is not a dir", pluginPath) + p.Error = errors.Errorf("local app path %q is not a directory", pluginPath) return p } p.srcPath = pluginPath @@ -149,7 +150,7 @@ func newPlugin(pluginsDir string, cp pluginsconfig.Plugin, options ...Option) *P } parts := strings.Split(pluginPath, "/") if len(parts) < 3 { - p.Error = errors.Errorf("plugin path %q is not a valid repository URL", pluginPath) + p.Error = errors.Errorf("app path %q is not a valid repository URL", pluginPath) return p } p.repoPath = path.Join(parts[:3]...) @@ -239,7 +240,7 @@ func (p *Plugin) load(ctx context.Context) { logLevel = hclog.Trace } logger := hclog.New(&hclog.LoggerOptions{ - Name: fmt.Sprintf("plugin %s", p.Path), + Name: fmt.Sprintf("app %s", p.Path), Output: os.Stderr, Level: logLevel, }) @@ -321,8 +322,8 @@ func (p *Plugin) fetch() { if p.Error != nil { return } - p.ev.Send(fmt.Sprintf("Fetching plugin %q", p.cloneURL), events.ProgressStart()) - defer p.ev.Send(fmt.Sprintf("Plugin fetched %q", p.cloneURL), events.ProgressFinish()) + p.ev.Send(fmt.Sprintf("Fetching app %q", p.cloneURL), events.ProgressStart()) + defer p.ev.Send(fmt.Sprintf("%s App fetched %q", icons.OK, p.cloneURL), events.ProgressFinish()) urlref := strings.Join([]string{p.cloneURL, p.reference}, "@") err := xgit.Clone(context.Background(), urlref, p.cloneDir) @@ -336,8 +337,8 @@ func (p *Plugin) build(ctx context.Context) { if p.Error != nil { return } - p.ev.Send(fmt.Sprintf("Building plugin %q", p.Path), events.ProgressStart()) - defer p.ev.Send(fmt.Sprintf("Plugin built %q", p.Path), events.ProgressFinish()) + p.ev.Send(fmt.Sprintf("Building app %q", p.Path), events.ProgressStart()) + defer p.ev.Send(fmt.Sprintf("%s App built %q", icons.OK, p.Path), events.ProgressFinish()) if err := gocmd.ModTidy(ctx, p.srcPath); err != nil { p.Error = errors.Wrapf(err, "go mod tidy") @@ -391,7 +392,7 @@ func (p *Plugin) outdatedBinary() bool { return nil }) if err != nil { - fmt.Printf("error while walking plugin source path %q\n", p.srcPath) + fmt.Printf("error while walking app source path %q\n", p.srcPath) return false } return mostRecent.After(binaryTime) diff --git a/ignite/services/plugin/plugin_test.go b/ignite/services/plugin/plugin_test.go index 08d59f91b6..da3795b913 100644 --- a/ignite/services/plugin/plugin_test.go +++ b/ignite/services/plugin/plugin_test.go @@ -34,21 +34,21 @@ func TestNewPlugin(t *testing.T) { { name: "fail: empty path", expectedPlugin: Plugin{ - Error: errors.Errorf(`missing plugin property "path"`), + Error: errors.Errorf(`missing app property "path"`), }, }, { name: "fail: local plugin doesnt exists", - pluginCfg: pluginsconfig.Plugin{Path: "/xxx/yyy/plugin"}, + pluginCfg: pluginsconfig.Plugin{Path: "/xxx/yyy/app"}, expectedPlugin: Plugin{ - Error: errors.Errorf(`local plugin path "/xxx/yyy/plugin" not found`), + Error: errors.Errorf(`local app path "/xxx/yyy/app" not found`), }, }, { - name: "fail: local plugin is not a dir", + name: "fail: local plugin is not a directory", pluginCfg: pluginsconfig.Plugin{Path: path.Join(wd, "testdata/fakebin")}, expectedPlugin: Plugin{ - Error: errors.Errorf(fmt.Sprintf("local plugin path %q is not a dir", path.Join(wd, "testdata/fakebin"))), + Error: errors.Errorf(fmt.Sprintf("local app path %q is not a directory", path.Join(wd, "testdata/fakebin"))), }, }, { @@ -63,85 +63,85 @@ func TestNewPlugin(t *testing.T) { name: "fail: remote plugin with only domain", pluginCfg: pluginsconfig.Plugin{Path: "github.com"}, expectedPlugin: Plugin{ - Error: errors.Errorf(`plugin path "github.com" is not a valid repository URL`), + Error: errors.Errorf(`app path "github.com" is not a valid repository URL`), }, }, { name: "fail: remote plugin with incomplete URL", pluginCfg: pluginsconfig.Plugin{Path: "github.com/ignite"}, expectedPlugin: Plugin{ - Error: errors.Errorf(`plugin path "github.com/ignite" is not a valid repository URL`), + Error: errors.Errorf(`app path "github.com/ignite" is not a valid repository URL`), }, }, { - name: "ok: remote plugin", - pluginCfg: pluginsconfig.Plugin{Path: "github.com/ignite/plugin"}, + name: "ok: remote app", + pluginCfg: pluginsconfig.Plugin{Path: "github.com/ignite/app"}, expectedPlugin: Plugin{ - repoPath: "github.com/ignite/plugin", - cloneURL: "https://github.com/ignite/plugin", - cloneDir: ".ignite/apps/github.com/ignite/plugin", + repoPath: "github.com/ignite/app", + cloneURL: "https://github.com/ignite/app", + cloneDir: ".ignite/apps/github.com/ignite/app", reference: "", - srcPath: ".ignite/apps/github.com/ignite/plugin", - name: "plugin", + srcPath: ".ignite/apps/github.com/ignite/app", + name: "app", }, }, { name: "ok: remote plugin with @ref", - pluginCfg: pluginsconfig.Plugin{Path: "github.com/ignite/plugin@develop"}, + pluginCfg: pluginsconfig.Plugin{Path: "github.com/ignite/app@develop"}, expectedPlugin: Plugin{ - repoPath: "github.com/ignite/plugin@develop", - cloneURL: "https://github.com/ignite/plugin", - cloneDir: ".ignite/apps/github.com/ignite/plugin-develop", + repoPath: "github.com/ignite/app@develop", + cloneURL: "https://github.com/ignite/app", + cloneDir: ".ignite/apps/github.com/ignite/app-develop", reference: "develop", - srcPath: ".ignite/apps/github.com/ignite/plugin-develop", - name: "plugin", + srcPath: ".ignite/apps/github.com/ignite/app-develop", + name: "app", }, }, { name: "ok: remote plugin with @ref containing slash", - pluginCfg: pluginsconfig.Plugin{Path: "github.com/ignite/plugin@package/v1.0.0"}, + pluginCfg: pluginsconfig.Plugin{Path: "github.com/ignite/app@package/v1.0.0"}, expectedPlugin: Plugin{ - repoPath: "github.com/ignite/plugin@package/v1.0.0", - cloneURL: "https://github.com/ignite/plugin", - cloneDir: ".ignite/apps/github.com/ignite/plugin-package-v1.0.0", + repoPath: "github.com/ignite/app@package/v1.0.0", + cloneURL: "https://github.com/ignite/app", + cloneDir: ".ignite/apps/github.com/ignite/app-package-v1.0.0", reference: "package/v1.0.0", - srcPath: ".ignite/apps/github.com/ignite/plugin-package-v1.0.0", - name: "plugin", + srcPath: ".ignite/apps/github.com/ignite/app-package-v1.0.0", + name: "app", }, }, { name: "ok: remote plugin with subpath", - pluginCfg: pluginsconfig.Plugin{Path: "github.com/ignite/plugin/plugin1"}, + pluginCfg: pluginsconfig.Plugin{Path: "github.com/ignite/app/plugin1"}, expectedPlugin: Plugin{ - repoPath: "github.com/ignite/plugin", - cloneURL: "https://github.com/ignite/plugin", - cloneDir: ".ignite/apps/github.com/ignite/plugin", + repoPath: "github.com/ignite/app", + cloneURL: "https://github.com/ignite/app", + cloneDir: ".ignite/apps/github.com/ignite/app", reference: "", - srcPath: ".ignite/apps/github.com/ignite/plugin/plugin1", + srcPath: ".ignite/apps/github.com/ignite/app/plugin1", name: "plugin1", }, }, { name: "ok: remote plugin with subpath and @ref", - pluginCfg: pluginsconfig.Plugin{Path: "github.com/ignite/plugin/plugin1@develop"}, + pluginCfg: pluginsconfig.Plugin{Path: "github.com/ignite/app/plugin1@develop"}, expectedPlugin: Plugin{ - repoPath: "github.com/ignite/plugin@develop", - cloneURL: "https://github.com/ignite/plugin", - cloneDir: ".ignite/apps/github.com/ignite/plugin-develop", + repoPath: "github.com/ignite/app@develop", + cloneURL: "https://github.com/ignite/app", + cloneDir: ".ignite/apps/github.com/ignite/app-develop", reference: "develop", - srcPath: ".ignite/apps/github.com/ignite/plugin-develop/plugin1", + srcPath: ".ignite/apps/github.com/ignite/app-develop/plugin1", name: "plugin1", }, }, { name: "ok: remote plugin with subpath and @ref containing slash", - pluginCfg: pluginsconfig.Plugin{Path: "github.com/ignite/plugin/plugin1@package/v1.0.0"}, + pluginCfg: pluginsconfig.Plugin{Path: "github.com/ignite/app/plugin1@package/v1.0.0"}, expectedPlugin: Plugin{ - repoPath: "github.com/ignite/plugin@package/v1.0.0", - cloneURL: "https://github.com/ignite/plugin", - cloneDir: ".ignite/apps/github.com/ignite/plugin-package-v1.0.0", + repoPath: "github.com/ignite/app@package/v1.0.0", + cloneURL: "https://github.com/ignite/app", + cloneDir: ".ignite/apps/github.com/ignite/app-package-v1.0.0", reference: "package/v1.0.0", - srcPath: ".ignite/apps/github.com/ignite/plugin-package-v1.0.0/plugin1", + srcPath: ".ignite/apps/github.com/ignite/app-package-v1.0.0/plugin1", name: "plugin1", }, }, @@ -241,7 +241,7 @@ func TestPluginLoad(t *testing.T) { repoPath: "/xxxx/yyyy", cloneURL: "/xxxx/yyyy", cloneDir: cloneDir, - srcPath: path.Join(cloneDir, "plugin"), + srcPath: path.Join(cloneDir, "app"), } }, expectedError: `cloning "/xxxx/yyyy": repository not found`, @@ -410,10 +410,10 @@ func TestPluginLoadSharedHost(t *testing.T) { for i := len(plugins) - 1; i >= 0; i-- { plugins[i].KillClient() if tt.sharesHost && i > 0 { - assert.False(plugins[i].client.Exited(), "non host plugin can't kill host plugin") - assert.True(checkConfCache(plugins[i].Path), "non host plugin doesn't remove config cache when killed") + assert.False(plugins[i].client.Exited(), "non host app can't kill host app") + assert.True(checkConfCache(plugins[i].Path), "non host app doesn't remove config cache when killed") } else { - assert.True(plugins[i].client.Exited(), "plugin should be killed") + assert.True(plugins[i].client.Exited(), "app should be killed") } assert.False(plugins[i].isHost, "killed plugins are no longer host") } @@ -426,21 +426,21 @@ func TestPluginLoadSharedHost(t *testing.T) { assert.True(checkConfCache(plugins[i].Path), "sharedHost must have a cache entry") if i == 0 { // first plugin is the host - assert.True(plugins[i].isHost, "first plugin is the host") + assert.True(plugins[i].isHost, "first app is the host") // Assert reattach config has been saved hostConf = plugins[i].client.ReattachConfig() ref, err := readConfigCache(plugins[i].Path) if assert.NoError(err) { - assert.Equal(hostConf, &ref, "wrong cache entry for plugin host") + assert.Equal(hostConf, &ref, "wrong cache entry for app host") } } else { // plugins after first aren't host - assert.False(plugins[i].isHost, "plugin %d can't be host", i) - assert.Equal(hostConf, plugins[i].client.ReattachConfig(), "ReattachConfig different from host plugin") + assert.False(plugins[i].isHost, "app %d can't be host", i) + assert.Equal(hostConf, plugins[i].client.ReattachConfig(), "ReattachConfig different from host app") } } else { - assert.False(plugins[i].isHost, "plugin %d can't be host if sharedHost is disabled", i) - assert.False(checkConfCache(plugins[i].Path), "plugin %d can't have a cache entry if sharedHost is disabled", i) + assert.False(plugins[i].isHost, "app %d can't be host if sharedHost is disabled", i) + assert.False(checkConfCache(plugins[i].Path), "app %d can't have a cache entry if sharedHost is disabled", i) } } }) @@ -454,7 +454,7 @@ func TestPluginClean(t *testing.T) { expectRemove bool }{ { - name: "dont clean local plugin", + name: "dont clean local app", plugin: &Plugin{ Plugin: pluginsconfig.Plugin{Path: "/local"}, }, @@ -466,7 +466,7 @@ func TestPluginClean(t *testing.T) { { name: "ok", plugin: &Plugin{ - cloneURL: "https://github.com/ignite/plugin", + cloneURL: "https://github.com/ignite/app", }, expectRemove: true, }, diff --git a/ignite/services/plugin/scaffold.go b/ignite/services/plugin/scaffold.go index e8bbc2f651..ff2a7fe093 100644 --- a/ignite/services/plugin/scaffold.go +++ b/ignite/services/plugin/scaffold.go @@ -32,7 +32,7 @@ func Scaffold(dir, moduleName string, sharedHost bool) (string, error) { ) if _, err := os.Stat(finalDir); err == nil { // finalDir already exists, don't overwrite stuff - return "", errors.Errorf("dir %q already exists, abort scaffolding", finalDir) + return "", errors.Errorf("directory %q already exists, abort scaffolding", finalDir) } if err := g.Box(template); err != nil { return "", errors.WithStack(err) diff --git a/ignite/services/plugin/template/main.go.plush b/ignite/services/plugin/template/main.go.plush index dbdbcd3e63..077f10f250 100644 --- a/ignite/services/plugin/template/main.go.plush +++ b/ignite/services/plugin/template/main.go.plush @@ -17,9 +17,9 @@ func init() { gob.Register(plugin.ExecutedHook{}) } -type p struct{} +type app struct{} -func (p) Manifest() (plugin.Manifest, error) { +func (app) Manifest() (plugin.Manifest, error) { return plugin.Manifest{ Name: "<%= Name %>", // Add commands here @@ -49,7 +49,7 @@ func (p) Manifest() (plugin.Manifest, error) { }, nil } -func (p) Execute(cmd plugin.ExecutedCommand) error { +func (app) Execute(cmd plugin.ExecutedCommand) error { // TODO: write command execution here fmt.Printf("Hello I'm the <%= Name %> plugin\n") fmt.Printf("My executed command: %q\n", cmd.Path) @@ -75,17 +75,17 @@ func (p) Execute(cmd plugin.ExecutedCommand) error { return nil } -func (p) ExecuteHookPre(hook plugin.ExecutedHook) error { +func (app) ExecuteHookPre(hook plugin.ExecutedHook) error { fmt.Printf("Executing hook pre %q\n", hook.Name) return nil } -func (p) ExecuteHookPost(hook plugin.ExecutedHook) error { +func (app) ExecuteHookPost(hook plugin.ExecutedHook) error { fmt.Printf("Executing hook post %q\n", hook.Name) return nil } -func (p) ExecuteHookCleanUp(hook plugin.ExecutedHook) error { +func (app) ExecuteHookCleanUp(hook plugin.ExecutedHook) error { fmt.Printf("Executing hook cleanup %q\n", hook.Name) return nil } @@ -107,7 +107,7 @@ func getChain(cmd plugin.ExecutedCommand, chainOption ...chain.Option) (*chain.C func main() { pluginMap := map[string]hplugin.Plugin{ - "<%= Name %>": &plugin.InterfacePlugin{Impl: &p{}}, + "<%= Name %>": &plugin.InterfacePlugin{Impl: &app{}}, } hplugin.Serve(&hplugin.ServeConfig{ diff --git a/integration/plugin/plugin_test.go b/integration/plugin/plugin_test.go index 551ce600ee..813af11180 100644 --- a/integration/plugin/plugin_test.go +++ b/integration/plugin/plugin_test.go @@ -36,9 +36,9 @@ func TestAddRemovePlugin(t *testing.T) { // no plugins expected assertPlugins(nil, nil) - env.Must(env.Exec("add plugin locally", + env.Must(env.Exec("install plugin locally", step.NewSteps(step.New( - step.Exec(envtest.IgniteApp, "plugin", "add", pluginRepo, "k1=v1", "k2=v2"), + step.Exec(envtest.IgniteApp, "app", "install", pluginRepo, "k1=v1", "k2=v2"), step.Workdir(app.SourcePath()), )), )) @@ -57,9 +57,9 @@ func TestAddRemovePlugin(t *testing.T) { nil, ) - env.Must(env.Exec("remove plugin locally", + env.Must(env.Exec("uninstall plugin locally", step.NewSteps(step.New( - step.Exec(envtest.IgniteApp, "plugin", "remove", pluginRepo), + step.Exec(envtest.IgniteApp, "app", "uninstall", pluginRepo), step.Workdir(app.SourcePath()), )), )) @@ -67,9 +67,9 @@ func TestAddRemovePlugin(t *testing.T) { // no plugins expected assertPlugins(nil, nil) - env.Must(env.Exec("add plugin globally", + env.Must(env.Exec("install plugin globally", step.NewSteps(step.New( - step.Exec(envtest.IgniteApp, "plugin", "add", pluginRepo, "-g"), + step.Exec(envtest.IgniteApp, "app", "install", pluginRepo, "-g"), step.Workdir(app.SourcePath()), )), )) @@ -84,9 +84,9 @@ func TestAddRemovePlugin(t *testing.T) { }, ) - env.Must(env.Exec("remove plugin globally", + env.Must(env.Exec("uninstall plugin globally", step.NewSteps(step.New( - step.Exec(envtest.IgniteApp, "plugin", "remove", pluginRepo, "-g"), + step.Exec(envtest.IgniteApp, "app", "uninstall", pluginRepo, "-g"), step.Workdir(app.SourcePath()), )), )) @@ -100,9 +100,9 @@ func TestAddRemovePlugin(t *testing.T) { func TestPluginScaffold(t *testing.T) { env := envtest.New(t) - env.Must(env.Exec("add a plugin", + env.Must(env.Exec("install a plugin", step.NewSteps(step.New( - step.Exec(envtest.IgniteApp, "plugin", "scaffold", "test"), + step.Exec(envtest.IgniteApp, "app", "scaffold", "test"), step.Workdir(env.TmpDir()), )), ))