222 lines
5.6 KiB
Go
222 lines
5.6 KiB
Go
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
|
|
// Revel Framework source code and usage is governed by a MIT style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"go/build"
|
|
"math/rand"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/revel/revel"
|
|
)
|
|
|
|
var cmdNew = &Command{
|
|
UsageLine: "new [path] [skeleton]",
|
|
Short: "create a skeleton Revel application",
|
|
Long: `
|
|
New creates a few files to get a new Revel application running quickly.
|
|
|
|
It puts all of the files in the given import path, taking the final element in
|
|
the path to be the app name.
|
|
|
|
Skeleton is an optional argument, provided as an import path
|
|
|
|
For example:
|
|
|
|
revel new import/path/helloworld
|
|
|
|
revel new import/path/helloworld import/path/skeleton
|
|
`,
|
|
}
|
|
|
|
func init() {
|
|
cmdNew.Run = newApp
|
|
}
|
|
|
|
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
|
|
if len(args) == 0 {
|
|
errorf("No import path given.\nRun 'revel help new' for usage.\n")
|
|
}
|
|
if len(args) > 2 {
|
|
errorf("Too many arguments provided.\nRun 'revel help new' for usage.\n")
|
|
}
|
|
|
|
// checking and setting go paths
|
|
initGoPaths()
|
|
|
|
// checking and setting application
|
|
setApplicationPath(args)
|
|
|
|
// checking and setting skeleton
|
|
setSkeletonPath(args)
|
|
|
|
// copy files to new app directory
|
|
copyNewAppFiles()
|
|
|
|
// 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)
|
|
}
|
|
|
|
const alphaNumeric = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
|
|
|
func generateSecret() string {
|
|
chars := make([]byte, 64)
|
|
for i := 0; i < 64; i++ {
|
|
chars[i] = alphaNumeric[rand.Intn(len(alphaNumeric))]
|
|
}
|
|
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]
|
|
|
|
// 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)
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
revelPkg, err = build.Import(revel.RevelImportPath, "", build.FindOnly)
|
|
if err != nil {
|
|
errorf("Abort: Could not find Revel source code: %s\n", err)
|
|
}
|
|
|
|
appName = filepath.Base(appPath)
|
|
basePath = filepath.ToSlash(filepath.Dir(importPath))
|
|
|
|
if basePath == "." {
|
|
// we need to remove the a single '.' when
|
|
// the app is in the $GOROOT/src directory
|
|
basePath = ""
|
|
} else {
|
|
// we need to append a '/' when the app is
|
|
// is a subdirectory such as $GOROOT/src/path/to/revelapp
|
|
basePath += "/"
|
|
}
|
|
}
|
|
|
|
func setSkeletonPath(args []string) {
|
|
var err error
|
|
if len(args) == 2 { // user specified
|
|
skeletonName := args[1]
|
|
_, err = build.Import(skeletonName, "", build.FindOnly)
|
|
if err != nil {
|
|
// Execute "go get <pkg>"
|
|
getCmd := exec.Command(gocmd, "get", "-d", skeletonName)
|
|
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)
|
|
}
|
|
}
|
|
// use the
|
|
skeletonPath = filepath.Join(srcRoot, skeletonName)
|
|
|
|
} else {
|
|
// use the revel default
|
|
revelCmdPkg, err = build.Import(RevelCmdImportPath, "", build.FindOnly)
|
|
if err != nil {
|
|
errorf("Abort: Could not find Revel Cmd source code: %s\n", err)
|
|
}
|
|
|
|
skeletonPath = filepath.Join(revelCmdPkg.Dir, "revel", "skeleton")
|
|
}
|
|
}
|
|
|
|
func copyNewAppFiles() {
|
|
var err error
|
|
err = os.MkdirAll(appPath, 0777)
|
|
panicOnError(err, "Failed to create directory "+appPath)
|
|
|
|
_ = mustCopyDir(appPath, skeletonPath, map[string]interface{}{
|
|
// app.conf
|
|
"AppName": appName,
|
|
"BasePath": 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))
|
|
|
|
}
|