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 diff --git a/.gitignore b/.gitignore index b87687085d..c48ae0cac3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ scripts/**/nodetime-* dist/ node_modules .DS_Store -apps/ .idea .vscode docs/.vuepress/dist diff --git a/changelog.md b/changelog.md index aa3b8d3a2b..f19516e1ac 100644 --- a/changelog.md +++ b/changelog.md @@ -20,8 +20,11 @@ - [#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 +- [#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 @@ -30,6 +33,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/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/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 new file mode 100644 index 0000000000..459d6c7ea3 --- /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 +`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: + +```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 53% rename from docs/docs/plugins/02-dev-plugins.md rename to docs/docs/apps/02-developing-apps.md index 39702303f8..d900d9cbda 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(context.Context) (*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(context.Context, *ExecutedCommand) error @@ -70,17 +71,16 @@ 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` proto message definition: ```protobuf title=proto/ignite/services/plugin/grpc/v1/types.proto message Manifest { - // Plugin name. + // App name. string name = 1; // Commands contains the commands that will be added to the list of ignite commands. @@ -90,49 +90,48 @@ message Manifest { // Hooks contains the hooks that will be attached to the existing ignite commands. repeated Command commands = 3; - // Enables sharing a single plugin server across all running instances of a plugin. - // Useful if a plugin adds or extends long running commands. + // Enables sharing a single app server across all running instances of an Ignite 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 when the command is run. Now if you want to interact with that instance - // from commands defined in that plugin, you need to enable shared host, or else the - // commands will just instantiate separate plugin servers. + // 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 app, you need to enable shared host, or else the + // commands will just instantiate separate app servers. // - // When enabled, all plugins of the same path loaded from the same configuration will + // 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 + // If an app instance has no other running app servers, it will create one and it // will be the host. repeated Hook hooks = 4; } ``` -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(context.Context) (*plugin.Manifest, error) { +func (app) Manifest(context.Context) (*plugin.Manifest, error) { return &plugin.Manifest{ Name: "oracle", Commands: []*plugin.Command{ @@ -152,11 +151,11 @@ func (p) Manifest(context.Context) (*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(_ context.Context, cmd *plugin.ExecutedCommand) error { +func (app) Execute(_ context.Context, cmd *plugin.ExecutedCommand) error { if len(cmd.Args) == 0 { return fmt.Errorf("oracle name missing") } @@ -181,23 +180,24 @@ func (p) Execute(_ context.Context, 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. @@ -205,7 +205,7 @@ resulting in `post` and `clean up` not executing. The following is an example of a `hook` definition. ```go -func (p) Manifest(context.Context) (*plugin.Manifest, error) { +func (app) Manifest(context.Context) (*plugin.Manifest, error) { return &plugin.Manifest{ Name: "oracle", Hooks: []*plugin.Hook{ @@ -217,7 +217,7 @@ func (p) Manifest(context.Context) (*plugin.Manifest, error) { }, nil } -func (p) ExecuteHookPre(_ context.Context, h *plugin.ExecutedHook) error { +func (app) ExecuteHookPre(_ context.Context, h *plugin.ExecutedHook) error { switch h.Hook.GetName() { case "my-hook": fmt.Println("I'm executed before ignite chain build") @@ -227,7 +227,7 @@ func (p) ExecuteHookPre(_ context.Context, h *plugin.ExecutedHook) error { return nil } -func (p) ExecuteHookPost(_ context.Context, h *plugin.ExecutedHook) error { +func (app) ExecuteHookPost(_ context.Context, h *plugin.ExecutedHook) error { switch h.Hook.GetName() { case "my-hook": fmt.Println("I'm executed after ignite chain build (if no error)") @@ -237,7 +237,7 @@ func (p) ExecuteHookPost(_ context.Context, h *plugin.ExecutedHook) error { return nil } -func (p) ExecuteHookCleanUp(_ context.Context, h *plugin.ExecutedHook) error { +func (app) ExecuteHookCleanUp(_ context.Context, h *plugin.ExecutedHook) error { switch h.Hook.GetName() { case "my-hook": fmt.Println("I'm executed after ignite chain build (regardless errors)") @@ -248,7 +248,7 @@ func (p) ExecuteHookCleanUp(_ context.Context, h *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`. 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/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.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/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.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/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 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/go.sum b/go.sum index e96291f6dd..0319f6dba5 100644 --- a/go.sum +++ b/go.sum @@ -1407,8 +1407,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/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/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 6eca39cec4..28c3586d90 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) @@ -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 @@ -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 } @@ -172,11 +172,11 @@ func linkPluginHook(rootCmd *cobra.Command, p *plugin.Plugin, hook *plugin.Hook) cmdPath := hook.CommandPath() 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 } @@ -208,7 +208,7 @@ func linkPluginHook(rootCmd *cobra.Command, p *plugin.Plugin, hook *plugin.Hook) err := p.Interface.ExecuteHookPre(ctx, 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 } @@ -223,7 +223,7 @@ func linkPluginHook(rootCmd *cobra.Command, p *plugin.Plugin, hook *plugin.Hook) ctx := cmd.Context() err := p.Interface.ExecuteHookCleanUp(ctx, 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 @@ -241,7 +241,7 @@ func linkPluginHook(rootCmd *cobra.Command, p *plugin.Plugin, hook *plugin.Hook) defer func() { err := p.Interface.ExecuteHookCleanUp(ctx, execHook) if err != nil { - fmt.Printf("plugin %q ExecuteHookCleanUp() error: %v", p.Path, err) + fmt.Printf("app %q ExecuteHookCleanUp() error: %v", p.Path, err) } }() @@ -255,7 +255,7 @@ func linkPluginHook(rootCmd *cobra.Command, p *plugin.Plugin, hook *plugin.Hook) err := p.Interface.ExecuteHookPost(ctx, 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 } @@ -279,11 +279,11 @@ func linkPluginCmd(rootCmd *cobra.Command, p *plugin.Plugin, pluginCmd *plugin.C cmdPath := pluginCmd.Path() 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 } @@ -293,7 +293,7 @@ func linkPluginCmd(rootCmd *cobra.Command, p *plugin.Plugin, pluginCmd *plugin.C 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 } } @@ -356,30 +356,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(cmd.Context(), s) @@ -388,12 +388,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 @@ -401,7 +404,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 @@ -411,25 +414,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() @@ -449,9 +451,9 @@ 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]) + return fmt.Errorf("app %s is already installed", args[0]) } } @@ -478,7 +480,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 @@ -486,16 +488,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") - conf.Plugins = append(conf.Plugins, p) + 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 }, } @@ -505,11 +507,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)) @@ -530,9 +534,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 } @@ -540,14 +544,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 @@ -559,12 +563,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() @@ -582,19 +589,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 @@ -602,12 +609,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)) ctx := cmd.Context() @@ -616,18 +624,24 @@ func NewPluginDescribe() *cobra.Command { if p.Path == args[0] { manifest, err := p.Interface.Manifest(ctx) 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.Path(), 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.Path(), 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.CommandPath()) + + 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.CommandPath()) + } } + break } } @@ -637,43 +651,41 @@ func NewPluginDescribe() *cobra.Command { } } +func getPluginLocationName(p *plugin.Plugin) string { + if p.IsGlobal() { + return "global" + } + return "local" +} + +func getPluginStatus(ctx context.Context, p *plugin.Plugin) string { + if p.Error != nil { + return fmt.Sprintf("%s Error: %v", icons.NotOK, p.Error) + } + + _, err := p.Interface.Manifest(ctx) + if err != nil { + return fmt.Sprintf("%s Error: Manifest() returned %v", icons.NotOK, err) + } + + return fmt.Sprintf("%s Loaded", icons.OK) +} + func printPlugins(ctx context.Context, 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(ctx) - 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(ctx, 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 } 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 8cdc2fa7a0..9ffcc189ad 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,18 +61,12 @@ 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{ 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/cmd/plugin_test.go b/ignite/cmd/plugin_test.go index 40ab6cf412..c196b13198 100644 --- a/ignite/cmd/plugin_test.go +++ b/ignite/cmd/plugin_test.go @@ -187,7 +187,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", @@ -205,7 +205,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", @@ -222,7 +222,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", @@ -239,7 +239,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", @@ -257,7 +257,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", @@ -459,27 +459,6 @@ func TestLinkPluginHooks(t *testing.T) { expectedError string setup func(*testing.T, context.Context, *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, ctx context.Context, 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, ctx context.Context, p *mocks.PluginInterface) { @@ -496,7 +475,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", @@ -514,7 +493,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/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..8b386a2d9b 100644 --- a/ignite/config/plugins/config.go +++ b/ignite/config/plugins/config.go @@ -13,10 +13,11 @@ import ( ) 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 +39,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:"-"` } @@ -85,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 @@ -134,7 +136,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/plugins.yml b/ignite/config/plugins/testdata/igniteapps.yml similarity index 93% rename from ignite/config/plugins/testdata/plugins.yml rename to ignite/config/plugins/testdata/igniteapps.yml index 9e268110e2..2d541a7553 100644 --- a/ignite/config/plugins/testdata/plugins.yml +++ 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/other/plugins.yaml b/ignite/config/plugins/testdata/other/igniteapps.yaml similarity index 93% rename from ignite/config/plugins/testdata/other/plugins.yaml rename to ignite/config/plugins/testdata/other/igniteapps.yaml index 9e268110e2..2d541a7553 100644 --- a/ignite/config/plugins/testdata/other/plugins.yaml +++ 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/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/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/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/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/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/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/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) + }) + } +} 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" 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/cache.go b/ignite/services/plugin/cache.go index 7801571c44..bcdce5fa28 100644 --- a/ignite/services/plugin/cache.go +++ b/ignite/services/plugin/cache.go @@ -31,7 +31,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 d16c7f7c08..956eab972a 100644 --- a/ignite/services/plugin/interface.go +++ b/ignite/services/plugin/interface.go @@ -28,14 +28,14 @@ type ( ExecutedCommand = v1.ExecutedCommand ) -// 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(context.Context) (*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(context.Context, *ExecutedCommand) error diff --git a/ignite/services/plugin/plugin.go b/ignite/services/plugin/plugin.go index a75cbdffc2..346f94e36e 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" @@ -31,7 +32,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. @@ -80,11 +81,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 { @@ -120,7 +121,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 } @@ -133,11 +134,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 @@ -152,7 +153,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]...) @@ -166,8 +167,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) @@ -248,7 +249,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, }) @@ -331,8 +332,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) @@ -346,8 +347,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") @@ -401,7 +402,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 e74ef493fe..5a15d3547d 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/plugins/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/plugins/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/plugins/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/plugins/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/plugins/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/plugins/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/plugins/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/plugins/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/plugins/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/plugins/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/plugins/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/plugins/github.com/ignite/plugin-package-v1.0.0/plugin1", + srcPath: ".ignite/apps/github.com/ignite/app-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) }) @@ -247,7 +247,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`, @@ -417,10 +417,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") } @@ -433,21 +433,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) } } }) @@ -461,7 +461,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"}, }, @@ -473,7 +473,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 4de7c7f35d..500e1f6645 100644 --- a/ignite/services/plugin/scaffold.go +++ b/ignite/services/plugin/scaffold.go @@ -33,7 +33,7 @@ func Scaffold(ctx context.Context, dir, moduleName string, sharedHost bool) (str 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 { diff --git a/ignite/services/plugin/template/main.go.plush b/ignite/services/plugin/template/main.go.plush index 191117ad2a..abec154c2d 100644 --- a/ignite/services/plugin/template/main.go.plush +++ b/ignite/services/plugin/template/main.go.plush @@ -11,9 +11,9 @@ import ( "github.com/ignite/cli/ignite/services/plugin" ) -type p struct{} +type app struct{} -func (p) Manifest(ctx context.Context) (*plugin.Manifest, error) { +func (app) Manifest(ctx context.Context) (*plugin.Manifest, error) { return &plugin.Manifest{ Name: "<%= Name %>", // Add commands here @@ -43,7 +43,7 @@ func (p) Manifest(ctx context.Context) (*plugin.Manifest, error) { }, nil } -func (p) Execute(ctx context.Context, cmd *plugin.ExecutedCommand) error { +func (app) Execute(ctx context.Context, cmd *plugin.ExecutedCommand) error { // TODO: write command execution here fmt.Printf("Hello I'm the example-plugin plugin\n") fmt.Printf("My executed command: %q\n", cmd.Path) @@ -75,17 +75,17 @@ func (p) Execute(ctx context.Context, cmd *plugin.ExecutedCommand) error { return nil } -func (p) ExecuteHookPre(ctx context.Context, h *plugin.ExecutedHook) error { +func (app) ExecuteHookPre(ctx context.Context, h *plugin.ExecutedHook) error { fmt.Printf("Executing hook pre %q\n", h.Hook.GetName()) return nil } -func (p) ExecuteHookPost(ctx context.Context, h *plugin.ExecutedHook) error { +func (app) ExecuteHookPost(ctx context.Context, h *plugin.ExecutedHook) error { fmt.Printf("Executing hook post %q\n", h.Hook.GetName()) return nil } -func (p) ExecuteHookCleanUp(ctx context.Context, h *plugin.ExecutedHook) error { +func (app) ExecuteHookCleanUp(ctx context.Context, h *plugin.ExecutedHook) error { fmt.Printf("Executing hook cleanup %q\n", h.Hook.GetName()) return nil } @@ -114,7 +114,7 @@ func main() { hplugin.Serve(&hplugin.ServeConfig{ HandshakeConfig: plugin.HandshakeConfig(), Plugins: map[string]hplugin.Plugin{ - "<%= Name %>": plugin.NewGRPC(&p{}), + "<%= Name %>": plugin.NewGRPC(&app{}), }, GRPCServer: hplugin.DefaultGRPCServer, }) 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, 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") diff --git a/integration/plugin/plugin_test.go b/integration/plugin/plugin_test.go index 3653f94d59..9f0fe6268e 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") } ) @@ -42,7 +42,7 @@ func TestAddRemovePlugin(t *testing.T) { env.Must(env.Exec("add 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()), )), )) @@ -61,9 +61,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()), )), )) @@ -71,9 +71,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()), )), )) @@ -88,9 +88,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()), )), )) @@ -104,9 +104,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()), )), ))