From 02ccba744c89b1fb931ee698f2e452db30f12ca5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=B0lker=20G=C3=B6ktu=C4=9F=20=C3=96zt=C3=BCrk?= Date: Thu, 16 Jul 2020 15:27:40 +0300 Subject: [PATCH] cmd: app command should support dashes in app name (#26) * cmd: app command should support dashes in app name also we now have a strict validation check for user given app name both against go.mod module name rules and go package name rules for generated x/ pkg. * Remove dashes from appName in startServe * cmd/app: keep binary name same as the user give one also refactor the parts related to handling user given app path/name. * templates/app/templates/Makefile: rm dashes from conf dirs Co-authored-by: Denis Fadeev Co-authored-by: Denis Fadeev --- cmd/app.go | 24 ++++--- go.mod | 3 + go.sum | 3 + pkg/gomodulepath/gomodulepath.go | 70 +++++++++++++++++++ pkg/gomodulepath/gomodulepath_test.go | 42 +++++++++++ templates/app/new.go | 2 + templates/app/options.go | 7 +- templates/app/templates/Makefile.plush | 32 ++++----- .../main.go.plush | 0 .../genaccounts.go.plush | 0 .../main.go.plush | 0 11 files changed, 153 insertions(+), 30 deletions(-) create mode 100644 pkg/gomodulepath/gomodulepath.go create mode 100644 pkg/gomodulepath/gomodulepath_test.go rename templates/app/templates/cmd/{{{appName}}cli => {{binaryNamePrefix}}cli}/main.go.plush (100%) rename templates/app/templates/cmd/{{{appName}}d => {{binaryNamePrefix}}d}/genaccounts.go.plush (100%) rename templates/app/templates/cmd/{{{appName}}d => {{binaryNamePrefix}}d}/main.go.plush (100%) diff --git a/cmd/app.go b/cmd/app.go index c33b557c79..7369dd3ed9 100644 --- a/cmd/app.go +++ b/cmd/app.go @@ -4,10 +4,10 @@ import ( "context" "fmt" "os" - "strings" "github.com/gobuffalo/genny" "github.com/spf13/cobra" + "github.com/tendermint/starport/pkg/gomodulepath" "github.com/tendermint/starport/templates/app" ) @@ -15,21 +15,22 @@ var appCmd = &cobra.Command{ Use: "app [github.com/org/repo]", Short: "Generates an empty application", Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - denom, _ := cmd.Flags().GetString("denom") - var appName string - if t := strings.Split(args[0], "/"); len(t) > 0 { - appName = t[len(t)-1] + RunE: func(cmd *cobra.Command, args []string) error { + path, err := gomodulepath.Parse(args[0]) + if err != nil { + return err } + denom, _ := cmd.Flags().GetString("denom") g, _ := app.New(&app.Options{ - ModulePath: args[0], - AppName: appName, - Denom: denom, + ModulePath: path.RawPath, + AppName: path.Package, + BinaryNamePrefix: path.Root, + Denom: denom, }) run := genny.WetRunner(context.Background()) run.With(g) pwd, _ := os.Getwd() - run.Root = pwd + "/" + appName + run.Root = pwd + "/" + path.Root run.Run() message := ` ⭐️ Successfully created a Cosmos app '%[1]v'. @@ -40,6 +41,7 @@ var appCmd = &cobra.Command{ NOTE: add -v flag for advanced use. ` - fmt.Printf(message, appName) + fmt.Printf(message, path.Root) + return nil }, } diff --git a/go.mod b/go.mod index 60bcfc6df5..482a254c81 100644 --- a/go.mod +++ b/go.mod @@ -11,12 +11,15 @@ require ( github.com/gobuffalo/plushgen v0.1.2 github.com/gorilla/mux v1.7.4 github.com/karrick/godirwalk v1.15.6 // indirect + github.com/pkg/errors v0.8.1 github.com/radovskyb/watcher v1.0.7 github.com/rogpeppe/go-internal v1.6.0 // indirect github.com/sirupsen/logrus v1.6.0 // indirect github.com/spf13/cobra v1.0.0 github.com/spf13/pflag v1.0.5 // indirect + github.com/stretchr/testify v1.5.1 golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 // indirect + golang.org/x/mod v0.3.0 golang.org/x/net v0.0.0-20200625001655-4c5254603344 // indirect golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 // indirect golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae // indirect diff --git a/go.sum b/go.sum index 247e9e20f0..3c6ce277d9 100644 --- a/go.sum +++ b/go.sum @@ -220,7 +220,10 @@ golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/pkg/gomodulepath/gomodulepath.go b/pkg/gomodulepath/gomodulepath.go new file mode 100644 index 0000000000..2e7e097353 --- /dev/null +++ b/pkg/gomodulepath/gomodulepath.go @@ -0,0 +1,70 @@ +package gomodulepath + +import ( + "fmt" + "go/parser" + "go/token" + "strings" + + "github.com/pkg/errors" + "golang.org/x/mod/module" +) + +// Path represents a Go module's path. +type Path struct { + // Path is Go module's full path. + // e.g.: github.com/tendermint/starport. + RawPath string + + // Root is the root directory name of Go module. + // e.g.: starport for github.com/tendermint/starport. + Root string + + // Package is the default package name for the Go module that can be used + // to host main functionality of the module. + // e.g.: starport for github.com/tendermint/starport. + Package string +} + +// Parse parses rawpath into a module Path. +func Parse(rawpath string) (Path, error) { + if err := validateModulePath(rawpath); err != nil { + return Path{}, err + } + rootName := root(rawpath) + // package name cannot contain "-" so gracefully remove them + // if they present. + packageName := strings.ReplaceAll(rootName, "-", "") + if err := validatePackageName(packageName); err != nil { + return Path{}, err + } + p := Path{ + RawPath: rawpath, + Root: rootName, + Package: packageName, + } + return p, nil +} + +func validateModulePath(path string) error { + if err := module.CheckPath(path); err != nil { + return fmt.Errorf("app name is an invalid go module name: %w", err) + } + return nil +} + +func validatePackageName(name string) error { + fset := token.NewFileSet() + src := fmt.Sprintf("package %s", name) + if _, err := parser.ParseFile(fset, "", src, parser.PackageClauseOnly); err != nil { + // parser error is very low level here so let's hide it from the user + // completely. + return errors.New("app name is an invalid go package name") + } + return nil +} + +func root(path string) string { + sp := strings.Split(path, "/") + return sp[len(sp)-1] +} diff --git a/pkg/gomodulepath/gomodulepath_test.go b/pkg/gomodulepath/gomodulepath_test.go new file mode 100644 index 0000000000..0efacb3d59 --- /dev/null +++ b/pkg/gomodulepath/gomodulepath_test.go @@ -0,0 +1,42 @@ +package gomodulepath + +import ( + "errors" + "fmt" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestParse(t *testing.T) { + cases := []struct { + name string + rawpath string + path Path + err error + }{ + {"standard", + "github.com/a/b", Path{"github.com/a/b", "b", "b"}, nil, + }, + {"with dash", + "github.com/a/b-c", Path{"github.com/a/b-c", "b-c", "bc"}, nil, + }, + {"long", + "github.com/a/b/c", Path{"github.com/a/b/c", "c", "c"}, nil, + }, + {"invalid as go.mod module name", + "github.com/a/b/c@", Path{}, fmt.Errorf("app name is an invalid go module name: %w", + errors.New(`malformed module path "github.com/a/b/c@": invalid char '@'`)), + }, + } + for _, tt := range cases { + t.Run(tt.name, func(t *testing.T) { + path, err := Parse(tt.rawpath) + require.Equal(t, tt.err, err) + if err != nil { + return + } + require.Equal(t, tt.path, path) + }) + } +} diff --git a/templates/app/new.go b/templates/app/new.go index 871999dd60..ae01d8cdcc 100644 --- a/templates/app/new.go +++ b/templates/app/new.go @@ -18,10 +18,12 @@ func New(opts *Options) (*genny.Generator, error) { ctx := plush.NewContext() ctx.Set("ModulePath", opts.ModulePath) ctx.Set("AppName", opts.AppName) + ctx.Set("BinaryNamePrefix", opts.BinaryNamePrefix) ctx.Set("Denom", opts.Denom) ctx.Set("title", strings.Title) g.Transformer(plushgen.Transformer(ctx)) g.Transformer(genny.Replace("{{appName}}", opts.AppName)) + g.Transformer(genny.Replace("{{binaryNamePrefix}}", opts.BinaryNamePrefix)) return g, nil } diff --git a/templates/app/options.go b/templates/app/options.go index 369e25ad74..f77a46f11c 100644 --- a/templates/app/options.go +++ b/templates/app/options.go @@ -2,9 +2,10 @@ package app // Options ... type Options struct { - AppName string - ModulePath string - Denom string + AppName string + BinaryNamePrefix string + ModulePath string + Denom string } // Validate that options are usuable diff --git a/templates/app/templates/Makefile.plush b/templates/app/templates/Makefile.plush index 7aebc17bcf..28d80deba6 100644 --- a/templates/app/templates/Makefile.plush +++ b/templates/app/templates/Makefile.plush @@ -4,8 +4,8 @@ VERSION := $(shell echo $(shell git describe --tags) | sed 's/^v//') COMMIT := $(shell git log -1 --format='%H') ldflags = -X github.com/cosmos/cosmos-sdk/version.Name=NewApp \ - -X github.com/cosmos/cosmos-sdk/version.ServerName=<%= AppName %>d \ - -X github.com/cosmos/cosmos-sdk/version.ClientName=<%= AppName %>cli \ + -X github.com/cosmos/cosmos-sdk/version.ServerName=<%= BinaryNamePrefix %>d \ + -X github.com/cosmos/cosmos-sdk/version.ClientName=<%= BinaryNamePrefix %>cli \ -X github.com/cosmos/cosmos-sdk/version.Version=$(VERSION) \ -X github.com/cosmos/cosmos-sdk/version.Commit=$(COMMIT) @@ -14,8 +14,8 @@ BUILD_FLAGS := -ldflags '$(ldflags)' all: install install: go.sum - go install -mod=readonly $(BUILD_FLAGS) ./cmd/<%= AppName %>d - go install -mod=readonly $(BUILD_FLAGS) ./cmd/<%= AppName %>cli + go install -mod=readonly $(BUILD_FLAGS) ./cmd/<%= BinaryNamePrefix %>d + go install -mod=readonly $(BUILD_FLAGS) ./cmd/<%= BinaryNamePrefix %>cli go.sum: go.mod @echo "--> Ensure dependencies have not been modified" @@ -24,21 +24,21 @@ go.sum: go.mod init-pre: rm -rf ~/.<%= AppName %>cli rm -rf ~/.<%= AppName %>d - <%= AppName %>d init mynode --chain-id <%= AppName %> - <%= AppName %>cli config keyring-backend test + <%= BinaryNamePrefix %>d init mynode --chain-id <%= AppName %> + <%= BinaryNamePrefix %>cli config keyring-backend test init-user1: - <%= AppName %>cli keys add user1 --output json 2>&1 + <%= BinaryNamePrefix %>cli keys add user1 --output json 2>&1 init-user2: - <%= AppName %>cli keys add user2 --output json 2>&1 + <%= BinaryNamePrefix %>cli keys add user2 --output json 2>&1 init-post: - <%= AppName %>d add-genesis-account $$(<%= AppName %>cli keys show user1 -a) 1000token,100000000stake - <%= AppName %>d add-genesis-account $$(<%= AppName %>cli keys show user2 -a) 500token - <%= AppName %>cli config chain-id <%= AppName %> - <%= AppName %>cli config output json - <%= AppName %>cli config indent true - <%= AppName %>cli config trust-node true - <%= AppName %>d gentx --name user1 --keyring-backend test - <%= AppName %>d collect-gentxs \ No newline at end of file + <%= BinaryNamePrefix %>d add-genesis-account $$(<%= BinaryNamePrefix %>cli keys show user1 -a) 1000token,100000000stake + <%= BinaryNamePrefix %>d add-genesis-account $$(<%= BinaryNamePrefix %>cli keys show user2 -a) 500token + <%= BinaryNamePrefix %>cli config chain-id <%= AppName %> + <%= BinaryNamePrefix %>cli config output json + <%= BinaryNamePrefix %>cli config indent true + <%= BinaryNamePrefix %>cli config trust-node true + <%= BinaryNamePrefix %>d gentx --name user1 --keyring-backend test + <%= BinaryNamePrefix %>d collect-gentxs diff --git a/templates/app/templates/cmd/{{appName}}cli/main.go.plush b/templates/app/templates/cmd/{{binaryNamePrefix}}cli/main.go.plush similarity index 100% rename from templates/app/templates/cmd/{{appName}}cli/main.go.plush rename to templates/app/templates/cmd/{{binaryNamePrefix}}cli/main.go.plush diff --git a/templates/app/templates/cmd/{{appName}}d/genaccounts.go.plush b/templates/app/templates/cmd/{{binaryNamePrefix}}d/genaccounts.go.plush similarity index 100% rename from templates/app/templates/cmd/{{appName}}d/genaccounts.go.plush rename to templates/app/templates/cmd/{{binaryNamePrefix}}d/genaccounts.go.plush diff --git a/templates/app/templates/cmd/{{appName}}d/main.go.plush b/templates/app/templates/cmd/{{binaryNamePrefix}}d/main.go.plush similarity index 100% rename from templates/app/templates/cmd/{{appName}}d/main.go.plush rename to templates/app/templates/cmd/{{binaryNamePrefix}}d/main.go.plush