diff --git a/.gitignore b/.gitignore index 5e3a88c..45b5408 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ vendor/ .idea *.iml +.vscode/ diff --git a/README.md b/README.md index 7d5698f..b77bafa 100644 --- a/README.md +++ b/README.md @@ -31,11 +31,19 @@ kit help Also read this [medium story](https://medium.com/@kujtimii.h/creating-a-todo-app-using-gokit-cli-20f066a58e1) # Create a new service +The kit tool use modules to manage dependencies, please make sure your go version >= 1.3, or +GO111MODULE is set on. If you want to specify the module name, you should use the --module flag, otherwise, the module name in the go.mod file will be set as your project name. ```bash kit new service hello kit n s hello # using aliases ``` -This will generate the initial folder structure and the service interface +or +```bash +kit new service hello --module github.com/{group name}/hello +kit n s hello -m github.com/{group name}/hello # using aliases +``` + +This will generate the initial folder structure, the go.mod file and the service interface `service-name/pkg/service/service.go` ```go @@ -47,6 +55,7 @@ type HelloService interface { // e.x: Foo(ctx context.Context,s string)(rs string, err error) } ``` +When you are generating the service and the client library, the module name in the go.mod file could be autodetected. # Generate the service ```bash @@ -122,4 +131,4 @@ docker-compose up ``` After you run `docker-compose up` your services will start up and any change you make to your code will automatically - rebuild and restart your service (only the service that is changed) \ No newline at end of file + rebuild and restart your service (only the service that is changed) diff --git a/cmd/root.go b/cmd/root.go index 70d2306..5bf263f 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -31,6 +31,7 @@ func init() { RootCmd.PersistentFlags().BoolP("debug", "d", false, "If you want to se the debug logs.") RootCmd.PersistentFlags().BoolP("force", "f", false, "Force overide existing files without asking.") RootCmd.PersistentFlags().StringP("folder", "b", "", "If you want to specify the base folder of the project.") + viper.BindPFlag("gk_folder", RootCmd.PersistentFlags().Lookup("folder")) viper.BindPFlag("gk_force", RootCmd.PersistentFlags().Lookup("force")) viper.BindPFlag("gk_debug", RootCmd.PersistentFlags().Lookup("debug")) diff --git a/cmd/service.go b/cmd/service.go index 47baf00..2d27760 100644 --- a/cmd/service.go +++ b/cmd/service.go @@ -4,6 +4,7 @@ import ( "github.com/kujtimiihoxha/kit/generator" "github.com/sirupsen/logrus" "github.com/spf13/cobra" + "github.com/spf13/viper" ) var serviceCmd = &cobra.Command{ @@ -24,4 +25,6 @@ var serviceCmd = &cobra.Command{ func init() { newCmd.AddCommand(serviceCmd) + serviceCmd.Flags().StringP("module", "m", "", "The module name that you plan to set in the project") + viper.BindPFlag("n_s_module", serviceCmd.Flags().Lookup("module")) } diff --git a/generator/generate_service.go b/generator/generate_service.go index cc0db5e..8ab3531 100644 --- a/generator/generate_service.go +++ b/generator/generate_service.go @@ -1683,9 +1683,16 @@ func (g *generateCmd) generateRun() (*PartialGenerator, error) { jen.Lit("URL"), jen.Id("*zipkinURL"), ), - jen.List(jen.Id("collector"), jen.Err()).Op(":=").Qual( - "github.com/openzipkin/zipkin-go-opentracing", "NewHTTPCollector", + jen.Id("reporter").Op(":=").Qual( + "github.com/openzipkin/zipkin-go/reporter/http", "NewReporter", ).Call(jen.Id("*zipkinURL")), + jen.Defer().Id("reporter").Dot("Close").Call(), + jen.List(jen.Id("endpoint"), jen.Id("err")).Op(":=").Qual( + "github.com/openzipkin/zipkin-go", "NewEndpoint", + ).Call( + jen.Lit(g.name), + jen.Lit("localhost:80"), + ), jen.If(jen.Err().Op("!=").Nil()).Block( jen.Id("logger").Dot("Log").Call( jen.Lit("err"), @@ -1693,18 +1700,10 @@ func (g *generateCmd) generateRun() (*PartialGenerator, error) { ), jen.Qual("os", "Exit").Call(jen.Lit(1)), ), - jen.Defer().Id("collector").Dot("Close").Call(), - jen.Id("recorder").Op(":=").Qual( - "github.com/openzipkin/zipkin-go-opentracing", "NewRecorder", - ).Call( - jen.Id("collector"), - jen.Lit(false), - jen.Lit("localhost:80"), - jen.Lit(g.name), - ), - jen.List(jen.Id("tracer"), jen.Id("err")).Op("=").Qual( - "github.com/openzipkin/zipkin-go-opentracing", "NewTracer", - ).Call(jen.Id("recorder")), + jen.Id("localEndpoint").Op(":=").Qual("github.com/openzipkin/zipkin-go", "WithLocalEndpoint").Call(jen.Id("endpoint")), + jen.List(jen.Id("nativeTracer"), jen.Id("err")).Op(":=").Qual( + "github.com/openzipkin/zipkin-go", "NewTracer", + ).Call(jen.Id("reporter"), jen.Id("localEndpoint")), jen.If(jen.Err().Op("!=").Nil()).Block( jen.Id("logger").Dot("Log").Call( jen.Lit("err"), @@ -1712,6 +1711,11 @@ func (g *generateCmd) generateRun() (*PartialGenerator, error) { ), jen.Qual("os", "Exit").Call(jen.Lit(1)), ), + jen.Id("tracer").Op("=").Qual( + "github.com/openzipkin-contrib/zipkin-go-opentracing", "Wrap", + ).Call( + jen.Id("nativeTracer"), + ), ).Else().If(jen.Id("*lightstepToken").Op("!=").Lit("")).Block( jen.Id("logger").Dot("Log").Call( jen.Lit("tracer"), @@ -1728,8 +1732,8 @@ func (g *generateCmd) generateRun() (*PartialGenerator, error) { ), ), jen.Defer().Qual( - "github.com/lightstep/lightstep-tracer-go", "FlushLightStepTracer", - ).Call(jen.Id("tracer")), + "github.com/lightstep/lightstep-tracer-go", "Flush", + ).Call(jen.Qual("context","Background").Call(), jen.Id("tracer")), ).Else().If(jen.Id("*appdashAddr").Op("!=").Lit("")).Block( jen.Id("logger").Dot("Log").Call( jen.Lit("tracer"), diff --git a/generator/new_service.go b/generator/new_service.go index 37510da..29a2cb7 100644 --- a/generator/new_service.go +++ b/generator/new_service.go @@ -2,6 +2,7 @@ package generator import ( "fmt" + "os/exec" "path" "strings" @@ -40,6 +41,12 @@ func NewNewService(name string) Gen { // Generate will run the generator. func (g *NewService) Generate() error { g.CreateFolderStructure(g.destPath) + err := g.genModule() + if err != nil { + println(err.Error()) + return err + } + comments := []string{ "Add your methods here", "e.x: Foo(ctx context.Context,s string)(rs string, err error)", @@ -53,3 +60,19 @@ func (g *NewService) Generate() error { ) return g.fs.WriteFile(g.filePath, g.srcFile.GoString(), false) } + +func (g *NewService) genModule() error { + exist, _ := g.fs.Exists(g.name + "/go.mod") + if exist { + return nil + } + + moduleName := g.name + if viper.GetString("n_s_module") != "" { + moduleName = viper.GetString("n_s_module") + } + cmdStr := "cd " + g.name + " && go mod init " + moduleName + cmd := exec.Command("sh", "-c", cmdStr) + _, err := cmd.Output() + return err +} diff --git a/main.go b/main.go index e273478..132f06f 100644 --- a/main.go +++ b/main.go @@ -1,42 +1,16 @@ package main import ( - "os" "path" - "path/filepath" "runtime" - "strings" "github.com/kujtimiihoxha/kit/cmd" - "github.com/kujtimiihoxha/kit/utils" - "github.com/sirupsen/logrus" - "github.com/spf13/afero" "github.com/spf13/viper" ) func main() { setDefaults() viper.AutomaticEnv() - gosrc := strings.TrimSuffix(utils.GetGOPATH(), afero.FilePathSeparator ) + afero.FilePathSeparator + "src" + afero.FilePathSeparator - pwd, err := os.Getwd() - if err != nil { - logrus.Error(err) - return - } - gosrc, err = filepath.EvalSymlinks(gosrc) - if err != nil { - logrus.Error(err) - return - } - pwd, err = filepath.EvalSymlinks(pwd) - if err != nil { - logrus.Error(err) - return - } - if !strings.HasPrefix(pwd, gosrc) { - logrus.Error("The project must be in the $GOPATH/src folder for the generator to work.") - return - } cmd.Execute() } diff --git a/utils/utils.go b/utils/utils.go index dee0f9d..798c863 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -9,6 +9,7 @@ import ( "fmt" "github.com/alioygur/godash" + "github.com/kujtimiihoxha/kit/fs" "github.com/spf13/viper" "golang.org/x/tools/imports" ) @@ -55,122 +56,32 @@ func GoImportsSource(path string, s string) (string, error) { // GetServiceImportPath returns the import path of the service interface. func GetServiceImportPath(name string) (string, error) { - gosrc := GetGOPATH() + "/src/" - gosrc = strings.Replace(gosrc, "\\", "/", -1) - pwd, err := os.Getwd() - if err != nil { - return "", err - } - if viper.GetString("gk_folder") != "" { - pwd += "/" + viper.GetString("gk_folder") - } - pwd = strings.Replace(pwd, "\\", "/", -1) - projectPath := strings.Replace(pwd, gosrc, "", 1) - svcPath := fmt.Sprintf(viper.GetString("gk_service_path_format"), ToLowerSnakeCase(name)) - - svcPath = strings.Replace(svcPath, "\\", "/", -1) - serviceImport := projectPath + "/" + svcPath - return serviceImport, nil + return getImportPath(name, "gk_service_path_format") } // GetCmdServiceImportPath returns the import path of the cmd service (used by cmd/main.go). func GetCmdServiceImportPath(name string) (string, error) { - gosrc := GetGOPATH() + "/src/" - gosrc = strings.Replace(gosrc, "\\", "/", -1) - pwd, err := os.Getwd() - if err != nil { - return "", err - } - if viper.GetString("gk_folder") != "" { - pwd += "/" + viper.GetString("gk_folder") - } - pwd = strings.Replace(pwd, "\\", "/", -1) - projectPath := strings.Replace(pwd, gosrc, "", 1) - svcPath := fmt.Sprintf(viper.GetString("gk_cmd_service_path_format"), ToLowerSnakeCase(name)) - - svcPath = strings.Replace(svcPath, "\\", "/", -1) - serviceImport := projectPath + "/" + svcPath - return serviceImport, nil + return getImportPath(name, "gk_cmd_service_path_format") } // GetEndpointImportPath returns the import path of the service endpoints. func GetEndpointImportPath(name string) (string, error) { - gosrc := GetGOPATH() + "/src/" - gosrc = strings.Replace(gosrc, "\\", "/", -1) - pwd, err := os.Getwd() - if err != nil { - return "", err - } - if viper.GetString("gk_folder") != "" { - pwd += "/" + viper.GetString("gk_folder") - } - pwd = strings.Replace(pwd, "\\", "/", -1) - projectPath := strings.Replace(pwd, gosrc, "", 1) - epPath := fmt.Sprintf(viper.GetString("gk_endpoint_path_format"), ToLowerSnakeCase(name)) - - epPath = strings.Replace(epPath, "\\", "/", -1) - endpointImport := projectPath + "/" + epPath - return endpointImport, nil + return getImportPath(name, "gk_endpoint_path_format") } // GetGRPCTransportImportPath returns the import path of the service grpc transport. func GetGRPCTransportImportPath(name string) (string, error) { - gosrc := GetGOPATH() + "/src/" - gosrc = strings.Replace(gosrc, "\\", "/", -1) - pwd, err := os.Getwd() - if err != nil { - return "", err - } - if viper.GetString("gk_folder") != "" { - pwd += "/" + viper.GetString("gk_folder") - } - pwd = strings.Replace(pwd, "\\", "/", -1) - projectPath := strings.Replace(pwd, gosrc, "", 1) - epPath := fmt.Sprintf(viper.GetString("gk_grpc_path_format"), ToLowerSnakeCase(name)) - - epPath = strings.Replace(epPath, "\\", "/", -1) - endpointImport := projectPath + "/" + epPath - return endpointImport, nil + return getImportPath(name, "gk_grpc_path_format") } // GetPbImportPath returns the import path of the generated service grpc pb. func GetPbImportPath(name string) (string, error) { - gosrc := GetGOPATH() + "/src/" - gosrc = strings.Replace(gosrc, "\\", "/", -1) - pwd, err := os.Getwd() - if err != nil { - return "", err - } - if viper.GetString("gk_folder") != "" { - pwd += "/" + viper.GetString("gk_folder") - } - pwd = strings.Replace(pwd, "\\", "/", -1) - projectPath := strings.Replace(pwd, gosrc, "", 1) - epPath := fmt.Sprintf(viper.GetString("gk_grpc_pb_path_format"), ToLowerSnakeCase(name)) - - epPath = strings.Replace(epPath, "\\", "/", -1) - endpointImport := projectPath + "/" + epPath - return endpointImport, nil + return getImportPath(name, "gk_grpc_pb_path_format") } // GetHTTPTransportImportPath returns the import path of the service http transport. func GetHTTPTransportImportPath(name string) (string, error) { - gosrc := GetGOPATH() + "/src/" - gosrc = strings.Replace(gosrc, "\\", "/", -1) - pwd, err := os.Getwd() - if err != nil { - return "", err - } - if viper.GetString("gk_folder") != "" { - pwd += "/" + viper.GetString("gk_folder") - } - pwd = strings.Replace(pwd, "\\", "/", -1) - projectPath := strings.Replace(pwd, gosrc, "", 1) - epPath := fmt.Sprintf(viper.GetString("gk_http_path_format"), ToLowerSnakeCase(name)) - - epPath = strings.Replace(epPath, "\\", "/", -1) - httpImports := projectPath + "/" + epPath - return httpImports, nil + return getImportPath(name, "gk_http_path_format") } // GetDockerFileProjectPath returns the path of the project. @@ -184,8 +95,10 @@ func GetDockerFileProjectPath() (string, error) { if viper.GetString("gk_folder") != "" { pwd += "/" + viper.GetString("gk_folder") } + pwd = strings.Replace(pwd, "\\", "/", -1) projectPath := strings.Replace(pwd, gosrc, "", 1) + return projectPath, nil } @@ -215,3 +128,77 @@ func defaultGOPATH() string { } return "" } + +func getImportPath(name string, key string) (string, error) { + modName, err := getModNameFromModFile(name) + if err != nil { + return "", err + } + + gosrc := GetGOPATH() + "/src/" + gosrc = strings.Replace(gosrc, "\\", "/", -1) + pwd, err := os.Getwd() + if err != nil { + return "", err + } + if viper.GetString("gk_folder") != "" { + pwd += "/" + viper.GetString("gk_folder") + } + + pwd = strings.Replace(pwd, "\\", "/", -1) + projectPath := strings.Replace(pwd, gosrc, "", 1) + + svcPath := fmt.Sprintf(viper.GetString(key), ToLowerSnakeCase(name)) + + path := strings.Replace(svcPath, "\\", "/", -1) + if modName != "" { + modName = strings.Replace(modName, "\\", "/", -1) + modNameArr := strings.Split(modName, "/") + if len(modNameArr) <= 1 { + projectPath = "" + } else { + projectPath = strings.Join(modNameArr[0:len(modNameArr)-1], "/") + } + } + var importPath string + if projectPath == "" { + importPath = path + } else { + importPath = projectPath + "/" + path + } + return importPath, nil +} + +func getModNameFromModFile(name string) (string, error) { + modFile := "go.mod" + filePath := name + "/" + modFile + exists, _ := fs.Get().Exists(filePath) + var modFileInParentLevel bool + if exists == false { + //if the service level has no go.mod file, it will check the parent level + exists, err := fs.Get().Exists(modFile) + if exists == false { + return "", err + } + filePath = modFile + modFileInParentLevel = true + } + + content, err := fs.Get().ReadFile(filePath) + if err != nil { + return "", err + } + + modDataArr := strings.Split(content, "\n") + if len(modDataArr) != 0 { + modNameArr := strings.Split(modDataArr[0], " ") + if len(modNameArr) < 2 { // go.mod file: module XXXX/XXXX/{projectName} + return "", nil + } + if modFileInParentLevel == true { + return modNameArr[1] + "/" + name, nil + } + return modNameArr[1], nil + } + return "", nil +}