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: apply auto tools migration to other commands #3524

Merged
merged 7 commits into from
Jun 5, 2023
8 changes: 6 additions & 2 deletions ignite/cmd/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ import (
"github.com/ignite/cli/ignite/pkg/cosmosgen"
"github.com/ignite/cli/ignite/pkg/goanalysis"
"github.com/ignite/cli/ignite/pkg/xast"
"github.com/ignite/cli/ignite/services/doctor"
)

const (
msgMigration = "Migrating blockchain config file from v%d to v%d..."
msgMigrationPrefix = "Your blockchain config version is v%d and the latest is v%d."
msgMigrationPrompt = "Would you like to upgrade your config file to v%d"
toolsFile = "tools/tools.go"
)

// NewChain returns a command that groups sub commands related to compiling, serving
Expand Down Expand Up @@ -116,7 +116,11 @@ func toolsMigrationPreRunHandler(cmd *cobra.Command, session *cliui.Session) (er
session.StartSpinner("Checking missing tools...")

appPath := flagGetPath(cmd)
toolsFilename := filepath.Join(appPath, toolsFile)
toolsFilename := filepath.Join(appPath, doctor.ToolsFile)
if _, err := os.Stat(toolsFilename); os.IsNotExist(err) {
return errors.New("the dependency tools file is missing, run `ignite doctor` and try again")
}

f, _, err := xast.ParseFile(toolsFilename)
if err != nil {
return err
Expand Down
18 changes: 15 additions & 3 deletions ignite/cmd/generate.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package ignitecmd

import "github.com/spf13/cobra"
import (
"github.com/spf13/cobra"

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

// NewGenerate returns a command that groups code generation related sub commands.
func NewGenerate() *cobra.Command {
Expand All @@ -15,8 +19,9 @@ functionality, for example, generating an OpenAPI spec.
Produced source code can be regenerated by running a command again and is not
meant to be edited by hand.
`,
Aliases: []string{"g"},
Args: cobra.ExactArgs(1),
Aliases: []string{"g"},
Args: cobra.ExactArgs(1),
PersistentPreRunE: generatePreRunHandler,
}

flagSetPath(c)
Expand All @@ -30,3 +35,10 @@ meant to be edited by hand.

return c
}

func generatePreRunHandler(cmd *cobra.Command, _ []string) error {
session := cliui.New()
defer session.End()

return toolsMigrationPreRunHandler(cmd, session)
}
2 changes: 1 addition & 1 deletion ignite/pkg/cosmosgen/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func InstallDepTools(ctx context.Context, appPath string) error {
}
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 errors.New("unable to install dependency tools, run `ignite doctor` and try again")
}
return err
}
Expand Down
158 changes: 121 additions & 37 deletions ignite/services/doctor/doctor.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,17 @@ import (
"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/goanalysis"
"github.com/ignite/cli/ignite/pkg/gomodulepath"
"github.com/ignite/cli/ignite/pkg/xast"
"github.com/ignite/cli/ignite/templates/app"
)

const (
// ToolsFile defines the app relative path to the Go tools file.
ToolsFile = "tools/tools.go"
)

// 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 {
Expand Down Expand Up @@ -54,6 +61,7 @@ func (d *Doctor) MigrateConfig(_ context.Context) error {
if err != nil {
return errf(err)
}

f, err := os.Open(configPath)
if err != nil {
return errf(err)
Expand All @@ -64,21 +72,31 @@ func (d *Doctor) MigrateConfig(_ context.Context) error {
if err != nil {
return errf(err)
}

status := "OK"

if version != chainconfig.LatestVersion {
f.Seek(0, 0)

// migrate config file
// Convert the current config to the latest version and update the YAML file
var buf bytes.Buffer
f.Seek(0, 0)
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())

status = "migrated"
}
d.ev.Send("config file OK", events.Icon(icons.OK), events.ProgressFinish())

d.ev.Send(
fmt.Sprintf("config file %s", colors.Success(status)),
events.Icon(icons.OK),
events.ProgressFinish(),
)

return nil
}
Expand All @@ -93,53 +111,119 @@ func (d *Doctor) FixDependencyTools(ctx context.Context) error {

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

const toolsGoFile = "tools/tools.go"
_, err := os.Stat(toolsGoFile)
_, err := os.Stat(ToolsFile)

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
d.ev.Send(
fmt.Sprintf("%s %s", ToolsFile, colors.Success("exists")),
events.Icon(icons.OK),
events.ProgressUpdate(),
)

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},
})
updated, err := d.ensureDependencyImports(ToolsFile)
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)

status := "OK"
if updated {
status = "updated"
}
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 {
d.ev.Send(
fmt.Sprintf("tools file %s", colors.Success(status)),
events.Icon(icons.OK),
events.ProgressFinish(),
)

case os.IsNotExist(err):
if err := d.createToolsFile(ctx, ToolsFile); 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
}

func (d Doctor) createToolsFile(ctx context.Context, toolsFilename string) error {
pathInfo, err := gomodulepath.ParseAt(".")
if err != nil {
return err
}

g, err := app.NewGenerator(&app.Options{
ModulePath: pathInfo.RawPath,
AppName: pathInfo.Package,
BinaryNamePrefix: pathInfo.Root,
IncludePrefixes: []string{toolsFilename},
})
if err != nil {
return err
}

runner := genny.WetRunner(ctx)
if err := runner.With(g); err != nil {
return err
}

if err := runner.Run(); err != nil {
return err
}

d.ev.Send(
fmt.Sprintf("%s %s", toolsFilename, 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 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(),
)
}

return nil
}

func (d Doctor) ensureDependencyImports(toolsFilename string) (bool, error) {
d.ev.Send("Ensuring required tools imports", events.ProgressStart())

f, _, err := xast.ParseFile(toolsFilename)
if err != nil {
return false, err
}

var (
buf bytes.Buffer
missing = cosmosgen.MissingTools(f)
unused = cosmosgen.UnusedTools(f)
)

// Check if the tools file should be fixed
if len(missing) == 0 && len(unused) == 0 {
return false, nil
}

err = goanalysis.UpdateInitImports(f, &buf, missing, unused)
if err != nil {
return false, err
}

err = os.WriteFile(toolsFilename, buf.Bytes(), 0o644)
if err != nil {
return false, err
}

return true, nil
}