mirror of
https://github.com/kevin-DL/revel-cmd.git
synced 2026-01-20 06:05:10 +00:00
Initial rewrite of revel/cmd to provide vendor support and enhanced CLI options
This commit is contained in:
81
utils/error.go
Normal file
81
utils/error.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Creates a link based on the configuration setting "errors.link"
|
||||
func (e *Error) SetLink(errorLink string) {
|
||||
errorLink = strings.Replace(errorLink, "{{Path}}", e.Path, -1)
|
||||
errorLink = strings.Replace(errorLink, "{{Line}}", strconv.Itoa(e.Line), -1)
|
||||
|
||||
e.Link = "<a href=" + errorLink + ">" + e.Path + ":" + strconv.Itoa(e.Line) + "</a>"
|
||||
}
|
||||
|
||||
// Error method constructs a plaintext version of the error, taking
|
||||
// account that fields are optionally set. Returns e.g. Compilation Error
|
||||
// (in views/header.html:51): expected right delim in end; got "}"
|
||||
func (e *Error) Error() string {
|
||||
if e == nil {
|
||||
panic("opps")
|
||||
}
|
||||
loc := ""
|
||||
if e.Path != "" {
|
||||
line := ""
|
||||
if e.Line != 0 {
|
||||
line = fmt.Sprintf(":%d", e.Line)
|
||||
}
|
||||
loc = fmt.Sprintf("(in %s%s)", e.Path, line)
|
||||
}
|
||||
header := loc
|
||||
if e.Title != "" {
|
||||
if loc != "" {
|
||||
header = fmt.Sprintf("%s %s: ", e.Title, loc)
|
||||
} else {
|
||||
header = fmt.Sprintf("%s: ", e.Title)
|
||||
}
|
||||
}
|
||||
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 {
|
||||
if e.SourceLines == nil {
|
||||
return nil
|
||||
}
|
||||
start := (e.Line - 1) - 5
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
end := (e.Line - 1) + 5
|
||||
if end > len(e.SourceLines) {
|
||||
end = len(e.SourceLines)
|
||||
}
|
||||
|
||||
lines := make([]SourceLine, end-start)
|
||||
for i, src := range e.SourceLines[start:end] {
|
||||
fileLine := start + i + 1
|
||||
lines[i] = SourceLine{src, fileLine, fileLine == e.Line}
|
||||
}
|
||||
return lines
|
||||
}
|
||||
|
||||
// SourceLine structure to hold the per-source-line details.
|
||||
type SourceLine struct {
|
||||
Source string
|
||||
Line int
|
||||
IsError bool
|
||||
}
|
||||
285
utils/file.go
Normal file
285
utils/file.go
Normal file
@@ -0,0 +1,285 @@
|
||||
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"
|
||||
)
|
||||
|
||||
func DirExists(filename string) bool {
|
||||
fileInfo, err := os.Stat(filename)
|
||||
return err == nil && fileInfo.IsDir()
|
||||
}
|
||||
|
||||
// MustReadLines reads the lines of the given file. Panics in the case of error.
|
||||
func MustReadLines(filename string) []string {
|
||||
r, err := ReadLines(filename)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// ReadLines reads the lines of the given file. Panics in the case of error.
|
||||
func ReadLines(filename string) ([]string, error) {
|
||||
dataBytes, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return strings.Split(string(dataBytes), "\n"), nil
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
|
||||
// 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) {
|
||||
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
|
||||
}
|
||||
sourceCode := b.String()
|
||||
filePath := filepath.Dir(filename)
|
||||
if !DirExists(filePath) {
|
||||
err = os.Mkdir(filePath, 0777)
|
||||
if err != nil && !os.IsExist(err) {
|
||||
Logger.Fatal("Failed to make directory","dir", filePath, "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Create the file
|
||||
file, err := os.Create(filename)
|
||||
if err != nil {
|
||||
Logger.Fatal("Failed to create file","error", err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
_ = file.Close()
|
||||
}()
|
||||
|
||||
if _, err = file.WriteString(sourceCode); err != nil {
|
||||
Logger.Fatal("Failed to write to file: ", "error", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Given the target path and source path and data. A template
|
||||
func MustRenderTemplate(destPath, srcPath string, data 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())
|
||||
}
|
||||
|
||||
// Given the target path and source path and data. A template
|
||||
func MustRenderTemplateToStream(output io.Writer, srcPath []string, data interface{}) {
|
||||
tmpl, err := template.ParseFiles(srcPath...)
|
||||
PanicOnError(err, "Failed to parse template "+srcPath[0])
|
||||
|
||||
err = tmpl.Execute(output, data)
|
||||
PanicOnError(err, "Failed to render template "+srcPath[0])
|
||||
}
|
||||
|
||||
func MustChmod(filename string, mode os.FileMode) {
|
||||
err := os.Chmod(filename, mode)
|
||||
PanicOnError(err, fmt.Sprintf("Failed to chmod %d %q", mode, filename))
|
||||
}
|
||||
|
||||
// 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))
|
||||
}
|
||||
}
|
||||
|
||||
// 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 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.
|
||||
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 Walk(root string, walkFn filepath.WalkFunc) error {
|
||||
return fsWalk(root,root,walkFn)
|
||||
}
|
||||
func fsWalk(fname string, linkName string, walkFn filepath.WalkFunc) error {
|
||||
fsWalkFunc := func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var name string
|
||||
name, err = filepath.Rel(fname, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
path = filepath.Join(linkName, name)
|
||||
|
||||
if err == nil && info.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||
var symlinkPath string
|
||||
symlinkPath, err = filepath.EvalSymlinks(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// https://github.com/golang/go/blob/master/src/path/filepath/path.go#L392
|
||||
info, err = os.Lstat(symlinkPath)
|
||||
|
||||
if err != nil {
|
||||
return walkFn(path, info, err)
|
||||
}
|
||||
|
||||
if info.IsDir() {
|
||||
return fsWalk(symlinkPath, path, walkFn)
|
||||
}
|
||||
}
|
||||
|
||||
return walkFn(path, info, err)
|
||||
}
|
||||
err := filepath.Walk(fname, fsWalkFunc)
|
||||
return err
|
||||
}
|
||||
|
||||
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()
|
||||
}()
|
||||
|
||||
_ = 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")
|
||||
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 {
|
||||
Logger.Infof("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)
|
||||
}
|
||||
43
utils/log.go
Normal file
43
utils/log.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/revel/cmd/logger"
|
||||
"github.com/revel/config"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
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")
|
||||
newContext.SetOption("log.debug.output", "stdout")
|
||||
}
|
||||
newContext.SetOption("log.warn.output","stderr")
|
||||
newContext.SetOption("log.error.output","stderr")
|
||||
newContext.SetOption("log.crit.output","stderr")
|
||||
Logger.SetHandler(logger.InitializeFromConfig(basePath, newContext))
|
||||
}
|
||||
|
||||
// This function is to throw a panic that may be caught by the packger so it can perform the needed
|
||||
// imports
|
||||
func Retry(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(format) // Panic instead of os.Exit so that deferred will run.
|
||||
}
|
||||
|
||||
type LoggedError struct{ error }
|
||||
|
||||
func NewLoggedError(err error) *LoggedError {
|
||||
return &LoggedError{err}
|
||||
}
|
||||
11
utils/strings.go
Normal file
11
utils/strings.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package utils
|
||||
|
||||
// Return true if the target string is in the list
|
||||
func ContainsString(list []string, target string) bool {
|
||||
for _, el := range list {
|
||||
if el == target {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user