Initial rewrite of revel/cmd to provide vendor support and enhanced CLI options

This commit is contained in:
NotZippy
2018-09-13 13:21:10 -07:00
parent d2ac018544
commit d0baaeb9e9
40 changed files with 4435 additions and 1125 deletions

View File

@@ -5,70 +5,86 @@
package main
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/revel/cmd/harness"
"github.com/revel/revel"
"github.com/revel/cmd/model"
"go/build"
"github.com/revel/cmd/utils"
"fmt"
)
var cmdBuild = &Command{
UsageLine: "build [import path] [target path] [run mode]",
UsageLine: "build -i [import path] -t [target path] -r [run mode]",
Short: "build a Revel application (e.g. for deployment)",
Long: `
Build the Revel web application named by the given import path.
This allows it to be deployed and run on a machine that lacks a Go installation.
The run mode is used to select which set of app.conf configuration should
apply and may be used to determine logic in the application itself.
Run mode defaults to "dev".
WARNING: The target path will be completely deleted, if it already exists!
For example:
revel build github.com/revel/examples/chat /tmp/chat
revel build -a github.com/revel/examples/chat -t /tmp/chat
`,
}
func init() {
cmdBuild.Run = buildApp
cmdBuild.RunWith = buildApp
cmdBuild.UpdateConfig = updateBuildConfig
}
func buildApp(args []string) {
// The update config updates the configuration command so that it can run
func updateBuildConfig(c *model.CommandConfig, args []string) (bool) {
c.Index = BUILD
if len(args) < 2 {
fmt.Fprintf(os.Stderr, "%s\n%s", cmdBuild.UsageLine, cmdBuild.Long)
return
return false
}
c.Build.ImportPath = args[0]
c.Build.TargetPath = args[1]
if len(args)>2 {
c.Build.Mode = args[1]
}
return true
}
// The main entry point to build application from command line
func buildApp(c *model.CommandConfig) {
c.ImportPath = c.Build.ImportPath
appImportPath, destPath, mode := c.Build.ImportPath , c.Build.TargetPath, DefaultRunMode
if len(c.Build.Mode) > 0 {
mode = c.Build.Mode
}
appImportPath, destPath, mode := args[0], args[1], DefaultRunMode
if len(args) >= 3 {
mode = args[2]
}
// Convert target to absolute path
destPath, _ = filepath.Abs(destPath)
if !revel.Initialized {
revel.Init(mode, appImportPath, "")
}
revel_paths := model.NewRevelPaths(mode, appImportPath, "", model.DoNothingRevelCallback)
// First, verify that it is either already empty or looks like a previous
// build (to avoid clobbering anything)
if exists(destPath) && !empty(destPath) && !exists(filepath.Join(destPath, "run.sh")) {
errorf("Abort: %s exists and does not look like a build directory.", destPath)
if utils.Exists(destPath) && !utils.Empty(destPath) && !utils.Exists(filepath.Join(destPath, "run.sh")) {
utils.Logger.Errorf("Abort: %s exists and does not look like a build directory.", destPath)
return
}
if err := os.RemoveAll(destPath); err != nil && !os.IsNotExist(err) {
revel.RevelLog.Fatal("Remove all error","error", err)
utils.Logger.Error("Remove all error","error", err)
return
}
if err := os.MkdirAll(destPath, 0777); err != nil {
revel.RevelLog.Fatal("makedir error","error",err)
utils.Logger.Error("makedir error","error",err)
return
}
app, reverr := harness.Build()
panicOnError(reverr, "Failed to build")
app, reverr := harness.Build(c,revel_paths)
if reverr!=nil {
utils.Logger.Error("Failed to build application","error",reverr)
return
}
// Included are:
// - run scripts
@@ -79,15 +95,15 @@ func buildApp(args []string) {
// Revel and the app are in a directory structure mirroring import path
srcPath := filepath.Join(destPath, "src")
destBinaryPath := filepath.Join(destPath, filepath.Base(app.BinaryPath))
tmpRevelPath := filepath.Join(srcPath, filepath.FromSlash(revel.RevelImportPath))
mustCopyFile(destBinaryPath, app.BinaryPath)
mustChmod(destBinaryPath, 0755)
_ = mustCopyDir(filepath.Join(tmpRevelPath, "conf"), filepath.Join(revel.RevelPath, "conf"), nil)
_ = mustCopyDir(filepath.Join(tmpRevelPath, "templates"), filepath.Join(revel.RevelPath, "templates"), nil)
_ = mustCopyDir(filepath.Join(srcPath, filepath.FromSlash(appImportPath)), revel.BasePath, nil)
tmpRevelPath := filepath.Join(srcPath, filepath.FromSlash(model.RevelImportPath))
utils.MustCopyFile(destBinaryPath, app.BinaryPath)
utils.MustChmod(destBinaryPath, 0755)
_ = utils.MustCopyDir(filepath.Join(tmpRevelPath, "conf"), filepath.Join(revel_paths.RevelPath, "conf"), nil)
_ = utils.MustCopyDir(filepath.Join(tmpRevelPath, "templates"), filepath.Join(revel_paths.RevelPath, "templates"), nil)
_ = utils.MustCopyDir(filepath.Join(srcPath, filepath.FromSlash(appImportPath)), revel_paths.BasePath, nil)
// Find all the modules used and copy them over.
config := revel.Config.Raw()
config := revel_paths.Config.Raw()
modulePaths := make(map[string]string) // import path => filesystem path
for _, section := range config.Sections() {
options, _ := config.SectionOptions(section)
@@ -99,32 +115,46 @@ func buildApp(args []string) {
if moduleImportPath == "" {
continue
}
modulePath, err := revel.ResolveImportPath(moduleImportPath)
modPkg, err := build.Import(c.ImportPath, revel_paths.RevelPath, build.FindOnly)
if err != nil {
revel.RevelLog.Fatalf("Failed to load module %s: %s", key[len("module."):], err)
utils.Logger.Fatalf("Failed to load module %s (%s): %s", key[len("module."):], c.ImportPath, err)
}
modulePaths[moduleImportPath] = modulePath
modulePaths[moduleImportPath] = modPkg.Dir
}
}
for importPath, fsPath := range modulePaths {
_ = mustCopyDir(filepath.Join(srcPath, importPath), fsPath, nil)
_ = utils.MustCopyDir(filepath.Join(srcPath, importPath), fsPath, nil)
}
tmplData, runShPath := map[string]interface{}{
tmplData := map[string]interface{}{
"BinName": filepath.Base(app.BinaryPath),
"ImportPath": appImportPath,
"Mode": mode,
}, filepath.Join(destPath, "run.sh")
}
mustRenderTemplate(
runShPath,
filepath.Join(revel.RevelPath, "..", "cmd", "revel", "package_run.sh.template"),
tmplData)
mustChmod(runShPath, 0755)
mustRenderTemplate(
utils.MustGenerateTemplate(
filepath.Join(destPath, "run.sh"),
PACKAGE_RUN_SH,
tmplData,
)
utils.MustChmod(filepath.Join(destPath, "run.sh"), 0755)
utils.MustGenerateTemplate(
filepath.Join(destPath, "run.bat"),
filepath.Join(revel.RevelPath, "..", "cmd", "revel", "package_run.bat.template"),
tmplData)
PACKAGE_RUN_BAT,
tmplData,
)
fmt.Println("Your application has been built in:", destPath)
}
const PACKAGE_RUN_SH = `#!/bin/sh
SCRIPTPATH=$(cd "$(dirname "$0")"; pwd)
"$SCRIPTPATH/{{.BinName}}" -importPath {{.ImportPath}} -srcPath "$SCRIPTPATH/src" -runMode {{.Mode}}
`
const PACKAGE_RUN_BAT = `@echo off
{{.BinName}} -importPath {{.ImportPath}} -srcPath "%CD%\src" -runMode {{.Mode}}
`

View File

@@ -6,39 +6,52 @@ package main
import (
"fmt"
"github.com/revel/cmd/model"
"github.com/revel/cmd/utils"
"go/build"
"os"
"path/filepath"
)
var cmdClean = &Command{
UsageLine: "clean [import path]",
UsageLine: "clean -i [import path]",
Short: "clean a Revel application's temp files",
Long: `
Clean the Revel web application named by the given import path.
For example:
revel clean github.com/revel/examples/chat
revel clean -a github.com/revel/examples/chat
It removes the app/tmp and app/routes directory.
`,
}
func init() {
cmdClean.Run = cleanApp
cmdClean.UpdateConfig = updateCleanConfig
cmdClean.RunWith = cleanApp
}
func cleanApp(args []string) {
// Update the clean command configuration, using old method
func updateCleanConfig(c *model.CommandConfig, args []string) bool {
c.Index = CLEAN
if len(args) == 0 {
fmt.Fprintf(os.Stderr, cmdClean.Long)
return
return false
}
c.Clean.ImportPath = args[0]
return true
}
appPkg, err := build.Import(args[0], "", build.FindOnly)
// Clean the source directory of generated files
func cleanApp(c *model.CommandConfig) {
c.ImportPath = c.Clean.ImportPath
appPkg, err := build.Import(c.ImportPath, "", build.FindOnly)
if err != nil {
fmt.Fprintln(os.Stderr, "Abort: Failed to find import path:", err)
return
utils.Logger.Fatal("Abort: Failed to find import path:", "error", err)
}
purgeDirs := []string{
@@ -50,7 +63,7 @@ func cleanApp(args []string) {
fmt.Println("Removing:", dir)
err = os.RemoveAll(dir)
if err != nil {
fmt.Fprintln(os.Stderr, "Abort:", err)
utils.Logger.Error("Failed to clean dir", "error", err)
return
}
}

View File

@@ -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
`
)

View File

@@ -10,11 +10,12 @@ import (
"os"
"path/filepath"
"github.com/revel/revel"
"github.com/revel/cmd/model"
"github.com/revel/cmd/utils"
)
var cmdPackage = &Command{
UsageLine: "package [import path] [run mode]",
UsageLine: "package -i [import path] -r [run mode]",
Short: "package a Revel application (e.g. for deployment)",
Long: `
Package the Revel web application named by the given import path.
@@ -27,43 +28,60 @@ Run mode defaults to "dev".
For example:
revel package github.com/revel/examples/chat
revel package -i github.com/revel/examples/chat
`,
}
func init() {
cmdPackage.Run = packageApp
cmdPackage.RunWith = packageApp
cmdPackage.UpdateConfig = updatePackageConfig
}
func packageApp(args []string) {
// Called when unable to parse the command line automatically and assumes an old launch
func updatePackageConfig(c *model.CommandConfig, args []string) bool {
c.Index = PACAKAGE
if len(args) == 0 {
fmt.Fprint(os.Stderr, cmdPackage.Long)
return
fmt.Fprintf(os.Stderr, cmdPackage.Long)
return false
}
c.New.ImportPath = args[0]
if len(args)>1 {
c.New.Skeleton = args[1]
}
return true
}
func packageApp(c *model.CommandConfig) {
// Determine the run mode.
mode := DefaultRunMode
if len(args) >= 2 {
mode = args[1]
if len(c.Package.Mode) >= 0 {
mode = c.Package.Mode
}
appImportPath := args[0]
revel.Init(mode, appImportPath, "")
appImportPath := c.Package.ImportPath
revel_paths := model.NewRevelPaths(mode, appImportPath, "", model.DoNothingRevelCallback)
// Remove the archive if it already exists.
destFile := filepath.Base(revel.BasePath) + ".tar.gz"
destFile := filepath.Base(revel_paths.BasePath) + ".tar.gz"
if err := os.Remove(destFile); err != nil && !os.IsNotExist(err) {
revel.RevelLog.Fatal("Unable to remove target file","error",err,"file",destFile)
utils.Logger.Error("Unable to remove target file","error",err,"file",destFile)
os.Exit(1)
}
// Collect stuff in a temp directory.
tmpDir, err := ioutil.TempDir("", filepath.Base(revel.BasePath))
panicOnError(err, "Failed to get temp dir")
tmpDir, err := ioutil.TempDir("", filepath.Base(revel_paths.BasePath))
utils.PanicOnError(err, "Failed to get temp dir")
buildApp([]string{args[0], tmpDir, mode})
// Build expects the command the build to contain the proper data
c.Build.ImportPath = appImportPath
c.Build.Mode = mode
c.Build.TargetPath = tmpDir
buildApp(c)
// Create the zip file.
archiveName := mustTarGzDir(destFile, tmpDir)
archiveName := utils.MustTarGzDir(destFile, tmpDir)
fmt.Println("Your archive is ready:", archiveName)
}

View File

@@ -1,2 +0,0 @@
@echo off
{{.BinName}} -importPath {{.ImportPath}} -srcPath "%CD%\src" -runMode {{.Mode}}

View File

@@ -1,3 +0,0 @@
#!/bin/sh
SCRIPTPATH=$(cd "$(dirname "$0")"; pwd)
"$SCRIPTPATH/{{.BinName}}" -importPath {{.ImportPath}} -srcPath "$SCRIPTPATH/src" -runMode {{.Mode}}

View File

@@ -1,144 +0,0 @@
// 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.
// The command line tool for running Revel apps.
package main
import (
"flag"
"fmt"
"io"
"math/rand"
"os"
"runtime"
"strings"
"text/template"
"time"
"github.com/agtorre/gocolorize"
)
const (
// RevelCmdImportPath Revel framework cmd tool import path
RevelCmdImportPath = "github.com/revel/cmd"
// DefaultRunMode for revel's application
DefaultRunMode = "dev"
)
// Command structure cribbed from the genius organization of the "go" command.
type Command struct {
Run func(args []string)
UsageLine, Short, Long string
}
// Name returns command name from usage line
func (cmd *Command) Name() string {
name := cmd.UsageLine
i := strings.Index(name, " ")
if i >= 0 {
name = name[:i]
}
return name
}
var commands = []*Command{
cmdNew,
cmdRun,
cmdBuild,
cmdPackage,
cmdClean,
cmdTest,
cmdVersion,
}
func main() {
if runtime.GOOS == "windows" {
gocolorize.SetPlain(true)
}
fmt.Fprintf(os.Stdout, gocolorize.NewColor("blue").Paint(header))
flag.Usage = func() { usage(1) }
flag.Parse()
args := flag.Args()
if len(args) < 1 || args[0] == "help" {
if len(args) == 1 {
usage(0)
}
if len(args) > 1 {
for _, cmd := range commands {
if cmd.Name() == args[1] {
tmpl(os.Stdout, helpTemplate, cmd)
return
}
}
}
usage(2)
}
// Commands use panic to abort execution when something goes wrong.
// Panics are logged at the point of error. Ignore those.
defer func() {
if err := recover(); err != nil {
if _, ok := err.(LoggedError); !ok {
// This panic was not expected / logged.
panic(err)
}
os.Exit(1)
}
}()
for _, cmd := range commands {
if cmd.Name() == args[0] {
cmd.Run(args[1:])
return
}
}
errorf("unknown command %q\nRun 'revel help' for usage.\n", args[0])
}
func errorf(format string, args ...interface{}) {
// Ensure the user's command prompt starts on the next line.
if !strings.HasSuffix(format, "\n") {
format += "\n"
}
fmt.Fprintf(os.Stderr, format, args...)
panic(LoggedError{}) // Panic instead of os.Exit so that deferred will run.
}
const header = `~
~ revel! http://revel.github.io
~
`
const usageTemplate = `usage: revel command [arguments]
The commands are:
{{range .}}
{{.Name | printf "%-11s"}} {{.Short}}{{end}}
Use "revel help [command]" for more information.
`
var helpTemplate = `usage: revel {{.UsageLine}}
{{.Long}}
`
func usage(exitCode int) {
tmpl(os.Stderr, usageTemplate, commands)
os.Exit(exitCode)
}
func tmpl(w io.Writer, text string, data interface{}) {
t := template.New("top")
template.Must(t.Parse(text))
if err := t.Execute(w, data); err != nil {
panic(err)
}
}
func init() {
rand.Seed(time.Now().UnixNano())
}

300
revel/revel.go Normal file
View File

@@ -0,0 +1,300 @@
// 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.
// The command line tool for running Revel apps.
package main
import (
"flag"
"fmt"
"io"
"math/rand"
"os"
"runtime"
"strings"
"text/template"
"time"
"github.com/jessevdk/go-flags"
"github.com/agtorre/gocolorize"
"github.com/revel/cmd/model"
"github.com/revel/cmd/utils"
"github.com/revel/cmd/logger"
"os/exec"
"path/filepath"
"go/build"
)
const (
// RevelCmdImportPath Revel framework cmd tool import path
RevelCmdImportPath = "github.com/revel/cmd"
// DefaultRunMode for revel's application
DefaultRunMode = "dev"
)
// Command structure cribbed from the genius organization of the "go" command.
type Command struct {
UpdateConfig func(c *model.CommandConfig, args []string) bool
RunWith func(c *model.CommandConfig)
UsageLine, Short, Long string
}
// Name returns command name from usage line
func (cmd *Command) Name() string {
name := cmd.UsageLine
i := strings.Index(name, " ")
if i >= 0 {
name = name[:i]
}
return name
}
// The constants
const (
NEW model.COMMAND = iota +1
RUN
BUILD
PACAKAGE
CLEAN
TEST
VERSION
)
// The commands
var commands = []*Command{
nil, // Safety net, prevent missing index from running
cmdNew,
cmdRun,
cmdBuild,
cmdPackage,
cmdClean,
cmdTest,
cmdVersion,
}
func main() {
if runtime.GOOS == "windows" {
gocolorize.SetPlain(true)
}
c := &model.CommandConfig{}
wd,_ := os.Getwd()
utils.InitLogger(wd,logger.LvlError)
parser := flags.NewParser(c, flags.HelpFlag | flags.PassDoubleDash)
if ini:=flag.String("ini","none","");*ini!="none" {
if err:=flags.NewIniParser(parser).ParseFile(*ini);err!=nil {
utils.Logger.Error("Unable to load ini", "error",err)
}
} else {
if _, err := parser.Parse(); err != nil {
utils.Logger.Info("Command line options failed", "error", err.Error())
// Decode nature of error
if perr,ok:=err.(*flags.Error); ok {
if perr.Type == flags.ErrRequired {
// Try the old way
if !main_parse_old(c) {
println("Command line error:", err.Error())
parser.WriteHelp(os.Stdout)
os.Exit(1)
}
} else {
println("Command line error:", err.Error())
parser.WriteHelp(os.Stdout)
os.Exit(1)
}
} else {
println("Command line error:", err.Error())
parser.WriteHelp(os.Stdout)
os.Exit(1)
}
} else {
switch parser.Active.Name {
case "new":
c.Index = NEW
case "run":
c.Index = RUN
case "build":
c.Index = BUILD
case "package":
c.Index = PACAKAGE
case "clean":
c.Index = CLEAN
case "test":
c.Index = TEST
case "version":
c.Index = VERSION
}
}
}
// Switch based on the verbose flag
if c.Verbose {
utils.InitLogger(wd, logger.LvlDebug)
} else {
utils.InitLogger(wd, logger.LvlWarn)
}
println("Revel executing:", commands[c.Index].Short)
// checking and setting go paths
initGoPaths(c)
commands[c.Index].RunWith(c)
}
// Try to populate the CommandConfig using the old techniques
func main_parse_old(c *model.CommandConfig) bool {
// Take the old command format and try to parse them
flag.Usage = func() { usage(1) }
flag.Parse()
args := flag.Args()
if len(args) < 1 || args[0] == "help" {
if len(args) == 1 {
usage(0)
}
if len(args) > 1 {
for _, cmd := range commands {
if cmd!=nil && cmd.Name() == args[1] {
tmpl(os.Stdout, helpTemplate, cmd)
return false
}
}
}
usage(2)
}
for _, cmd := range commands {
if cmd!=nil && cmd.Name() == args[0] {
println("Running", cmd.Name())
return cmd.UpdateConfig(c, args[1:])
}
}
return false
}
func main_old() {
if runtime.GOOS == "windows" {
gocolorize.SetPlain(true)
}
fmt.Fprintf(os.Stdout, gocolorize.NewColor("blue").Paint(header))
flag.Usage = func() { usage(1) }
flag.Parse()
args := flag.Args()
if len(args) < 1 || args[0] == "help" {
if len(args) == 1 {
usage(0)
}
if len(args) > 1 {
for _, cmd := range commands {
if cmd.Name() == args[1] {
tmpl(os.Stdout, helpTemplate, cmd)
return
}
}
}
usage(2)
}
// Commands use panic to abort execution when something goes wrong.
// Panics are logged at the point of error. Ignore those.
defer func() {
if err := recover(); err != nil {
if _, ok := err.(utils.LoggedError); !ok {
// This panic was not expected / logged.
panic(err)
}
os.Exit(1)
}
}()
//for _, cmd := range commands {
// if cmd.Name() == args[0] {
// cmd.UpdateConfig(args[1:])
// return
// }
//}
utils.Logger.Fatalf("unknown command %q\nRun 'revel help' for usage.\n", args[0])
}
const header = `~
~ revel! http://revel.github.io
~
`
const usageTemplate = `usage: revel command [arguments]
The commands are:
{{range .}}
{{.Name | printf "%-11s"}} {{.Short}}{{end}}
Use "revel help [command]" for more information.
`
var helpTemplate = `usage: revel {{.UsageLine}}
{{.Long}}
`
func usage(exitCode int) {
tmpl(os.Stderr, usageTemplate, commands)
os.Exit(exitCode)
}
func tmpl(w io.Writer, text string, data interface{}) {
t := template.New("top")
template.Must(t.Parse(text))
if err := t.Execute(w, data); err != nil {
panic(err)
}
}
func init() {
rand.Seed(time.Now().UnixNano())
}
// lookup and set Go related variables
func initGoPaths(c *model.CommandConfig) {
// lookup go path
c.GoPath = build.Default.GOPATH
if c.GoPath == "" {
utils.Logger.Fatal("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
c.GoCmd, err = exec.LookPath("go")
if err != nil {
utils.Logger.Fatal("Go executable not found in PATH.")
}
// revel/revel#1004 choose go path relative to current working directory
workingDir, _ := os.Getwd()
goPathList := filepath.SplitList(c.GoPath)
for _, path := range goPathList {
if strings.HasPrefix(strings.ToLower(workingDir), strings.ToLower(path)) {
c.SrcRoot = path
break
}
path, _ = filepath.EvalSymlinks(path)
if len(path) > 0 && strings.HasPrefix(strings.ToLower(workingDir), strings.ToLower(path)) {
c.SrcRoot = path
break
}
}
if len(c.SrcRoot) == 0 {
utils.Logger.Fatal("Abort: could not create a Revel application outside of GOPATH.")
}
// set go src path
c.SrcRoot = filepath.Join(c.SrcRoot, "src")
}

View File

@@ -5,11 +5,13 @@
package main
import (
"go/build"
"strconv"
"fmt"
"github.com/revel/cmd/harness"
"github.com/revel/revel"
"github.com/revel/cmd/model"
"github.com/revel/cmd/utils"
"go/build"
)
var cmdRun = &Command{
@@ -40,49 +42,41 @@ type RunArgs struct {
}
func init() {
cmdRun.Run = runApp
cmdRun.RunWith = runApp
cmdRun.UpdateConfig = updateRunConfig
}
func parseRunArgs(args []string) *RunArgs {
inputArgs := RunArgs{
ImportPath: importPathFromCurrentDir(),
Mode: DefaultRunMode,
Port: revel.HTTPPort,
}
func updateRunConfig(c *model.CommandConfig, args []string) bool {
switch len(args) {
case 3:
// Possible combinations
// revel run [import-path] [run-mode] [port]
port, err := strconv.Atoi(args[2])
if err != nil {
errorf("Failed to parse port as integer: %s", args[2])
}
inputArgs.ImportPath = args[0]
inputArgs.Mode = args[1]
inputArgs.Port = port
c.Run.ImportPath = args[0]
c.Run.Mode = args[1]
c.Run.Port = args[2]
case 2:
// Possible combinations
// 1. revel run [import-path] [run-mode]
// 2. revel run [import-path] [port]
// 3. revel run [run-mode] [port]
// Check to see if the import path evaluates out to something that may be on a gopath
if _, err := build.Import(args[0], "", build.FindOnly); err == nil {
// 1st arg is the import path
inputArgs.ImportPath = args[0]
if port, err := strconv.Atoi(args[1]); err == nil {
c.Run.ImportPath = args[0]
if _, err := strconv.Atoi(args[1]); err == nil {
// 2nd arg is the port number
inputArgs.Port = port
c.Run.Port = args[1]
} else {
// 2nd arg is the run mode
inputArgs.Mode = args[1]
c.Run.Mode = args[1]
}
} else {
// 1st arg is the run mode
port, err := strconv.Atoi(args[1])
if err != nil {
errorf("Failed to parse port as integer: %s", args[1])
}
inputArgs.Mode = args[0]
inputArgs.Port = port
c.Run.Mode = args[0]
c.Run.Port = args[1]
}
case 1:
// Possible combinations
@@ -91,52 +85,62 @@ func parseRunArgs(args []string) *RunArgs {
// 3. revel run [run-mode]
_, err := build.Import(args[0], "", build.FindOnly)
if err != nil {
revel.RevelLog.Warn("Unable to run using an import path, assuming import path is working directory %s %s", "Argument", args[0], "error", err.Error())
utils.Logger.Warn("Unable to run using an import path, assuming import path is working directory %s %s", "Argument", args[0], "error", err.Error())
}
println("Trying to build with", args[0], err)
utils.Logger.Info("Trying to build with", args[0], err)
if err == nil {
// 1st arg is the import path
inputArgs.ImportPath = args[0]
} else if port, err := strconv.Atoi(args[0]); err == nil {
c.Run.ImportPath = args[0]
} else if _, err := strconv.Atoi(args[0]); err == nil {
// 1st arg is the port number
inputArgs.Port = port
c.Run.Port = args[0]
} else {
// 1st arg is the run mode
inputArgs.Mode = args[0]
c.Run.Mode = args[0]
}
}
return &inputArgs
c.Index = RUN
return true
}
func runApp(args []string) {
runArgs := parseRunArgs(args)
// Find and parse app.conf
revel.Init(runArgs.Mode, runArgs.ImportPath, "")
revel.LoadMimeConfig()
// fallback to default port
if runArgs.Port == 0 {
runArgs.Port = revel.HTTPPort
func runApp(c *model.CommandConfig) {
if c.Run.Mode == "" {
c.Run.Mode = "dev"
}
revel.RevelLog.Infof("Running %s (%s) in %s mode\n", revel.AppName, revel.ImportPath, runArgs.Mode)
revel.RevelLog.Debug("Base path:", "path", revel.BasePath)
revel_path := model.NewRevelPaths(c.Run.Mode, c.Run.ImportPath, "", model.DoNothingRevelCallback)
if c.Run.Port != "" {
port, err := strconv.Atoi(c.Run.Port)
if err != nil {
utils.Logger.Fatalf("Failed to parse port as integer: %s", c.Run.Port)
}
revel_path.HTTPPort = port
}
utils.Logger.Infof("Running %s (%s) in %s mode\n", revel_path.AppName, revel_path.ImportPath, revel_path.RunMode)
utils.Logger.Debug("Base path:", "path", revel_path.BasePath)
// If the app is run in "watched" mode, use the harness to run it.
if revel.Config.BoolDefault("watch", true) && revel.Config.BoolDefault("watch.code", true) {
revel.RevelLog.Debug("Running in watched mode.")
revel.HTTPPort = runArgs.Port
harness.NewHarness().Run() // Never returns.
if revel_path.Config.BoolDefault("watch", true) && revel_path.Config.BoolDefault("watch.code", true) {
utils.Logger.Info("Running in watched mode.")
runMode := fmt.Sprintf(`{"mode":"%s", "specialUseFlag":%v}`, revel_path.RunMode, c.Verbose)
if c.HistoricMode {
runMode = revel_path.RunMode
}
// **** Never returns.
harness.NewHarness(c, revel_path, runMode, c.Run.NoProxy).Run()
}
// Else, just build and run the app.
revel.RevelLog.Debug("Running in live build mode.")
app, err := harness.Build()
utils.Logger.Debug("Running in live build mode.")
app, err := harness.Build(c, revel_path)
if err != nil {
errorf("Failed to build app: %s", err)
utils.Logger.Errorf("Failed to build app: %s", err)
}
app.Port = runArgs.Port
app.Cmd().Run()
app.Port = revel_path.HTTPPort
runMode := fmt.Sprintf(`{"mode":"%s", "specialUseFlag":%v}`, app.Paths.RunMode, c.Verbose)
if c.HistoricMode {
runMode = revel_path.RunMode
}
app.Cmd(runMode).Run()
}

View File

@@ -26,6 +26,7 @@ func init() {
HeaderFilter, // Add some security based headers
revel.InterceptorFilter, // Run interceptors around the action.
revel.CompressFilter, // Compress the result.
revel.BeforeAfterFilter, // Call the before and after filter functions
revel.ActionInvoker, // Invoke the action.
}

View File

@@ -143,7 +143,7 @@ watch = true
# Rebuild when a new request is received and changes have been detected.
# "eager"
# Rebuild as soon as changes are detected.
watch.mode = normal
watch.mode = eager
# Watch the entire `$GOPATH` for changes.
# Values:

View File

@@ -16,8 +16,9 @@ import (
"time"
"github.com/revel/cmd/harness"
"github.com/revel/modules/testrunner/app/controllers"
"github.com/revel/revel"
"github.com/revel/cmd/model"
"github.com/revel/cmd/tests"
"github.com/revel/cmd/utils"
)
var cmdTest = &Command{
@@ -47,80 +48,104 @@ or one of UserTest's methods:
}
func init() {
cmdTest.Run = testApp
cmdTest.RunWith = testApp
cmdTest.UpdateConfig = updateTestConfig
}
func testApp(args []string) {
var err error
if len(args) == 0 {
errorf("No import path given.\nRun 'revel help test' for usage.\n")
// Called to update the config command with from the older stype
func updateTestConfig(c *model.CommandConfig, args []string) bool {
c.Index = TEST
// The full test runs
// revel test <import path> (run mode) (suite(.function))
if len(args) < 1 {
return false
}
c.Test.ImportPath = args[0]
if len(args) > 1 {
c.Test.Mode = args[1]
}
if len(args) > 2 {
c.Test.Function = args[2]
}
return true
}
// Called to test the application
func testApp(c *model.CommandConfig) {
var err error
mode := DefaultRunMode
if len(args) >= 2 {
mode = args[1]
if c.Test.Mode != "" {
mode = c.Test.Mode
}
// Find and parse app.conf
revel.Init(mode, args[0], "")
revel_path := model.NewRevelPaths(mode, c.Test.ImportPath, "", model.DoNothingRevelCallback)
// Ensure that the testrunner is loaded in this mode.
checkTestRunner()
// todo checkTestRunner()
// Create a directory to hold the test result files.
resultPath := filepath.Join(revel.BasePath, "test-results")
resultPath := filepath.Join(revel_path.BasePath, "test-results")
if err = os.RemoveAll(resultPath); err != nil {
errorf("Failed to remove test result directory %s: %s", resultPath, err)
utils.Logger.Errorf("Failed to remove test result directory %s: %s", resultPath, err)
}
if err = os.Mkdir(resultPath, 0777); err != nil {
errorf("Failed to create test result directory %s: %s", resultPath, err)
utils.Logger.Errorf("Failed to create test result directory %s: %s", resultPath, err)
}
// Direct all the output into a file in the test-results directory.
file, err := os.OpenFile(filepath.Join(resultPath, "app.log"), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
errorf("Failed to create test result log file: %s", err)
utils.Logger.Errorf("Failed to create test result log file: %s", err)
}
app, reverr := harness.Build()
app, reverr := harness.Build(c, revel_path)
if reverr != nil {
errorf("Error building: %s", reverr)
utils.Logger.Errorf("Error building: %s", reverr)
}
cmd := app.Cmd()
runMode := fmt.Sprintf(`{"mode":"%s","testModeFlag":true, "specialUseFlag":%v}`, app.Paths.RunMode, c.Verbose)
if c.HistoricMode {
runMode = app.Paths.RunMode
}
cmd := app.Cmd(runMode)
cmd.Stderr = io.MultiWriter(cmd.Stderr, file)
cmd.Stdout = io.MultiWriter(cmd.Stderr, file)
// Start the app...
if err := cmd.Start(); err != nil {
errorf("%s", err)
if err := cmd.Start(c); err != nil {
utils.Logger.Errorf("%s", err)
}
defer cmd.Kill()
revel.INFO.Printf("Testing %s (%s) in %s mode\n", revel.AppName, revel.ImportPath, mode)
var httpAddr = revel.HTTPAddr
var httpAddr = revel_path.HTTPAddr
if httpAddr == "" {
httpAddr = "127.0.0.1"
httpAddr = "localhost"
}
var httpProto = "http"
if revel.HTTPSsl {
if revel_path.HTTPSsl {
httpProto = "https"
}
// Get a list of tests
var baseURL = fmt.Sprintf("%s://%s:%d", httpProto, httpAddr, revel.HTTPPort)
var baseURL = fmt.Sprintf("%s://%s:%d", httpProto, httpAddr, revel_path.HTTPPort)
utils.Logger.Infof("Testing %s (%s) in %s mode URL %s \n", revel_path.AppName, revel_path.ImportPath, mode, baseURL)
testSuites, _ := getTestsList(baseURL)
// If a specific TestSuite[.Method] is specified, only run that suite/test
if len(args) == 3 {
testSuites = filterTestSuites(testSuites, args[2])
if c.Test.Function != "" {
testSuites = filterTestSuites(testSuites, c.Test.Function)
}
testSuiteCount := len(*testSuites)
fmt.Printf("\n%d test suite%s to run.\n", testSuiteCount, pluralize(testSuiteCount, "", "s"))
fmt.Println()
// Run each suite.
failedResults, overallSuccess := runTestSuites(baseURL, resultPath, testSuites)
failedResults, overallSuccess := runTestSuites(revel_path, baseURL, resultPath, testSuites)
fmt.Println()
if overallSuccess {
@@ -137,16 +162,18 @@ func testApp(args []string) {
}
}
writeResultFile(resultPath, "result.failed", "failed")
errorf("Some tests failed. See file://%s for results.", resultPath)
utils.Logger.Errorf("Some tests failed. See file://%s for results.", resultPath)
}
}
// Outputs the results to a file
func writeResultFile(resultPath, name, content string) {
if err := ioutil.WriteFile(filepath.Join(resultPath, name), []byte(content), 0666); err != nil {
errorf("Failed to write result file %s: %s", filepath.Join(resultPath, name), err)
utils.Logger.Errorf("Failed to write result file %s: %s", filepath.Join(resultPath, name), err)
}
}
// Determines if response should be plural
func pluralize(num int, singular, plural string) string {
if num == 1 {
return singular
@@ -156,7 +183,7 @@ func pluralize(num int, singular, plural string) string {
// Filters test suites and individual tests to match
// the parsed command line parameter
func filterTestSuites(suites *[]controllers.TestSuiteDesc, suiteArgument string) *[]controllers.TestSuiteDesc {
func filterTestSuites(suites *[]tests.TestSuiteDesc, suiteArgument string) *[]tests.TestSuiteDesc {
var suiteName, testName string
argArray := strings.Split(suiteArgument, ".")
suiteName = argArray[0]
@@ -171,54 +198,34 @@ func filterTestSuites(suites *[]controllers.TestSuiteDesc, suiteArgument string)
continue
}
if testName == "" {
return &[]controllers.TestSuiteDesc{suite}
return &[]tests.TestSuiteDesc{suite}
}
// Only run a particular test in a suite
for _, test := range suite.Tests {
if test.Name != testName {
continue
}
return &[]controllers.TestSuiteDesc{
return &[]tests.TestSuiteDesc{
{
Name: suite.Name,
Tests: []controllers.TestDesc{test},
Tests: []tests.TestDesc{test},
},
}
}
errorf("Couldn't find test %s in suite %s", testName, suiteName)
utils.Logger.Errorf("Couldn't find test %s in suite %s", testName, suiteName)
}
errorf("Couldn't find test suite %s", suiteName)
utils.Logger.Errorf("Couldn't find test suite %s", suiteName)
return nil
}
func checkTestRunner() {
testRunnerFound := false
for _, module := range revel.Modules {
if module.ImportPath == revel.Config.StringDefault("module.testrunner", "github.com/revel/modules/testrunner") {
testRunnerFound = true
break
}
}
if !testRunnerFound {
errorf(`Error: The testrunner module is not running.
You can add it to a run mode configuration with the following line:
module.testrunner = github.com/revel/modules/testrunner
`)
}
}
// Get a list of tests from server.
// Since this is the first request to the server, retry/sleep a couple times
// in case it hasn't finished starting up yet.
func getTestsList(baseURL string) (*[]controllers.TestSuiteDesc, error) {
func getTestsList(baseURL string) (*[]tests.TestSuiteDesc, error) {
var (
err error
resp *http.Response
testSuites []controllers.TestSuiteDesc
testSuites []tests.TestSuiteDesc
)
for i := 0; ; i++ {
if resp, err = http.Get(baseURL + "/@tests.list"); err == nil {
@@ -231,9 +238,9 @@ func getTestsList(baseURL string) (*[]controllers.TestSuiteDesc, error) {
continue
}
if err != nil {
errorf("Failed to request test list: %s", err)
utils.Logger.Fatalf("Failed to request test list: %s %s", baseURL, err)
} else {
errorf("Failed to request test list: non-200 response")
utils.Logger.Fatalf("Failed to request test list: non-200 response %s", baseURL)
}
}
defer func() {
@@ -245,21 +252,15 @@ func getTestsList(baseURL string) (*[]controllers.TestSuiteDesc, error) {
return &testSuites, err
}
func runTestSuites(baseURL, resultPath string, testSuites *[]controllers.TestSuiteDesc) (*[]controllers.TestSuiteResult, bool) {
// Load the result template, which we execute for each suite.
module, _ := revel.ModuleByName("testrunner")
TemplateLoader := revel.NewTemplateLoader([]string{filepath.Join(module.Path, "app", "views")})
if err := TemplateLoader.Refresh(); err != nil {
errorf("Failed to compile templates: %s", err)
}
resultTemplate, err := TemplateLoader.Template("TestRunner/SuiteResult.html")
if err != nil {
errorf("Failed to load suite result template: %s", err)
}
// Run the testsuites using the container
func runTestSuites(paths *model.RevelContainer, baseURL, resultPath string, testSuites *[]tests.TestSuiteDesc) (*[]tests.TestSuiteResult, bool) {
// We can determine the testsuite location by finding the test module and extracting the data from it
resultFilePath := filepath.Join(paths.ModulePathMap["testrunner"], "app", "views", "TestRunner/SuiteResult.html")
var (
overallSuccess = true
failedResults []controllers.TestSuiteResult
failedResults []tests.TestSuiteResult
)
for _, suite := range *testSuites {
// Print the name of the suite we're running.
@@ -271,21 +272,24 @@ func runTestSuites(baseURL, resultPath string, testSuites *[]controllers.TestSui
// Run every test.
startTime := time.Now()
suiteResult := controllers.TestSuiteResult{Name: suite.Name, Passed: true}
suiteResult := tests.TestSuiteResult{Name: suite.Name, Passed: true}
for _, test := range suite.Tests {
testURL := baseURL + "/@tests/" + suite.Name + "/" + test.Name
resp, err := http.Get(testURL)
if err != nil {
errorf("Failed to fetch test result at url %s: %s", testURL, err)
utils.Logger.Errorf("Failed to fetch test result at url %s: %s", testURL, err)
}
defer func() {
_ = resp.Body.Close()
}()
var testResult controllers.TestResult
var testResult tests.TestResult
err = json.NewDecoder(resp.Body).Decode(&testResult)
if err == nil && !testResult.Passed {
suiteResult.Passed = false
fmt.Printf(" %s.%s : FAILED\n", suite.Name, test.Name)
} else {
fmt.Printf(" %s.%s : PASSED\n", suite.Name, test.Name)
}
suiteResult.Results = append(suiteResult.Results, testResult)
}
@@ -301,13 +305,7 @@ func runTestSuites(baseURL, resultPath string, testSuites *[]controllers.TestSui
// Create the result HTML file.
suiteResultFilename := filepath.Join(resultPath,
fmt.Sprintf("%s.%s.html", suite.Name, strings.ToLower(suiteResultStr)))
suiteResultFile, err := os.Create(suiteResultFilename)
if err != nil {
errorf("Failed to create result file %s: %s", suiteResultFilename, err)
}
if err = resultTemplate.Render(suiteResultFile, suiteResult); err != nil {
errorf("Failed to render result template: %s", err)
}
utils.MustRenderTemplate(suiteResultFilename, resultFilePath, suiteResult)
}
return &failedResults, overallSuccess

View File

@@ -1,176 +0,0 @@
// 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 (
"archive/tar"
"compress/gzip"
"fmt"
"go/build"
"io"
"os"
"path/filepath"
"strings"
"text/template"
"github.com/revel/revel"
)
// LoggedError is wrapper to differentiate logged panics from unexpected ones.
type LoggedError struct{ error }
func panicOnError(err error, msg string) {
if revErr, ok := err.(*revel.Error); (ok && revErr != nil) || (!ok && err != nil) {
fmt.Fprintf(os.Stderr, "Abort: %s: %s\n", msg, err)
panic(LoggedError{err})
}
}
func mustCopyFile(destFilename, srcFilename string) {
destFile, err := os.Create(destFilename)
panicOnError(err, "Failed to create file "+destFilename)
srcFile, err := os.Open(srcFilename)
panicOnError(err, "Failed to open file "+srcFilename)
_, err = io.Copy(destFile, srcFile)
panicOnError(err,
fmt.Sprintf("Failed to copy data from %s to %s", srcFile.Name(), destFile.Name()))
err = destFile.Close()
panicOnError(err, "Failed to close file "+destFile.Name())
err = srcFile.Close()
panicOnError(err, "Failed to close file "+srcFile.Name())
}
func mustRenderTemplate(destPath, srcPath string, data map[string]interface{}) {
tmpl, err := template.ParseFiles(srcPath)
panicOnError(err, "Failed to parse template "+srcPath)
f, err := os.Create(destPath)
panicOnError(err, "Failed to create "+destPath)
err = tmpl.Execute(f, data)
panicOnError(err, "Failed to render template "+srcPath)
err = f.Close()
panicOnError(err, "Failed to close "+f.Name())
}
func mustChmod(filename string, mode os.FileMode) {
err := os.Chmod(filename, mode)
panicOnError(err, fmt.Sprintf("Failed to chmod %d %q", mode, filename))
}
// copyDir copies a directory tree over to a new directory. Any files ending in
// ".template" are treated as a Go template and rendered using the given data.
// Additionally, the trailing ".template" is stripped from the file name.
// Also, dot files and dot directories are skipped.
func mustCopyDir(destDir, srcDir string, data map[string]interface{}) error {
return revel.Walk(srcDir, func(srcPath string, info os.FileInfo, err error) error {
// Get the relative path from the source base, and the corresponding path in
// the dest directory.
relSrcPath := strings.TrimLeft(srcPath[len(srcDir):], string(os.PathSeparator))
destPath := filepath.Join(destDir, relSrcPath)
// Skip dot files and dot directories.
if strings.HasPrefix(relSrcPath, ".") {
if info.IsDir() {
return filepath.SkipDir
}
return nil
}
// Create a subdirectory if necessary.
if info.IsDir() {
err := os.MkdirAll(filepath.Join(destDir, relSrcPath), 0777)
if !os.IsExist(err) {
panicOnError(err, "Failed to create directory")
}
return nil
}
// If this file ends in ".template", render it as a template.
if strings.HasSuffix(relSrcPath, ".template") {
mustRenderTemplate(destPath[:len(destPath)-len(".template")], srcPath, data)
return nil
}
// Else, just copy it over.
mustCopyFile(destPath, srcPath)
return nil
})
}
func mustTarGzDir(destFilename, srcDir string) string {
zipFile, err := os.Create(destFilename)
panicOnError(err, "Failed to create archive")
defer func() {
_ = zipFile.Close()
}()
gzipWriter := gzip.NewWriter(zipFile)
defer func() {
_ = gzipWriter.Close()
}()
tarWriter := tar.NewWriter(gzipWriter)
defer func() {
_ = tarWriter.Close()
}()
_ = revel.Walk(srcDir, func(srcPath string, info os.FileInfo, err error) error {
if info.IsDir() {
return nil
}
srcFile, err := os.Open(srcPath)
panicOnError(err, "Failed to read source file")
defer func() {
_ = srcFile.Close()
}()
err = tarWriter.WriteHeader(&tar.Header{
Name: strings.TrimLeft(srcPath[len(srcDir):], string(os.PathSeparator)),
Size: info.Size(),
Mode: int64(info.Mode()),
ModTime: info.ModTime(),
})
panicOnError(err, "Failed to write tar entry header")
_, err = io.Copy(tarWriter, srcFile)
panicOnError(err, "Failed to copy")
return nil
})
return zipFile.Name()
}
func exists(filename string) bool {
_, err := os.Stat(filename)
return err == nil
}
// empty returns true if the given directory is empty.
// the directory must exist.
func empty(dirname string) bool {
dir, err := os.Open(dirname)
if err != nil {
errorf("error opening directory: %s", err)
}
defer func() {
_ = dir.Close()
}()
results, _ := dir.Readdir(1)
return len(results) == 0
}
func importPathFromCurrentDir() string {
pwd, _ := os.Getwd()
importPath, _ := filepath.Rel(filepath.Join(build.Default.GOPATH, "src"), pwd)
return filepath.ToSlash(importPath)
}

View File

@@ -12,7 +12,14 @@ import (
"fmt"
"runtime"
"github.com/revel/revel"
"github.com/revel/cmd/model"
"go/build"
"go/token"
"go/parser"
"go/ast"
"io/ioutil"
"path/filepath"
"github.com/revel/cmd/utils"
)
var cmdVersion = &Command{
@@ -28,11 +35,46 @@ For example:
}
func init() {
cmdVersion.Run = versionApp
cmdVersion.RunWith = versionApp
}
func versionApp(args []string) {
fmt.Printf("Version(s):")
fmt.Printf("\n Revel v%v (%v)", revel.Version, revel.BuildDate)
// Displays the version of go and Revel
func versionApp(c *model.CommandConfig) {
revelPkg, err := build.Import(model.RevelImportPath, c.Version.ImportPath, build.FindOnly)
if err != nil {
utils.Logger.Errorf("Failed to find Revel with error:", "error", err)
}
fset := token.NewFileSet() // positions are relative to fset
version, err := ioutil.ReadFile(filepath.Join(revelPkg.Dir,"version.go"))
if err != nil {
utils.Logger.Errorf("Failed to find Revel version:", "error", err)
}
// Parse src but stop after processing the imports.
f, err := parser.ParseFile(fset, "", version, parser.ParseComments)
if err != nil {
utils.Logger.Errorf("Failed to parse Revel version error:", "error", err)
}
// Print the imports from the file's AST.
for _, s := range f.Decls {
genDecl, ok := s.(*ast.GenDecl)
if !ok {
continue
}
if genDecl.Tok != token.CONST {
continue
}
for _, a := range genDecl.Specs {
spec := a.(*ast.ValueSpec)
r := spec.Values[0].(*ast.BasicLit)
fmt.Printf("Revel %s = %s\n",spec.Names[0].Name,r.Value)
}
}
fmt.Printf("\n %s %s/%s\n\n", runtime.Version(), runtime.GOOS, runtime.GOARCH)
}