diff --git a/Makefile b/Makefile index a606a0acea..4c01f68e40 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ BUILD_TAGS = netgo ledger BUILD_FLAGS = -tags "${BUILD_TAGS}" -ldflags "-X github.com/cosmos/cosmos-sdk/version.GitCommit=${COMMIT_HASH}" GCC := $(shell command -v gcc 2> /dev/null) LEDGER_ENABLED ?= true -all: get_tools get_vendor_deps install install_examples test_lint test +all: get_tools get_vendor_deps install install_examples install_cosmos-sdk-cli test_lint test ######################################## ### CI @@ -37,6 +37,13 @@ endif build-linux: LEDGER_ENABLED=false GOOS=linux GOARCH=amd64 $(MAKE) build +build_cosmos-sdk-cli: +ifeq ($(OS),Windows_NT) + go build $(BUILD_FLAGS) -o build/cosmos-sdk-cli.exe ./cmd/cosmos-sdk-cli +else + go build $(BUILD_FLAGS) -o build/cosmos-sdk-cli ./cmd/cosmos-sdk-cli +endif + build_examples: ifeq ($(OS),Windows_NT) go build $(BUILD_FLAGS) -o build/basecoind.exe ./examples/basecoin/cmd/basecoind @@ -60,6 +67,9 @@ install_examples: go install $(BUILD_FLAGS) ./examples/democoin/cmd/democoind go install $(BUILD_FLAGS) ./examples/democoin/cmd/democli +install_cosmos-sdk-cli: + go install $(BUILD_FLAGS) ./cmd/cosmos-sdk-cli + install_debug: go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiadebug @@ -198,7 +208,7 @@ remotenet-status: # To avoid unintended conflicts with file names, always add to .PHONY # unless there is a reason not to. # https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html -.PHONY: build build_examples install install_examples install_debug dist \ +.PHONY: build build_cosmos-sdk-cli build_examples install install_examples install_cosmos-sdk-cli install_debug dist \ check_tools get_tools get_vendor_deps draw_deps test test_cli test_unit \ test_cover test_lint benchmark devdoc_init devdoc devdoc_save devdoc_update \ build-linux build-docker-gaiadnode localnet-start localnet-stop remotenet-start \ diff --git a/PENDING.md b/PENDING.md index 3d6d0d92de..f297e6a4af 100644 --- a/PENDING.md +++ b/PENDING.md @@ -6,6 +6,8 @@ BREAKING CHANGES FEATURES * [lcd] Can now query governance proposals by ProposalStatus +* Added support for cosmos-sdk-cli tool under cosmos-sdk/cmd + * This allows SDK users to init a new project repository with a single command. IMPROVEMENTS * [baseapp] Allow any alphanumeric character in route diff --git a/cmd/cosmos-sdk-cli/cmd/init.go b/cmd/cosmos-sdk-cli/cmd/init.go new file mode 100644 index 0000000000..5a0f3ddb38 --- /dev/null +++ b/cmd/cosmos-sdk-cli/cmd/init.go @@ -0,0 +1,152 @@ +package cmd + +import ( + "fmt" + "go/build" + "io/ioutil" + "os" + "strings" + + "github.com/cosmos/cosmos-sdk/version" + "github.com/spf13/cobra" + tmversion "github.com/tendermint/tendermint/version" + "path/filepath" +) + +var remoteBasecoinPath = "github.com/cosmos/cosmos-sdk/examples/basecoin" + +// Replacer to replace all instances of basecoin/basecli/BasecoinApp to project specific names +// Gets initialized when initCmd is executing after getting the project name from user +var replacer *strings.Replacer + +// Remote path for the project. +var remoteProjectPath string + +func init() { + initCmd.Flags().StringVarP(&remoteProjectPath, "project-path", "p", "", "Remote project path. eg: github.com/your_user_name/project_name") + rootCmd.AddCommand(initCmd) +} + +var initCmd = &cobra.Command{ + Use: "init [ProjectName]", + Short: "Initialize your new cosmos zone", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + fmt.Print("Thanks for choosing Cosmos-SDK to build your project.\n\n") + projectName := args[0] + capitalizedProjectName := strings.Title(projectName) + shortProjectName := strings.ToLower(projectName) + remoteProjectPath = strings.ToLower(strings.TrimSpace(remoteProjectPath)) + if remoteProjectPath == "" { + remoteProjectPath = strings.ToLower(shortProjectName) + } + replacer = strings.NewReplacer("basecli", shortProjectName+"cli", + "basecoind", shortProjectName+"d", + "BasecoinApp", capitalizedProjectName+"App", + remoteBasecoinPath, remoteProjectPath, + "basecoin", shortProjectName) + setupBasecoinWorkspace(shortProjectName, remoteProjectPath) + return nil + }, +} + +func resolveProjectPath(remoteProjectPath string) string { + gopath := os.Getenv("GOPATH") + if gopath == "" { + gopath = build.Default.GOPATH + // Use $HOME/go + } + return gopath + string(os.PathSeparator) + "src" + string(os.PathSeparator) + remoteProjectPath +} + +func copyBasecoinTemplate(projectName string, projectPath string, remoteProjectPath string) { + basecoinProjectPath := resolveProjectPath(remoteBasecoinPath) + filepath.Walk(basecoinProjectPath, func(path string, f os.FileInfo, err error) error { + if !f.IsDir() { + data, err := ioutil.ReadFile(path) + if err != nil { + return err + } + contents := string(data) + // Extract relative file path eg: app/app.go instead of /Users/..../github.com/cosmos/...examples/basecoin/app/app.go + relativeFilePath := path[len(basecoinProjectPath)+1:] + // Evaluating the filepath in the new project folder + projectFilePath := projectPath + string(os.PathSeparator) + relativeFilePath + projectFilePath = replacer.Replace(projectFilePath) + lengthOfRootDir := strings.LastIndex(projectFilePath, string(os.PathSeparator)) + // Extracting the path of root directory from the filepath + rootDir := projectFilePath[0:lengthOfRootDir] + // Creating the required directory first + os.MkdirAll(rootDir, os.ModePerm) + fmt.Println("Creating " + projectFilePath) + // Writing the contents to a file in the project folder + contents = replacer.Replace(contents) + ioutil.WriteFile(projectFilePath, []byte(contents), os.ModePerm) + } + return nil + }) +} + +func createGopkg(projectPath string) { + // Create gopkg.toml file + dependencies := map[string]string{ + "github.com/cosmos/cosmos-sdk": "=" + version.Version, + "github.com/stretchr/testify": "=1.2.1", + "github.com/spf13/cobra": "=0.0.1", + "github.com/spf13/viper": "=1.0.0", + } + overrides := map[string]string{ + "github.com/golang/protobuf": "1.1.0", + "github.com/tendermint/tendermint": tmversion.Version, + } + contents := "" + for dependency, version := range dependencies { + contents += "[[constraint]]\n\tname = \"" + dependency + "\"\n\tversion = \"" + version + "\"\n\n" + } + for dependency, version := range overrides { + contents += "[[override]]\n\tname = \"" + dependency + "\"\n\tversion = \"=" + version + "\"\n\n" + } + contents += "[prune]\n\tgo-tests = true\n\tunused-packages = true" + ioutil.WriteFile(projectPath+"/Gopkg.toml", []byte(contents), os.ModePerm) +} + +func createMakefile(projectPath string) { + // Create makefile + // TODO: Should we use tools/ directory as in Cosmos-SDK to get tools for linting etc. + makefileContents := `PACKAGES=$(shell go list ./... | grep -v '/vendor/') + +all: get_tools get_vendor_deps build test + +get_tools: + go get github.com/golang/dep/cmd/dep + +build: + go build -o bin/basecli cmd/basecli/main.go && go build -o bin/basecoind cmd/basecoind/main.go + +get_vendor_deps: + @rm -rf vendor/ + @dep ensure + +test: + @go test $(PACKAGES) + +benchmark: + @go test -bench=. $(PACKAGES) + +.PHONY: all build test benchmark` + + // Replacing instances of base* to project specific names + makefileContents = replacer.Replace(makefileContents) + + ioutil.WriteFile(projectPath+"/Makefile", []byte(makefileContents), os.ModePerm) + +} + +func setupBasecoinWorkspace(projectName string, remoteProjectPath string) { + projectPath := resolveProjectPath(remoteProjectPath) + fmt.Println("Configuring your project in " + projectPath) + copyBasecoinTemplate(projectName, projectPath, remoteProjectPath) + createGopkg(projectPath) + createMakefile(projectPath) + fmt.Printf("\nInitialized a new project at %s.\nHappy hacking!\n", projectPath) +} diff --git a/cmd/cosmos-sdk-cli/cmd/root.go b/cmd/cosmos-sdk-cli/cmd/root.go new file mode 100644 index 0000000000..2eddd79b5d --- /dev/null +++ b/cmd/cosmos-sdk-cli/cmd/root.go @@ -0,0 +1,21 @@ +package cmd + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" +) + +var rootCmd = &cobra.Command{ + Use: "cosmos-sdk-cli", + Short: "Tools to develop on cosmos-sdk", +} + +// Execute the command +func Execute() { + if err := rootCmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) + } +} diff --git a/cmd/cosmos-sdk-cli/main.go b/cmd/cosmos-sdk-cli/main.go new file mode 100644 index 0000000000..ee5ac02156 --- /dev/null +++ b/cmd/cosmos-sdk-cli/main.go @@ -0,0 +1,9 @@ +package main + +import ( + "github.com/cosmos/cosmos-sdk/cmd/cosmos-sdk-cli/cmd" +) + +func main() { + cmd.Execute() +} diff --git a/docs/sdk/cosmos-sdk-cli.md b/docs/sdk/cosmos-sdk-cli.md new file mode 100644 index 0000000000..615d722522 --- /dev/null +++ b/docs/sdk/cosmos-sdk-cli.md @@ -0,0 +1,34 @@ +# cosmos-sdk-cli +Create a new blockchain project based on cosmos-sdk with a single command. + +--- + +# Installation + +```shell +$ go get github.com/cosmos/cosmos-sdk +$ cd $GOPATH/src/github.com/cosmos/cosmos-sdk +$ make install_cosmos-sdk-cli +``` + +This will install a binary cosmos-sdk-cli + +# Creating a new project + +**$cosmos-sdk-cli init** _Your-Project-Name_ + +This will initialize a project, the dependencies, directory structures with the specified project name. + +### Example: +```shell +$ cosmos-sdk-cli init testzone -p github.com/your_user_name/testzone +``` +`-p [remote-project-path]`. If this is not provided, it creates testzone under $GOPATH/src/ + + +```shell +$ cd $GOPATH/src/github.com/your_user_name/testzone +$ make +``` +This will create two binaries(testzonecli and testzoned) under bin folder. testzoned is the full node of the application which you can run, and testzonecli is your light client. +