Add basic scaffolding tool to generate new projects
This commit is contained in:
11
cmd/scaffold/command/all.go
Normal file
11
cmd/scaffold/command/all.go
Normal file
@ -0,0 +1,11 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func All() []*cli.Command {
|
||||
return []*cli.Command{
|
||||
newProjectCommand(),
|
||||
}
|
||||
}
|
276
cmd/scaffold/command/new_project.go
Normal file
276
cmd/scaffold/command/new_project.go
Normal file
@ -0,0 +1,276 @@
|
||||
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
|
||||
}
|
Reference in New Issue
Block a user