mirror of
https://github.com/kevin-DL/revel-cmd.git
synced 2026-01-17 04:54:52 +00:00
Enhancements to Revel command
Reformat of code Allow user to use a mix of command line arguments and flags Enhance the import tool to detect missing packages in the modules side Added test cases for all commands
This commit is contained in:
45
utils/build_error.go
Normal file
45
utils/build_error.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/revel/cmd/logger"
|
||||
)
|
||||
|
||||
type (
|
||||
BuildError struct {
|
||||
Stack interface{}
|
||||
Message string
|
||||
Args []interface{}
|
||||
}
|
||||
)
|
||||
|
||||
// Returns a new builed error
|
||||
func NewBuildError(message string, args ...interface{}) (b *BuildError) {
|
||||
Logger.Info(message, args...)
|
||||
b = &BuildError{}
|
||||
b.Message = message
|
||||
b.Args = args
|
||||
b.Stack = logger.NewCallStack()
|
||||
Logger.Info("Stack", "stack", b.Stack)
|
||||
return b
|
||||
}
|
||||
|
||||
// Returns a new BuildError if err is not nil
|
||||
func NewBuildIfError(err error, message string, args ...interface{}) (b error) {
|
||||
if err != nil {
|
||||
if berr, ok := err.(*BuildError); ok {
|
||||
// This is already a build error so just append the args
|
||||
berr.Args = append(berr.Args, args...)
|
||||
return berr
|
||||
} else {
|
||||
args = append(args, "error", err.Error())
|
||||
b = NewBuildError(message, args...)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// BuildError implements Error() string
|
||||
func (b *BuildError) Error() string {
|
||||
return fmt.Sprint(b.Message, b.Args)
|
||||
}
|
||||
27
utils/command.go
Normal file
27
utils/command.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"go/build"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Initialize the command based on the GO environment
|
||||
func CmdInit(c *exec.Cmd, basePath string) {
|
||||
c.Dir = basePath
|
||||
// Go 1.8 fails if we do not include the GOROOT
|
||||
c.Env = []string{"GOPATH=" + build.Default.GOPATH, "PATH=" + GetEnv("PATH"), "GOROOT="+ GetEnv("GOROOT")}
|
||||
|
||||
}
|
||||
|
||||
// Returns an environment variable
|
||||
func GetEnv(name string) string {
|
||||
for _, v := range os.Environ() {
|
||||
split := strings.Split(v, "=")
|
||||
if split[0] == name {
|
||||
return strings.Join(split[1:], "")
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -7,15 +7,22 @@ import (
|
||||
)
|
||||
|
||||
// The error is a wrapper for the
|
||||
type Error struct {
|
||||
SourceType string // The type of source that failed to build.
|
||||
Title, Path, Description string // Description of the error, as presented to the user.
|
||||
Line, Column int // Where the error was encountered.
|
||||
SourceLines []string // The entire source file, split into lines.
|
||||
Stack string // The raw stack trace string from debug.Stack().
|
||||
MetaError string // Error that occurred producing the error page.
|
||||
Link string // A configurable link to wrap the error source in
|
||||
}
|
||||
type (
|
||||
Error struct {
|
||||
SourceType string // The type of source that failed to build.
|
||||
Title, Path, Description string // Description of the error, as presented to the user.
|
||||
Line, Column int // Where the error was encountered.
|
||||
SourceLines []string // The entire source file, split into lines.
|
||||
Stack string // The raw stack trace string from debug.Stack().
|
||||
MetaError string // Error that occurred producing the error page.
|
||||
Link string // A configurable link to wrap the error source in
|
||||
}
|
||||
SourceLine struct {
|
||||
Source string
|
||||
Line int
|
||||
IsError bool
|
||||
}
|
||||
)
|
||||
|
||||
// Creates a link based on the configuration setting "errors.link"
|
||||
func (e *Error) SetLink(errorLink string) {
|
||||
@@ -50,6 +57,7 @@ func (e *Error) Error() string {
|
||||
}
|
||||
return fmt.Sprintf("%s%s", header, e.Description)
|
||||
}
|
||||
|
||||
// ContextSource method returns a snippet of the source around
|
||||
// where the error occurred.
|
||||
func (e *Error) ContextSource() []SourceLine {
|
||||
@@ -72,10 +80,3 @@ func (e *Error) ContextSource() []SourceLine {
|
||||
}
|
||||
return lines
|
||||
}
|
||||
|
||||
// SourceLine structure to hold the per-source-line details.
|
||||
type SourceLine struct {
|
||||
Source string
|
||||
Line int
|
||||
IsError bool
|
||||
}
|
||||
|
||||
186
utils/file.go
186
utils/file.go
@@ -1,21 +1,21 @@
|
||||
package utils
|
||||
|
||||
|
||||
// DirExists returns true if the given path exists and is a directory.
|
||||
import (
|
||||
"os"
|
||||
"archive/tar"
|
||||
"strings"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"compress/gzip"
|
||||
"go/build"
|
||||
"io/ioutil"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"html/template"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// DirExists returns true if the given path exists and is a directory.
|
||||
func DirExists(filename string) bool {
|
||||
fileInfo, err := os.Stat(filename)
|
||||
return err == nil && fileInfo.IsDir()
|
||||
@@ -39,49 +39,59 @@ func ReadLines(filename string) ([]string, error) {
|
||||
return strings.Split(string(dataBytes), "\n"), nil
|
||||
}
|
||||
|
||||
func MustCopyFile(destFilename, srcFilename string) {
|
||||
// Copy file returns error
|
||||
func CopyFile(destFilename, srcFilename string) (err error) {
|
||||
|
||||
destFile, err := os.Create(destFilename)
|
||||
PanicOnError(err, "Failed to create file "+destFilename)
|
||||
if err != nil {
|
||||
return NewBuildIfError(err, "Failed to create file", "file", destFilename)
|
||||
}
|
||||
|
||||
srcFile, err := os.Open(srcFilename)
|
||||
PanicOnError(err, "Failed to open file "+srcFilename)
|
||||
if err != nil {
|
||||
return NewBuildIfError(err, "Failed to open file", "file", srcFilename)
|
||||
}
|
||||
|
||||
_, err = io.Copy(destFile, srcFile)
|
||||
PanicOnError(err,
|
||||
fmt.Sprintf("Failed to copy data from %s to %s", srcFile.Name(), destFile.Name()))
|
||||
if err != nil {
|
||||
return NewBuildIfError(err, "Failed to copy data", "fromfile", srcFilename, "tofile", destFilename)
|
||||
}
|
||||
|
||||
err = destFile.Close()
|
||||
PanicOnError(err, "Failed to close file "+destFile.Name())
|
||||
if err != nil {
|
||||
return NewBuildIfError(err, "Failed to close file", "file", destFilename)
|
||||
}
|
||||
|
||||
err = srcFile.Close()
|
||||
PanicOnError(err, "Failed to close file "+srcFile.Name())
|
||||
}
|
||||
if err != nil {
|
||||
return NewBuildIfError(err, "Failed to close file", "file", srcFilename)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GenerateTemplate renders the given template to produce source code, which it writes
|
||||
// to the given file.
|
||||
func MustGenerateTemplate(filename, templateSource string, args map[string]interface{}) (err error) {
|
||||
func GenerateTemplate(filename, templateSource string, args map[string]interface{}) (err error) {
|
||||
tmpl := template.Must(template.New("").Parse(templateSource))
|
||||
|
||||
var b bytes.Buffer
|
||||
if err = tmpl.Execute(&b, args); err != nil {
|
||||
Logger.Fatal("ExecuteTemplate: Execute failed", "error", err)
|
||||
return
|
||||
return NewBuildIfError(err, "ExecuteTemplate: Execute failed")
|
||||
}
|
||||
sourceCode := b.String()
|
||||
filePath := filepath.Dir(filename)
|
||||
if !DirExists(filePath) {
|
||||
err = os.MkdirAll(filePath, 0777)
|
||||
if err != nil && !os.IsExist(err) {
|
||||
Logger.Fatal("Failed to make directory","dir", filePath, "error", err)
|
||||
return NewBuildIfError(err, "Failed to make directory", "dir", filePath)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Create the file
|
||||
file, err := os.Create(filename)
|
||||
if err != nil {
|
||||
Logger.Fatal("Failed to create file","error", err)
|
||||
Logger.Fatal("Failed to create file", "error", err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
@@ -96,27 +106,41 @@ func MustGenerateTemplate(filename, templateSource string, args map[string]inter
|
||||
}
|
||||
|
||||
// Given the target path and source path and data. A template
|
||||
func MustRenderTemplate(destPath, srcPath string, data interface{}) {
|
||||
func RenderTemplate(destPath, srcPath string, data interface{}) (err error) {
|
||||
tmpl, err := template.ParseFiles(srcPath)
|
||||
PanicOnError(err, "Failed to parse template "+srcPath)
|
||||
if err != nil {
|
||||
return NewBuildIfError(err, "Failed to parse template "+srcPath)
|
||||
}
|
||||
|
||||
f, err := os.Create(destPath)
|
||||
PanicOnError(err, "Failed to create "+destPath)
|
||||
if err != nil {
|
||||
return NewBuildIfError(err, "Failed to create ", "path", destPath)
|
||||
}
|
||||
|
||||
err = tmpl.Execute(f, data)
|
||||
PanicOnError(err, "Failed to render template "+srcPath)
|
||||
if err != nil {
|
||||
return NewBuildIfError(err, "Failed to Render template "+srcPath)
|
||||
}
|
||||
|
||||
err = f.Close()
|
||||
PanicOnError(err, "Failed to close "+f.Name())
|
||||
if err != nil {
|
||||
return NewBuildIfError(err, "Failed to close file stream "+destPath)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Given the target path and source path and data. A template
|
||||
func MustRenderTemplateToStream(output io.Writer, srcPath []string, data interface{}) {
|
||||
func RenderTemplateToStream(output io.Writer, srcPath []string, data interface{}) (err error) {
|
||||
tmpl, err := template.ParseFiles(srcPath...)
|
||||
PanicOnError(err, "Failed to parse template "+srcPath[0])
|
||||
if err != nil {
|
||||
return NewBuildIfError(err, "Failed to parse template "+srcPath[0])
|
||||
}
|
||||
|
||||
err = tmpl.Execute(output, data)
|
||||
PanicOnError(err, "Failed to render template "+srcPath[0])
|
||||
if err != nil {
|
||||
return NewBuildIfError(err, "Failed to render template "+srcPath[0])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func MustChmod(filename string, mode os.FileMode) {
|
||||
@@ -127,8 +151,7 @@ func MustChmod(filename string, mode os.FileMode) {
|
||||
// Called if panic
|
||||
func PanicOnError(err error, msg string) {
|
||||
if revErr, ok := err.(*Error); (ok && revErr != nil) || (!ok && err != nil) {
|
||||
Logger.Fatalf("Abort: %s: %s %s\n", msg, revErr, err)
|
||||
//panic(NewLoggedError(err))
|
||||
Logger.Panicf("Abort: %s: %s %s\n", msg, revErr, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +159,7 @@ func PanicOnError(err error, msg string) {
|
||||
// ".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 {
|
||||
func CopyDir(destDir, srcDir string, data map[string]interface{}) error {
|
||||
return fsWalk(srcDir, 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.
|
||||
@@ -155,26 +178,29 @@ func MustCopyDir(destDir, srcDir string, data map[string]interface{}) error {
|
||||
if info.IsDir() {
|
||||
err := os.MkdirAll(filepath.Join(destDir, relSrcPath), 0777)
|
||||
if !os.IsExist(err) {
|
||||
PanicOnError(err, "Failed to create directory")
|
||||
return NewBuildIfError(err, "Failed to create directory", "path", destDir+"/"+relSrcPath)
|
||||
}
|
||||
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
|
||||
|
||||
return RenderTemplate(destPath[:len(destPath)-len(".template")], srcPath, data)
|
||||
}
|
||||
|
||||
// Else, just copy it over.
|
||||
MustCopyFile(destPath, srcPath)
|
||||
return nil
|
||||
|
||||
return CopyFile(destPath, srcPath)
|
||||
})
|
||||
}
|
||||
|
||||
// Shortcut to fsWalk
|
||||
func Walk(root string, walkFn filepath.WalkFunc) error {
|
||||
return fsWalk(root,root,walkFn)
|
||||
return fsWalk(root, root, walkFn)
|
||||
}
|
||||
|
||||
// Walk the tree using the function
|
||||
func fsWalk(fname string, linkName string, walkFn filepath.WalkFunc) error {
|
||||
fsWalkFunc := func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
@@ -214,9 +240,13 @@ func fsWalk(fname string, linkName string, walkFn filepath.WalkFunc) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func MustTarGzDir(destFilename, srcDir string) string {
|
||||
// Tar gz the folder
|
||||
func TarGzDir(destFilename, srcDir string) (name string, err error) {
|
||||
zipFile, err := os.Create(destFilename)
|
||||
PanicOnError(err, "Failed to create archive")
|
||||
if err != nil {
|
||||
return "", NewBuildIfError(err, "Failed to create archive", "file", destFilename)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = zipFile.Close()
|
||||
}()
|
||||
@@ -231,13 +261,16 @@ func MustTarGzDir(destFilename, srcDir string) string {
|
||||
_ = tarWriter.Close()
|
||||
}()
|
||||
|
||||
_ = fsWalk(srcDir, srcDir, func(srcPath string, info os.FileInfo, err error) error {
|
||||
err = fsWalk(srcDir, 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")
|
||||
if err != nil {
|
||||
return NewBuildIfError(err, "Failed to read file", "file", srcPath)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = srcFile.Close()
|
||||
}()
|
||||
@@ -248,17 +281,22 @@ func MustTarGzDir(destFilename, srcDir string) string {
|
||||
Mode: int64(info.Mode()),
|
||||
ModTime: info.ModTime(),
|
||||
})
|
||||
PanicOnError(err, "Failed to write tar entry header")
|
||||
if err != nil {
|
||||
return NewBuildIfError(err, "Failed to write tar entry header", "file", srcPath)
|
||||
}
|
||||
|
||||
_, err = io.Copy(tarWriter, srcFile)
|
||||
PanicOnError(err, "Failed to copy")
|
||||
if err != nil {
|
||||
return NewBuildIfError(err, "Failed to copy file", "file", srcPath)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return zipFile.Name()
|
||||
return zipFile.Name(), err
|
||||
}
|
||||
|
||||
// Return true if the file exists
|
||||
func Exists(filename string) bool {
|
||||
_, err := os.Stat(filename)
|
||||
return err == nil
|
||||
@@ -278,8 +316,50 @@ func Empty(dirname string) bool {
|
||||
return len(results) == 0
|
||||
}
|
||||
|
||||
func ImportPathFromCurrentDir() string {
|
||||
pwd, _ := os.Getwd()
|
||||
importPath, _ := filepath.Rel(filepath.Join(build.Default.GOPATH, "src"), pwd)
|
||||
return filepath.ToSlash(importPath)
|
||||
// Find the full source dir for the import path, uses the build.Default.GOPATH to search for the directory
|
||||
func FindSrcPaths(appImportPath, revelImportPath string, packageResolver func(pkgName string) error) (appSourcePath, revelSourcePath string, err error) {
|
||||
var (
|
||||
gopaths = filepath.SplitList(build.Default.GOPATH)
|
||||
goroot = build.Default.GOROOT
|
||||
)
|
||||
|
||||
if len(gopaths) == 0 {
|
||||
err = errors.New("GOPATH environment variable is not set. " +
|
||||
"Please refer to http://golang.org/doc/code.html to configure your Go environment.")
|
||||
return
|
||||
}
|
||||
|
||||
if ContainsString(gopaths, goroot) {
|
||||
err = fmt.Errorf("GOPATH (%s) must not include your GOROOT (%s). "+
|
||||
"Please refer to http://golang.org/doc/code.html to configure your Go environment. ",
|
||||
build.Default.GOPATH, goroot)
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
appPkgDir := ""
|
||||
appPkgSrcDir := ""
|
||||
if len(appImportPath)>0 {
|
||||
Logger.Info("Seeking app package","app",appImportPath)
|
||||
appPkg, err := build.Import(appImportPath, "", build.FindOnly)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Failed to import " + appImportPath + " with error %s", err.Error())
|
||||
return "","",err
|
||||
}
|
||||
appPkgDir,appPkgSrcDir =appPkg.Dir, appPkg.SrcRoot
|
||||
}
|
||||
Logger.Info("Seeking remote package","using",appImportPath, "remote",revelImportPath)
|
||||
revelPkg, err := build.Default.Import(revelImportPath, appPkgDir, build.FindOnly)
|
||||
if err != nil {
|
||||
Logger.Info("Resolved called Seeking remote package","using",appImportPath, "remote",revelImportPath)
|
||||
packageResolver(revelImportPath)
|
||||
revelPkg, err = build.Import(revelImportPath, appPkgDir, build.FindOnly)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Failed to find Revel with error: %s", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
revelSourcePath, appSourcePath = revelPkg.Dir[:len(revelPkg.Dir)-len(revelImportPath)], appPkgSrcDir
|
||||
return
|
||||
}
|
||||
|
||||
23
utils/log.go
23
utils/log.go
@@ -1,9 +1,9 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/revel/cmd/logger"
|
||||
"github.com/revel/config"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
@@ -12,16 +12,21 @@ var Logger = logger.New()
|
||||
|
||||
func InitLogger(basePath string, logLevel logger.LogLevel) {
|
||||
newContext := config.NewContext()
|
||||
if logLevel<logger.LvlInfo {
|
||||
newContext.SetOption("log.info.output", "none")
|
||||
newContext.SetOption("log.debug.output", "none")
|
||||
} else {
|
||||
newContext.SetOption("log.info.output", "stdout")
|
||||
if logLevel == logger.LvlDebug {
|
||||
newContext.SetOption("log.debug.output", "stdout")
|
||||
println("Debug on")
|
||||
} else {
|
||||
newContext.SetOption("log.debug.output", "off")
|
||||
}
|
||||
newContext.SetOption("log.warn.output","stderr")
|
||||
newContext.SetOption("log.error.output","stderr")
|
||||
newContext.SetOption("log.crit.output","stderr")
|
||||
if logLevel >= logger.LvlInfo {
|
||||
newContext.SetOption("log.info.output", "stdout")
|
||||
} else {
|
||||
newContext.SetOption("log.inf.output", "off")
|
||||
}
|
||||
|
||||
newContext.SetOption("log.warn.output", "stderr")
|
||||
newContext.SetOption("log.error.output", "stderr")
|
||||
newContext.SetOption("log.crit.output", "stderr")
|
||||
Logger.SetHandler(logger.InitializeFromConfig(basePath, newContext))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user