github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/client/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  	"path"
     9  	"path/filepath"
    10  	"runtime"
    11  	"strings"
    12  	"text/template"
    13  	"time"
    14  
    15  	tmpl "github.com/tickoalcantara12/micro/v3/client/cli/new/template"
    16  	"github.com/tickoalcantara12/micro/v3/cmd"
    17  	"github.com/tickoalcantara12/micro/v3/cmd/usage"
    18  	"github.com/urfave/cli/v2"
    19  	"github.com/xlab/treeprint"
    20  )
    21  
    22  func protoComments(goDir, alias string) []string {
    23  	return []string{
    24  		"\ndownload protoc zip packages (protoc-$VERSION-$PLATFORM.zip) and install:\n",
    25  		"visit https://github.com/protocolbuffers/protobuf/releases",
    26  		"\ncompile the proto file " + alias + ".proto:\n",
    27  		"cd " + alias,
    28  		"make init",
    29  		"go mod vendor",
    30  		"make proto\n",
    31  	}
    32  }
    33  
    34  type config struct {
    35  	// foo
    36  	Alias string
    37  	// github.com/micro/foo
    38  	Dir string
    39  	// $GOPATH/src/github.com/micro/foo
    40  	GoDir string
    41  	// $GOPATH
    42  	GoPath string
    43  	// UseGoPath
    44  	UseGoPath bool
    45  	// Files
    46  	Files []file
    47  	// Comments
    48  	Comments []string
    49  }
    50  
    51  type file struct {
    52  	Path string
    53  	Tmpl string
    54  }
    55  
    56  func write(c config, file, tmpl string) error {
    57  	fn := template.FuncMap{
    58  		"title": func(s string) string {
    59  			return strings.ReplaceAll(strings.Title(s), "-", "")
    60  		},
    61  		"dehyphen": func(s string) string {
    62  			return strings.ReplaceAll(s, "-", "")
    63  		},
    64  		"lower": func(s string) string {
    65  			return strings.ToLower(s)
    66  		},
    67  	}
    68  
    69  	f, err := os.Create(file)
    70  	if err != nil {
    71  		return err
    72  	}
    73  	defer f.Close()
    74  
    75  	t, err := template.New("f").Funcs(fn).Parse(tmpl)
    76  	if err != nil {
    77  		return err
    78  	}
    79  
    80  	return t.Execute(f, c)
    81  }
    82  
    83  func create(c config) error {
    84  	// check if dir exists
    85  	if _, err := os.Stat(c.Dir); !os.IsNotExist(err) {
    86  		return fmt.Errorf("%s already exists", c.Dir)
    87  	}
    88  
    89  	// create usage report
    90  	u := usage.New("new")
    91  	// a single request/service
    92  	u.Metrics.Count["requests"] = uint64(1)
    93  	u.Metrics.Count["services"] = uint64(1)
    94  	// send report
    95  	go usage.Report(u)
    96  
    97  	// just wait
    98  	<-time.After(time.Millisecond * 250)
    99  
   100  	fmt.Printf("Creating service %s\n\n", c.Alias)
   101  
   102  	t := treeprint.New()
   103  
   104  	// write the files
   105  	for _, file := range c.Files {
   106  		f := filepath.Join(c.Dir, file.Path)
   107  		dir := filepath.Dir(f)
   108  
   109  		if _, err := os.Stat(dir); os.IsNotExist(err) {
   110  			if err := os.MkdirAll(dir, 0755); err != nil {
   111  				return err
   112  			}
   113  		}
   114  
   115  		addFileToTree(t, file.Path)
   116  		if err := write(c, f, file.Tmpl); err != nil {
   117  			return err
   118  		}
   119  	}
   120  
   121  	// print tree
   122  	fmt.Println(t.String())
   123  
   124  	for _, comment := range c.Comments {
   125  		fmt.Println(comment)
   126  	}
   127  
   128  	// just wait
   129  	<-time.After(time.Millisecond * 250)
   130  
   131  	return nil
   132  }
   133  
   134  func addFileToTree(root treeprint.Tree, file string) {
   135  	split := strings.Split(file, "/")
   136  	curr := root
   137  	for i := 0; i < len(split)-1; i++ {
   138  		n := curr.FindByValue(split[i])
   139  		if n != nil {
   140  			curr = n
   141  		} else {
   142  			curr = curr.AddBranch(split[i])
   143  		}
   144  	}
   145  	if curr.FindByValue(split[len(split)-1]) == nil {
   146  		curr.AddNode(split[len(split)-1])
   147  	}
   148  }
   149  
   150  func Run(ctx *cli.Context) error {
   151  	dir := ctx.Args().First()
   152  	if len(dir) == 0 {
   153  		fmt.Println("specify service name")
   154  		return nil
   155  	}
   156  
   157  	// check if the path is absolute, we don't want this
   158  	// we want to a relative path so we can install in GOPATH
   159  	if path.IsAbs(dir) {
   160  		fmt.Println("require relative path as service will be installed in GOPATH")
   161  		return nil
   162  	}
   163  
   164  	var goPath string
   165  	var goDir string
   166  
   167  	goPath = build.Default.GOPATH
   168  
   169  	// don't know GOPATH, runaway....
   170  	if len(goPath) == 0 {
   171  		fmt.Println("unknown GOPATH")
   172  		return nil
   173  	}
   174  
   175  	// attempt to split path if not windows
   176  	if runtime.GOOS == "windows" {
   177  		goPath = strings.Split(goPath, ";")[0]
   178  	} else {
   179  		goPath = strings.Split(goPath, ":")[0]
   180  	}
   181  	goDir = filepath.Join(goPath, "src", path.Clean(dir))
   182  
   183  	c := config{
   184  		Alias:     dir,
   185  		Comments:  protoComments(goDir, dir),
   186  		Dir:       dir,
   187  		GoDir:     goDir,
   188  		GoPath:    goPath,
   189  		UseGoPath: false,
   190  		Files: []file{
   191  			{"main.go", tmpl.MainSRV},
   192  			{"handler/" + dir + ".go", tmpl.HandlerSRV},
   193  			{"proto/" + dir + ".proto", tmpl.ProtoSRV},
   194  			{"Makefile", tmpl.Makefile},
   195  			{"README.md", tmpl.Readme},
   196  			{".gitignore", tmpl.GitIgnore},
   197  		},
   198  	}
   199  
   200  	// set gomodule
   201  	if os.Getenv("GO111MODULE") != "off" {
   202  		c.Files = append(c.Files, file{"go.mod", tmpl.Module})
   203  	}
   204  
   205  	// create the files
   206  	return create(c)
   207  }
   208  
   209  func init() {
   210  	cmd.Register(&cli.Command{
   211  		Name:        "new",
   212  		Usage:       "Create a service template",
   213  		Description: `'micro new' scaffolds a new service skeleton. Example: 'micro new helloworld && cd helloworld'`,
   214  		Action:      Run,
   215  	})
   216  }