mirror of
https://github.com/kevin-DL/revel-cmd.git
synced 2026-01-11 18:54:31 +00:00
Initial rewrite of revel/cmd to provide vendor support and enhanced CLI options
This commit is contained in:
291
revel/new.go
291
revel/new.go
@@ -14,11 +14,12 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/revel/revel"
|
||||
"github.com/revel/cmd/model"
|
||||
"github.com/revel/cmd/utils"
|
||||
)
|
||||
|
||||
var cmdNew = &Command{
|
||||
UsageLine: "new [path] [skeleton]",
|
||||
UsageLine: "new -i [path] -s [skeleton]",
|
||||
Short: "create a skeleton Revel application",
|
||||
Long: `
|
||||
New creates a few files to get a new Revel application running quickly.
|
||||
@@ -30,61 +31,113 @@ Skeleton is an optional argument, provided as an import path
|
||||
|
||||
For example:
|
||||
|
||||
revel new import/path/helloworld
|
||||
revel new -a import/path/helloworld
|
||||
|
||||
revel new -a import/path/helloworld -s import/path/skeleton
|
||||
|
||||
revel new import/path/helloworld import/path/skeleton
|
||||
`,
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmdNew.Run = newApp
|
||||
cmdNew.RunWith = newApp
|
||||
cmdNew.UpdateConfig = updateNewConfig
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
// go related paths
|
||||
gopath string
|
||||
gocmd string
|
||||
srcRoot string
|
||||
|
||||
// revel related paths
|
||||
revelPkg *build.Package
|
||||
revelCmdPkg *build.Package
|
||||
appPath string
|
||||
appName string
|
||||
basePath string
|
||||
importPath string
|
||||
skeletonPath string
|
||||
)
|
||||
|
||||
func newApp(args []string) {
|
||||
// check for proper args by count
|
||||
// Called when unable to parse the command line automatically and assumes an old launch
|
||||
func updateNewConfig(c *model.CommandConfig, args []string) bool {
|
||||
c.Index = NEW
|
||||
if len(args) == 0 {
|
||||
errorf("No import path given.\nRun 'revel help new' for usage.\n")
|
||||
fmt.Fprintf(os.Stderr, cmdNew.Long)
|
||||
return false
|
||||
}
|
||||
if len(args) > 2 {
|
||||
errorf("Too many arguments provided.\nRun 'revel help new' for usage.\n")
|
||||
c.New.ImportPath = args[0]
|
||||
if len(args)>1 {
|
||||
c.New.Skeleton = args[1]
|
||||
}
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
// Call to create a new application
|
||||
func newApp(c *model.CommandConfig) {
|
||||
// check for proper args by count
|
||||
c.ImportPath = c.New.ImportPath
|
||||
c.SkeletonPath = c.New.Skeleton
|
||||
|
||||
// Check for an existing folder so we dont clober it
|
||||
c.AppPath = filepath.Join(c.SrcRoot, filepath.FromSlash(c.ImportPath))
|
||||
_, err := build.Import(c.ImportPath, "", build.FindOnly)
|
||||
if err==nil || !utils.Empty(c.AppPath) {
|
||||
utils.Logger.Fatal("Abort: Import path already exists.","path", c.ImportPath)
|
||||
}
|
||||
|
||||
if c.New.Vendored {
|
||||
depPath, err := exec.LookPath("dep")
|
||||
if err != nil {
|
||||
// Do not halt build unless a new package needs to be imported
|
||||
utils.Logger.Fatal("New: `dep` executable not found in PATH, but vendor folder requested." +
|
||||
"You must install the dep tool before creating a vendored project. " +
|
||||
"You can install the `dep` tool by doing a `go get -u github.com/golang/dep/cmd/dep`")
|
||||
}
|
||||
vendorPath := filepath.Join(c.ImportPath,"vendor")
|
||||
if !utils.DirExists(vendorPath) {
|
||||
err := os.MkdirAll(vendorPath,os.ModePerm)
|
||||
utils.PanicOnError(err, "Failed to create " + vendorPath)
|
||||
}
|
||||
// In order for dep to run there needs to be a source file in the folder
|
||||
tempPath := filepath.Join(c.ImportPath,"tmp")
|
||||
if !utils.DirExists(tempPath) {
|
||||
err := os.MkdirAll(tempPath,os.ModePerm)
|
||||
utils.PanicOnError(err, "Failed to create " + vendorPath)
|
||||
err = utils.MustGenerateTemplate(filepath.Join(tempPath,"main.go"), NEW_MAIN_FILE, nil)
|
||||
utils.PanicOnError(err, "Failed to create main file " + vendorPath)
|
||||
|
||||
}
|
||||
packageFile := filepath.Join(c.ImportPath,"Gopkg.toml")
|
||||
if !utils.Exists(packageFile) {
|
||||
utils.MustGenerateTemplate(packageFile,VENDOR_GOPKG,nil)
|
||||
} else {
|
||||
utils.Logger.Info("Package file exists in skeleto, skipping adding")
|
||||
}
|
||||
|
||||
getCmd := exec.Command(depPath, "ensure", "-v")
|
||||
getCmd.Dir = c.ImportPath
|
||||
utils.Logger.Info("Exec:", "args", getCmd.Args)
|
||||
getCmd.Dir = c.ImportPath
|
||||
getOutput, err := getCmd.CombinedOutput()
|
||||
if err != nil {
|
||||
utils.Logger.Fatal(string(getOutput))
|
||||
}
|
||||
|
||||
// TODO build.Default.GOPATH = build.Default.GOPATH + string(os.PathListSeparator) + c.ImportPath
|
||||
}
|
||||
|
||||
// checking and setting go paths
|
||||
initGoPaths()
|
||||
|
||||
// checking and setting application
|
||||
setApplicationPath(args)
|
||||
setApplicationPath(c)
|
||||
|
||||
// checking and setting skeleton
|
||||
setSkeletonPath(args)
|
||||
setSkeletonPath(c)
|
||||
|
||||
// copy files to new app directory
|
||||
copyNewAppFiles()
|
||||
copyNewAppFiles(c)
|
||||
|
||||
|
||||
// goodbye world
|
||||
fmt.Fprintln(os.Stdout, "Your application is ready:\n ", appPath)
|
||||
fmt.Fprintln(os.Stdout, "\nYou can run it with:\n revel run", importPath)
|
||||
fmt.Fprintln(os.Stdout, "Your application is ready:\n ", c.AppPath)
|
||||
// Check to see if it should be run right off
|
||||
if c.New.Run {
|
||||
c.Run.ImportPath = c.ImportPath
|
||||
runApp(c)
|
||||
} else {
|
||||
fmt.Fprintln(os.Stdout, "\nYou can run it with:\n revel run -a ", c.ImportPath)
|
||||
}
|
||||
}
|
||||
|
||||
// Used to generate a new secret key
|
||||
const alphaNumeric = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
|
||||
// Generate a secret key
|
||||
func generateSecret() string {
|
||||
chars := make([]byte, 64)
|
||||
for i := 0; i < 64; i++ {
|
||||
@@ -93,129 +146,145 @@ func generateSecret() string {
|
||||
return string(chars)
|
||||
}
|
||||
|
||||
// lookup and set Go related variables
|
||||
func initGoPaths() {
|
||||
// lookup go path
|
||||
gopath = build.Default.GOPATH
|
||||
if gopath == "" {
|
||||
errorf("Abort: GOPATH environment variable is not set. " +
|
||||
"Please refer to http://golang.org/doc/code.html to configure your Go environment.")
|
||||
}
|
||||
|
||||
// check for go executable
|
||||
var err error
|
||||
gocmd, err = exec.LookPath("go")
|
||||
if err != nil {
|
||||
errorf("Go executable not found in PATH.")
|
||||
}
|
||||
|
||||
// revel/revel#1004 choose go path relative to current working directory
|
||||
workingDir, _ := os.Getwd()
|
||||
goPathList := filepath.SplitList(gopath)
|
||||
for _, path := range goPathList {
|
||||
if strings.HasPrefix(strings.ToLower(workingDir), strings.ToLower(path)) {
|
||||
srcRoot = path
|
||||
break
|
||||
}
|
||||
|
||||
path, _ = filepath.EvalSymlinks(path)
|
||||
if len(path) > 0 && strings.HasPrefix(strings.ToLower(workingDir), strings.ToLower(path)) {
|
||||
srcRoot = path
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(srcRoot) == 0 {
|
||||
revel.RevelLog.Fatal("Abort: could not create a Revel application outside of GOPATH.")
|
||||
}
|
||||
|
||||
// set go src path
|
||||
srcRoot = filepath.Join(srcRoot, "src")
|
||||
}
|
||||
|
||||
func setApplicationPath(args []string) {
|
||||
var err error
|
||||
importPath = args[0]
|
||||
// Sets the applicaiton path
|
||||
func setApplicationPath(c *model.CommandConfig) {
|
||||
|
||||
// revel/revel#1014 validate relative path, we cannot use built-in functions
|
||||
// since Go import path is valid relative path too.
|
||||
// so check basic part of the path, which is "."
|
||||
if filepath.IsAbs(importPath) || strings.HasPrefix(importPath, ".") {
|
||||
errorf("Abort: '%s' looks like a directory. Please provide a Go import path instead.",
|
||||
importPath)
|
||||
if filepath.IsAbs(c.ImportPath) || strings.HasPrefix(c.ImportPath, ".") {
|
||||
utils.Logger.Fatalf("Abort: '%s' looks like a directory. Please provide a Go import path instead.",
|
||||
c.ImportPath)
|
||||
}
|
||||
|
||||
appPath = filepath.Join(srcRoot, filepath.FromSlash(importPath))
|
||||
|
||||
_, err = build.Import(importPath, "", build.FindOnly)
|
||||
if err == nil && !empty(appPath) {
|
||||
errorf("Abort: Import path %s already exists.\n", importPath)
|
||||
// If we are running a vendored version of Revel we do not need to check for it.
|
||||
if !c.New.Vendored {
|
||||
var err error
|
||||
_, err = build.Import(model.RevelImportPath, "", build.FindOnly)
|
||||
if err != nil {
|
||||
// Go get the revel project
|
||||
|
||||
utils.Logger.Fatal("Abort: Could not find Revel source code:", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
revelPkg, err = build.Import(revel.RevelImportPath, "", build.FindOnly)
|
||||
if err != nil {
|
||||
errorf("Abort: Could not find Revel source code: %s\n", err)
|
||||
}
|
||||
c.AppName = filepath.Base(c.AppPath)
|
||||
c.BasePath = filepath.ToSlash(filepath.Dir(c.ImportPath))
|
||||
|
||||
appName = filepath.Base(appPath)
|
||||
basePath = filepath.ToSlash(filepath.Dir(importPath))
|
||||
|
||||
if basePath == "." {
|
||||
if c.BasePath == "." {
|
||||
// we need to remove the a single '.' when
|
||||
// the app is in the $GOROOT/src directory
|
||||
basePath = ""
|
||||
c.BasePath = ""
|
||||
} else {
|
||||
// we need to append a '/' when the app is
|
||||
// is a subdirectory such as $GOROOT/src/path/to/revelapp
|
||||
basePath += "/"
|
||||
c.BasePath += "/"
|
||||
}
|
||||
}
|
||||
|
||||
func setSkeletonPath(args []string) {
|
||||
// Set the skeleton path
|
||||
func setSkeletonPath(c *model.CommandConfig) {
|
||||
var err error
|
||||
if len(args) == 2 { // user specified
|
||||
skeletonName := args[1]
|
||||
_, err = build.Import(skeletonName, "", build.FindOnly)
|
||||
if len(c.SkeletonPath) > 0 { // user specified
|
||||
|
||||
_, err = build.Import(c.SkeletonPath, "", build.FindOnly)
|
||||
if err != nil {
|
||||
// Execute "go get <pkg>"
|
||||
getCmd := exec.Command(gocmd, "get", "-d", skeletonName)
|
||||
getCmd := exec.Command(c.GoCmd, "get", "-d", c.SkeletonPath)
|
||||
fmt.Println("Exec:", getCmd.Args)
|
||||
getOutput, err := getCmd.CombinedOutput()
|
||||
|
||||
// check getOutput for no buildible string
|
||||
bpos := bytes.Index(getOutput, []byte("no buildable Go source files in"))
|
||||
if err != nil && bpos == -1 {
|
||||
errorf("Abort: Could not find or 'go get' Skeleton source code: %s\n%s\n", getOutput, skeletonName)
|
||||
utils.Logger.Fatalf("Abort: Could not find or 'go get' Skeleton source code: %s\n%s\n", getOutput, c.SkeletonPath)
|
||||
}
|
||||
}
|
||||
// use the
|
||||
skeletonPath = filepath.Join(srcRoot, skeletonName)
|
||||
c.SkeletonPath = filepath.Join(c.SrcRoot, c.SkeletonPath)
|
||||
|
||||
} else {
|
||||
// use the revel default
|
||||
revelCmdPkg, err = build.Import(RevelCmdImportPath, "", build.FindOnly)
|
||||
revelCmdPkg, err := build.Import(RevelCmdImportPath, "", build.FindOnly)
|
||||
if err != nil {
|
||||
errorf("Abort: Could not find Revel Cmd source code: %s\n", err)
|
||||
utils.Logger.Fatalf("Abort: Could not find Revel Cmd source code: %s\n", err)
|
||||
}
|
||||
|
||||
skeletonPath = filepath.Join(revelCmdPkg.Dir, "revel", "skeleton")
|
||||
c.SkeletonPath = filepath.Join(revelCmdPkg.Dir, "revel", "skeleton")
|
||||
}
|
||||
}
|
||||
|
||||
func copyNewAppFiles() {
|
||||
func copyNewAppFiles(c *model.CommandConfig) {
|
||||
var err error
|
||||
err = os.MkdirAll(appPath, 0777)
|
||||
panicOnError(err, "Failed to create directory "+appPath)
|
||||
err = os.MkdirAll(c.AppPath, 0777)
|
||||
utils.PanicOnError(err, "Failed to create directory "+c.AppPath)
|
||||
|
||||
_ = mustCopyDir(appPath, skeletonPath, map[string]interface{}{
|
||||
_ = utils.MustCopyDir(c.AppPath, c.SkeletonPath, map[string]interface{}{
|
||||
// app.conf
|
||||
"AppName": appName,
|
||||
"BasePath": basePath,
|
||||
"AppName": c.AppName,
|
||||
"BasePath": c.BasePath,
|
||||
"Secret": generateSecret(),
|
||||
})
|
||||
|
||||
// Dotfiles are skipped by mustCopyDir, so we have to explicitly copy the .gitignore.
|
||||
gitignore := ".gitignore"
|
||||
mustCopyFile(filepath.Join(appPath, gitignore), filepath.Join(skeletonPath, gitignore))
|
||||
utils.MustCopyFile(filepath.Join(c.AppPath, gitignore), filepath.Join(c.SkeletonPath, gitignore))
|
||||
|
||||
}
|
||||
|
||||
const (
|
||||
VENDOR_GOPKG = `#
|
||||
# Revel Gopkg.toml
|
||||
#
|
||||
# If you want to use a specific version of Revel change the branches below
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
required = ["github.com/revel/cmd/revel"]
|
||||
|
||||
[[override]]
|
||||
branch = "master"
|
||||
name = "github.com/revel/modules"
|
||||
|
||||
[[override]]
|
||||
branch = "master"
|
||||
name = "github.com/revel/revel"
|
||||
|
||||
[[override]]
|
||||
branch = "master"
|
||||
name = "github.com/revel/cmd"
|
||||
|
||||
[[override]]
|
||||
branch = "master"
|
||||
name = "github.com/revel/log15"
|
||||
|
||||
[[override]]
|
||||
branch = "master"
|
||||
name = "github.com/revel/cron"
|
||||
|
||||
[[override]]
|
||||
branch = "master"
|
||||
name = "github.com/xeonx/timeago"
|
||||
|
||||
`
|
||||
NEW_MAIN_FILE = `package main
|
||||
|
||||
`
|
||||
)
|
||||
Reference in New Issue
Block a user