go-micro.dev/v5@v5.12.0/cmd/micro/cli/new/new.go (about) 1 // Package new generates micro service templates 2 package new 3 4 import ( 5 "fmt" 6 "go/build" 7 "os" 8 "os/exec" 9 "path" 10 "path/filepath" 11 "runtime" 12 "strings" 13 "text/template" 14 "time" 15 16 "github.com/urfave/cli/v2" 17 "github.com/xlab/treeprint" 18 tmpl "go-micro.dev/v5/cmd/micro/cli/new/template" 19 ) 20 21 func protoComments(goDir, alias string) []string { 22 return []string{ 23 "\ndownload protoc zip packages (protoc-$VERSION-$PLATFORM.zip) and install:\n", 24 "visit https://github.com/protocolbuffers/protobuf/releases", 25 "\ncompile the proto file " + alias + ".proto:\n", 26 "cd " + alias, 27 "go mod tidy", 28 "make proto\n", 29 } 30 } 31 32 type config struct { 33 // foo 34 Alias string 35 // github.com/micro/foo 36 Dir string 37 // $GOPATH/src/github.com/micro/foo 38 GoDir string 39 // $GOPATH 40 GoPath string 41 // UseGoPath 42 UseGoPath bool 43 // Files 44 Files []file 45 // Comments 46 Comments []string 47 } 48 49 type file struct { 50 Path string 51 Tmpl string 52 } 53 54 func write(c config, file, tmpl string) error { 55 fn := template.FuncMap{ 56 "title": func(s string) string { 57 return strings.ReplaceAll(strings.Title(s), "-", "") 58 }, 59 "dehyphen": func(s string) string { 60 return strings.ReplaceAll(s, "-", "") 61 }, 62 "lower": func(s string) string { 63 return strings.ToLower(s) 64 }, 65 } 66 67 f, err := os.Create(file) 68 if err != nil { 69 return err 70 } 71 defer f.Close() 72 73 t, err := template.New("f").Funcs(fn).Parse(tmpl) 74 if err != nil { 75 return err 76 } 77 78 return t.Execute(f, c) 79 } 80 81 func create(c config) error { 82 // check if dir exists 83 if _, err := os.Stat(c.Dir); !os.IsNotExist(err) { 84 return fmt.Errorf("%s already exists", c.Dir) 85 } 86 87 fmt.Printf("Creating service %s\n\n", c.Alias) 88 89 t := treeprint.New() 90 91 // write the files 92 for _, file := range c.Files { 93 f := filepath.Join(c.Dir, file.Path) 94 dir := filepath.Dir(f) 95 96 if _, err := os.Stat(dir); os.IsNotExist(err) { 97 if err := os.MkdirAll(dir, 0755); err != nil { 98 return err 99 } 100 } 101 102 addFileToTree(t, file.Path) 103 if err := write(c, f, file.Tmpl); err != nil { 104 return err 105 } 106 } 107 108 // print tree 109 fmt.Println(t.String()) 110 111 for _, comment := range c.Comments { 112 fmt.Println(comment) 113 } 114 115 // just wait 116 <-time.After(time.Millisecond * 250) 117 118 return nil 119 } 120 121 func addFileToTree(root treeprint.Tree, file string) { 122 split := strings.Split(file, "/") 123 curr := root 124 for i := 0; i < len(split)-1; i++ { 125 n := curr.FindByValue(split[i]) 126 if n != nil { 127 curr = n 128 } else { 129 curr = curr.AddBranch(split[i]) 130 } 131 } 132 if curr.FindByValue(split[len(split)-1]) == nil { 133 curr.AddNode(split[len(split)-1]) 134 } 135 } 136 137 func Run(ctx *cli.Context) error { 138 dir := ctx.Args().First() 139 if len(dir) == 0 { 140 fmt.Println("specify service name") 141 return nil 142 } 143 144 // check if the path is absolute, we don't want this 145 // we want to a relative path so we can install in GOPATH 146 if path.IsAbs(dir) { 147 fmt.Println("require relative path as service will be installed in GOPATH") 148 return nil 149 } 150 151 // Check for protoc 152 if _, err := exec.LookPath("protoc"); err != nil { 153 fmt.Println("WARNING: protoc is not installed or not in your PATH.") 154 fmt.Println("Please install protoc from https://github.com/protocolbuffers/protobuf/releases") 155 fmt.Println("After installing, re-run 'make proto' in your service directory if needed.") 156 } 157 158 var goPath string 159 var goDir string 160 161 goPath = build.Default.GOPATH 162 163 // don't know GOPATH, runaway.... 164 if len(goPath) == 0 { 165 fmt.Println("unknown GOPATH") 166 return nil 167 } 168 169 // attempt to split path if not windows 170 if runtime.GOOS == "windows" { 171 goPath = strings.Split(goPath, ";")[0] 172 } else { 173 goPath = strings.Split(goPath, ":")[0] 174 } 175 goDir = filepath.Join(goPath, "src", path.Clean(dir)) 176 177 c := config{ 178 Alias: dir, 179 Comments: nil, // Remove redundant protoComments 180 Dir: dir, 181 GoDir: goDir, 182 GoPath: goPath, 183 UseGoPath: false, 184 Files: []file{ 185 {"main.go", tmpl.MainSRV}, 186 {"handler/" + dir + ".go", tmpl.HandlerSRV}, 187 {"proto/" + dir + ".proto", tmpl.ProtoSRV}, 188 {"Makefile", tmpl.Makefile}, 189 {"README.md", tmpl.Readme}, 190 {".gitignore", tmpl.GitIgnore}, 191 }, 192 } 193 194 // set gomodule 195 if os.Getenv("GO111MODULE") != "off" { 196 c.Files = append(c.Files, file{"go.mod", tmpl.Module}) 197 } 198 199 // create the files 200 if err := create(c); err != nil { 201 return err 202 } 203 204 // Run go mod tidy and make proto 205 fmt.Println("\nRunning 'go mod tidy' and 'make proto'...") 206 if err := runInDir(dir, "go mod tidy"); err != nil { 207 fmt.Printf("Error running 'go mod tidy': %v\n", err) 208 } 209 if err := runInDir(dir, "make proto"); err != nil { 210 fmt.Printf("Error running 'make proto': %v\n", err) 211 } 212 213 // Print updated tree including generated files 214 fmt.Println("\nProject structure after 'make proto':") 215 printTree(dir) 216 217 fmt.Println("\nService created successfully! Start coding in your new service directory.") 218 return nil 219 } 220 221 func runInDir(dir, cmd string) error { 222 parts := strings.Fields(cmd) 223 c := exec.Command(parts[0], parts[1:]...) 224 c.Dir = dir 225 c.Stdout = os.Stdout 226 c.Stderr = os.Stderr 227 return c.Run() 228 } 229 230 func printTree(dir string) { 231 t := treeprint.New() 232 walk := func(path string, info os.FileInfo, err error) error { 233 if err != nil { 234 return err 235 } 236 rel, _ := filepath.Rel(dir, path) 237 if rel == "." { 238 return nil 239 } 240 parts := strings.Split(rel, string(os.PathSeparator)) 241 curr := t 242 for i := 0; i < len(parts)-1; i++ { 243 n := curr.FindByValue(parts[i]) 244 if n != nil { 245 curr = n 246 } else { 247 curr = curr.AddBranch(parts[i]) 248 } 249 } 250 if !info.IsDir() { 251 curr.AddNode(parts[len(parts)-1]) 252 } 253 return nil 254 } 255 filepath.Walk(dir, walk) 256 fmt.Println(t.String()) 257 }