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:
NotZippy
2018-09-27 21:08:40 -07:00
parent 01ccd695d4
commit f4fb2ec091
65 changed files with 2014 additions and 1281 deletions

View File

@@ -61,28 +61,30 @@ func NewAppCmd(binPath string, port int, runMode string, paths *model.RevelConta
// Start the app server, and wait until it is ready to serve requests.
func (cmd AppCmd) Start(c *model.CommandConfig) error {
listeningWriter := &startupListeningWriter{os.Stdout, make(chan bool),c}
listeningWriter := &startupListeningWriter{os.Stdout, make(chan bool), c}
cmd.Stdout = listeningWriter
utils.Logger.Info("Exec app:", "path", cmd.Path, "args", cmd.Args)
utils.Logger.Info("Exec app:", "path", cmd.Path, "args", cmd.Args, "dir", cmd.Dir, "env", cmd.Env)
utils.CmdInit(cmd.Cmd, c.AppPath)
if err := cmd.Cmd.Start(); err != nil {
utils.Logger.Fatal("Error running:", "error", err)
}
select {
case exitState := <-cmd.waitChan():
println("Revel proxy is listening, point your browser to :", c.Run.Port)
return errors.New("revel/harness: app died reason: " + exitState)
case <-time.After(60 * time.Second):
println("Revel proxy is listening, point your browser to :", c.Run.Port)
utils.Logger.Error("Killing revel server process did not respond after wait timeout.", "processid", cmd.Process.Pid)
cmd.Kill()
return errors.New("revel/harness: app timed out")
case <-listeningWriter.notifyReady:
println("Revel proxy is listening, point your browser to :", c.Run.Port)
return nil
}
// TODO remove this unreachable code and document it
panic("Impossible")
}
// Run the app server inline. Never returns.
@@ -111,7 +113,7 @@ func (cmd AppCmd) waitChan() <-chan string {
_ = cmd.Wait()
state := cmd.ProcessState
exitStatus := " unknown "
if state!=nil {
if state != nil {
exitStatus = state.String()
}
@@ -126,7 +128,7 @@ func (cmd AppCmd) waitChan() <-chan string {
type startupListeningWriter struct {
dest io.Writer
notifyReady chan bool
c *model.CommandConfig
c *model.CommandConfig
}
func (w *startupListeningWriter) Write(p []byte) (int, error) {

View File

@@ -36,12 +36,12 @@ func (c ByString) Less(i, j int) bool { return c[i].String() < c[j].String() }
// 2. Run the appropriate "go build" command.
// Requires that revel.Init has been called previously.
// Returns the path to the built binary, and an error if there was a problem building it.
func Build(c *model.CommandConfig, paths *model.RevelContainer) (app *App, compileError *utils.Error) {
func Build(c *model.CommandConfig, paths *model.RevelContainer) (_ *App, err error) {
// First, clear the generated files (to avoid them messing with ProcessSource).
cleanSource(paths, "tmp", "routes")
sourceInfo, compileError := parser.ProcessSource(paths)
if compileError != nil {
sourceInfo, err := parser.ProcessSource(paths)
if err != nil {
return
}
@@ -68,9 +68,15 @@ func Build(c *model.CommandConfig, paths *model.RevelContainer) (app *App, compi
// without being the main thread
cleanSource(paths, "tmp", "routes")
genSource(paths, "tmp", "main.go", RevelMainTemplate, templateArgs)
genSource(paths, filepath.Join("tmp", "run"), "run.go", RevelRunTemplate, templateArgs)
genSource(paths, "routes", "routes.go", RevelRoutesTemplate, templateArgs)
if err = genSource(paths, "tmp", "main.go", RevelMainTemplate, templateArgs); err != nil {
return
}
if err = genSource(paths, filepath.Join("tmp", "run"), "run.go", RevelRunTemplate, templateArgs); err != nil {
return
}
if err = genSource(paths, "routes", "routes.go", RevelRoutesTemplate, templateArgs); err != nil {
return
}
// Read build config.
buildTags := paths.Config.StringDefault("build.tags", "")
@@ -102,23 +108,9 @@ func Build(c *model.CommandConfig, paths *model.RevelContainer) (app *App, compi
}
}
var depPath string
if useVendor {
utils.Logger.Info("Vendor folder detected, scanning for deps in path")
depPath, err = exec.LookPath("dep")
if err != nil {
// Do not halt build unless a new package needs to be imported
utils.Logger.Warn("Build: `dep` executable not found in PATH, but vendor folder detected." +
"Packages can only be added automatically to the vendor folder using the `dep` tool. " +
"You can install the `dep` tool by doing a `go get -u github.com/golang/dep/cmd/dep`")
}
} else {
utils.Logger.Info("No vendor folder detected, not using dependency manager to import files")
}
pkg, err := build.Default.Import(paths.ImportPath, "", build.FindOnly)
if err != nil {
utils.Logger.Fatal("Failure importing", "path", paths.ImportPath)
return
}
// Binary path is a combination of $GOBIN/revel.d directory, app's import path and its name.
@@ -191,6 +183,7 @@ func Build(c *model.CommandConfig, paths *model.RevelContainer) (app *App, compi
buildCmd.Env = append(os.Environ(),
"GOPATH="+gopath,
)
utils.CmdInit(buildCmd, c.AppPath)
utils.Logger.Info("Exec:", "args", buildCmd.Args)
output, err := buildCmd.CombinedOutput()
@@ -220,32 +213,9 @@ func Build(c *model.CommandConfig, paths *model.RevelContainer) (app *App, compi
return nil, newCompileError(paths, output)
}
gotten[pkgName] = struct{}{}
// Execute "go get <pkg>"
// Or dep `dep ensure -add <pkg>` if it is there
var getCmd *exec.Cmd
if useVendor {
if depPath == "" {
utils.Logger.Warn("Build: Vendor folder found, but the `dep` tool was not found, " +
"if you use a different vendoring (package management) tool please add the following packages by hand, " +
"or install the `dep` tool into your gopath by doing a `go get -u github.com/golang/dep/cmd/dep`. " +
"For more information and usage of the tool please see http://github.com/golang/dep")
for _, pkg := range matches {
utils.Logger.Warn("Missing package", "package", pkg[1])
}
}
getCmd = exec.Command(depPath, "ensure", "-add", pkgName)
getCmd.Dir = paths.AppPath
} else {
getCmd = exec.Command(goPath, "get", pkgName)
}
utils.Logger.Info("Exec:", "args", getCmd.Args)
getOutput, err := getCmd.CombinedOutput()
if err != nil {
utils.Logger.Error("Build failed", "message", stOutput, "error", err)
utils.Logger.Error("Failed to fetch the output", "getOutput", string(getOutput))
return nil, newCompileError(paths, output)
if err := c.PackageResolver(pkgName); err != nil {
utils.Logger.Error("Unable to resolve package", "package", pkgName, "error", err)
return nil, newCompileError(paths, []byte(err.Error()))
}
}
@@ -336,12 +306,9 @@ func cleanDir(paths *model.RevelContainer, dir string) {
// genSource renders the given template to produce source code, which it writes
// to the given directory and file.
func genSource(paths *model.RevelContainer, dir, filename, templateSource string, args map[string]interface{}) {
func genSource(paths *model.RevelContainer, dir, filename, templateSource string, args map[string]interface{}) error {
err := utils.MustGenerateTemplate(filepath.Join(paths.AppPath, dir, filename), templateSource, args)
if err != nil {
utils.Logger.Fatal("Failed to generate template for source file", "error", err)
}
return utils.GenerateTemplate(filepath.Join(paths.AppPath, dir, filename), templateSource, args)
}
// Looks through all the method args and returns a set of unique import paths
@@ -433,7 +400,8 @@ func newCompileError(paths *model.RevelContainer, output []byte) *utils.Error {
// Extract the paths from the gopaths, and search for file there first
gopaths := filepath.SplitList(build.Default.GOPATH)
for _, gp := range gopaths {
newPath := filepath.Join(gp, relFilename)
newPath := filepath.Join(gp,"src", paths.ImportPath, relFilename)
println(newPath)
if utils.Exists(newPath) {
return newPath
}
@@ -443,6 +411,7 @@ func newCompileError(paths *model.RevelContainer, output []byte) *utils.Error {
return newPath
}
// Read the source for the offending file.
var (
relFilename = string(errorMatch[1]) // e.g. "src/revel/sample/app/controllers/app.go"
@@ -467,7 +436,7 @@ func newCompileError(paths *model.RevelContainer, output []byte) *utils.Error {
fileStr, err := utils.ReadLines(absFilename)
if err != nil {
compileError.MetaError = absFilename + ": " + err.Error()
utils.Logger.Error("Unable to readlines "+compileError.MetaError, "error", err)
utils.Logger.Info("Unable to readlines "+compileError.MetaError, "error", err)
return compileError
}

View File

@@ -31,9 +31,9 @@ import (
"github.com/revel/cmd/model"
"github.com/revel/cmd/utils"
"github.com/revel/cmd/watcher"
"sync"
"html/template"
"io/ioutil"
"sync"
)
var (
@@ -63,31 +63,31 @@ func (h *Harness) renderError(iw http.ResponseWriter, ir *http.Request, err erro
// 1) Application/views/errors
// 2) revel_home/views/errors
// 3) views/errors
if err==nil {
if err == nil {
utils.Logger.Panic("Caller passed in a nil error")
}
templateSet := template.New("__root__")
seekViewOnPath:=func(view string) (path string) {
seekViewOnPath := func(view string) (path string) {
path = filepath.Join(h.paths.ViewsPath, "errors", view)
if !utils.Exists(path) {
path = filepath.Join(h.paths.RevelPath, "templates", "errors", view)
}
data,err := ioutil.ReadFile(path)
if err!=nil {
data, err := ioutil.ReadFile(path)
if err != nil {
utils.Logger.Error("Unable to read template file", path)
}
_,err = templateSet.New("errors/"+view).Parse(string(data))
if err!=nil {
_, err = templateSet.New("errors/" + view).Parse(string(data))
if err != nil {
utils.Logger.Error("Unable to parse template file", path)
}
return
}
target := []string{seekViewOnPath("500.html"),seekViewOnPath("500-dev.html")}
target := []string{seekViewOnPath("500.html"), seekViewOnPath("500-dev.html")}
if !utils.Exists(target[0]) {
fmt.Fprintf(iw, "Target template not found not found %s<br />\n", target[0])
fmt.Fprintf(iw, "An error ocurred %s", err.Error())
return
return
}
var revelError *utils.Error
switch e := err.(type) {
@@ -108,16 +108,11 @@ func (h *Harness) renderError(iw http.ResponseWriter, ir *http.Request, err erro
viewArgs["DevMode"] = h.paths.DevMode
viewArgs["Error"] = revelError
// Render the template from the file
err = templateSet.ExecuteTemplate(iw,"errors/500.html",viewArgs)
if err!=nil {
utils.Logger.Error("Failed to execute","error",err)
err = templateSet.ExecuteTemplate(iw, "errors/500.html", viewArgs)
if err != nil {
utils.Logger.Error("Failed to execute", "error", err)
}
fmt.Println("template ",templateSet.Templates()[0].Name(), templateSet.Templates()[1].Name())
//utils.MustRenderTemplateToStream(iw,target, viewArgs)
}
// ServeHTTP handles all requests.
@@ -192,7 +187,6 @@ func NewHarness(c *model.CommandConfig, paths *model.RevelContainer, runMode str
useProxy: !noProxy,
config: c,
runMode: runMode,
}
if paths.HTTPSsl {
@@ -219,9 +213,17 @@ func (h *Harness) Refresh() (err *utils.Error) {
}
utils.Logger.Info("Rebuild Called")
h.app, err = Build(h.config, h.paths)
if err != nil {
utils.Logger.Error("Build detected an error", "error", err)
var newErr error
h.app, newErr = Build(h.config, h.paths)
if newErr != nil {
utils.Logger.Error("Build detected an error", "error", newErr)
if castErr, ok := newErr.(*utils.Error); ok {
return castErr
}
err = &utils.Error{
Title: "App failed to start up",
Description: err.Error(),
}
return
}
@@ -269,7 +271,7 @@ func (h *Harness) Run() {
if h.useProxy {
go func() {
// Check the port to start on a random port
if h.paths.HTTPPort==0 {
if h.paths.HTTPPort == 0 {
h.paths.HTTPPort = getFreePort()
}
addr := fmt.Sprintf("%s:%d", h.paths.HTTPAddr, h.paths.HTTPPort)