Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: new ignite doctor command #3381

Merged
merged 13 commits into from
Apr 6, 2023
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

### Features

- [#3381](https://github.com/ignite/cli/pull/3381) Add `ignite doctor` command
- [#3238](https://github.com/ignite/cli/pull/3238) Add `Sharedhost` plugin option
- [#3214](https://github.com/ignite/cli/pull/3214) Global plugins config.
- [#3142](https://github.com/ignite/cli/pull/3142) Add `ignite network request param-change` command.
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ require (
github.com/pelletier/go-toml v1.9.5
github.com/pkg/errors v0.9.1
github.com/radovskyb/watcher v1.0.7
github.com/rogpeppe/go-internal v1.9.0
github.com/rs/cors v1.8.2
github.com/spf13/cast v1.5.0
github.com/spf13/cobra v1.6.1
Expand Down Expand Up @@ -297,6 +298,7 @@ require (
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d // indirect
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/polyfloyd/go-errorlint v1.0.5 // indirect
github.com/prometheus/client_golang v1.12.2 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1479,6 +1479,7 @@ github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
Expand Down Expand Up @@ -1579,6 +1580,7 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U=
github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
Expand Down
1 change: 1 addition & 0 deletions ignite/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ To get started, create a blockchain:
c.AddCommand(NewDocs())
c.AddCommand(NewVersion())
c.AddCommand(NewPlugin())
c.AddCommand(NewDoctor())
jeronimoalbi marked this conversation as resolved.
Show resolved Hide resolved
c.AddCommand(deprecated()...)

// Load plugins if any
Expand Down
30 changes: 30 additions & 0 deletions ignite/cmd/doctor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package ignitecmd

import (
"github.com/spf13/cobra"

"github.com/ignite/cli/ignite/pkg/cliui"
"github.com/ignite/cli/ignite/services/doctor"
)

func NewDoctor() *cobra.Command {
return &cobra.Command{
Use: "doctor",
Short: "Fix chain configuration",
Hidden: true,
RunE: func(cmd *cobra.Command, args []string) error {
session := cliui.New()
defer session.End()

doc := doctor.New(doctor.CollectEvents(session.EventBus()))

if err := doc.MigrateConfig(cmd.Context()); err != nil {
return err
}
if err := doc.FixDependencyTools(cmd.Context()); err != nil {
jeronimoalbi marked this conversation as resolved.
Show resolved Hide resolved
return err
}
return nil
},
}
}
1 change: 0 additions & 1 deletion ignite/pkg/cliui/cliui.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,6 @@ func (s *Session) StartSpinner(text string) {
// with an invalid format.
if s.options.verbosity == uilog.VerbosityVerbose {
fmt.Fprint(s.out.Stdout(), text)

return
}

Expand Down
12 changes: 7 additions & 5 deletions ignite/pkg/cosmosgen/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cosmosgen

import (
"context"
"errors"

"github.com/ignite/cli/ignite/pkg/gocmd"
)
Expand All @@ -23,11 +24,12 @@ func DepTools() []string {

// InstallDepTools installs protoc dependencies needed by Cosmos ecosystem.
func InstallDepTools(ctx context.Context, appPath string) error {
// NOTE(tb): go get is not required thanks to tools.go, but for retrocompat
// with older scaffolded chains that doesn't have this file, we need to run
// it anyway.
if err := gocmd.Get(ctx, appPath, DepTools()); err != nil {
if err := gocmd.ModTidy(ctx, appPath); err != nil {
return err
}
return gocmd.Install(ctx, appPath, DepTools())
err := gocmd.Install(ctx, appPath, DepTools())
if gocmd.IsInstallError(err) {
return errors.New("unable to install dependency tools, try to run `ignite doctor` and try again")
}
return err
}
18 changes: 17 additions & 1 deletion ignite/pkg/gocmd/gocmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,14 @@ func Fmt(ctx context.Context, path string, options ...exec.Option) error {

// ModTidy runs go mod tidy on path with options.
func ModTidy(ctx context.Context, path string, options ...exec.Option) error {
return exec.Exec(ctx, []string{Name(), CommandMod, CommandModTidy}, append(options, exec.StepOption(step.Workdir(path)))...)
return exec.Exec(ctx, []string{Name(), CommandMod, CommandModTidy},
append(options,
exec.StepOption(step.Workdir(path)),
// FIXME(tb) untagged version of ignite/cli triggers a 404 not found when go
// mod tidy requests the sumdb, until we understand why, we disable sumdb.
// related issue: https://github.com/golang/go/issues/56174
exec.StepOption(step.Env("GOSUMDB=off")),
)...)
}

// ModVerify runs go mod verify on path with options.
Expand Down Expand Up @@ -139,6 +146,15 @@ func Install(ctx context.Context, path string, pkgs []string, options ...exec.Op
return exec.Exec(ctx, command, append(options, exec.StepOption(step.Workdir(path)))...)
}

// IsInstallError returns true if err is interpreted as a go install error.
func IsInstallError(err error) bool {
if err == nil {
return false
}
const label = "no required module provides package"
return strings.Contains(err.Error(), label)
tbruyelle marked this conversation as resolved.
Show resolved Hide resolved
}

// Get runs go get pkgs on path with options.
func Get(ctx context.Context, path string, pkgs []string, options ...exec.Option) error {
command := []string{
Expand Down
18 changes: 18 additions & 0 deletions ignite/pkg/gocmd/gocmd_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package gocmd_test

import (
"errors"
"testing"

"github.com/stretchr/testify/assert"

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

func TestIsInstallError(t *testing.T) {
assert.False(t, gocmd.IsInstallError(errors.New("oups")))

err := errors.New(`error while running command go install github.com/regen-network/cosmos-proto/protoc-gen-gocosmos github.com/golang/protobuf/protoc-gen-go github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2: no required module provides package github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2; to add it:
go get github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2`)
assert.True(t, gocmd.IsInstallError(err))
}
9 changes: 9 additions & 0 deletions ignite/pkg/xfilepath/xfilepath.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ type PathRetriever func() (path string, err error)
// PathsRetriever is a function that retrieves the contained list of paths or an error.
type PathsRetriever func() (path []string, err error)

// MustInvoke invokes the PathsRetriever func and panics if it returns an error.
func MustInvoke(p PathRetriever) string {
path, err := p()
if err != nil {
panic(err)
}
return path
}

// Path returns a path retriever from the provided path.
func Path(path string) PathRetriever {
return func() (string, error) { return path, nil }
Expand Down
144 changes: 144 additions & 0 deletions ignite/services/doctor/doctor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package doctor

import (
"bytes"
"context"
"fmt"
"os"
"path"

"github.com/gobuffalo/genny/v2"

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/cosmosgen"
"github.com/ignite/cli/ignite/pkg/events"
"github.com/ignite/cli/ignite/pkg/gomodulepath"
"github.com/ignite/cli/ignite/templates/app"
)

// DONTCOVER: Doctor read and write the filesystem intensively, so it's better
// to rely on integration tests only. See integration/doctor package.
type Doctor struct {
jeronimoalbi marked this conversation as resolved.
Show resolved Hide resolved
ev events.Bus
}

// New returns a new doctor.
func New(opts ...Option) *Doctor {
d := &Doctor{}
for _, opt := range opts {
opt(d)
}
return d
}

type Option func(*Doctor)

// CollectEvents sets doctor event bus.
func CollectEvents(ev events.Bus) Option {
return func(d *Doctor) {
d.ev = ev
}
}

// MigrateConfig migrates the chain config if required.
func (d *Doctor) MigrateConfig(ctx context.Context) error {
errf := func(err error) error {
return fmt.Errorf("doctor migrate config: %w", err)
}

d.ev.Send("Checking chain config file:", events.ProgressFinish())

configPath, err := chainconfig.LocateDefault(".")
if err != nil {
return errf(err)
}
f, err := os.Open(configPath)
if err != nil {
return errf(err)
}
defer f.Close()
tbruyelle marked this conversation as resolved.
Show resolved Hide resolved
version, err := chainconfig.ReadConfigVersion(f)
if err != nil {
return errf(err)
}
if version != chainconfig.LatestVersion {
// migrate config file
// Convert the current config to the latest version and update the YAML file
var buf bytes.Buffer
f.Seek(0, 0)
jeronimoalbi marked this conversation as resolved.
Show resolved Hide resolved
if err := chainconfig.MigrateLatest(f, &buf); err != nil {
return errf(err)
}
if err := os.WriteFile(configPath, buf.Bytes(), 0o755); err != nil {
return errf(fmt.Errorf("config file migration failed: %w", err))
}
d.ev.Send(fmt.Sprintf("config file %s", colors.Success("migrated")),
events.Icon(icons.OK), events.ProgressFinish())
}
d.ev.Send("config file OK", events.Icon(icons.OK), events.ProgressFinish())

return nil
}

// FixDependencyTools ensures that:
// - tools/tools.go is present and populated properly
// - dependency tools are installed.
func (d *Doctor) FixDependencyTools(ctx context.Context) error {
errf := func(err error) error {
return fmt.Errorf("doctor fix dependency tools: %w", err)
}

d.ev.Send("Checking dependency tools:", events.ProgressFinish())

const toolsGoFile = "tools/tools.go"
jeronimoalbi marked this conversation as resolved.
Show resolved Hide resolved
_, err := os.Stat(toolsGoFile)

switch {
case err == nil:
// tools.go exists
d.ev.Send(fmt.Sprintf("%s exists", toolsGoFile), events.Icon(icons.OK),
events.ProgressFinish())
// TODO ensure tools.go has the required dependencies

case os.IsNotExist(err):
// create tools.go
pathInfo, err := gomodulepath.ParseAt(".")
if err != nil {
return errf(err)
}
g, err := app.NewGenerator(&app.Options{
ModulePath: pathInfo.RawPath,
AppName: pathInfo.Package,
BinaryNamePrefix: pathInfo.Root,
IncludePrefixes: []string{toolsGoFile},
})
if err != nil {
return errf(err)
}
// run generator
runner := genny.WetRunner(ctx)
if err := runner.With(g); err != nil {
return errf(err)
}
if err := runner.Run(); err != nil {
return errf(err)
}
d.ev.Send(fmt.Sprintf("%s %s", toolsGoFile, colors.Success("created")),
events.Icon(icons.OK), events.ProgressFinish())

d.ev.Send("Installing dependency tools", events.ProgressStart())
if err := cosmosgen.InstallDepTools(ctx, "."); err != nil {
return errf(err)
}
for _, dep := range cosmosgen.DepTools() {
d.ev.Send(fmt.Sprintf("%s %s", path.Base(dep), colors.Success("installed")),
events.Icon(icons.OK), events.ProgressFinish())
}

default:
return errf(err)
}
return nil
}
7 changes: 1 addition & 6 deletions ignite/services/plugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ import (

"github.com/ignite/cli/ignite/config"
pluginsconfig "github.com/ignite/cli/ignite/config/plugins"
cliexec "github.com/ignite/cli/ignite/pkg/cmdrunner/exec"
"github.com/ignite/cli/ignite/pkg/cmdrunner/step"
"github.com/ignite/cli/ignite/pkg/env"
"github.com/ignite/cli/ignite/pkg/events"
"github.com/ignite/cli/ignite/pkg/gocmd"
Expand Down Expand Up @@ -339,10 +337,7 @@ func (p *Plugin) build(ctx context.Context) {
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())

// FIXME(tb) we need to disable sumdb to get the branch version of CLI
// because our git history is too fat.
opt := cliexec.StepOption(step.Env("GOSUMDB=off"))
if err := gocmd.ModTidy(ctx, p.srcPath, opt); err != nil {
if err := gocmd.ModTidy(ctx, p.srcPath); err != nil {
p.Error = errors.Wrapf(err, "go mod tidy")
return
}
Expand Down
7 changes: 1 addition & 6 deletions ignite/services/plugin/scaffold.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import (
"github.com/gobuffalo/plush/v4"
"github.com/pkg/errors"

"github.com/ignite/cli/ignite/pkg/cmdrunner/exec"
"github.com/ignite/cli/ignite/pkg/cmdrunner/step"
"github.com/ignite/cli/ignite/pkg/gocmd"
"github.com/ignite/cli/ignite/pkg/xgenny"
)
Expand Down Expand Up @@ -53,10 +51,7 @@ func Scaffold(dir, moduleName string, sharedHost bool) (string, error) {
if err := r.Run(); err != nil {
return "", errors.WithStack(err)
}
// FIXME(tb) we need to disable sumdb to get the branch version of CLI
// because our git history is too fat.
opt := exec.StepOption(step.Env("GOSUMDB=off"))
if err := gocmd.ModTidy(context.TODO(), finalDir, opt); err != nil {
if err := gocmd.ModTidy(context.TODO(), finalDir); err != nil {
return "", errors.WithStack(err)
}
return finalDir, nil
Expand Down
Loading