277 lines
5.1 KiB
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
|
|
}
|