// Package scaffolder initializes Ignite CLI apps and modifies existing ones // to add more features in a later time. package scaffolder import ( "context" "os" "path/filepath" "github.com/gobuffalo/genny/v2" chainconfig "github.com/ignite/cli/v29/ignite/config/chain" "github.com/ignite/cli/v29/ignite/pkg/cache" "github.com/ignite/cli/v29/ignite/pkg/cosmosanalysis" "github.com/ignite/cli/v29/ignite/pkg/cosmosbuf" "github.com/ignite/cli/v29/ignite/pkg/cosmosgen" "github.com/ignite/cli/v29/ignite/pkg/cosmosver" "github.com/ignite/cli/v29/ignite/pkg/errors" "github.com/ignite/cli/v29/ignite/pkg/gocmd" "github.com/ignite/cli/v29/ignite/pkg/gomodulepath" "github.com/ignite/cli/v29/ignite/pkg/xgenny" "github.com/ignite/cli/v29/ignite/version" ) // Scaffolder is Ignite CLI app scaffolder. type Scaffolder struct { // Version of the chain Version cosmosver.Version // appPath path of the app. appPath string // protoDir path of the proto folder. protoDir string // modpath represents the go module Path of the app. modpath gomodulepath.Path // runner represents the scaffold xgenny runner. runner *xgenny.Runner } // New creates a new scaffold app. func New(context context.Context, appPath, protoDir string) (Scaffolder, error) { path, err := filepath.Abs(appPath) if err != nil { return Scaffolder{}, err } modpath, path, err := gomodulepath.Find(path) if err != nil { return Scaffolder{}, err } ver, err := cosmosver.Detect(path) if err != nil { return Scaffolder{}, err } // Make sure that the app was scaffolded with a supported Cosmos SDK version if err := version.AssertSupportedCosmosSDKVersion(ver); err != nil { return Scaffolder{}, err } if err := cosmosanalysis.IsChainPath(path); err != nil { return Scaffolder{}, err } s := Scaffolder{ Version: ver, appPath: path, protoDir: protoDir, modpath: modpath, runner: xgenny.NewRunner(context, path), } return s, nil } func (s Scaffolder) ApplyModifications(options ...xgenny.ApplyOption) (xgenny.SourceModification, error) { return s.runner.ApplyModifications(options...) } func (s Scaffolder) Run(gens ...*genny.Generator) error { return s.runner.Run(gens...) } func (s Scaffolder) PostScaffold(ctx context.Context, cacheStorage cache.Storage, skipProto bool) error { return PostScaffold(ctx, cacheStorage, s.appPath, s.protoDir, s.modpath.RawPath, skipProto) } func PostScaffold(ctx context.Context, cacheStorage cache.Storage, path, protoDir, goModPath string, skipProto bool) error { wd, err := os.Getwd() if err != nil { return errors.Errorf("failed to get current working directory: %w", err) } // go to the app path, in other to use the go tools if err := os.Chdir(path); err != nil { return errors.Errorf("failed to change directory to %s: %w", path, err) } if !skipProto { // go mod tidy prior and after the proto generation is required. if err := gocmd.ModTidy(ctx, path); err != nil { return err } if err := protoc(ctx, cacheStorage, path, protoDir, goModPath); err != nil { return err } } if err := gocmd.ModTidy(ctx, path); err != nil { return err } if err := gocmd.Fmt(ctx, path); err != nil { return err } if err := gocmd.GoImports(ctx, path); err != nil { return err } // return to the original working directory if err := os.Chdir(wd); err != nil { return errors.Errorf("failed to change directory to %s: %w", wd, err) } return nil } func protoc(ctx context.Context, cacheStorage cache.Storage, projectPath, protoDir, goModPath string) error { confpath, err := chainconfig.LocateDefault(projectPath) if err != nil { return err } conf, err := chainconfig.ParseFile(confpath) if err != nil { return err } options := []cosmosgen.Option{ cosmosgen.UpdateBufModule(), cosmosgen.WithGoGeneration(), } // Generate Typescript client code if it's enabled if conf.Client.Typescript.Path != "" { //nolint:staticcheck,nolintlint tsClientPath := chainconfig.TSClientPath(*conf) if !filepath.IsAbs(tsClientPath) { tsClientPath = filepath.Join(projectPath, tsClientPath) } options = append(options, cosmosgen.WithTSClientGeneration( cosmosgen.TypescriptModulePath(tsClientPath), tsClientPath, true, ), ) } if conf.Client.OpenAPI.Path != "" { openAPIPath := conf.Client.OpenAPI.Path if !filepath.IsAbs(openAPIPath) { openAPIPath = filepath.Join(projectPath, openAPIPath) } options = append(options, cosmosgen.WithOpenAPIGeneration(openAPIPath, conf.Client.OpenAPI.ExcludeList)) } if err := cosmosgen.Generate( ctx, cacheStorage, projectPath, protoDir, goModPath, chainconfig.DefaultVuePath, options..., ); err != nil { return err } buf, err := cosmosbuf.New(cacheStorage, goModPath) if err != nil { return err } return buf.Format(ctx, projectPath) }