package command import ( "io" "io/ioutil" "log" "os" "path/filepath" "strings" "text/template" "github.com/manifoldco/promptui" rice "github.com/GeertJohan/go.rice" "github.com/Masterminds/sprig" "github.com/pkg/errors" "github.com/urfave/cli/v2" ) func newProjectCommand() *cli.Command { return &cli.Command{ Name: "new", Aliases: []string{"n"}, Flags: []cli.Flag{ &cli.StringFlag{ Name: "directory", Aliases: []string{"d"}, Value: "./", Usage: "project base directory", Required: false, }, &cli.StringFlag{ Name: "name", Aliases: []string{"n"}, Value: "", Usage: "project name", Required: false, }, &cli.StringFlag{ Name: "namespace", Aliases: []string{"ns"}, Value: "", Usage: "project module namespace", Required: false, }, &cli.StringFlag{ Name: "type", Aliases: []string{"t"}, Value: "", Usage: "project type", Required: false, }, }, Usage: "generate a new project", Action: newProjectAction, } } func newProjectAction(c *cli.Context) error { projectLayoutBox, err := rice.FindBox("../.project-layout") if err != nil { return err } destDir := c.String("directory") projectName := c.String("name") if projectName == "" { projectName, err = promptForProjectName() if err != nil { return err } } projectNamespace := c.String("namespace") if projectNamespace == "" { projectNamespace, err = promptForProjectNamespace() if err != nil { return err } } projectType := c.String("type") if projectType == "" { projectType, err = promptForProjectType() if err != nil { return err } } data := map[string]interface{}{ "ProjectName": projectName, "ProjectType": projectType, "ProjectNamespace": projectNamespace, } return copyTemplateDir(projectLayoutBox, projectType, destDir, data, ".gotpl") } func promptForProjectName() (string, error) { validate := func(input string) error { if input == "" { return errors.New("Project name cannot be empty") } return nil } prompt := promptui.Prompt{ Label: "Project Name", Validate: validate, } return prompt.Run() } func promptForProjectNamespace() (string, error) { validate := func(input string) error { if input == "" { return errors.New("Project namespace cannot be empty") } return nil } prompt := promptui.Prompt{ Label: "Project namespace", Validate: validate, } return prompt.Run() } func promptForProjectType() (string, error) { prompt := promptui.Select{ Label: "Project Type", Items: []string{"web"}, } _, result, err := prompt.Run() return result, err } func copyFile(box *rice.Box, src, dst string) (err error) { log.Printf("copying file '%s' to '%s'", src, dst) in, err := box.Open(src) if err != nil { return err } defer in.Close() out, err := os.Create(dst) if err != nil { return err } defer func() { if e := out.Close(); e != nil { err = e } }() _, err = io.Copy(out, in) if err != nil { return err } err = out.Sync() if err != nil { return err } return nil } func copyTemplateFile(box *rice.Box, src, dst string, data map[string]interface{}, templateExt string) (err error) { if !strings.HasSuffix(src, templateExt) { return copyFile(box, src, dst) } in, err := box.Open(src) if err != nil { return err } defer in.Close() templateData, err := ioutil.ReadAll(in) if err != nil { return err } tmpl, err := template.New(filepath.Base(src)).Funcs(sprig.TxtFuncMap()).Parse(string(templateData)) if err != nil { return err } dst = strings.TrimSuffix(dst, templateExt) log.Printf("templating file from '%s' to '%s'", src, dst) out, err := os.Create(dst) if err != nil { return err } defer func() { if e := out.Close(); e != nil { err = e } }() data["SourceFile"] = src data["DestFile"] = dst if err := tmpl.Execute(out, data); err != nil { return err } return nil } func copyTemplateDir(box *rice.Box, baseDir string, dst string, data map[string]interface{}, templateExt string) error { baseDir = filepath.Clean(baseDir) dst = filepath.Clean(dst) _, err := os.Stat(dst) if err != nil && !os.IsNotExist(err) { return err } if err := os.MkdirAll(dst, 0755); err != nil { return errors.Wrapf(err, "could not create directory '%s'", dst) } err = box.Walk(baseDir, func(srcPath string, info os.FileInfo, err error) error { if err != nil { return err } if srcPath == baseDir { return nil } relSrcPath, err := filepath.Rel(baseDir, srcPath) if err != nil { return err } dstPath := filepath.Join(dst, relSrcPath) if info.IsDir() { log.Printf("creating dir '%s'", dstPath) if err := os.MkdirAll(dstPath, 0755); err != nil { return errors.Wrapf(err, "could not create directory '%s'", dstPath) } return nil } err = copyTemplateFile(box, srcPath, dstPath, data, templateExt) if err != nil { return errors.Wrapf(err, "could not copy file '%s'", srcPath) } return nil }) if err != nil { return errors.Wrapf(err, "could not walk source directory '%s'", baseDir) } return nil }