goweb/cmd/scaffold/command/new_project.go

277 lines
5.1 KiB
Go

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
}