Compare commits

...

29 Commits

Author SHA1 Message Date
notzippy@gmail.com
1d9df256a0 Moved test cases to run last 2020-04-29 21:39:19 -07:00
notzippy@gmail.com
ad694c0fb0 Debug travis 2020-04-29 21:32:12 -07:00
notzippy@gmail.com
fb4b56513a Debug travis
Added verbose flag so we can see what is occurring,
Removed checkout for revel, not needed anymore
2020-04-29 21:13:00 -07:00
notzippy@gmail.com
20d5766eb6 Added gomod-flags
Added a gomod-flags parameter which allows you to run go mod commands on the go.mod file before the build is performed. This allows for development environments.
2020-04-29 17:05:39 -07:00
notzippy@gmail.com
0920905a0c Updated to build go 1.12 and up
Modified to use fsnotify directlyUpdated travis to not use go deps
2020-04-28 12:56:23 -07:00
notzippy@gmail.com
31cb64e496 Check-in of command_test,
remaps the go mod command to use the develop branch.
2020-04-27 09:07:04 -07:00
notzippy@gmail.com
33abc47c7a Fixed remaining test 2020-04-26 23:00:51 -07:00
notzippy@gmail.com
86736d6e43 Updated formating
Ran through testing individually for vendored Revel applications
2020-04-26 22:29:16 -07:00
notzippy@gmail.com
07d67846c1 Restructured command config
Removed go/build reference in clean
2020-04-26 22:28:46 -07:00
notzippy@gmail.com
c1aee24445 Corrected version detection, so that equal versions match 2020-04-26 22:28:46 -07:00
notzippy@gmail.com
f2b54f5a69 Updated sourceinfo
Added packagepathmap to the SourceInfo, this in turn allows the RevelCLI app command to pass the source paths directly to Revel directly
Added default to build to be "target" of the current folder
Renamed source processor
2020-04-26 22:28:46 -07:00
notzippy@gmail.com
3f54665d4e Added processor to read the functions in the imported files, and populate the SourceInfo object the same as before 2020-04-26 22:28:46 -07:00
notzippy@gmail.com
548cbc1764 Upatede Error type to SourceError
Added processor object to code
Verified compile errors appearing
Signed-off-by: notzippy@gmail.com
2020-04-26 22:28:46 -07:00
notzippy@gmail.com
9a9511d28f Updated so revel new works and revel run starts parsing the source. 2020-04-26 22:28:46 -07:00
notzippy@gmail.com
acb8fb631b Initial commit to go mod 2020-04-26 22:28:46 -07:00
Steve
d2014633af Merge pull request #176 from xXLokerXx/fix_windows_path
acept slash and inverted slash in src path validation
2020-04-13 07:32:55 -07:00
Steve
773f6889b4 Merge branch 'develop' into fix_windows_path 2020-04-13 07:32:03 -07:00
Steve
ca4cfa567e Merge pull request #165 from kumakichi/fixed_import_C
fixed import "C"
2020-04-13 07:30:30 -07:00
Steve
436869049c Merge pull request #179 from Laur1nMartins/Laur1nMartins/fix-linkerFlags
Fix linker flags inclusion in build command
2020-04-13 07:29:34 -07:00
Steve
cf2e617618 Merge branch 'develop' into Laur1nMartins/fix-linkerFlags 2020-04-13 07:29:15 -07:00
Laurin
424474a035 Fix linker flags inclusion in build comamnd 2020-02-10 10:35:43 +01:00
Steve
531aa1e209 Merge pull request #178 from helgix/package-bugfix
eliminates package bug on silent build problems
2019-09-30 16:54:18 -07:00
Олег Вакарев
83dfdb8ad2 this fixes error when revel package creates tar without indicating that build had errors due to unhandled error 2019-09-26 13:14:33 +03:00
xXlokerXx
6d8fcd90c1 Fix sintax error 2019-07-08 19:48:09 -05:00
xXlokerXx
aa459c1b66 Fix sintaxis error 2019-07-08 19:19:33 -05:00
xXlokerXx
0b23b3e494 Fix complexity 2019-07-08 18:38:10 -05:00
xXlokerXx
3f65e1ef41 acept slash and inverted slash in src path validation 2019-07-08 18:23:58 -05:00
san
7dce3d8967 fixed import "C" 2018-11-03 09:12:25 +08:00
NotZippy
5c8d5bca7f develop v1.0.0-dev 2018-10-30 06:23:53 -07:00
41 changed files with 1454 additions and 557 deletions

View File

@@ -1,13 +1,13 @@
{ {
"GOLANG": { "GOLANG": {
"ABC":[25, 35, 50, 70], "ABC":[33, 38, 50, 70],
"ARITY":[5,6,7,8], "ARITY":[5,6,7,8],
"BLOCK_NESTING":[7, 9, 11, 13], "BLOCK_NESTING":[9, 10, 12, 13],
"CYCLO":[20, 30, 45, 60], "CYCLO":[30, 35, 45, 60],
"TOO_MANY_IVARS": [20, 25, 40, 45], "TOO_MANY_IVARS": [28, 30, 40, 45],
"TOO_MANY_FUNCTIONS": [20, 30, 40, 50], "TOO_MANY_FUNCTIONS": [20, 30, 40, 50],
"TOTAL_COMPLEXITY": [150, 250, 400, 500], "TOTAL_COMPLEXITY": [150, 250, 400, 500],
"LOC": [100, 175, 250, 320], "LOC": [100, 175, 250, 320],
"TOTAL_LOC": [300, 400, 500, 600] "TOTAL_LOC": [300, 400, 500, 600]
} }
} }

View File

@@ -1,16 +1,15 @@
language: go language: go
go: go:
- "1.8.x" - "1.12.x"
- "1.9.x" # - "1.13.x"
- "1.10.x" # - "1.14.x"
- "1.11.x" # - "tip"
- "tip"
os: os:
- osx # - osx
- linux - linux
- windows # - windows
sudo: false sudo: false
@@ -19,6 +18,8 @@ branches:
- master - master
- develop - develop
env:
- GO111MODULE=on
install: install:
# Setting environments variables # Setting environments variables
@@ -26,43 +27,43 @@ install:
- export REVEL_BRANCH="develop" - export REVEL_BRANCH="develop"
- 'if [[ "$TRAVIS_BRANCH" == "master" ]]; then export REVEL_BRANCH="master"; fi' - 'if [[ "$TRAVIS_BRANCH" == "master" ]]; then export REVEL_BRANCH="master"; fi'
- 'echo "Travis branch: $TRAVIS_BRANCH, Revel dependency branch: $REVEL_BRANCH"' - 'echo "Travis branch: $TRAVIS_BRANCH, Revel dependency branch: $REVEL_BRANCH"'
- git clone -b $REVEL_BRANCH git://github.com/revel/revel ../revel/ #- git clone -b $REVEL_BRANCH git://github.com/revel/revel ../revel/
- git clone -b $REVEL_BRANCH git://github.com/revel/modules ../modules/ #- git clone -b $REVEL_BRANCH git://github.com/revel/modules ../modules/
# Since travis already checks out go build the commandline tool (revel)
- go get -t -v github.com/revel/cmd/revel - go get -t -v github.com/revel/cmd/revel
- go get -u github.com/golang/dep/cmd/dep
- echo $GOPATH - echo $GOPATH
- echo $PATH - echo $PATH
- pwd - pwd
script: script:
# Ensure the new-app flow works (plus the other commands).
#- revel version
#- revel new my/testapp
#- revel test my/testapp
#- revel clean my/testapp
#- revel build my/testapp build/testapp
#- revel build my/testapp build/testapp prod
#- revel package my/testapp
#- revel package my/testapp prod
# Ensure the new-app flow works (plus the other commands).
- revel new --gomod-flags "edit -replace=github.com/revel/revel=github.com/revel/revel@develop" -a my/testapp2 -v
- revel test --gomod-flags "edit -replace=github.com/revel/revel=github.com/revel/revel@develop" -a my/testapp2 -v
- revel clean --gomod-flags "edit -replace=github.com/revel/revel=github.com/revel/revel@develop" -a my/testapp2 -v
- revel build --gomod-flags "edit -replace=github.com/revel/revel=github.com/revel/revel@develop" -a my/testapp2 -v -t build/testapp2
- revel build --gomod-flags "edit -replace=github.com/revel/revel=github.com/revel/revel@develop" -a my/testapp2 -v -t build/testapp2 -m prod
- revel package --gomod-flags "edit -replace=github.com/revel/revel=github.com/revel/revel@develop" -a my/testapp2 -v
- revel package --gomod-flags "edit -replace=github.com/revel/revel=github.com/revel/revel@develop" -a my/testapp2 -v -m prod
- revel new --gomod-flags "edit -replace=github.com/revel/revel=github.com/revel/revel@develop" -v -a my/testapp3 -V
- revel test --gomod-flags "edit -replace=github.com/revel/revel=github.com/revel/revel@develop" -v -a my/testapp3
- revel clean --gomod-flags "edit -replace=github.com/revel/revel=github.com/revel/revel@develop" -v -a my/testapp3
- revel build --gomod-flags "edit -replace=github.com/revel/revel=github.com/revel/revel@develop" -a my/testapp3 -t build/testapp3
- revel build --gomod-flags "edit -replace=github.com/revel/revel=github.com/revel/revel@develop" -a my/testapp3 -t build/testapp3 -m prod
- revel package --gomod-flags "edit -replace=github.com/revel/revel=github.com/revel/revel@develop" -a my/testapp3
- revel package --gomod-flags "edit -replace=github.com/revel/revel=github.com/revel/revel@develop" -a my/testapp3 -m prod
- go test -v github.com/revel/cmd/revel/... - go test -v github.com/revel/cmd/revel/...
# Ensure the new-app flow works (plus the other commands).
- revel version
- revel new my/testapp
- revel test my/testapp
- revel clean my/testapp
- revel build my/testapp build/testapp
- revel build my/testapp build/testapp prod
- revel package my/testapp
- revel package my/testapp prod
# Ensure the new-app flow works (plus the other commands).
- revel new -a my/testapp2
- revel test -a my/testapp2
- revel clean -a my/testapp2
- revel build -a my/testapp2 -t build/testapp2
- revel build -a my/testapp2 -t build/testapp2 -m prod
- revel package -a my/testapp2
- revel package -a my/testapp2 -m prod
- revel new -v -a my/testapp3 -V
- revel test -v -a my/testapp3
- revel clean -v -a my/testapp3
- revel build -a my/testapp3 -t build/testapp3
- revel build -a my/testapp3 -t build/testapp3 -m prod
- revel package -a my/testapp3
- revel package -a my/testapp3 -m prod
matrix: matrix:
allow_failures: allow_failures:
- go: tip - go: tip

29
go.mod Normal file
View File

@@ -0,0 +1,29 @@
module github.com/revel/cmd
go 1.13
require (
github.com/BurntSushi/toml v0.3.1 // indirect
github.com/agtorre/gocolorize v1.0.0
github.com/fsnotify/fsnotify v1.4.7
github.com/inconshreveable/log15 v0.0.0-20200109203555-b30bc20e4fd1 // indirect
github.com/jessevdk/go-flags v1.4.0
github.com/mattn/go-colorable v0.1.4
github.com/myesui/uuid v1.0.0 // indirect
github.com/pkg/errors v0.9.1
github.com/revel/config v0.21.0
github.com/revel/log15 v2.11.20+incompatible
github.com/revel/modules v0.21.0 // indirect
github.com/revel/pathtree v0.0.0-20140121041023-41257a1839e9 // indirect
github.com/revel/revel v0.21.0
github.com/stretchr/testify v1.4.0
github.com/twinj/uuid v1.0.0 // indirect
github.com/xeonx/timeago v1.0.0-rc4 // indirect
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 // indirect
golang.org/x/tools v0.0.0-20200219054238-753a1d49df85
gopkg.in/fsnotify/fsnotify.v1 v1.4.7
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22
gopkg.in/natefinch/lumberjack.v2 v2.0.0
gopkg.in/stack.v0 v0.0.0-20141108040640-9b43fcefddd0
gopkg.in/stretchr/testify.v1 v1.2.2 // indirect
)

View File

@@ -21,15 +21,16 @@ import (
// App contains the configuration for running a Revel app. (Not for the app itself) // App contains the configuration for running a Revel app. (Not for the app itself)
// Its only purpose is constructing the command to execute. // Its only purpose is constructing the command to execute.
type App struct { type App struct {
BinaryPath string // Path to the app executable BinaryPath string // Path to the app executable
Port int // Port to pass as a command line argument. Port int // Port to pass as a command line argument.
cmd AppCmd // The last cmd returned. cmd AppCmd // The last cmd returned.
Paths *model.RevelContainer PackagePathMap map[string]string // Package to directory path map
Paths *model.RevelContainer
} }
// NewApp returns app instance with binary path in it // NewApp returns app instance with binary path in it
func NewApp(binPath string, paths *model.RevelContainer) *App { func NewApp(binPath string, paths *model.RevelContainer, packagePathMap map[string]string) *App {
return &App{BinaryPath: binPath, Paths: paths, Port: paths.HTTPPort} return &App{BinaryPath: binPath, Paths: paths, Port: paths.HTTPPort, PackagePathMap:packagePathMap}
} }
// Cmd returns a command to run the app server using the current configuration. // Cmd returns a command to run the app server using the current configuration.
@@ -64,7 +65,7 @@ func (cmd AppCmd) Start(c *model.CommandConfig) error {
listeningWriter := &startupListeningWriter{os.Stdout, make(chan bool), c, &bytes.Buffer{}} listeningWriter := &startupListeningWriter{os.Stdout, make(chan bool), c, &bytes.Buffer{}}
cmd.Stdout = listeningWriter cmd.Stdout = listeningWriter
utils.Logger.Info("Exec app:", "path", cmd.Path, "args", cmd.Args, "dir", cmd.Dir, "env", cmd.Env) utils.Logger.Info("Exec app:", "path", cmd.Path, "args", cmd.Args, "dir", cmd.Dir, "env", cmd.Env)
utils.CmdInit(cmd.Cmd, c.AppPath) utils.CmdInit(cmd.Cmd, !c.Vendored, c.AppPath)
if err := cmd.Cmd.Start(); err != nil { if err := cmd.Cmd.Start(); err != nil {
utils.Logger.Fatal("Error running:", "error", err) utils.Logger.Fatal("Error running:", "error", err)
} }
@@ -72,9 +73,9 @@ func (cmd AppCmd) Start(c *model.CommandConfig) error {
select { select {
case exitState := <-cmd.waitChan(): case exitState := <-cmd.waitChan():
fmt.Println("Startup failure view previous messages, \n Proxy is listening :", c.Run.Port) fmt.Println("Startup failure view previous messages, \n Proxy is listening :", c.Run.Port)
err := utils.NewError("","Revel Run Error", "starting your application there was an exception. See terminal output, " + exitState,"") err := utils.NewError("", "Revel Run Error", "starting your application there was an exception. See terminal output, " + exitState, "")
// TODO pretiffy command line output // TODO pretiffy command line output
// err.MetaError = listeningWriter.getLastOutput() // err.MetaError = listeningWriter.getLastOutput()
return err return err
case <-time.After(60 * time.Second): case <-time.After(60 * time.Second):
@@ -149,7 +150,7 @@ func (cmd AppCmd) Kill() {
case <-ch: case <-ch:
return return
case <-time.After(60 * time.Second): case <-time.After(60 * time.Second):
// Kill the process // Kill the process
utils.Logger.Error( utils.Logger.Error(
"Revel app failed to exit in 60 seconds - killing.", "Revel app failed to exit in 60 seconds - killing.",
"processid", cmd.Process.Pid, "processid", cmd.Process.Pid,
@@ -198,7 +199,7 @@ func (w *startupListeningWriter) Write(p []byte) (int, error) {
w.notifyReady = nil w.notifyReady = nil
} }
} }
if w.notifyReady!=nil { if w.notifyReady != nil {
w.buffer.Write(p) w.buffer.Write(p)
} }
return w.dest.Write(p) return w.dest.Write(p)

View File

@@ -19,17 +19,25 @@ import (
"time" "time"
"github.com/revel/cmd/model" "github.com/revel/cmd/model"
"github.com/revel/cmd/parser" _ "github.com/revel/cmd/parser"
"github.com/revel/cmd/utils" "github.com/revel/cmd/utils"
"github.com/revel/cmd/parser2"
"github.com/revel/cmd/parser"
) )
var importErrorPattern = regexp.MustCompile("cannot find package \"([^\"]+)\"") var importErrorPattern = regexp.MustCompile("cannot find package \"([^\"]+)\"")
type ByString []*model.TypeInfo type ByString []*model.TypeInfo
func (c ByString) Len() int { return len(c) } func (c ByString) Len() int {
func (c ByString) Swap(i, j int) { c[i], c[j] = c[j], c[i] } return len(c)
func (c ByString) Less(i, j int) bool { return c[i].String() < c[j].String() } }
func (c ByString) Swap(i, j int) {
c[i], c[j] = c[j], c[i]
}
func (c ByString) Less(i, j int) bool {
return c[i].String() < c[j].String()
}
// Build the app: // Build the app:
// 1. Generate the the main.go file. // 1. Generate the the main.go file.
@@ -40,7 +48,13 @@ func Build(c *model.CommandConfig, paths *model.RevelContainer) (_ *App, err err
// First, clear the generated files (to avoid them messing with ProcessSource). // First, clear the generated files (to avoid them messing with ProcessSource).
cleanSource(paths, "tmp", "routes") cleanSource(paths, "tmp", "routes")
sourceInfo, err := parser.ProcessSource(paths) var sourceInfo *model.SourceInfo
if c.HistoricBuildMode {
sourceInfo, err = parser.ProcessSource(paths)
} else {
sourceInfo, err = parser2.ProcessSource(paths)
}
if err != nil { if err != nil {
return return
} }
@@ -108,13 +122,8 @@ func Build(c *model.CommandConfig, paths *model.RevelContainer) (_ *App, err err
} }
} }
pkg, err := build.Default.Import(paths.ImportPath, "", build.FindOnly) // Binary path is a combination of BasePath/target/app directory, app's import path and its name.
if err != nil { binName := filepath.Join(paths.BasePath, "target", "app", paths.ImportPath, filepath.Base(paths.BasePath))
return
}
// Binary path is a combination of $GOBIN/revel.d directory, app's import path and its name.
binName := filepath.Join(pkg.BinDir, "revel.d", paths.ImportPath, filepath.Base(paths.BasePath))
// Change binary path for Windows build // Change binary path for Windows build
goos := runtime.GOOS goos := runtime.GOOS
@@ -135,8 +144,25 @@ func Build(c *model.CommandConfig, paths *model.RevelContainer) (_ *App, err err
return false return false
} }
if len(c.GoModFlags) > 0 {
for _, gomod := range c.GoModFlags {
goModCmd := exec.Command(goPath, append([]string{"mod"}, strings.Split(gomod, " ")...)...)
utils.CmdInit(goModCmd, !c.Vendored, c.AppPath)
output, err := goModCmd.CombinedOutput()
utils.Logger.Infof("Gomod applied ", "output", string(output))
// If the build succeeded, we're done.
if err != nil {
utils.Logger.Error("Gomod Failed continuing ", "error", err, "output", string(output))
}
}
}
for { for {
appVersion := getAppVersion(paths) appVersion := getAppVersion(paths)
if appVersion == "" {
appVersion = "noVersionProvided"
}
buildTime := time.Now().UTC().Format(time.RFC3339) buildTime := time.Now().UTC().Format(time.RFC3339)
versionLinkerFlags := fmt.Sprintf("-X %s/app.AppVersion=%s -X %s/app.BuildTime=%s", versionLinkerFlags := fmt.Sprintf("-X %s/app.AppVersion=%s -X %s/app.BuildTime=%s",
@@ -147,7 +173,6 @@ func Build(c *model.CommandConfig, paths *model.RevelContainer) (_ *App, err err
if len(c.BuildFlags) == 0 { if len(c.BuildFlags) == 0 {
flags = []string{ flags = []string{
"build", "build",
"-i",
"-ldflags", versionLinkerFlags, "-ldflags", versionLinkerFlags,
"-tags", buildTags, "-tags", buildTags,
"-o", binName} "-o", binName}
@@ -157,7 +182,12 @@ func Build(c *model.CommandConfig, paths *model.RevelContainer) (_ *App, err err
} }
flags = append(flags, c.BuildFlags...) flags = append(flags, c.BuildFlags...)
if !contains(flags, "-ldflags") { if !contains(flags, "-ldflags") {
flags = append(flags, "-ldflags", versionLinkerFlags) ldflags := "-ldflags= " + versionLinkerFlags
// Add in build flags
for i := range c.BuildFlags {
ldflags += "-X '" + c.BuildFlags[i] + "'"
}
flags = append(flags, ldflags)
} }
if !contains(flags, "-tags") { if !contains(flags, "-tags") {
flags = append(flags, "-tags", buildTags) flags = append(flags, "-tags", buildTags)
@@ -170,31 +200,35 @@ func Build(c *model.CommandConfig, paths *model.RevelContainer) (_ *App, err err
// Add in build flags // Add in build flags
flags = append(flags, c.BuildFlags...) flags = append(flags, c.BuildFlags...)
// This is Go main path
gopath := c.GoPath
for _, o := range paths.ModulePathMap {
gopath += string(filepath.ListSeparator) + o
}
// Note: It's not applicable for filepath.* usage // Note: It's not applicable for filepath.* usage
flags = append(flags, path.Join(paths.ImportPath, "app", "tmp")) flags = append(flags, path.Join(paths.ImportPath, "app", "tmp"))
buildCmd := exec.Command(goPath, flags...) buildCmd := exec.Command(goPath, flags...)
buildCmd.Env = append(os.Environ(), if !c.Vendored {
"GOPATH="+gopath, // This is Go main path
) gopath := c.GoPath
utils.CmdInit(buildCmd, c.AppPath) for _, o := range paths.ModulePathMap {
utils.Logger.Info("Exec:", "args", buildCmd.Args) gopath += string(filepath.ListSeparator) + o.Path
}
buildCmd.Env = append(os.Environ(),
"GOPATH=" + gopath,
)
}
utils.CmdInit(buildCmd, !c.Vendored, c.AppPath)
utils.Logger.Info("Exec:", "args", buildCmd.Args, "working dir", buildCmd.Dir)
output, err := buildCmd.CombinedOutput() output, err := buildCmd.CombinedOutput()
// If the build succeeded, we're done. // If the build succeeded, we're done.
if err == nil { if err == nil {
utils.Logger.Info("Build successful continuing") utils.Logger.Info("Build successful continuing")
return NewApp(binName, paths), nil return NewApp(binName, paths, sourceInfo.PackageMap), nil
} }
// Since there was an error, capture the output in case we need to report it // Since there was an error, capture the output in case we need to report it
stOutput := string(output) stOutput := string(output)
utils.Logger.Infof("Got error on build of app %s", stOutput)
// See if it was an import error that we can go get. // See if it was an import error that we can go get.
matches := importErrorPattern.FindAllStringSubmatch(stOutput, -1) matches := importErrorPattern.FindAllStringSubmatch(stOutput, -1)
@@ -246,7 +280,7 @@ func getAppVersion(paths *model.RevelContainer) string {
if (err != nil && os.IsNotExist(err)) || !info.IsDir() { if (err != nil && os.IsNotExist(err)) || !info.IsDir() {
return "" return ""
} }
gitCmd := exec.Command(gitPath, "--git-dir="+gitDir, "--work-tree="+paths.BasePath, "describe", "--always", "--dirty") gitCmd := exec.Command(gitPath, "--git-dir=" + gitDir, "--work-tree=" + paths.BasePath, "describe", "--always", "--dirty")
utils.Logger.Info("Exec:", "args", gitCmd.Args) utils.Logger.Info("Exec:", "args", gitCmd.Args)
output, err := gitCmd.Output() output, err := gitCmd.Output()
@@ -376,7 +410,7 @@ func containsValue(m map[string]string, val string) bool {
// Parse the output of the "go build" command. // Parse the output of the "go build" command.
// Return a detailed Error. // Return a detailed Error.
func newCompileError(paths *model.RevelContainer, output []byte) *utils.Error { func newCompileError(paths *model.RevelContainer, output []byte) *utils.SourceError {
errorMatch := regexp.MustCompile(`(?m)^([^:#]+):(\d+):(\d+:)? (.*)$`). errorMatch := regexp.MustCompile(`(?m)^([^:#]+):(\d+):(\d+:)? (.*)$`).
FindSubmatch(output) FindSubmatch(output)
if errorMatch == nil { if errorMatch == nil {
@@ -384,7 +418,7 @@ func newCompileError(paths *model.RevelContainer, output []byte) *utils.Error {
if errorMatch == nil { if errorMatch == nil {
utils.Logger.Error("Failed to parse build errors", "error", string(output)) utils.Logger.Error("Failed to parse build errors", "error", string(output))
return &utils.Error{ return &utils.SourceError{
SourceType: "Go code", SourceType: "Go code",
Title: "Go Compilation Error", Title: "Go Compilation Error",
Description: "See console for build error.", Description: "See console for build error.",
@@ -400,7 +434,7 @@ func newCompileError(paths *model.RevelContainer, output []byte) *utils.Error {
// Extract the paths from the gopaths, and search for file there first // Extract the paths from the gopaths, and search for file there first
gopaths := filepath.SplitList(build.Default.GOPATH) gopaths := filepath.SplitList(build.Default.GOPATH)
for _, gp := range gopaths { for _, gp := range gopaths {
newPath := filepath.Join(gp,"src", paths.ImportPath, relFilename) newPath := filepath.Join(gp, "src", paths.ImportPath, relFilename)
println(newPath) println(newPath)
if utils.Exists(newPath) { if utils.Exists(newPath) {
return newPath return newPath
@@ -414,11 +448,11 @@ func newCompileError(paths *model.RevelContainer, output []byte) *utils.Error {
// Read the source for the offending file. // Read the source for the offending file.
var ( var (
relFilename = string(errorMatch[1]) // e.g. "src/revel/sample/app/controllers/app.go" relFilename = string(errorMatch[1]) // e.g. "src/revel/sample/app/controllers/app.go"
absFilename = findInPaths(relFilename) absFilename = findInPaths(relFilename)
line, _ = strconv.Atoi(string(errorMatch[2])) line, _ = strconv.Atoi(string(errorMatch[2]))
description = string(errorMatch[4]) description = string(errorMatch[4])
compileError = &utils.Error{ compileError = &utils.SourceError{
SourceType: "Go code", SourceType: "Go code",
Title: "Go Compilation Error", Title: "Go Compilation Error",
Path: relFilename, Path: relFilename,
@@ -436,7 +470,7 @@ func newCompileError(paths *model.RevelContainer, output []byte) *utils.Error {
fileStr, err := utils.ReadLines(absFilename) fileStr, err := utils.ReadLines(absFilename)
if err != nil { if err != nil {
compileError.MetaError = absFilename + ": " + err.Error() compileError.MetaError = absFilename + ": " + err.Error()
utils.Logger.Info("Unable to readlines "+compileError.MetaError, "error", err) utils.Logger.Info("Unable to readlines " + compileError.MetaError, "error", err)
return compileError return compileError
} }

View File

@@ -34,6 +34,7 @@ import (
"html/template" "html/template"
"io/ioutil" "io/ioutil"
"sync" "sync"
"encoding/json"
) )
var ( var (
@@ -89,12 +90,12 @@ func (h *Harness) renderError(iw http.ResponseWriter, ir *http.Request, err erro
fmt.Fprintf(iw, "An error ocurred %s", err.Error()) fmt.Fprintf(iw, "An error ocurred %s", err.Error())
return return
} }
var revelError *utils.Error var revelError *utils.SourceError
switch e := err.(type) { switch e := err.(type) {
case *utils.Error: case *utils.SourceError:
revelError = e revelError = e
case error: case error:
revelError = &utils.Error{ revelError = &utils.SourceError{
Title: "Server Error", Title: "Server Error",
Description: e.Error(), Description: e.Error(),
} }
@@ -161,6 +162,7 @@ func NewHarness(c *model.CommandConfig, paths *model.RevelContainer, runMode str
addr := paths.HTTPAddr addr := paths.HTTPAddr
port := paths.Config.IntDefault("harness.port", 0) port := paths.Config.IntDefault("harness.port", 0)
scheme := "http" scheme := "http"
if paths.HTTPSsl { if paths.HTTPSsl {
scheme = "https" scheme = "https"
} }
@@ -199,7 +201,7 @@ func NewHarness(c *model.CommandConfig, paths *model.RevelContainer, runMode str
// Refresh method rebuilds the Revel application and run it on the given port. // Refresh method rebuilds the Revel application and run it on the given port.
// called by the watcher // called by the watcher
func (h *Harness) Refresh() (err *utils.Error) { func (h *Harness) Refresh() (err *utils.SourceError) {
// Allow only one thread to rebuild the process // Allow only one thread to rebuild the process
// If multiple requests to rebuild are queued only the last one is executed on // If multiple requests to rebuild are queued only the last one is executed on
// So before a build is started we wait for a second to determine if // So before a build is started we wait for a second to determine if
@@ -217,10 +219,10 @@ func (h *Harness) Refresh() (err *utils.Error) {
h.app, newErr = Build(h.config, h.paths) h.app, newErr = Build(h.config, h.paths)
if newErr != nil { if newErr != nil {
utils.Logger.Error("Build detected an error", "error", newErr) utils.Logger.Error("Build detected an error", "error", newErr)
if castErr, ok := newErr.(*utils.Error); ok { if castErr, ok := newErr.(*utils.SourceError); ok {
return castErr return castErr
} }
err = &utils.Error{ err = &utils.SourceError{
Title: "App failed to start up", Title: "App failed to start up",
Description: err.Error(), Description: err.Error(),
} }
@@ -229,12 +231,22 @@ func (h *Harness) Refresh() (err *utils.Error) {
if h.useProxy { if h.useProxy {
h.app.Port = h.port h.app.Port = h.port
if err2 := h.app.Cmd(h.runMode).Start(h.config); err2 != nil { runMode := h.runMode
if !h.config.HistoricMode {
// Recalulate run mode based on the config
var paths []byte
if len(h.app.PackagePathMap)>0 {
paths, _ = json.Marshal(h.app.PackagePathMap)
}
runMode = fmt.Sprintf(`{"mode":"%s", "specialUseFlag":%v,"packagePathMap":%s}`, h.app.Paths.RunMode, h.config.Verbose, string(paths))
}
if err2 := h.app.Cmd(runMode).Start(h.config); err2 != nil {
utils.Logger.Error("Could not start application", "error", err2) utils.Logger.Error("Could not start application", "error", err2)
if err,k :=err2.(*utils.Error);k { if err,k :=err2.(*utils.SourceError);k {
return err return err
} }
return &utils.Error{ return &utils.SourceError{
Title: "App failed to start up", Title: "App failed to start up",
Description: err2.Error(), Description: err2.Error(),
} }

View File

@@ -81,7 +81,7 @@ func (rl *RevelLogger) SetStackDepth(amount int) MultiLogger {
// Create a new logger // Create a new logger
func New(ctx ...interface{}) MultiLogger { func New(ctx ...interface{}) MultiLogger {
r := &RevelLogger{Logger: log15.New(ctx...)} r := &RevelLogger{Logger: log15.New(ctx...)}
r.SetStackDepth(1) r.SetStackDepth(0)
return r return r
} }
@@ -99,7 +99,7 @@ func (c callHandler) Log(log *log15.Record) error {
ctx := log.Ctx ctx := log.Ctx
var ctxMap ContextMap var ctxMap ContextMap
if len(ctx) > 0 { if len(ctx) > 0 {
ctxMap = make(ContextMap, len(ctx)/2) ctxMap = make(ContextMap, len(ctx) / 2)
for i := 0; i < len(ctx); i += 2 { for i := 0; i < len(ctx); i += 2 {
v := ctx[i] v := ctx[i]
@@ -108,8 +108,8 @@ func (c callHandler) Log(log *log15.Record) error {
key = fmt.Sprintf("LOGGER_INVALID_KEY %v", v) key = fmt.Sprintf("LOGGER_INVALID_KEY %v", v)
} }
var value interface{} var value interface{}
if len(ctx) > i+1 { if len(ctx) > i + 1 {
value = ctx[i+1] value = ctx[i + 1]
} else { } else {
value = "LOGGER_VALUE_MISSING" value = "LOGGER_VALUE_MISSING"
} }

10
model/command/build.go Normal file
View File

@@ -0,0 +1,10 @@
package command
type (
Build struct {
ImportCommand
TargetPath string `short:"t" long:"target-path" description:"Path to target folder. Folder will be completely deleted if it exists" required:"false"`
Mode string `short:"m" long:"run-mode" description:"The mode to run the application in"`
CopySource bool `short:"s" long:"include-source" description:"Copy the source code as well"`
}
)

6
model/command/clean.go Normal file
View File

@@ -0,0 +1,6 @@
package command
type (
Clean struct {
ImportCommand
}
)

View File

@@ -0,0 +1,7 @@
package command
type (
ImportCommand struct {
ImportPath string `short:"a" long:"application-path" description:"Path to application folder" required:"false"`
}
)

14
model/command/new.go Normal file
View File

@@ -0,0 +1,14 @@
package command
type (
New struct {
ImportCommand
SkeletonPath string `short:"s" long:"skeleton" description:"Path to skeleton folder (Must exist on GO PATH)" required:"false"`
Package string `short:"p" long:"package" description:"The package name, this becomes the repfix to the app name, if defined vendored is set to true" required:"false"`
NotVendored bool `short:"V" long:"vendor" description:"True if project should not be configured with a go.mod"`
Run bool `short:"r" long:"run" description:"True if you want to run the application right away"`
Callback func() error
}
)

9
model/command/package.go Normal file
View File

@@ -0,0 +1,9 @@
package command
type (
Package struct {
ImportCommand
TargetPath string `short:"t" long:"target-path" description:"Full path and filename of target package to deploy" required:"false"`
Mode string `short:"m" long:"run-mode" description:"The mode to run the application in"`
CopySource bool `short:"s" long:"include-source" description:"Copy the source code as well"`
}
)

9
model/command/run.go Normal file
View File

@@ -0,0 +1,9 @@
package command
type (
Run struct {
ImportCommand
Mode string `short:"m" long:"run-mode" description:"The mode to run the application in"`
Port int `short:"p" long:"port" default:"-1" description:"The port to listen" `
NoProxy bool `short:"n" long:"no-proxy" description:"True if proxy server should not be started. This will only update the main and routes files on change"`
}
)

View File

@@ -0,0 +1,9 @@
package command
type (
Test struct {
ImportCommand
Mode string `short:"m" long:"run-mode" description:"The mode to run the application in"`
Function string `short:"f" long:"suite-function" description:"The suite.function"`
}
)

8
model/command/version.go Normal file
View File

@@ -0,0 +1,8 @@
package command
type (
Version struct {
ImportCommand
Update bool `short:"u" long:"update" description:"Update the framework and modules" required:"false"`
UpdateVersion string `long:"update-version" description:"Specify the version the revel and app will be switched to" required:"false"`
}
)

View File

@@ -3,7 +3,6 @@ package model
import ( import (
"fmt" "fmt"
"github.com/revel/cmd" "github.com/revel/cmd"
"github.com/revel/cmd/logger"
"github.com/revel/cmd/utils" "github.com/revel/cmd/utils"
"go/ast" "go/ast"
"go/build" "go/build"
@@ -14,6 +13,7 @@ import (
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/revel/cmd/model/command"
) )
// The constants // The constants
@@ -33,63 +33,29 @@ type (
// The Command config for the line input // The Command config for the line input
CommandConfig struct { CommandConfig struct {
Index COMMAND // The index Index COMMAND // The index
Verbose []bool `short:"v" long:"debug" description:"If set the logger is set to verbose"` // True if debug is active Verbose []bool `short:"v" long:"debug" description:"If set the logger is set to verbose"` // True if debug is active
FrameworkVersion *Version // The framework version FrameworkVersion *Version // The framework version
CommandVersion *Version // The command version CommandVersion *Version // The command version
HistoricMode bool `long:"historic-run-mode" description:"If set the runmode is passed a string not json"` // True if debug is active HistoricMode bool `long:"historic-run-mode" description:"If set the runmode is passed a string not json"` // True if debug is active
ImportPath string // The import path (relative to a GOPATH) ImportPath string // The import path (relative to a GOPATH)
GoPath string // The GoPath GoPath string // The GoPath
GoCmd string // The full path to the go executable GoCmd string // The full path to the go executable
SrcRoot string // The source root SrcRoot string // The source root
AppPath string // The application path (absolute) AppPath string // The application path (absolute)
AppName string // The application name AppName string // The application name
Vendored bool // True if the application is vendored HistoricBuildMode bool `long:"historic-build-mode" description:"If set the code is scanned using the original parsers, not the go.1.11+"` // True if debug is active
PackageResolver func(pkgName string) error // a packge resolver for the config Vendored bool // True if the application is vendored
BuildFlags []string `short:"X" long:"build-flags" description:"These flags will be used when building the application. May be specified multiple times, only applicable for Build, Run, Package, Test commands"` PackageResolver func(pkgName string) error // a package resolver for the config
// The new command BuildFlags []string `short:"X" long:"build-flags" description:"These flags will be used when building the application. May be specified multiple times, only applicable for Build, Run, Package, Test commands"`
New struct { GoModFlags []string `long:"gomod-flags" description:"These flags will execut go mod commands for each flag, this happens during the build process"`
ImportPath string `short:"a" long:"application-path" description:"Path to application folder" required:"false"` New command.New `command:"new"`
SkeletonPath string `short:"s" long:"skeleton" description:"Path to skeleton folder (Must exist on GO PATH)" required:"false"` Build command.Build `command:"build"`
Vendored bool `short:"V" long:"vendor" description:"True if project should contain a vendor folder to be initialized. Creates the vendor folder and the 'Gopkg.toml' file in the root"` Run command.Run `command:"run"`
Run bool `short:"r" long:"run" description:"True if you want to run the application right away"` Package command.Package `command:"package"`
} `command:"new"` Clean command.Clean `command:"clean"`
// The build command Test command.Test `command:"test"`
Build struct { Version command.Version `command:"version"`
TargetPath string `short:"t" long:"target-path" description:"Path to target folder. Folder will be completely deleted if it exists" required:"false"`
ImportPath string `short:"a" long:"application-path" description:"Path to application folder" required:"false"`
Mode string `short:"m" long:"run-mode" description:"The mode to run the application in"`
CopySource bool `short:"s" long:"include-source" description:"Copy the source code as well"`
} `command:"build"`
// The run command
Run struct {
ImportPath string `short:"a" long:"application-path" description:"Path to application folder" required:"false"`
Mode string `short:"m" long:"run-mode" description:"The mode to run the application in"`
Port int `short:"p" long:"port" default:"-1" description:"The port to listen" `
NoProxy bool `short:"n" long:"no-proxy" description:"True if proxy server should not be started. This will only update the main and routes files on change"`
} `command:"run"`
// The package command
Package struct {
TargetPath string `short:"t" long:"target-path" description:"Full path and filename of target package to deploy" required:"false"`
Mode string `short:"m" long:"run-mode" description:"The mode to run the application in"`
ImportPath string `short:"a" long:"application-path" description:"Path to application folder" required:"false"`
CopySource bool `short:"s" long:"include-source" description:"Copy the source code as well"`
} `command:"package"`
// The clean command
Clean struct {
ImportPath string `short:"a" long:"application-path" description:"Path to application folder" required:"false"`
} `command:"clean"`
// The test command
Test struct {
Mode string `short:"m" long:"run-mode" description:"The mode to run the application in"`
ImportPath string `short:"a" long:"application-path" description:"Path to application folder" required:"false"`
Function string `short:"f" long:"suite-function" description:"The suite.function"`
} `command:"test"`
// The version command
Version struct {
ImportPath string `short:"a" long:"application-path" description:"Path to application folder" required:"false"`
Update bool `short:"u" long:"update" description:"Update the framework and modules" required:"false"`
} `command:"version"`
} }
) )
@@ -102,14 +68,19 @@ func (c *CommandConfig) UpdateImportPath() error {
importPath = c.New.ImportPath importPath = c.New.ImportPath
case RUN: case RUN:
importPath = c.Run.ImportPath importPath = c.Run.ImportPath
c.Vendored = utils.Exists(filepath.Join(importPath, "go.mod"))
case BUILD: case BUILD:
importPath = c.Build.ImportPath importPath = c.Build.ImportPath
c.Vendored = utils.Exists(filepath.Join(importPath, "go.mod"))
case PACKAGE: case PACKAGE:
importPath = c.Package.ImportPath importPath = c.Package.ImportPath
c.Vendored = utils.Exists(filepath.Join(importPath, "go.mod"))
case CLEAN: case CLEAN:
importPath = c.Clean.ImportPath importPath = c.Clean.ImportPath
c.Vendored = utils.Exists(filepath.Join(importPath, "go.mod"))
case TEST: case TEST:
importPath = c.Test.ImportPath importPath = c.Test.ImportPath
c.Vendored = utils.Exists(filepath.Join(importPath, "go.mod"))
case VERSION: case VERSION:
importPath = c.Version.ImportPath importPath = c.Version.ImportPath
required = false required = false
@@ -131,10 +102,10 @@ func (c *CommandConfig) UpdateImportPath() error {
if err == nil { if err == nil {
for _, path := range strings.Split(build.Default.GOPATH, string(filepath.ListSeparator)) { for _, path := range strings.Split(build.Default.GOPATH, string(filepath.ListSeparator)) {
utils.Logger.Infof("Checking import path %s with %s", currentPath, path) utils.Logger.Infof("Checking import path %s with %s", currentPath, path)
if strings.HasPrefix(currentPath, path) && len(currentPath) > len(path)+1 { if strings.HasPrefix(currentPath, path) && len(currentPath) > len(path) + 1 {
importPath = currentPath[len(path)+1:] importPath = currentPath[len(path) + 1:]
// Remove the source from the path if it is there // Remove the source from the path if it is there
if len(importPath) > 4 && strings.ToLower(importPath[0:4]) == "src/" { if len(importPath) > 4 && (strings.ToLower(importPath[0:4]) == "src/" || strings.ToLower(importPath[0:4]) == "src\\") {
importPath = importPath[4:] importPath = importPath[4:]
} else if importPath == "src" { } else if importPath == "src" {
if c.Index != VERSION { if c.Index != VERSION {
@@ -149,12 +120,14 @@ func (c *CommandConfig) UpdateImportPath() error {
} }
c.ImportPath = importPath c.ImportPath = importPath
utils.Logger.Info("Returned import path", "path", importPath, "buildpath", build.Default.GOPATH) // We need the source root determined at this point to check the setversions
c.initAppFolder()
utils.Logger.Info("Returned import path", "path", importPath)
if required && c.Index != NEW { if required && c.Index != NEW {
if err := c.SetVersions(); err != nil { if err := c.SetVersions(); err != nil {
utils.Logger.Panic("Failed to fetch revel versions", "error", err) utils.Logger.Panic("Failed to fetch revel versions", "error", err)
} }
if err:=c.FrameworkVersion.CompatibleFramework(c);err!=nil { if err := c.FrameworkVersion.CompatibleFramework(c); err != nil {
utils.Logger.Fatal("Compatibility Error", "message", err, utils.Logger.Fatal("Compatibility Error", "message", err,
"Revel framework version", c.FrameworkVersion.String(), "Revel tool version", c.CommandVersion.String()) "Revel framework version", c.FrameworkVersion.String(), "Revel tool version", c.CommandVersion.String())
} }
@@ -163,81 +136,149 @@ func (c *CommandConfig) UpdateImportPath() error {
if !required { if !required {
return nil return nil
} }
if len(importPath) == 0 { if len(importPath) == 0 {
return fmt.Errorf("Unable to determine import path from : %s", importPath) return fmt.Errorf("Unable to determine import path from : %s", importPath)
} }
return nil return nil
} }
// Used to initialize the package resolver func (c *CommandConfig) initAppFolder() (err error) {
func (c *CommandConfig) InitPackageResolver() { utils.Logger.Info("initAppFolder", "vendored", c.Vendored)
c.Vendored = utils.DirExists(filepath.Join(c.AppPath, "vendor"))
if c.Index == NEW && c.New.Vendored { // check for go executable
c.Vendored = true c.GoCmd, err = exec.LookPath("go")
if err != nil {
utils.Logger.Fatal("Go executable not found in PATH.")
} }
utils.Logger.Info("InitPackageResolver", "useVendor", c.Vendored, "path", c.AppPath) // First try to determine where the application is located - this should be the import value
appFolder := c.ImportPath
wd, err := os.Getwd()
if len(appFolder) == 0 {
// We will assume the working directory is the appFolder
appFolder = wd
} else if strings.LastIndex(wd, appFolder) == len(wd) - len(appFolder) {
// Check for existence of an /app folder
if utils.Exists(filepath.Join(wd, "app")) {
appFolder = wd
} else {
appFolder = filepath.Join(wd, appFolder)
}
} else if strings.Contains(appFolder, ".") {
appFolder = filepath.Join(wd, filepath.Base(c.ImportPath))
} else if !filepath.IsAbs(appFolder) {
appFolder = filepath.Join(wd, appFolder)
}
var ( utils.Logger.Info("Determined app folder to be", "appfolder", appFolder, "working", wd, "importPath", c.ImportPath)
depPath string
err error
)
if c.Vendored { // Use app folder to read the go.mod if it exists and extract the package information
utils.Logger.Info("Vendor folder detected, scanning for deps in path") goModFile := filepath.Join(appFolder, "go.mod")
depPath, err = exec.LookPath("dep") if utils.Exists(goModFile) {
c.Vendored = true
file, err := ioutil.ReadFile(goModFile)
if err != nil { if err != nil {
// Do not halt build unless a new package needs to be imported return err
utils.Logger.Fatal("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. " + for _, line := range strings.Split(string(file), "\n") {
"You can install the `dep` tool by doing a `go get -u github.com/golang/dep/cmd/dep`") if strings.Index(line, "module ") == 0 {
c.ImportPath = strings.TrimSpace(strings.Split(line, "module")[1])
c.AppPath = appFolder
c.SrcRoot = appFolder
utils.Logger.Info("Set application path and package based on go mod", "path", c.AppPath, "sourceroot", c.SrcRoot)
return nil
}
} }
} }
utils.Logger.Debug("Trying to set path based on gopath")
// 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.")
}
// revel/revel#1004 choose go path relative to current working directory
// What we want to do is to add the import to the end of the
// gopath, and discover which import exists - If none exist this is an error except in the case
// where we are dealing with new which is a special case where we will attempt to target the working directory first
workingDir, _ := os.Getwd()
goPathList := filepath.SplitList(c.GoPath)
bestpath := ""
if !c.Vendored {
for _, path := range goPathList {
if c.Index == NEW {
// If the GOPATH is part of the working dir this is the most likely target
if strings.HasPrefix(workingDir, path) {
bestpath = path
}
} else {
if utils.Exists(filepath.Join(path, "src", c.ImportPath)) {
c.SrcRoot = path
break
}
}
}
if len(c.SrcRoot) == 0 && len(bestpath) > 0 {
c.SrcRoot = bestpath
}
} else {
c.SrcRoot = appFolder
}
utils.Logger.Info("Source root", "path", c.SrcRoot, "cwd", workingDir, "gopath", c.GoPath, "bestpath", bestpath)
// If source root is empty and this isn't a version then skip it
if len(c.SrcRoot) == 0 {
if c.Index == NEW {
c.SrcRoot = c.New.ImportPath
} else {
if c.Index != VERSION {
utils.Logger.Fatal("Abort: could not create a Revel application outside of GOPATH.")
}
return nil
}
}
// set go src path
if c.Vendored {
c.AppPath = c.SrcRoot
} else {
c.SrcRoot = filepath.Join(c.SrcRoot, "src")
c.AppPath = filepath.Join(c.SrcRoot, filepath.FromSlash(c.ImportPath))
}
utils.Logger.Info("Set application path", "path", c.AppPath)
return nil
}
// Used to initialize the package resolver
func (c *CommandConfig) InitPackageResolver() {
utils.Logger.Info("InitPackageResolver", "useVendor", c.Vendored, "path", c.AppPath)
// This should get called when needed // This should get called when needed
c.PackageResolver = func(pkgName string) error { c.PackageResolver = func(pkgName string) error {
//useVendor := utils.DirExists(filepath.Join(c.AppPath, "vendor")) //useVendor := utils.DirExists(filepath.Join(c.AppPath, "vendor"))
var getCmd *exec.Cmd //var getCmd *exec.Cmd
utils.Logger.Info("Request for package ", "package", pkgName, "use vendor", c.Vendored) utils.Logger.Info("Request for package ", "package", pkgName, "use vendor", c.Vendored)
if c.Vendored { if c.Vendored {
utils.Logger.Info("Using dependency manager to import package", "package", pkgName) goModCmd := exec.Command("go", "mod", "tidy")
utils.CmdInit(goModCmd, !c.Vendored, c.AppPath)
if depPath == "" { goModCmd.Run()
utils.Logger.Error("Build: Vendor folder found, but the `dep` tool was not found, " + return nil
"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")
utils.Logger.Error("Missing package", "package", pkgName)
return fmt.Errorf("Missing package %s", pkgName)
}
// Check to see if the package exists locally
_, err := build.Import(pkgName, c.AppPath, build.FindOnly)
if err != nil {
getCmd = exec.Command(depPath, "ensure", "-add", pkgName)
} else {
getCmd = exec.Command(depPath, "ensure", "-update", pkgName)
}
} else {
utils.Logger.Info("No vendor folder detected, not using dependency manager to import package", "package", pkgName)
getCmd = exec.Command(c.GoCmd, "get", "-u", pkgName)
} }
utils.CmdInit(getCmd, c.AppPath) return nil
utils.Logger.Info("Go get command ", "exec", getCmd.Path, "dir", getCmd.Dir, "args", getCmd.Args, "env", getCmd.Env, "package", pkgName)
output, err := getCmd.CombinedOutput()
if err != nil {
fmt.Printf("Error stack %v\n", logger.NewCallStack())
utils.Logger.Error("Failed to import package", "error", err, "gopath", build.Default.GOPATH, "GO-ROOT", build.Default.GOROOT, "output", string(output))
}
return err
} }
} }
// lookup and set Go related variables // lookup and set Go related variables
func (c *CommandConfig) InitGoPaths() { func (c *CommandConfig) InitGoPathsOld() {
utils.Logger.Info("InitGoPaths") utils.Logger.Info("InitGoPaths")
// lookup go path // lookup go path
c.GoPath = build.Default.GOPATH c.GoPath = build.Default.GOPATH
@@ -275,17 +316,21 @@ func (c *CommandConfig) InitGoPaths() {
} }
} }
utils.Logger.Info("Source root", "path", c.SrcRoot, "cwd", workingDir, "gopath", c.GoPath, "bestpath",bestpath) utils.Logger.Info("Source root", "path", c.SrcRoot, "cwd", workingDir, "gopath", c.GoPath, "bestpath", bestpath)
if len(c.SrcRoot) == 0 && len(bestpath) > 0 { if len(c.SrcRoot) == 0 && len(bestpath) > 0 {
c.SrcRoot = bestpath c.SrcRoot = bestpath
} }
// If source root is empty and this isn't a version then skip it // If source root is empty and this isn't a version then skip it
if len(c.SrcRoot) == 0 { if len(c.SrcRoot) == 0 {
if c.Index != VERSION { if c.Index == NEW {
utils.Logger.Fatal("Abort: could not create a Revel application outside of GOPATH.") c.SrcRoot = c.New.ImportPath
} else {
if c.Index != VERSION {
utils.Logger.Fatal("Abort: could not create a Revel application outside of GOPATH.")
}
return
} }
return
} }
// set go src path // set go src path
@@ -298,14 +343,14 @@ func (c *CommandConfig) InitGoPaths() {
// Sets the versions on the command config // Sets the versions on the command config
func (c *CommandConfig) SetVersions() (err error) { func (c *CommandConfig) SetVersions() (err error) {
c.CommandVersion, _ = ParseVersion(cmd.Version) c.CommandVersion, _ = ParseVersion(cmd.Version)
_, revelPath, err := utils.FindSrcPaths(c.ImportPath, RevelImportPath, c.PackageResolver) pathMap, err := utils.FindSrcPaths(c.AppPath, []string{RevelImportPath}, c.PackageResolver)
if err == nil { if err == nil {
utils.Logger.Info("Fullpath to revel", "dir", revelPath) utils.Logger.Info("Fullpath to revel", "dir", pathMap[RevelImportPath])
fset := token.NewFileSet() // positions are relative to fset fset := token.NewFileSet() // positions are relative to fset
versionData, err := ioutil.ReadFile(filepath.Join(revelPath, RevelImportPath, "version.go")) versionData, err := ioutil.ReadFile(filepath.Join(pathMap[RevelImportPath], "version.go"))
if err != nil { if err != nil {
utils.Logger.Error("Failed to find Revel version:", "error", err, "path", revelPath) utils.Logger.Error("Failed to find Revel version:", "error", err, "path", pathMap[RevelImportPath])
} }
// Parse src but stop after processing the imports. // Parse src but stop after processing the imports.

View File

@@ -4,13 +4,12 @@ package model
import ( import (
"github.com/revel/cmd/utils" "github.com/revel/cmd/utils"
"github.com/revel/config" "github.com/revel/config"
"go/build"
"errors" "errors"
"fmt" "fmt"
"path/filepath" "path/filepath"
"sort" "sort"
"strings" "strings"
"golang.org/x/tools/go/packages"
) )
type ( type (
@@ -65,7 +64,11 @@ type (
CookieSecure bool // True if cookie is secure CookieSecure bool // True if cookie is secure
SecretStr string // The secret string SecretStr string // The secret string
MimeConfig *config.Context // The mime configuration MimeConfig *config.Context // The mime configuration
ModulePathMap map[string]string // The module path map ModulePathMap map[string]*ModuleInfo // The module path map
}
ModuleInfo struct {
ImportPath string
Path string
} }
WrappedRevelCallback struct { WrappedRevelCallback struct {
@@ -96,30 +99,22 @@ var RevelModulesImportPath = "github.com/revel/modules"
// This function returns a container object describing the revel application // This function returns a container object describing the revel application
// eventually this type of function will replace the global variables. // eventually this type of function will replace the global variables.
func NewRevelPaths(mode, importPath, srcPath string, callback RevelCallback) (rp *RevelContainer, err error) { func NewRevelPaths(mode, importPath, appSrcPath string, callback RevelCallback) (rp *RevelContainer, err error) {
rp = &RevelContainer{ModulePathMap: map[string]string{}} rp = &RevelContainer{ModulePathMap: map[string]*ModuleInfo{}}
// Ignore trailing slashes. // Ignore trailing slashes.
rp.ImportPath = strings.TrimRight(importPath, "/") rp.ImportPath = strings.TrimRight(importPath, "/")
rp.SourcePath = srcPath rp.SourcePath = appSrcPath
rp.RunMode = mode rp.RunMode = mode
// If the SourcePath is not specified, find it using build.Import. // We always need to determine the paths for files
var revelSourcePath string // may be different from the app source path pathMap, err := utils.FindSrcPaths(appSrcPath, []string{importPath+"/app", RevelImportPath}, callback.PackageResolver)
if rp.SourcePath == "" { if err != nil {
rp.SourcePath, revelSourcePath, err = utils.FindSrcPaths(importPath, RevelImportPath, callback.PackageResolver) return
if err != nil {
return
}
} else {
// If the SourcePath was specified, assume both Revel and the app are within it.
rp.SourcePath = filepath.Clean(rp.SourcePath)
revelSourcePath = rp.SourcePath
} }
rp.AppPath, rp.RevelPath = pathMap[importPath], pathMap[RevelImportPath]
// Setup paths for application // Setup paths for application
rp.RevelPath = filepath.Join(revelSourcePath, filepath.FromSlash(RevelImportPath)) rp.BasePath = rp.SourcePath
rp.BasePath = filepath.Join(rp.SourcePath, filepath.FromSlash(importPath)) rp.PackageInfo.Vendor = utils.Exists(filepath.Join(rp.BasePath, "go.mod"))
rp.PackageInfo.Vendor = utils.Exists(filepath.Join(rp.BasePath, "vendor"))
rp.AppPath = filepath.Join(rp.BasePath, "app") rp.AppPath = filepath.Join(rp.BasePath, "app")
// Sanity check , ensure app and conf paths exist // Sanity check , ensure app and conf paths exist
@@ -188,6 +183,7 @@ func NewRevelPaths(mode, importPath, srcPath string, callback RevelCallback) (rp
rp.SecretStr = rp.Config.StringDefault("app.secret", "") rp.SecretStr = rp.Config.StringDefault("app.secret", "")
callback.FireEvent(REVEL_BEFORE_MODULES_LOADED, nil) callback.FireEvent(REVEL_BEFORE_MODULES_LOADED, nil)
utils.Logger.Info("Loading modules")
if err := rp.loadModules(callback); err != nil { if err := rp.loadModules(callback); err != nil {
return rp, err return rp, err
} }
@@ -248,9 +244,10 @@ func (rp *RevelContainer) loadModules(callback RevelCallback) (err error) {
// Adds a module paths to the container object // Adds a module paths to the container object
func (rp *RevelContainer) addModulePaths(name, importPath, modulePath string) { func (rp *RevelContainer) addModulePaths(name, importPath, modulePath string) {
utils.Logger.Info("Adding module path","name", name,"import path", importPath,"system path", modulePath)
if codePath := filepath.Join(modulePath, "app"); utils.DirExists(codePath) { if codePath := filepath.Join(modulePath, "app"); utils.DirExists(codePath) {
rp.CodePaths = append(rp.CodePaths, codePath) rp.CodePaths = append(rp.CodePaths, codePath)
rp.ModulePathMap[name] = modulePath rp.ModulePathMap[name] = &ModuleInfo{importPath, modulePath}
if viewsPath := filepath.Join(modulePath, "app", "views"); utils.DirExists(viewsPath) { if viewsPath := filepath.Join(modulePath, "app", "views"); utils.DirExists(viewsPath) {
rp.TemplatePaths = append(rp.TemplatePaths, viewsPath) rp.TemplatePaths = append(rp.TemplatePaths, viewsPath)
} }
@@ -273,13 +270,21 @@ func (rp *RevelContainer) ResolveImportPath(importPath string) (string, error) {
if rp.Packaged { if rp.Packaged {
return filepath.Join(rp.SourcePath, importPath), nil return filepath.Join(rp.SourcePath, importPath), nil
} }
config := &packages.Config{
Mode: packages.LoadSyntax,
Dir:rp.AppPath,
}
modPkg, err := build.Import(importPath, rp.AppPath, build.FindOnly) pkgs, err := packages.Load(config, importPath)
if len(pkgs)==0 {
return "", errors.New("No packages found for import " + importPath +" using app path "+ rp.AppPath)
}
// modPkg, err := build.Import(importPath, rp.AppPath, build.FindOnly)
if err != nil { if err != nil {
return "", err return "", err
} }
if rp.PackageInfo.Vendor && !strings.HasPrefix(modPkg.Dir,rp.BasePath) { if len(pkgs[0].GoFiles)>0 {
return "", fmt.Errorf("Module %s was found outside of path %s.",importPath, modPkg.Dir) return filepath.Dir(pkgs[0].GoFiles[0]), nil
} }
return modPkg.Dir, nil return pkgs[0].PkgPath, errors.New("No files found in import path " + importPath)
} }

View File

@@ -12,14 +12,14 @@ import (
type SourceInfo struct { type SourceInfo struct {
// StructSpecs lists type info for all structs found under the code paths. // StructSpecs lists type info for all structs found under the code paths.
// They may be queried to determine which ones (transitively) embed certain types. // They may be queried to determine which ones (transitively) embed certain types.
StructSpecs []*TypeInfo StructSpecs []*TypeInfo
// ValidationKeys provides a two-level lookup. The keys are: // ValidationKeys provides a two-level lookup. The keys are:
// 1. The fully-qualified function name, // 1. The fully-qualified function name,
// e.g. "github.com/revel/examples/chat/app/controllers.(*Application).Action" // e.g. "github.com/revel/examples/chat/app/controllers.(*Application).Action"
// 2. Within that func's file, the line number of the (overall) expression statement. // 2. Within that func's file, the line number of the (overall) expression statement.
// e.g. the line returned from runtime.Caller() // e.g. the line returned from runtime.Caller()
// The result of the lookup the name of variable being validated. // The result of the lookup the name of variable being validated.
ValidationKeys map[string]map[int]string ValidationKeys map[string]map[int]string
// A list of import paths. // A list of import paths.
// Revel notices files with an init() function and imports that package. // Revel notices files with an init() function and imports that package.
InitImportPaths []string InitImportPaths []string
@@ -28,7 +28,9 @@ type SourceInfo struct {
// app/controllers/... that embed (directly or indirectly) revel.Controller // app/controllers/... that embed (directly or indirectly) revel.Controller
controllerSpecs []*TypeInfo controllerSpecs []*TypeInfo
// testSuites list the types that constitute the set of application tests. // testSuites list the types that constitute the set of application tests.
testSuites []*TypeInfo testSuites []*TypeInfo
// packageMap a map of import to system directory (if available)
PackageMap map[string]string
} }
// TypesThatEmbed returns all types that (directly or indirectly) embed the // TypesThatEmbed returns all types that (directly or indirectly) embed the
@@ -74,7 +76,7 @@ func (s *SourceInfo) TypesThatEmbed(targetType, packageFilter string) (filtered
utils.Logger.Info("Debug: Skipping adding spec for unexported type", utils.Logger.Info("Debug: Skipping adding spec for unexported type",
"type", filteredItem.StructName, "type", filteredItem.StructName,
"package", filteredItem.ImportPath) "package", filteredItem.ImportPath)
filtered = append(filtered[:i], filtered[i+1:]...) filtered = append(filtered[:i], filtered[i + 1:]...)
exit = false exit = false
break break
} }
@@ -97,8 +99,8 @@ func (s *SourceInfo) TypesThatEmbed(targetType, packageFilter string) (filtered
// Report non controller structures in controller folder. // Report non controller structures in controller folder.
if !found && !strings.HasPrefix(spec.StructName, "Test") { if !found && !strings.HasPrefix(spec.StructName, "Test") {
utils.Logger.Warn("Type found in package: "+packageFilter+ utils.Logger.Warn("Type found in package: " + packageFilter +
", but did not embed from: "+filepath.Base(targetType), ", but did not embed from: " + filepath.Base(targetType),
"name", spec.StructName, "importpath", spec.ImportPath, "foundstructures", unfoundNames) "name", spec.StructName, "importpath", spec.ImportPath, "foundstructures", unfoundNames)
} }
} }
@@ -110,7 +112,7 @@ func (s *SourceInfo) TypesThatEmbed(targetType, packageFilter string) (filtered
// `revel.Controller` // `revel.Controller`
func (s *SourceInfo) ControllerSpecs() []*TypeInfo { func (s *SourceInfo) ControllerSpecs() []*TypeInfo {
if s.controllerSpecs == nil { if s.controllerSpecs == nil {
s.controllerSpecs = s.TypesThatEmbed(RevelImportPath+".Controller", "controllers") s.controllerSpecs = s.TypesThatEmbed(RevelImportPath + ".Controller", "controllers")
} }
return s.controllerSpecs return s.controllerSpecs
} }
@@ -119,7 +121,19 @@ func (s *SourceInfo) ControllerSpecs() []*TypeInfo {
// `testing.TestSuite` // `testing.TestSuite`
func (s *SourceInfo) TestSuites() []*TypeInfo { func (s *SourceInfo) TestSuites() []*TypeInfo {
if s.testSuites == nil { if s.testSuites == nil {
s.testSuites = s.TypesThatEmbed(RevelImportPath+"/testing.TestSuite", "testsuite") s.testSuites = s.TypesThatEmbed(RevelImportPath + "/testing.TestSuite", "testsuite")
} }
return s.testSuites return s.testSuites
} }
func (s *SourceInfo) Merge(srcInfo2 *SourceInfo) {
s.StructSpecs = append(s.StructSpecs, srcInfo2.StructSpecs...)
s.InitImportPaths = append(s.InitImportPaths, srcInfo2.InitImportPaths...)
for k, v := range srcInfo2.ValidationKeys {
if _, ok := s.ValidationKeys[k]; ok {
utils.Logger.Warn("Warn: Key conflict when scanning validation calls:", "key", k)
continue
}
s.ValidationKeys[k] = v
}
}

View File

@@ -8,19 +8,20 @@ import (
) )
type Version struct { type Version struct {
Prefix string Prefix string
Major int Major int
Minor int Minor int
Maintenance int Maintenance int
Suffix string Suffix string
BuildDate string BuildDate string
MinGoVersion string MinGoVersion string
} }
// The compatibility list // The compatibility list
var frameworkCompatibleRangeList = [][]string{ var frameworkCompatibleRangeList = [][]string{
{"0.0.0", "0.20.0"}, // minimum Revel version to use with this version of the tool {"0.0.0", "0.20.0"}, // minimum Revel version to use with this version of the tool
{"0.19.99", "0.30.0"}, // Compatible with Framework V 0.19.99 - 0.30.0 {"0.19.99", "0.30.0"}, // Compatible with Framework V 0.19.99 - 0.30.0
{"1.0.0", "1.1.0"}, // Compatible with Framework V 1.0 - 1.1
} }
// Parses a version like v1.2.3a or 1.2 // Parses a version like v1.2.3a or 1.2
@@ -70,6 +71,7 @@ func (v *Version) CompatibleFramework(c *CommandConfig) error {
if !v.Newer(start) || v.Newer(end) { if !v.Newer(start) || v.Newer(end) {
continue continue
} }
// Framework is older then 0.20, turn on historic mode // Framework is older then 0.20, turn on historic mode
if i == 0 { if i == 0 {
c.HistoricMode = true c.HistoricMode = true
@@ -109,7 +111,7 @@ func (v *Version) Newer(o *Version) bool {
if v.Maintenance != o.Maintenance { if v.Maintenance != o.Maintenance {
return v.Maintenance > o.Maintenance return v.Maintenance > o.Maintenance
} }
return false return true
} }
// Convert the version to a string // Convert the version to a string

View File

@@ -29,7 +29,10 @@ func addImports(imports map[string]string, decl ast.Decl, srcDir string) {
continue continue
} }
} }
quotedPath := importSpec.Path.Value // e.g. "\"sample/app/models\"" quotedPath := importSpec.Path.Value // e.g. "\"sample/app/models\""
if quotedPath == `"C"` {
continue
}
fullPath := quotedPath[1 : len(quotedPath)-1] // Remove the quotes fullPath := quotedPath[1 : len(quotedPath)-1] // Remove the quotes
// If the package was not aliased (common case), we have to import it // If the package was not aliased (common case), we have to import it
@@ -39,13 +42,14 @@ func addImports(imports map[string]string, decl ast.Decl, srcDir string) {
// 2. Exempt the standard library; their directories always match the package name. // 2. Exempt the standard library; their directories always match the package name.
// 3. Can use build.FindOnly and then use parser.ParseDir with mode PackageClauseOnly // 3. Can use build.FindOnly and then use parser.ParseDir with mode PackageClauseOnly
if pkgAlias == "" { if pkgAlias == "" {
utils.Logger.Debug("Reading from build", "path", fullPath, "srcPath", srcDir, "gopath", build.Default.GOPATH) utils.Logger.Debug("Reading from build", "path", fullPath, "srcPath", srcDir, "gopath", build.Default.GOPATH)
pkg, err := build.Import(fullPath, srcDir, 0) pkg, err := build.Import(fullPath, srcDir, 0)
if err != nil { if err != nil {
// We expect this to happen for apps using reverse routing (since we // We expect this to happen for apps using reverse routing (since we
// have not yet generated the routes). Don't log that. // have not yet generated the routes). Don't log that.
if !strings.HasSuffix(fullPath, "/app/routes") { if !strings.HasSuffix(fullPath, "/app/routes") {
utils.Logger.Error("Could not find import:", "path", fullPath, "srcPath", srcDir, "error", err) utils.Logger.Warn("Could not find import:", "path", fullPath, "srcPath", srcDir, "error", err)
} }
continue continue
} else { } else {

View File

@@ -85,7 +85,7 @@ func (pc *processContainer) processPath(path string, info os.FileInfo, err error
if err != nil { if err != nil {
if errList, ok := err.(scanner.ErrorList); ok { if errList, ok := err.(scanner.ErrorList); ok {
var pos = errList[0].Pos var pos = errList[0].Pos
newError := &utils.Error{ newError := &utils.SourceError{
SourceType: ".go source", SourceType: ".go source",
Title: "Go Compilation Error", Title: "Go Compilation Error",
Path: pos.Filename, Path: pos.Filename,

View File

@@ -0,0 +1,379 @@
package parser2
import (
"github.com/revel/cmd/utils"
"golang.org/x/tools/go/packages"
"github.com/revel/cmd/model"
"go/ast"
"go/token"
"strings"
"path/filepath"
)
type (
SourceInfoProcessor struct {
sourceProcessor *SourceProcessor
}
)
func NewSourceInfoProcessor(sourceProcessor *SourceProcessor) *SourceInfoProcessor {
return &SourceInfoProcessor{sourceProcessor:sourceProcessor}
}
func (s *SourceInfoProcessor) processPackage(p *packages.Package) (sourceInfo *model.SourceInfo) {
sourceInfo = &model.SourceInfo{
ValidationKeys: map[string]map[int]string{},
}
var (
isController = strings.HasSuffix(p.PkgPath, "/controllers") ||
strings.Contains(p.PkgPath, "/controllers/")
isTest = strings.HasSuffix(p.PkgPath, "/tests") ||
strings.Contains(p.PkgPath, "/tests/")
methodMap = map[string][]*model.MethodSpec{}
)
for _, tree := range p.Syntax {
for _, decl := range tree.Decls {
s.sourceProcessor.packageMap[p.PkgPath] = filepath.Dir(p.Fset.Position(decl.Pos()).Filename)
//println("*** checking", p.Fset.Position(decl.Pos()).Filename)
spec, found := s.getStructTypeDecl(decl, p.Fset)
if found {
if isController || isTest {
controllerSpec := s.getControllerSpec(spec, p)
sourceInfo.StructSpecs = append(sourceInfo.StructSpecs, controllerSpec)
}
} else {
// Not a type definition, this could be a method for a controller try to extract that
// Func declaration?
funcDecl, ok := decl.(*ast.FuncDecl)
if !ok {
continue
}
// This could be a controller action endpoint, check and add if needed
if isController &&
funcDecl.Recv != nil && // Must have a receiver
funcDecl.Name.IsExported() && // be public
funcDecl.Type.Results != nil && len(funcDecl.Type.Results.List) == 1 {
// return one result
if m, receiver := s.getControllerFunc(funcDecl, p); m != nil {
methodMap[receiver] = append(methodMap[receiver], m)
s.sourceProcessor.log.Info("Added method map to ", "receiver", receiver, "method", m.Name)
}
}
// Check for validation
if lineKeyMap := s.getValidation(funcDecl, p); len(lineKeyMap) > 1 {
sourceInfo.ValidationKeys[p.PkgPath + "." + s.getFuncName(funcDecl)] = lineKeyMap
}
if funcDecl.Name.Name == "init" {
sourceInfo.InitImportPaths = append(sourceInfo.InitImportPaths, p.PkgPath)
}
}
}
}
// Add the method specs to the struct specs.
for _, spec := range sourceInfo.StructSpecs {
spec.MethodSpecs = methodMap[spec.StructName]
}
return
}
// Scan app source code for calls to X.Y(), where X is of type *Validation.
//
// Recognize these scenarios:
// - "Y" = "Validation" and is a member of the receiver.
// (The common case for inline validation)
// - "X" is passed in to the func as a parameter.
// (For structs implementing Validated)
//
// The line number to which a validation call is attributed is that of the
// surrounding ExprStmt. This is so that it matches what runtime.Callers()
// reports.
//
// The end result is that we can set the default validation key for each call to
// be the same as the local variable.
func (s *SourceInfoProcessor) getValidation(funcDecl *ast.FuncDecl, p *packages.Package) (map[int]string) {
var (
lineKeys = make(map[int]string)
// Check the func parameters and the receiver's members for the *revel.Validation type.
validationParam = s.getValidationParameter(funcDecl)
)
ast.Inspect(funcDecl.Body, func(node ast.Node) bool {
// e.g. c.Validation.Required(arg) or v.Required(arg)
callExpr, ok := node.(*ast.CallExpr)
if !ok {
return true
}
// e.g. c.Validation.Required or v.Required
funcSelector, ok := callExpr.Fun.(*ast.SelectorExpr)
if !ok {
return true
}
switch x := funcSelector.X.(type) {
case *ast.SelectorExpr: // e.g. c.Validation
if x.Sel.Name != "Validation" {
return true
}
case *ast.Ident: // e.g. v
if validationParam == nil || x.Obj != validationParam {
return true
}
default:
return true
}
if len(callExpr.Args) == 0 {
return true
}
// Given the validation expression, extract the key.
key := callExpr.Args[0]
switch expr := key.(type) {
case *ast.BinaryExpr:
// If the argument is a binary expression, take the first expression.
// (e.g. c.Validation.Required(myName != ""))
key = expr.X
case *ast.UnaryExpr:
// If the argument is a unary expression, drill in.
// (e.g. c.Validation.Required(!myBool)
key = expr.X
case *ast.BasicLit:
// If it's a literal, skip it.
return true
}
if typeExpr := model.NewTypeExprFromAst("", key); typeExpr.Valid {
lineKeys[p.Fset.Position(callExpr.End()).Line] = typeExpr.TypeName("")
} else {
s.sourceProcessor.log.Error("Error: Failed to generate key for field validation. Make sure the field name is valid.", "file", p.PkgPath,
"line", p.Fset.Position(callExpr.End()).Line, "function", funcDecl.Name.String())
}
return true
})
return lineKeys
}
// Check to see if there is a *revel.Validation as an argument.
func (s *SourceInfoProcessor) getValidationParameter(funcDecl *ast.FuncDecl) *ast.Object {
for _, field := range funcDecl.Type.Params.List {
starExpr, ok := field.Type.(*ast.StarExpr) // e.g. *revel.Validation
if !ok {
continue
}
selExpr, ok := starExpr.X.(*ast.SelectorExpr) // e.g. revel.Validation
if !ok {
continue
}
xIdent, ok := selExpr.X.(*ast.Ident) // e.g. rev
if !ok {
continue
}
if selExpr.Sel.Name == "Validation" && s.sourceProcessor.importMap[xIdent.Name] == model.RevelImportPath {
return field.Names[0].Obj
}
}
return nil
}
func (s *SourceInfoProcessor) getControllerFunc(funcDecl *ast.FuncDecl, p *packages.Package) (method *model.MethodSpec, recvTypeName string) {
selExpr, ok := funcDecl.Type.Results.List[0].Type.(*ast.SelectorExpr)
if !ok {
return
}
if selExpr.Sel.Name != "Result" {
return
}
if pkgIdent, ok := selExpr.X.(*ast.Ident); !ok || s.sourceProcessor.importMap[pkgIdent.Name] != model.RevelImportPath {
return
}
method = &model.MethodSpec{
Name: funcDecl.Name.Name,
}
// Add a description of the arguments to the method.
for _, field := range funcDecl.Type.Params.List {
for _, name := range field.Names {
var importPath string
typeExpr := model.NewTypeExprFromAst(p.Name, field.Type)
if !typeExpr.Valid {
utils.Logger.Warn("Warn: Didn't understand argument '%s' of action %s. Ignoring.", name, s.getFuncName(funcDecl))
return // We didn't understand one of the args. Ignore this action.
}
// Local object
if typeExpr.PkgName == p.Name {
importPath = p.PkgPath
} else if typeExpr.PkgName != "" {
var ok bool
if importPath, ok = s.sourceProcessor.importMap[typeExpr.PkgName]; !ok {
utils.Logger.Fatalf("Failed to find import for arg of type: %s , %s", typeExpr.PkgName, typeExpr.TypeName(""))
}
}
method.Args = append(method.Args, &model.MethodArg{
Name: name.Name,
TypeExpr: typeExpr,
ImportPath: importPath,
})
}
}
// Add a description of the calls to Render from the method.
// Inspect every node (e.g. always return true).
method.RenderCalls = []*model.MethodCall{}
ast.Inspect(funcDecl.Body, func(node ast.Node) bool {
// Is it a function call?
callExpr, ok := node.(*ast.CallExpr)
if !ok {
return true
}
// Is it calling (*Controller).Render?
selExpr, ok := callExpr.Fun.(*ast.SelectorExpr)
if !ok {
return true
}
// The type of the receiver is not easily available, so just store every
// call to any method called Render.
if selExpr.Sel.Name != "Render" {
return true
}
// Add this call's args to the renderArgs.
pos := p.Fset.Position(callExpr.Lparen)
methodCall := &model.MethodCall{
Line: pos.Line,
Names: []string{},
}
for _, arg := range callExpr.Args {
argIdent, ok := arg.(*ast.Ident)
if !ok {
continue
}
methodCall.Names = append(methodCall.Names, argIdent.Name)
}
method.RenderCalls = append(method.RenderCalls, methodCall)
return true
})
var recvType = funcDecl.Recv.List[0].Type
if recvStarType, ok := recvType.(*ast.StarExpr); ok {
recvTypeName = recvStarType.X.(*ast.Ident).Name
} else {
recvTypeName = recvType.(*ast.Ident).Name
}
return
}
func (s *SourceInfoProcessor) getControllerSpec(spec *ast.TypeSpec, p *packages.Package) (controllerSpec *model.TypeInfo) {
structType := spec.Type.(*ast.StructType)
// At this point we know it's a type declaration for a struct.
// Fill in the rest of the info by diving into the fields.
// Add it provisionally to the Controller list -- it's later filtered using field info.
controllerSpec = &model.TypeInfo{
StructName: spec.Name.Name,
ImportPath: p.PkgPath,
PackageName: p.Name,
}
for _, field := range structType.Fields.List {
// If field.Names is set, it's not an embedded type.
if field.Names != nil {
continue
}
// A direct "sub-type" has an ast.Field as either:
// Ident { "AppController" }
// SelectorExpr { "rev", "Controller" }
// Additionally, that can be wrapped by StarExprs.
fieldType := field.Type
pkgName, typeName := func() (string, string) {
// Drill through any StarExprs.
for {
if starExpr, ok := fieldType.(*ast.StarExpr); ok {
fieldType = starExpr.X
continue
}
break
}
// If the embedded type is in the same package, it's an Ident.
if ident, ok := fieldType.(*ast.Ident); ok {
return "", ident.Name
}
if selectorExpr, ok := fieldType.(*ast.SelectorExpr); ok {
if pkgIdent, ok := selectorExpr.X.(*ast.Ident); ok {
return pkgIdent.Name, selectorExpr.Sel.Name
}
}
return "", ""
}()
// If a typename wasn't found, skip it.
if typeName == "" {
continue
}
// Find the import path for this type.
// If it was referenced without a package name, use the current package import path.
// Else, look up the package's import path by name.
var importPath string
if pkgName == "" {
importPath = p.PkgPath
} else {
var ok bool
if importPath, ok = s.sourceProcessor.importMap[pkgName]; !ok {
s.sourceProcessor.log.Error("Error: Failed to find import path for ", "package", pkgName, "type", typeName, "map", s.sourceProcessor.importMap)
continue
}
}
controllerSpec.EmbeddedTypes = append(controllerSpec.EmbeddedTypes, &model.EmbeddedTypeName{
ImportPath: importPath,
StructName: typeName,
})
}
s.sourceProcessor.log.Info("Added controller spec", "name", controllerSpec.StructName, "package", controllerSpec.ImportPath)
return
}
func (s *SourceInfoProcessor) getStructTypeDecl(decl ast.Decl, fset *token.FileSet) (spec *ast.TypeSpec, found bool) {
genDecl, ok := decl.(*ast.GenDecl)
if !ok {
return
}
if genDecl.Tok != token.TYPE {
return
}
if len(genDecl.Specs) == 0 {
utils.Logger.Warn("Warn: Surprising: %s:%d Decl contains no specifications", fset.Position(decl.Pos()).Filename, fset.Position(decl.Pos()).Line)
return
}
spec = genDecl.Specs[0].(*ast.TypeSpec)
_, found = spec.Type.(*ast.StructType)
return
}
func (s *SourceInfoProcessor) getFuncName(funcDecl *ast.FuncDecl) string {
prefix := ""
if funcDecl.Recv != nil {
recvType := funcDecl.Recv.List[0].Type
if recvStarType, ok := recvType.(*ast.StarExpr); ok {
prefix = "(*" + recvStarType.X.(*ast.Ident).Name + ")"
} else {
prefix = recvType.(*ast.Ident).Name
}
prefix += "."
}
return prefix + funcDecl.Name.Name
}

217
parser2/source_processor.go Normal file
View File

@@ -0,0 +1,217 @@
package parser2
import (
"go/ast"
"go/token"
"github.com/revel/cmd/model"
"golang.org/x/tools/go/packages"
"github.com/revel/cmd/utils"
"errors"
"strings"
"github.com/revel/cmd/logger"
)
type (
SourceProcessor struct {
revelContainer *model.RevelContainer
log logger.MultiLogger
packageList []*packages.Package
importMap map[string]string
packageMap map[string]string
sourceInfoProcessor *SourceInfoProcessor
sourceInfo *model.SourceInfo
}
)
func ProcessSource(revelContainer *model.RevelContainer) (sourceInfo *model.SourceInfo, compileError error) {
utils.Logger.Info("ProcessSource")
processor := NewSourceProcessor(revelContainer)
compileError = processor.parse()
sourceInfo = processor.sourceInfo
if compileError == nil {
processor.log.Infof("From parsers : Structures:%d InitImports:%d ValidationKeys:%d %v", len(sourceInfo.StructSpecs), len(sourceInfo.InitImportPaths), len(sourceInfo.ValidationKeys), sourceInfo.PackageMap)
}
if false {
compileError = errors.New("Incompleted")
utils.Logger.Panic("Not implemented")
}
return
}
func NewSourceProcessor(revelContainer *model.RevelContainer) *SourceProcessor {
s := &SourceProcessor{revelContainer:revelContainer, log:utils.Logger.New("parser", "SourceProcessor")}
s.sourceInfoProcessor = NewSourceInfoProcessor(s)
return s
}
func (s *SourceProcessor) parse() (compileError error) {
if compileError = s.addPackages(); compileError != nil {
return
}
if compileError = s.addImportMap(); compileError != nil {
return
}
if compileError = s.addSourceInfo(); compileError != nil {
return
}
s.sourceInfo.PackageMap = map[string]string{}
getImportFromMap := func(packagePath string) string {
for path := range s.packageMap {
if strings.Index(path, packagePath) == 0 {
fullPath := s.packageMap[path]
return fullPath[:(len(fullPath) - len(path) + len(packagePath))]
}
}
return ""
}
s.sourceInfo.PackageMap[model.RevelImportPath] = getImportFromMap(model.RevelImportPath)
s.sourceInfo.PackageMap[s.revelContainer.ImportPath] = getImportFromMap(s.revelContainer.ImportPath)
for _, module := range s.revelContainer.ModulePathMap {
s.sourceInfo.PackageMap[module.ImportPath] = getImportFromMap(module.ImportPath)
}
return
}
func (s *SourceProcessor) addPackages() (err error) {
allPackages := []string{s.revelContainer.ImportPath + "/...", model.RevelImportPath}
for _, module := range s.revelContainer.ModulePathMap {
allPackages = append(allPackages, module.ImportPath + "/...") // +"/app/controllers/...")
}
//allPackages = []string{s.revelContainer.ImportPath + "/..."} //+"/app/controllers/..."}
config := &packages.Config{
// ode: packages.NeedSyntax | packages.NeedCompiledGoFiles,
Mode:
packages.NeedTypes | // For compile error
packages.NeedDeps | // To load dependent files
packages.NeedName | // Loads the full package name
packages.NeedSyntax, // To load ast tree (for end points)
//Mode: packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles |
// packages.NeedImports | packages.NeedDeps | packages.NeedExportsFile |
// packages.NeedTypes | packages.NeedSyntax | packages.NeedTypesInfo |
// packages.NeedTypesSizes,
//Mode: packages.NeedName | packages.NeedImports | packages.NeedDeps | packages.NeedExportsFile | packages.NeedFiles |
// packages.NeedCompiledGoFiles | packages.NeedTypesSizes |
// packages.NeedSyntax | packages.NeedCompiledGoFiles ,
//Mode: packages.NeedSyntax | packages.NeedCompiledGoFiles | packages.NeedName | packages.NeedFiles |
// packages.LoadTypes | packages.NeedTypes | packages.NeedDeps, //, // |
// packages.NeedTypes, // packages.LoadTypes | packages.NeedSyntax | packages.NeedTypesInfo,
//packages.LoadSyntax | packages.NeedDeps,
Dir:s.revelContainer.AppPath,
}
s.packageList, err = packages.Load(config, allPackages...)
s.log.Info("Loaded packages ", "len results", len(s.packageList), "error", err)
return
}
func (s *SourceProcessor) addImportMap() (err error) {
s.importMap = map[string]string{}
s.packageMap = map[string]string{}
for _, p := range s.packageList {
if len(p.Errors) > 0 {
// Generate a compile error
for _, e := range p.Errors {
if !strings.Contains(e.Msg, "fsnotify") {
err = utils.NewCompileError("", "", e)
}
}
}
for _, tree := range p.Syntax {
for _, decl := range tree.Decls {
genDecl, ok := decl.(*ast.GenDecl)
if !ok {
continue
}
if genDecl.Tok == token.IMPORT {
for _, spec := range genDecl.Specs {
importSpec := spec.(*ast.ImportSpec)
//fmt.Printf("*** import specification %#v\n", importSpec)
var pkgAlias string
if importSpec.Name != nil {
pkgAlias = importSpec.Name.Name
if pkgAlias == "_" {
continue
}
}
quotedPath := importSpec.Path.Value // e.g. "\"sample/app/models\""
fullPath := quotedPath[1 : len(quotedPath) - 1] // Remove the quotes
if pkgAlias == "" {
pkgAlias = fullPath
if index := strings.LastIndex(pkgAlias, "/"); index > 0 {
pkgAlias = pkgAlias[index + 1:]
}
}
s.importMap[pkgAlias] = fullPath
}
}
}
}
}
return
}
func (s *SourceProcessor) addSourceInfo() (err error) {
for _, p := range s.packageList {
if sourceInfo := s.sourceInfoProcessor.processPackage(p); sourceInfo != nil {
if s.sourceInfo != nil {
s.sourceInfo.Merge(sourceInfo)
} else {
s.sourceInfo = sourceInfo
}
}
}
return
}
// Add imports to the map from the source dir
//func addImports(imports map[string]string, decl ast.Decl, srcDir string) {
// genDecl, ok := decl.(*ast.GenDecl)
// if !ok {
// return
// }
//
// if genDecl.Tok != token.IMPORT {
// return
// }
//
// for _, spec := range genDecl.Specs {
// importSpec := spec.(*ast.ImportSpec)
// var pkgAlias string
// if importSpec.Name != nil {
// pkgAlias = importSpec.Name.Name
// if pkgAlias == "_" {
// continue
// }
// }
// quotedPath := importSpec.Path.Value // e.g. "\"sample/app/models\""
// fullPath := quotedPath[1 : len(quotedPath)-1] // Remove the quotes
//
// // If the package was not aliased (common case), we have to import it
// // to see what the package name is.
// // TODO: Can improve performance here a lot:
// // 1. Do not import everything over and over again. Keep a cache.
// // 2. Exempt the standard library; their directories always match the package name.
// // 3. Can use build.FindOnly and then use parser.ParseDir with mode PackageClauseOnly
// if pkgAlias == "" {
//
// utils.Logger.Debug("Reading from build", "path", fullPath, "srcPath", srcDir, "gopath", build.Default.GOPATH)
// pkg, err := build.Import(fullPath, srcDir, 0)
// if err != nil {
// // We expect this to happen for apps using reverse routing (since we
// // have not yet generated the routes). Don't log that.
// if !strings.HasSuffix(fullPath, "/app/routes") {
// utils.Logger.Warn("Could not find import:", "path", fullPath, "srcPath", srcDir, "error", err)
// }
// continue
// } else {
// utils.Logger.Debug("Found package in dir", "dir", pkg.Dir, "name", pkg.ImportPath)
// }
// pkgAlias = pkg.Name
// }
//
// imports[pkgAlias] = fullPath
// }
//}

View File

@@ -13,7 +13,6 @@ import (
"github.com/revel/cmd/harness" "github.com/revel/cmd/harness"
"github.com/revel/cmd/model" "github.com/revel/cmd/model"
"github.com/revel/cmd/utils" "github.com/revel/cmd/utils"
"go/build"
) )
var cmdBuild = &Command{ var cmdBuild = &Command{
@@ -38,6 +37,12 @@ func init() {
// The update config updates the configuration command so that it can run // The update config updates the configuration command so that it can run
func updateBuildConfig(c *model.CommandConfig, args []string) bool { func updateBuildConfig(c *model.CommandConfig, args []string) bool {
c.Index = model.BUILD c.Index = model.BUILD
if c.Build.TargetPath == "" {
c.Build.TargetPath = "target"
}
if len(args) == 0 && c.Build.ImportPath != "" {
return true
}
// If arguments were passed in then there must be two // If arguments were passed in then there must be two
if len(args) < 2 { if len(args) < 2 {
fmt.Fprintf(os.Stderr, "%s\n%s", cmdBuild.UsageLine, cmdBuild.Long) fmt.Fprintf(os.Stderr, "%s\n%s", cmdBuild.UsageLine, cmdBuild.Long)
@@ -64,12 +69,14 @@ func buildApp(c *model.CommandConfig) (err error) {
c.Build.Mode = mode c.Build.Mode = mode
c.Build.ImportPath = appImportPath c.Build.ImportPath = appImportPath
revel_paths, err := model.NewRevelPaths(mode, appImportPath, "", model.NewWrappedRevelCallback(nil, c.PackageResolver)) revel_paths, err := model.NewRevelPaths(mode, appImportPath, c.AppPath, model.NewWrappedRevelCallback(nil, c.PackageResolver))
if err != nil { if err != nil {
return return
} }
buildSafetyCheck(destPath) if err = buildSafetyCheck(destPath); err != nil {
return
}
// Ensure the application can be built, this generates the main file // Ensure the application can be built, this generates the main file
app, err := harness.Build(c, revel_paths) app, err := harness.Build(c, revel_paths)
@@ -87,7 +94,7 @@ func buildApp(c *model.CommandConfig) (err error) {
if err != nil { if err != nil {
return return
} }
err = buildCopyModules(c, revel_paths, packageFolders) err = buildCopyModules(c, revel_paths, packageFolders, app)
if err != nil { if err != nil {
return return
} }
@@ -147,14 +154,14 @@ func buildCopyFiles(c *model.CommandConfig, app *harness.App, revel_paths *model
} }
// Based on the section copy over the build modules // Based on the section copy over the build modules
func buildCopyModules(c *model.CommandConfig, revel_paths *model.RevelContainer, packageFolders []string) (err error) { func buildCopyModules(c *model.CommandConfig, revel_paths *model.RevelContainer, packageFolders []string, app *harness.App) (err error) {
destPath := filepath.Join(c.Build.TargetPath, "src") destPath := filepath.Join(c.Build.TargetPath, "src")
// Find all the modules used and copy them over. // Find all the modules used and copy them over.
config := revel_paths.Config.Raw() config := revel_paths.Config.Raw()
modulePaths := make(map[string]string) // import path => filesystem path
// We should only copy over the section of options what the build is targeted for // We should only copy over the section of options what the build is targeted for
// We will default to prod // We will default to prod
moduleImportList := []string{}
for _, section := range config.Sections() { for _, section := range config.Sections() {
// If the runmode is defined we will only import modules defined for that run mode // If the runmode is defined we will only import modules defined for that run mode
if c.Build.Mode != "" && c.Build.Mode != section { if c.Build.Mode != "" && c.Build.Mode != section {
@@ -169,17 +176,14 @@ func buildCopyModules(c *model.CommandConfig, revel_paths *model.RevelContainer,
if moduleImportPath == "" { if moduleImportPath == "" {
continue continue
} }
moduleImportList = append(moduleImportList, moduleImportPath)
modPkg, err := build.Import(moduleImportPath, revel_paths.RevelPath, build.FindOnly)
if err != nil {
utils.Logger.Fatalf("Failed to load module %s (%s): %s", key[len("module."):], c.ImportPath, err)
}
modulePaths[moduleImportPath] = modPkg.Dir
} }
} }
// Copy the the paths for each of the modules // Copy the the paths for each of the modules
for importPath, fsPath := range modulePaths { for _, importPath := range moduleImportList {
fsPath := app.PackagePathMap[importPath]
utils.Logger.Info("Copy files ", "to", filepath.Join(destPath, importPath), "from", fsPath) utils.Logger.Info("Copy files ", "to", filepath.Join(destPath, importPath), "from", fsPath)
if c.Build.CopySource { if c.Build.CopySource {
err = utils.CopyDir(filepath.Join(destPath, importPath), fsPath, nil) err = utils.CopyDir(filepath.Join(destPath, importPath), fsPath, nil)

View File

@@ -8,7 +8,7 @@ import (
"fmt" "fmt"
"github.com/revel/cmd/model" "github.com/revel/cmd/model"
"github.com/revel/cmd/utils" "github.com/revel/cmd/utils"
"go/build"
"os" "os"
"path/filepath" "path/filepath"
) )
@@ -37,6 +37,9 @@ func init() {
// Update the clean command configuration, using old method // Update the clean command configuration, using old method
func updateCleanConfig(c *model.CommandConfig, args []string) bool { func updateCleanConfig(c *model.CommandConfig, args []string) bool {
c.Index = model.CLEAN c.Index = model.CLEAN
if len(args) == 0 && c.Clean.ImportPath != "" {
return true
}
if len(args) == 0 { if len(args) == 0 {
fmt.Fprintf(os.Stderr, cmdClean.Long) fmt.Fprintf(os.Stderr, cmdClean.Long)
return false return false
@@ -47,14 +50,10 @@ func updateCleanConfig(c *model.CommandConfig, args []string) bool {
// Clean the source directory of generated files // Clean the source directory of generated files
func cleanApp(c *model.CommandConfig) (err error) { func cleanApp(c *model.CommandConfig) (err error) {
appPkg, err := build.Import(c.ImportPath, "", build.FindOnly)
if err != nil {
utils.Logger.Fatal("Abort: Failed to find import path:", "error", err)
}
purgeDirs := []string{ purgeDirs := []string{
filepath.Join(appPkg.Dir, "app", "tmp"), filepath.Join(c.AppPath, "app", "tmp"),
filepath.Join(appPkg.Dir, "app", "routes"), filepath.Join(c.AppPath, "app", "routes"),
} }
for _, dir := range purgeDirs { for _, dir := range purgeDirs {

View File

@@ -22,12 +22,12 @@ func TestClean(t *testing.T) {
main.Commands[model.NEW].RunWith(c) main.Commands[model.NEW].RunWith(c)
c.Index = model.TEST c.Index = model.TEST
main.Commands[model.TEST].RunWith(c) main.Commands[model.TEST].RunWith(c)
a.True(utils.Exists(filepath.Join(gopath, "src", "clean-test", "app", "tmp", "main.go")), a.True(utils.Exists(filepath.Join(gopath, "clean-test", "app", "tmp", "main.go")),
"Missing main from path "+filepath.Join(gopath, "src", "clean-test", "app", "tmp", "main.go")) "Missing main from path "+filepath.Join(gopath, "clean-test", "app", "tmp", "main.go"))
c.Clean.ImportPath = c.ImportPath c.Clean.ImportPath = c.ImportPath
a.Nil(main.Commands[model.CLEAN].RunWith(c), "Failed to run clean-test") a.Nil(main.Commands[model.CLEAN].RunWith(c), "Failed to run clean-test")
a.False(utils.Exists(filepath.Join(gopath, "src", "clean-test", "app", "tmp", "main.go")), a.False(utils.Exists(filepath.Join(gopath, "clean-test", "app", "tmp", "main.go")),
"Did not remove main from path "+filepath.Join(gopath, "src", "clean-test", "app", "tmp", "main.go")) "Did not remove main from path "+filepath.Join(gopath, "clean-test", "app", "tmp", "main.go"))
}) })
if !t.Failed() { if !t.Failed() {
if err := os.RemoveAll(gopath); err != nil { if err := os.RemoveAll(gopath); err != nil {

View File

@@ -7,7 +7,9 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"go/build" "go/build"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"fmt"
) )
// Test that the event handler can be attached and it dispatches the event received // Test that the event handler can be attached and it dispatches the event received
@@ -43,10 +45,26 @@ func setup(suffix string, a *assert.Assertions) (string) {
// Create a new app for the name // Create a new app for the name
func newApp(name string, command model.COMMAND, precall func(c *model.CommandConfig), a *assert.Assertions) *model.CommandConfig { func newApp(name string, command model.COMMAND, precall func(c *model.CommandConfig), a *assert.Assertions) *model.CommandConfig {
c := &model.CommandConfig{} c := &model.CommandConfig{Vendored:true}
switch command { switch command {
case model.NEW: case model.NEW:
c.New.ImportPath = name c.New.ImportPath = name
c.New.Callback=func() error {
// On callback we will invoke a specific branch of revel so that it works
goModCmd := exec.Command("go", "mod", "tidy")
utils.CmdInit(goModCmd, !c.Vendored, c.AppPath)
getOutput, _ := goModCmd.CombinedOutput()
fmt.Printf("Calling go mod tidy %s",string(getOutput))
goModCmd = exec.Command("go", "mod", "edit", "-replace=github.com/revel/revel=github.com/revel/revel@develop")
utils.CmdInit(goModCmd, !c.Vendored, c.AppPath)
getOutput, _ = goModCmd.CombinedOutput()
fmt.Printf("Calling go mod edit %v",string(getOutput))
return nil
}
case model.BUILD: case model.BUILD:
c.Build.ImportPath = name c.Build.ImportPath = name
case model.TEST: case model.TEST:
@@ -68,7 +86,7 @@ func newApp(name string, command model.COMMAND, precall func(c *model.CommandCon
if c.UpdateImportPath()!=nil { if c.UpdateImportPath()!=nil {
a.Fail("Unable to update import path") a.Fail("Unable to update import path")
} }
c.InitGoPaths()
c.InitPackageResolver() c.InitPackageResolver()
return c return c
} }

View File

@@ -46,14 +46,23 @@ func init() {
// Called when unable to parse the command line automatically and assumes an old launch // Called when unable to parse the command line automatically and assumes an old launch
func updateNewConfig(c *model.CommandConfig, args []string) bool { func updateNewConfig(c *model.CommandConfig, args []string) bool {
c.Index = model.NEW c.Index = model.NEW
if len(c.New.Package) > 0 {
c.New.NotVendored = false
}
c.Vendored = !c.New.NotVendored
if len(args) == 0 { if len(args) == 0 {
fmt.Fprintf(os.Stderr, cmdNew.Long) if len(c.New.ImportPath) == 0 {
return false fmt.Fprintf(os.Stderr, cmdNew.Long)
return false
}
return true
} }
c.New.ImportPath = args[0] c.New.ImportPath = args[0]
if len(args) > 1 { if len(args) > 1 {
c.New.SkeletonPath = args[1] c.New.SkeletonPath = args[1]
} }
return true return true
} }
@@ -67,7 +76,7 @@ func newApp(c *model.CommandConfig) (err error) {
} }
// checking and setting skeleton // checking and setting skeleton
if err=setSkeletonPath(c);err!=nil { if err = setSkeletonPath(c); err != nil {
return return
} }
@@ -76,72 +85,26 @@ func newApp(c *model.CommandConfig) (err error) {
return utils.NewBuildError("Abort: Unable to create app path.", "path", c.AppPath) return utils.NewBuildError("Abort: Unable to create app path.", "path", c.AppPath)
} }
if c.New.Vendored {
utils.Logger.Info("Creating a new vendor app")
vendorPath := filepath.Join(c.AppPath, "vendor")
if !utils.DirExists(vendorPath) {
if err := os.MkdirAll(vendorPath, os.ModePerm); err != nil {
return utils.NewBuildError("Failed to create "+vendorPath, "error", err)
}
}
// In order for dep to run there needs to be a source file in the folder
tempPath := filepath.Join(c.AppPath, "tmp")
utils.Logger.Info("Checking for temp folder for source code", "path", tempPath)
if !utils.DirExists(tempPath) {
if err := os.MkdirAll(tempPath, os.ModePerm); err != nil {
return utils.NewBuildIfError(err, "Failed to create "+vendorPath)
}
if err = utils.GenerateTemplate(filepath.Join(tempPath, "main.go"), NEW_MAIN_FILE, nil); err != nil {
return utils.NewBuildIfError(err, "Failed to create main file "+vendorPath)
}
}
// Create a package template file if it does not exist
packageFile := filepath.Join(c.AppPath, "Gopkg.toml")
utils.Logger.Info("Checking for Gopkg.toml", "path", packageFile)
if !utils.Exists(packageFile) {
utils.Logger.Info("Generating Gopkg.toml", "path", packageFile)
if err := utils.GenerateTemplate(packageFile, VENDOR_GOPKG, nil); err != nil {
return utils.NewBuildIfError(err, "Failed to generate template")
}
} else {
utils.Logger.Info("Package file exists in skeleto, skipping adding")
}
getCmd := exec.Command("dep", "ensure", "-v")
utils.CmdInit(getCmd, c.AppPath)
utils.Logger.Info("Exec:", "args", getCmd.Args, "env", getCmd.Env, "workingdir",getCmd.Dir)
getOutput, err := getCmd.CombinedOutput()
if err != nil {
return utils.NewBuildIfError(err, string(getOutput))
}
}
// checking and setting application // checking and setting application
if err = setApplicationPath(c); err != nil { if err = setApplicationPath(c); err != nil {
return err return err
} }
// At this point the versions can be set
c.SetVersions() // This kicked off the download of the revel app, not needed for vendor
if !c.Vendored {
// At this point the versions can be set
c.SetVersions()
}
// copy files to new app directory // copy files to new app directory
if err = copyNewAppFiles(c);err != nil { if err = copyNewAppFiles(c); err != nil {
return return
} }
// Rerun the dep tool if vendored // Run the vendor tool if needed
if c.New.Vendored { if c.Vendored {
getCmd := exec.Command("dep", "ensure", "-v") if err = createModVendor(c); err != nil {
utils.CmdInit(getCmd, c.AppPath) return
utils.Logger.Info("Exec:", "args", getCmd.Args)
getOutput, err := getCmd.CombinedOutput()
if err != nil {
utils.Logger.Fatal(string(getOutput))
} }
} }
@@ -156,6 +119,72 @@ func newApp(c *model.CommandConfig) (err error) {
return return
} }
func createModVendor(c *model.CommandConfig) (err error) {
utils.Logger.Info("Creating a new mod app")
goModCmd := exec.Command("go", "mod", "init", filepath.Join(c.New.Package, c.AppName))
utils.CmdInit(goModCmd, !c.Vendored, c.AppPath)
utils.Logger.Info("Exec:", "args", goModCmd.Args, "env", goModCmd.Env, "workingdir", goModCmd.Dir)
getOutput, err := goModCmd.CombinedOutput()
if c.New.Callback != nil {
err = c.New.Callback()
}
if err != nil {
return utils.NewBuildIfError(err, string(getOutput))
}
return
}
func createDepVendor(c *model.CommandConfig) (err error) {
utils.Logger.Info("Creating a new vendor app")
vendorPath := filepath.Join(c.AppPath, "vendor")
if !utils.DirExists(vendorPath) {
if err := os.MkdirAll(vendorPath, os.ModePerm); err != nil {
return utils.NewBuildError("Failed to create " + vendorPath, "error", err)
}
}
// In order for dep to run there needs to be a source file in the folder
tempPath := filepath.Join(c.AppPath, "tmp")
utils.Logger.Info("Checking for temp folder for source code", "path", tempPath)
if !utils.DirExists(tempPath) {
if err := os.MkdirAll(tempPath, os.ModePerm); err != nil {
return utils.NewBuildIfError(err, "Failed to create " + vendorPath)
}
if err = utils.GenerateTemplate(filepath.Join(tempPath, "main.go"), NEW_MAIN_FILE, nil); err != nil {
return utils.NewBuildIfError(err, "Failed to create main file " + vendorPath)
}
}
// Create a package template file if it does not exist
packageFile := filepath.Join(c.AppPath, "Gopkg.toml")
utils.Logger.Info("Checking for Gopkg.toml", "path", packageFile)
if !utils.Exists(packageFile) {
utils.Logger.Info("Generating Gopkg.toml", "path", packageFile)
if err := utils.GenerateTemplate(packageFile, VENDOR_GOPKG, nil); err != nil {
return utils.NewBuildIfError(err, "Failed to generate template")
}
} else {
utils.Logger.Info("Package file exists in skeleto, skipping adding")
}
getCmd := exec.Command("dep", "ensure", "-v")
utils.CmdInit(getCmd, !c.Vendored, c.AppPath)
utils.Logger.Info("Exec:", "args", getCmd.Args, "env", getCmd.Env, "workingdir", getCmd.Dir)
getOutput, err := getCmd.CombinedOutput()
if err != nil {
return utils.NewBuildIfError(err, string(getOutput))
}
return
}
// Used to generate a new secret key // Used to generate a new secret key
const alphaNumeric = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" const alphaNumeric = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
@@ -180,13 +209,13 @@ func setApplicationPath(c *model.CommandConfig) (err error) {
} }
// If we are running a vendored version of Revel we do not need to check for it. // If we are running a vendored version of Revel we do not need to check for it.
if !c.New.Vendored { if !c.Vendored {
_, err = build.Import(model.RevelImportPath, "", build.FindOnly) _, err = build.Import(model.RevelImportPath, "", build.FindOnly)
if err != nil { if err != nil {
//// Go get the revel project //// Go get the revel project
err = c.PackageResolver(model.RevelImportPath) err = c.PackageResolver(model.RevelImportPath)
if err != nil { if err != nil {
return utils.NewBuildIfError(err, "Failed to fetch revel "+model.RevelImportPath) return utils.NewBuildIfError(err, "Failed to fetch revel " + model.RevelImportPath)
} }
} }
} }
@@ -210,13 +239,13 @@ func setSkeletonPath(c *model.CommandConfig) (err error) {
switch strings.ToLower(sp.Scheme) { switch strings.ToLower(sp.Scheme) {
// TODO Add support for ftp, sftp, scp ?? // TODO Add support for ftp, sftp, scp ??
case "" : case "" :
sp.Scheme="file" sp.Scheme = "file"
fallthrough fallthrough
case "file" : case "file" :
fullpath := sp.String()[7:] fullpath := sp.String()[7:]
if !filepath.IsAbs(fullpath) { if !filepath.IsAbs(fullpath) {
fullpath, err = filepath.Abs(fullpath) fullpath, err = filepath.Abs(fullpath)
if err!=nil { if err != nil {
return return
} }
} }
@@ -251,11 +280,11 @@ func newLoadFromGit(c *model.CommandConfig, sp *url.URL) (err error) {
targetPath := filepath.Join(os.TempDir(), "revel", "skeleton") targetPath := filepath.Join(os.TempDir(), "revel", "skeleton")
os.RemoveAll(targetPath) os.RemoveAll(targetPath)
pathpart := strings.Split(sp.Path, ":") pathpart := strings.Split(sp.Path, ":")
getCmd := exec.Command("git", "clone", sp.Scheme+"://"+sp.Host+pathpart[0], targetPath) getCmd := exec.Command("git", "clone", sp.Scheme + "://" + sp.Host + pathpart[0], targetPath)
utils.Logger.Info("Exec:", "args", getCmd.Args) utils.Logger.Info("Exec:", "args", getCmd.Args)
getOutput, err := getCmd.CombinedOutput() getOutput, err := getCmd.CombinedOutput()
if err != nil { if err != nil {
utils.Logger.Fatal("Abort: could not clone the Skeleton source code: ","output", string(getOutput), "path", c.New.SkeletonPath) utils.Logger.Fatal("Abort: could not clone the Skeleton source code: ", "output", string(getOutput), "path", c.New.SkeletonPath)
} }
outputPath := targetPath outputPath := targetPath
if len(pathpart) > 1 { if len(pathpart) > 1 {

View File

@@ -3,95 +3,42 @@ package main_test
import ( import (
"github.com/revel/cmd/model" "github.com/revel/cmd/model"
"github.com/revel/cmd/revel" "github.com/revel/cmd/revel"
"github.com/revel/cmd/utils"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"io/ioutil"
"os" "os"
"path/filepath"
"testing" "testing"
) )
// test the commands // test the commands
func TestNew(t *testing.T) { func TestNew(t *testing.T) {
a := assert.New(t) a := assert.New(t)
gopath := setup("revel-test-new", a) gopath := setup("revel-test-new", a)
t.Run("New", func(t *testing.T) {
a := assert.New(t)
c := newApp("new-test", model.NEW, nil, a)
a.Nil(main.Commands[model.NEW].RunWith(c), "New failed")
})
t.Run("Path", func(t *testing.T) {
a := assert.New(t)
c := newApp("new/test/a", model.NEW, nil, a)
a.Nil(main.Commands[model.NEW].RunWith(c), "New path failed")
})
t.Run("Path-Duplicate", func(t *testing.T) {
a := assert.New(t)
c := newApp("new/test/b", model.NEW, nil, a)
a.Nil(main.Commands[model.NEW].RunWith(c), "New path failed")
c = newApp("new/test/b", model.NEW, nil, a)
a.NotNil(main.Commands[model.NEW].RunWith(c), "Duplicate path Did Not failed")
})
t.Run("Skeleton-Git", func(t *testing.T) {
a := assert.New(t)
c := newApp("new/test/c/1", model.NEW, nil, a)
c.New.SkeletonPath = "git://github.com/revel/skeletons:basicnsadnsak"
a.NotNil(main.Commands[model.NEW].RunWith(c), "Expected Failed to run with new")
// We need to pick a different path
c = newApp("new/test/c/2", model.NEW, nil, a)
c.New.SkeletonPath = "git://github.com/revel/skeletons:basic/bootstrap4"
a.Nil(main.Commands[model.NEW].RunWith(c), "Failed to run with new skeleton git")
})
if !t.Failed() {
if err := os.RemoveAll(gopath); err != nil {
a.Fail("Failed to remove test path")
}
}
}
// test the commands
func TestNewVendor(t *testing.T) {
a := assert.New(t)
gopath := setup("revel-test-new-vendor", a)
precall := func(c *model.CommandConfig) {
c.New.Vendored = true
}
t.Run("New", func(t *testing.T) { t.Run("New", func(t *testing.T) {
a := assert.New(t) a := assert.New(t)
c := newApp("onlyone/v/a", model.NEW, precall, a) c := newApp("new-test", model.NEW, nil, a)
c.New.Vendored = true
a.Nil(main.Commands[model.NEW].RunWith(c), "New failed") a.Nil(main.Commands[model.NEW].RunWith(c), "New failed")
}) })
t.Run("Test", func(t *testing.T) { t.Run("Path", func(t *testing.T) {
a := assert.New(t) a := assert.New(t)
c := newApp("onlyone/v/a", model.TEST, nil, a) c := newApp("new/test/a", model.NEW, nil, a)
a.Nil(main.Commands[model.TEST].RunWith(c), "Test failed") a.Nil(main.Commands[model.NEW].RunWith(c), "New path failed")
}) })
t.Run("Build", func(t *testing.T) { t.Run("Path-Duplicate", func(t *testing.T) {
a := assert.New(t) a := assert.New(t)
c := newApp("onlyone/v/a", model.BUILD, nil, a) c := newApp("new/test/b", model.NEW, nil, a)
c.Index = model.BUILD a.Nil(main.Commands[model.NEW].RunWith(c), "New path failed")
c.Build.TargetPath = filepath.Join(gopath, "src/onlyone/v/a", "target") c = newApp("new/test/b", model.NEW, nil, a)
a.Nil(main.Commands[model.BUILD].RunWith(c), " Build failed") a.NotNil(main.Commands[model.NEW].RunWith(c), "Duplicate path Did Not failed")
a.True(utils.DirExists(c.Build.TargetPath), "Target folder not made", c.Build.TargetPath)
}) })
t.Run("Package", func(t *testing.T) { t.Run("Skeleton-Git", func(t *testing.T) {
a := assert.New(t) a := assert.New(t)
c := newApp("onlyone/v/a", model.PACKAGE, nil, a) c := newApp("new/test/c/1", model.NEW, nil, a)
c.Package.TargetPath = filepath.Join(gopath, "src/onlyone/v/a", "target.tar.gz") c.New.SkeletonPath = "git://github.com/revel/skeletons:basicnsadnsak"
a.Nil(main.Commands[model.PACKAGE].RunWith(c), "Package Failed") a.NotNil(main.Commands[model.NEW].RunWith(c), "Expected Failed to run with new")
a.True(utils.Exists(c.Package.TargetPath), "Target package not made", c.Package.TargetPath) // We need to pick a different path
}) c = newApp("new/test/c/2", model.NEW, nil, a)
t.Run("TestVendorDir", func(t *testing.T) { c.New.SkeletonPath = "git://github.com/revel/skeletons:basic/bootstrap4"
// Check to see that no additional packages were downloaded outside the vendor folder a.Nil(main.Commands[model.NEW].RunWith(c), "Failed to run with new skeleton git")
files, err := ioutil.ReadDir(gopath)
a.Nil(err, "Failed to read gopath folder")
// bin/ onlyone/ pkg/ src/
a.Equal(3, len(files), "Expected single file in "+gopath)
files, err = ioutil.ReadDir(filepath.Join(gopath, "src"))
a.Nil(err, "Failed to read src folder")
a.Equal(1, len(files), "Expected single file in source folder", filepath.Join(gopath, "src"))
}) })
if !t.Failed() { if !t.Failed() {
if err := os.RemoveAll(gopath); err != nil { if err := os.RemoveAll(gopath); err != nil {
@@ -99,3 +46,4 @@ func TestNewVendor(t *testing.T) {
} }
} }
} }

View File

@@ -40,6 +40,9 @@ func init() {
// Called when unable to parse the command line automatically and assumes an old launch // Called when unable to parse the command line automatically and assumes an old launch
func updatePackageConfig(c *model.CommandConfig, args []string) bool { func updatePackageConfig(c *model.CommandConfig, args []string) bool {
c.Index = model.PACKAGE c.Index = model.PACKAGE
if len(args)==0 && c.Package.ImportPath!="" {
return true
}
c.Package.ImportPath = args[0] c.Package.ImportPath = args[0]
if len(args) > 1 { if len(args) > 1 {
c.Package.Mode = args[1] c.Package.Mode = args[1]
@@ -58,7 +61,7 @@ func packageApp(c *model.CommandConfig) (err error) {
} }
appImportPath := c.ImportPath appImportPath := c.ImportPath
revel_paths, err := model.NewRevelPaths(mode, appImportPath, "", model.NewWrappedRevelCallback(nil, c.PackageResolver)) revel_paths, err := model.NewRevelPaths(mode, appImportPath, c.AppPath, model.NewWrappedRevelCallback(nil, c.PackageResolver))
if err != nil { if err != nil {
return return
} }
@@ -86,7 +89,9 @@ func packageApp(c *model.CommandConfig) (err error) {
} }
c.Build.TargetPath = tmpDir c.Build.TargetPath = tmpDir
c.Build.CopySource = c.Package.CopySource c.Build.CopySource = c.Package.CopySource
buildApp(c) if err = buildApp(c); err != nil {
return
}
// Create the zip file. // Create the zip file.

View File

@@ -71,27 +71,27 @@ func main() {
wd, _ := os.Getwd() wd, _ := os.Getwd()
utils.InitLogger(wd, logger.LvlError) utils.InitLogger(wd, logger.LvlError)
parser := flags.NewParser(c, flags.HelpFlag|flags.PassDoubleDash) parser := flags.NewParser(c, flags.HelpFlag | flags.PassDoubleDash)
if len(os.Args)<2 { if len(os.Args) < 2 {
parser.WriteHelp(os.Stdout) parser.WriteHelp(os.Stdout)
os.Exit(1) os.Exit(1)
} }
if err := ParseArgs(c, parser, os.Args[1:]); err != nil { if err := ParseArgs(c, parser, os.Args[1:]); err != nil {
fmt.Fprint(os.Stderr, err.Error() +"\n") fmt.Fprint(os.Stderr, err.Error() + "\n")
os.Exit(1) os.Exit(1)
} }
// Switch based on the verbose flag // Switch based on the verbose flag
if len(c.Verbose)>1 { if len(c.Verbose) > 1 {
utils.InitLogger(wd, logger.LvlDebug) utils.InitLogger(wd, logger.LvlDebug)
} else if len(c.Verbose)>0 { } else if len(c.Verbose) > 0 {
utils.InitLogger(wd, logger.LvlInfo) utils.InitLogger(wd, logger.LvlInfo)
} else { } else {
utils.InitLogger(wd, logger.LvlWarn) utils.InitLogger(wd, logger.LvlWarn)
} }
if err := c.UpdateImportPath();err!=nil { if err := c.UpdateImportPath(); err != nil {
utils.Logger.Error(err.Error()) utils.Logger.Error(err.Error())
parser.WriteHelp(os.Stdout) parser.WriteHelp(os.Stdout)
os.Exit(1) os.Exit(1)
@@ -101,13 +101,13 @@ func main() {
println("Revel executing:", command.Short) println("Revel executing:", command.Short)
// Setting go paths // Setting go paths
c.InitGoPaths() // c.InitGoPaths()
// Setup package resolver // Setup package resolver
c.InitPackageResolver() c.InitPackageResolver()
if err := command.RunWith(c); err != nil { if err := command.RunWith(c); err != nil {
utils.Logger.Error("Unable to execute","error",err) utils.Logger.Error("Unable to execute", "error", err)
os.Exit(1) os.Exit(1)
} }
} }
@@ -142,13 +142,10 @@ func ParseArgs(c *model.CommandConfig, parser *flags.Parser, args []string) (err
} }
} }
if len(extraArgs) > 0 { if !Commands[c.Index].UpdateConfig(c, extraArgs) {
utils.Logger.Info("Found additional arguements, setting them") buffer := &bytes.Buffer{}
if !Commands[c.Index].UpdateConfig(c, extraArgs) { parser.WriteHelp(buffer)
buffer := &bytes.Buffer{} err = fmt.Errorf("Invalid command line arguements %v\n%s", extraArgs, buffer.String())
parser.WriteHelp(buffer)
err = fmt.Errorf("Invalid command line arguements %v\n%s", extraArgs, buffer.String())
}
} }
return return

View File

@@ -6,7 +6,7 @@ package main
import ( import (
"strconv" "strconv"
"encoding/json"
"fmt" "fmt"
"github.com/revel/cmd/harness" "github.com/revel/cmd/harness"
"github.com/revel/cmd/model" "github.com/revel/cmd/model"
@@ -106,7 +106,9 @@ func updateRunConfig(c *model.CommandConfig, args []string) bool {
} }
case 0: case 0:
// Attempt to set the import path to the current working director. // Attempt to set the import path to the current working director.
c.Run.ImportPath,_ = os.Getwd() if c.Run.ImportPath == "" {
c.Run.ImportPath, _ = os.Getwd()
}
} }
c.Index = model.RUN c.Index = model.RUN
return true return true
@@ -114,7 +116,7 @@ func updateRunConfig(c *model.CommandConfig, args []string) bool {
// Returns true if this is an absolute path or a relative gopath // Returns true if this is an absolute path or a relative gopath
func runIsImportPath(pathToCheck string) bool { func runIsImportPath(pathToCheck string) bool {
if _, err := build.Import(pathToCheck, "", build.FindOnly);err==nil { if _, err := build.Import(pathToCheck, "", build.FindOnly); err == nil {
return true return true
} }
return filepath.IsAbs(pathToCheck) return filepath.IsAbs(pathToCheck)
@@ -126,7 +128,7 @@ func runApp(c *model.CommandConfig) (err error) {
c.Run.Mode = "dev" c.Run.Mode = "dev"
} }
revel_path, err := model.NewRevelPaths(c.Run.Mode, c.ImportPath, "", model.NewWrappedRevelCallback(nil, c.PackageResolver)) revel_path, err := model.NewRevelPaths(c.Run.Mode, c.ImportPath, c.AppPath, model.NewWrappedRevelCallback(nil, c.PackageResolver))
if err != nil { if err != nil {
return utils.NewBuildIfError(err, "Revel paths") return utils.NewBuildIfError(err, "Revel paths")
} }
@@ -157,7 +159,11 @@ func runApp(c *model.CommandConfig) (err error) {
utils.Logger.Errorf("Failed to build app: %s", err) utils.Logger.Errorf("Failed to build app: %s", err)
} }
app.Port = revel_path.HTTPPort app.Port = revel_path.HTTPPort
runMode := fmt.Sprintf(`{"mode":"%s", "specialUseFlag":%v}`, app.Paths.RunMode, c.Verbose) var paths []byte
if len(app.PackagePathMap) > 0 {
paths, _ = json.Marshal(app.PackagePathMap)
}
runMode := fmt.Sprintf(`{"mode":"%s", "specialUseFlag":%v,"packagePathMap":%s}`, app.Paths.RunMode, c.Verbose, string(paths))
if c.HistoricMode { if c.HistoricMode {
runMode = revel_path.RunMode runMode = revel_path.RunMode
} }

View File

@@ -55,6 +55,10 @@ func init() {
// Called to update the config command with from the older stype // Called to update the config command with from the older stype
func updateTestConfig(c *model.CommandConfig, args []string) bool { func updateTestConfig(c *model.CommandConfig, args []string) bool {
c.Index = model.TEST c.Index = model.TEST
if len(args) == 0 && c.Test.ImportPath != "" {
return true
}
// The full test runs // The full test runs
// revel test <import path> (run mode) (suite(.function)) // revel test <import path> (run mode) (suite(.function))
if len(args) < 1 { if len(args) < 1 {
@@ -78,7 +82,7 @@ func testApp(c *model.CommandConfig) (err error) {
} }
// Find and parse app.conf // Find and parse app.conf
revel_path, err := model.NewRevelPaths(mode, c.ImportPath, "", model.NewWrappedRevelCallback(nil, c.PackageResolver)) revel_path, err := model.NewRevelPaths(mode, c.ImportPath, c.AppPath, model.NewWrappedRevelCallback(nil, c.PackageResolver))
if err != nil { if err != nil {
return return
} }
@@ -95,7 +99,7 @@ func testApp(c *model.CommandConfig) (err error) {
} }
// Direct all the output into a file in the test-results directory. // 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) file, err := os.OpenFile(filepath.Join(resultPath, "app.log"), os.O_CREATE | os.O_WRONLY | os.O_APPEND, 0666)
if err != nil { if err != nil {
return utils.NewBuildError("Failed to create test result log file: ", "error", err) return utils.NewBuildError("Failed to create test result log file: ", "error", err)
} }
@@ -104,11 +108,16 @@ func testApp(c *model.CommandConfig) (err error) {
if reverr != nil { if reverr != nil {
return utils.NewBuildIfError(reverr, "Error building: ") return utils.NewBuildIfError(reverr, "Error building: ")
} }
runMode := fmt.Sprintf(`{"mode":"%s","testModeFlag":true, "specialUseFlag":%v}`, app.Paths.RunMode, c.Verbose) var paths []byte
if len(app.PackagePathMap) > 0 {
paths, _ = json.Marshal(app.PackagePathMap)
}
runMode := fmt.Sprintf(`{"mode":"%s", "specialUseFlag":%v,"packagePathMap":%s}`, app.Paths.RunMode, c.Verbose, string(paths))
if c.HistoricMode { if c.HistoricMode {
runMode = app.Paths.RunMode runMode = app.Paths.RunMode
} }
cmd := app.Cmd(runMode) cmd := app.Cmd(runMode)
cmd.Dir = c.AppPath
cmd.Stderr = io.MultiWriter(cmd.Stderr, file) cmd.Stderr = io.MultiWriter(cmd.Stderr, file)
cmd.Stdout = io.MultiWriter(cmd.Stderr, file) cmd.Stdout = io.MultiWriter(cmd.Stderr, file)
@@ -225,7 +234,7 @@ func filterTestSuites(suites *[]tests.TestSuiteDesc, suiteArgument string) *[]te
// in case it hasn't finished starting up yet. // in case it hasn't finished starting up yet.
func getTestsList(baseURL string) (*[]tests.TestSuiteDesc, error) { func getTestsList(baseURL string) (*[]tests.TestSuiteDesc, error) {
var ( var (
err error err error
resp *http.Response resp *http.Response
testSuites []tests.TestSuiteDesc testSuites []tests.TestSuiteDesc
) )
@@ -258,7 +267,7 @@ func getTestsList(baseURL string) (*[]tests.TestSuiteDesc, error) {
func runTestSuites(paths *model.RevelContainer, baseURL, resultPath string, testSuites *[]tests.TestSuiteDesc) (*[]tests.TestSuiteResult, bool) { 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 // 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") resultFilePath := filepath.Join(paths.ModulePathMap["testrunner"].Path, "app", "views", "TestRunner/SuiteResult.html")
var ( var (
overallSuccess = true overallSuccess = true

View File

@@ -29,10 +29,10 @@ import (
type ( type (
// The version container // The version container
VersionCommand struct { VersionCommand struct {
Command *model.CommandConfig // The command Command *model.CommandConfig // The command
revelVersion *model.Version // The Revel framework version revelVersion *model.Version // The Revel framework version
modulesVersion *model.Version // The Revel modules version modulesVersion *model.Version // The Revel modules version
cmdVersion *model.Version // The tool version cmdVersion *model.Version // The tool version
} }
) )
@@ -74,10 +74,10 @@ func (v *VersionCommand) RunWith(c *model.CommandConfig) (err error) {
versionInfo := "" versionInfo := ""
for x := 0; x < 2 && needsUpdates; x++ { for x := 0; x < 2 && needsUpdates; x++ {
needsUpdates = false needsUpdates = false
versionInfo, needsUpdates = v.doRepoCheck(x==0) versionInfo, needsUpdates = v.doRepoCheck(x == 0)
} }
fmt.Println(versionInfo) fmt.Printf("%s\n\nGo Location:%s\n\n", versionInfo, c.GoCmd)
cmd := exec.Command(c.GoCmd, "version") cmd := exec.Command(c.GoCmd, "version")
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
if e := cmd.Start(); e != nil { if e := cmd.Start(); e != nil {
@@ -112,7 +112,7 @@ func (v *VersionCommand) doRepoCheck(updateLibs bool) (versionInfo string, needs
// Only do an update on the first loop, and if specified to update // Only do an update on the first loop, and if specified to update
shouldUpdate := updateLibs && v.Command.Version.Update shouldUpdate := updateLibs && v.Command.Version.Update
if v.Command.Version.Update { if v.Command.Version.Update {
if localVersion == nil || (versonFromRepo != nil && versonFromRepo.Newer(localVersion)) { if localVersion == nil || (versonFromRepo != nil && versonFromRepo.Newer(localVersion)) {
needsUpdate = true needsUpdate = true
if shouldUpdate { if shouldUpdate {
v.doUpdate(title, repo, localVersion, versonFromRepo) v.doUpdate(title, repo, localVersion, versonFromRepo)
@@ -127,7 +127,7 @@ func (v *VersionCommand) doRepoCheck(updateLibs bool) (versionInfo string, needs
// Checks for updates if needed // Checks for updates if needed
func (v *VersionCommand) doUpdate(title, repo string, local, remote *model.Version) { func (v *VersionCommand) doUpdate(title, repo string, local, remote *model.Version) {
utils.Logger.Info("Updating package", "package", title, "repo",repo) utils.Logger.Info("Updating package", "package", title, "repo", repo)
fmt.Println("Attempting to update package", title) fmt.Println("Attempting to update package", title)
if err := v.Command.PackageResolver(repo); err != nil { if err := v.Command.PackageResolver(repo); err != nil {
utils.Logger.Error("Unable to update repo", "repo", repo, "error", err) utils.Logger.Error("Unable to update repo", "repo", repo, "error", err)
@@ -228,34 +228,26 @@ func (v *VersionCommand) versionFromBytes(sourceStream []byte) (version *model.V
} }
// Fetch the local version of revel from the file system // Fetch the local version of revel from the file system
func (v *VersionCommand) updateLocalVersions() { func (v *VersionCommand) updateLocalVersions() {
v.cmdVersion = &model.Version{} v.cmdVersion = &model.Version{}
v.cmdVersion.ParseVersion(cmd.Version) v.cmdVersion.ParseVersion(cmd.Version)
v.cmdVersion.BuildDate = cmd.BuildDate v.cmdVersion.BuildDate = cmd.BuildDate
v.cmdVersion.MinGoVersion = cmd.MinimumGoVersion v.cmdVersion.MinGoVersion = cmd.MinimumGoVersion
var modulePath, revelPath string pathMap, err := utils.FindSrcPaths(v.Command.AppPath, []string{model.RevelImportPath, model.RevelModulesImportPath}, v.Command.PackageResolver)
_, revelPath, err := utils.FindSrcPaths(v.Command.ImportPath, model.RevelImportPath, v.Command.PackageResolver)
if err != nil { if err != nil {
utils.Logger.Warn("Unable to extract version information from Revel library", "error,err") utils.Logger.Warn("Unable to extract version information from Revel library", "path", pathMap[model.RevelImportPath], "error", err)
return return
} }
revelPath = revelPath + model.RevelImportPath utils.Logger.Info("Fullpath to revel modules", "dir", pathMap[model.RevelImportPath])
utils.Logger.Info("Fullpath to revel", "dir", revelPath) v.revelVersion, err = v.versionFromFilepath(pathMap[model.RevelImportPath])
v.revelVersion, err = v.versionFromFilepath(revelPath)
if err != nil { if err != nil {
utils.Logger.Warn("Unable to extract version information from Revel", "error,err") utils.Logger.Warn("Unable to extract version information from Revel", "error,err")
} }
_, modulePath, err = utils.FindSrcPaths(v.Command.ImportPath, model.RevelModulesImportPath, v.Command.PackageResolver) v.modulesVersion, err = v.versionFromFilepath(pathMap[model.RevelModulesImportPath])
if err != nil { if err != nil {
utils.Logger.Warn("Unable to extract version information from Revel library", "error,err") utils.Logger.Warn("Unable to extract version information from Revel Modules", "path", pathMap[model.RevelModulesImportPath], "error", err)
return
}
modulePath = modulePath + model.RevelModulesImportPath
v.modulesVersion, err = v.versionFromFilepath(modulePath)
if err != nil {
utils.Logger.Warn("Unable to extract version information from Revel Modules", "error", err)
} }
return return

View File

@@ -107,7 +107,7 @@ func describeSuite(testSuite interface{}) TestSuiteDesc {
} }
// errorSummary gets an error and returns its summary in human readable format. // errorSummary gets an error and returns its summary in human readable format.
func errorSummary(err *utils.Error) (message string) { func errorSummary(err *utils.SourceError) (message string) {
expectedPrefix := "(expected)" expectedPrefix := "(expected)"
actualPrefix := "(actual)" actualPrefix := "(actual)"
errDesc := err.Description errDesc := err.Description

View File

@@ -3,6 +3,8 @@ package utils
import ( import (
"fmt" "fmt"
"github.com/revel/cmd/logger" "github.com/revel/cmd/logger"
"strconv"
"regexp"
) )
type ( type (
@@ -43,3 +45,60 @@ func NewBuildIfError(err error, message string, args ...interface{}) (b error) {
func (b *BuildError) Error() string { func (b *BuildError) Error() string {
return fmt.Sprint(b.Message, b.Args) return fmt.Sprint(b.Message, b.Args)
} }
// Parse the output of the "go build" command.
// Return a detailed Error.
func NewCompileError(importPath, errorLink string, error error) *SourceError {
// Get the stack from the error
errorMatch := regexp.MustCompile(`(?m)^([^:#]+):(\d+):(\d+:)? (.*)$`).
FindSubmatch([]byte(error.Error()))
if errorMatch == nil {
errorMatch = regexp.MustCompile(`(?m)^(.*?):(\d+):\s(.*?)$`).FindSubmatch([]byte(error.Error()))
if errorMatch == nil {
Logger.Error("Failed to parse build errors", "error", error)
return &SourceError{
SourceType: "Go code",
Title: "Go Compilation Error",
Description: "See console for build error.",
}
}
errorMatch = append(errorMatch, errorMatch[3])
Logger.Error("Build errors", "errors", error)
}
// Read the source for the offending file.
var (
relFilename = string(errorMatch[1]) // e.g. "src/revel/sample/app/controllers/app.go"
absFilename = relFilename
line, _ = strconv.Atoi(string(errorMatch[2]))
description = string(errorMatch[4])
compileError = &SourceError{
SourceType: "Go code",
Title: "Go Compilation Error",
Path: relFilename,
Description: description,
Line: line,
}
)
// errorLink := paths.Config.StringDefault("error.link", "")
if errorLink != "" {
compileError.SetLink(errorLink)
}
fileStr, err := ReadLines(absFilename)
if err != nil {
compileError.MetaError = absFilename + ": " + err.Error()
Logger.Info("Unable to readlines " + compileError.MetaError, "error", err)
return compileError
}
compileError.SourceLines = fileStr
return compileError
}

View File

@@ -10,25 +10,27 @@ import (
) )
// Initialize the command based on the GO environment // Initialize the command based on the GO environment
func CmdInit(c *exec.Cmd, basePath string) { func CmdInit(c *exec.Cmd, addGoPath bool, basePath string) {
c.Dir = basePath c.Dir = basePath
// Dep does not like paths that are not real, convert all paths in go to real paths // Dep does not like paths that are not real, convert all paths in go to real paths
realPath := &bytes.Buffer{} realPath := &bytes.Buffer{}
for _, p := range filepath.SplitList(build.Default.GOPATH) { if addGoPath {
rp,_ := filepath.EvalSymlinks(p) for _, p := range filepath.SplitList(build.Default.GOPATH) {
if realPath.Len() > 0 { rp, _ := filepath.EvalSymlinks(p)
realPath.WriteString(string(filepath.ListSeparator)) if realPath.Len() > 0 {
realPath.WriteString(string(filepath.ListSeparator))
}
realPath.WriteString(rp)
} }
realPath.WriteString(rp) // Go 1.8 fails if we do not include the GOROOT
c.Env = []string{"GOPATH=" + realPath.String(), "GOROOT=" + os.Getenv("GOROOT")}
} }
// Go 1.8 fails if we do not include the GOROOT
c.Env = []string{"GOPATH=" + realPath.String(), "GOROOT="+ os.Getenv("GOROOT")}
// Fetch the rest of the env variables // Fetch the rest of the env variables
for _, e := range os.Environ() { for _, e := range os.Environ() {
pair := strings.Split(e, "=") pair := strings.Split(e, "=")
if pair[0]=="GOPATH" || pair[0]=="GOROOT" { if pair[0] == "GOPATH" || pair[0] == "GOROOT" {
continue continue
} }
c.Env = append(c.Env,e) c.Env = append(c.Env, e)
} }
} }

View File

@@ -8,7 +8,7 @@ import (
// The error is a wrapper for the // The error is a wrapper for the
type ( type (
Error struct { SourceError struct {
SourceType string // The type of source that failed to build. SourceType string // The type of source that failed to build.
Title, Path, Description string // Description of the error, as presented to the user. Title, Path, Description string // Description of the error, as presented to the user.
Line, Column int // Where the error was encountered. Line, Column int // Where the error was encountered.
@@ -24,8 +24,8 @@ type (
} }
) )
// Return a new error object // Return a new error object
func NewError(source, title,path,description string) *Error { func NewError(source, title, path, description string) *SourceError {
return &Error { return &SourceError{
SourceType:source, SourceType:source,
Title:title, Title:title,
Path:path, Path:path,
@@ -34,7 +34,7 @@ func NewError(source, title,path,description string) *Error {
} }
// Creates a link based on the configuration setting "errors.link" // Creates a link based on the configuration setting "errors.link"
func (e *Error) SetLink(errorLink string) { func (e *SourceError) SetLink(errorLink string) {
errorLink = strings.Replace(errorLink, "{{Path}}", e.Path, -1) errorLink = strings.Replace(errorLink, "{{Path}}", e.Path, -1)
errorLink = strings.Replace(errorLink, "{{Line}}", strconv.Itoa(e.Line), -1) errorLink = strings.Replace(errorLink, "{{Line}}", strconv.Itoa(e.Line), -1)
@@ -44,7 +44,7 @@ func (e *Error) SetLink(errorLink string) {
// Error method constructs a plaintext version of the error, taking // Error method constructs a plaintext version of the error, taking
// account that fields are optionally set. Returns e.g. Compilation Error // account that fields are optionally set. Returns e.g. Compilation Error
// (in views/header.html:51): expected right delim in end; got "}" // (in views/header.html:51): expected right delim in end; got "}"
func (e *Error) Error() string { func (e *SourceError) Error() string {
if e == nil { if e == nil {
panic("opps") panic("opps")
} }
@@ -69,7 +69,7 @@ func (e *Error) Error() string {
// ContextSource method returns a snippet of the source around // ContextSource method returns a snippet of the source around
// where the error occurred. // where the error occurred.
func (e *Error) ContextSource() []SourceLine { func (e *SourceError) ContextSource() []SourceLine {
if e.SourceLines == nil { if e.SourceLines == nil {
return nil return nil
} }
@@ -82,7 +82,7 @@ func (e *Error) ContextSource() []SourceLine {
end = len(e.SourceLines) end = len(e.SourceLines)
} }
lines := make([]SourceLine, end-start) lines := make([]SourceLine, end - start)
for i, src := range e.SourceLines[start:end] { for i, src := range e.SourceLines[start:end] {
fileLine := start + i + 1 fileLine := start + i + 1
lines[i] = SourceLine{src, fileLine, fileLine == e.Line} lines[i] = SourceLine{src, fileLine, fileLine == e.Line}

View File

@@ -4,15 +4,15 @@ import (
"archive/tar" "archive/tar"
"bytes" "bytes"
"compress/gzip" "compress/gzip"
"errors"
"fmt" "fmt"
"go/build" "errors"
"html/template" "html/template"
"io" "io"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"golang.org/x/tools/go/packages"
) )
// DirExists returns true if the given path exists and is a directory. // DirExists returns true if the given path exists and is a directory.
@@ -109,7 +109,7 @@ func GenerateTemplate(filename, templateSource string, args map[string]interface
func RenderTemplate(destPath, srcPath string, data interface{}) (err error) { func RenderTemplate(destPath, srcPath string, data interface{}) (err error) {
tmpl, err := template.ParseFiles(srcPath) tmpl, err := template.ParseFiles(srcPath)
if err != nil { if err != nil {
return NewBuildIfError(err, "Failed to parse template "+srcPath) return NewBuildIfError(err, "Failed to parse template " + srcPath)
} }
f, err := os.Create(destPath) f, err := os.Create(destPath)
@@ -119,12 +119,12 @@ func RenderTemplate(destPath, srcPath string, data interface{}) (err error) {
err = tmpl.Execute(f, data) err = tmpl.Execute(f, data)
if err != nil { if err != nil {
return NewBuildIfError(err, "Failed to Render template "+srcPath) return NewBuildIfError(err, "Failed to Render template " + srcPath)
} }
err = f.Close() err = f.Close()
if err != nil { if err != nil {
return NewBuildIfError(err, "Failed to close file stream "+destPath) return NewBuildIfError(err, "Failed to close file stream " + destPath)
} }
return return
} }
@@ -133,12 +133,12 @@ func RenderTemplate(destPath, srcPath string, data interface{}) (err error) {
func RenderTemplateToStream(output io.Writer, srcPath []string, data interface{}) (err error) { func RenderTemplateToStream(output io.Writer, srcPath []string, data interface{}) (err error) {
tmpl, err := template.ParseFiles(srcPath...) tmpl, err := template.ParseFiles(srcPath...)
if err != nil { if err != nil {
return NewBuildIfError(err, "Failed to parse template "+srcPath[0]) return NewBuildIfError(err, "Failed to parse template " + srcPath[0])
} }
err = tmpl.Execute(output, data) err = tmpl.Execute(output, data)
if err != nil { if err != nil {
return NewBuildIfError(err, "Failed to render template "+srcPath[0]) return NewBuildIfError(err, "Failed to render template " + srcPath[0])
} }
return return
} }
@@ -150,7 +150,7 @@ func MustChmod(filename string, mode os.FileMode) {
// Called if panic // Called if panic
func PanicOnError(err error, msg string) { func PanicOnError(err error, msg string) {
if revErr, ok := err.(*Error); (ok && revErr != nil) || (!ok && err != nil) { if revErr, ok := err.(*SourceError); (ok && revErr != nil) || (!ok && err != nil) {
Logger.Panicf("Abort: %s: %s %s", msg, revErr, err) Logger.Panicf("Abort: %s: %s %s", msg, revErr, err)
} }
} }
@@ -181,7 +181,7 @@ func CopyDir(destDir, srcDir string, data map[string]interface{}) error {
if info.IsDir() { if info.IsDir() {
err := os.MkdirAll(filepath.Join(destDir, relSrcPath), 0777) err := os.MkdirAll(filepath.Join(destDir, relSrcPath), 0777)
if !os.IsExist(err) { if !os.IsExist(err) {
return NewBuildIfError(err, "Failed to create directory", "path", destDir+"/"+relSrcPath) return NewBuildIfError(err, "Failed to create directory", "path", destDir + "/" + relSrcPath)
} }
return nil return nil
} }
@@ -189,7 +189,7 @@ func CopyDir(destDir, srcDir string, data map[string]interface{}) error {
// If this file ends in ".template", render it as a template. // If this file ends in ".template", render it as a template.
if strings.HasSuffix(relSrcPath, ".template") { if strings.HasSuffix(relSrcPath, ".template") {
return RenderTemplate(destPath[:len(destPath)-len(".template")], srcPath, data) return RenderTemplate(destPath[:len(destPath) - len(".template")], srcPath, data)
} }
// Else, just copy it over. // Else, just copy it over.
@@ -218,7 +218,7 @@ func fsWalk(fname string, linkName string, walkFn filepath.WalkFunc) error {
path = filepath.Join(linkName, name) path = filepath.Join(linkName, name)
if err == nil && info.Mode()&os.ModeSymlink == os.ModeSymlink { if err == nil && info.Mode() & os.ModeSymlink == os.ModeSymlink {
var symlinkPath string var symlinkPath string
symlinkPath, err = filepath.EvalSymlinks(path) symlinkPath, err = filepath.EvalSymlinks(path)
if err != nil { if err != nil {
@@ -320,49 +320,63 @@ func Empty(dirname string) bool {
} }
// Find the full source dir for the import path, uses the build.Default.GOPATH to search for the directory // 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) { func FindSrcPaths(appPath string, packageList []string, packageResolver func(pkgName string) error) (sourcePathsmap map[string]string, err error) {
var ( sourcePathsmap, missingList, err := findSrcPaths(appPath, packageList)
gopaths = filepath.SplitList(build.Default.GOPATH) if err != nil && packageResolver != nil {
goroot = build.Default.GOROOT for _, item := range missingList {
) if err = packageResolver(item); err != nil {
return
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 sourcePathsmap, missingList, err = findSrcPaths(appPath, packageList)
} }
Logger.Info("Seeking remote package","using",appImportPath, "remote",revelImportPath) if err != nil && len(missingList) > 0 {
revelPkg, err := build.Default.Import(revelImportPath, appPkgDir, build.FindOnly) for _, missing := range missingList {
if err != nil { Logger.Error("Unable to import this package", "package", missing)
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
}
var NO_APP_FOUND = errors.New("No app found")
var NO_REVEL_FOUND = errors.New("No revel found")
// Find the full source dir for the import path, uses the build.Default.GOPATH to search for the directory
func findSrcPaths(appPath string, packagesList []string) (sourcePathsmap map[string]string, missingList[] string, err error) {
// Use packages to fetch
// by not specifying env, we will use the default env
config := &packages.Config{
Mode: packages.NeedName | packages.NeedFiles,
Dir:appPath,
}
sourcePathsmap = map[string]string{}
pkgs, err := packages.Load(config, packagesList...)
Logger.Info("Loaded packages ", "len results", len(pkgs), "error", err, "basedir", appPath)
for _, packageName := range packagesList {
found := false
log := Logger.New("seeking", packageName)
for _, pck := range pkgs {
log.Info("Found package", "package", pck.ID)
if pck.ID == packageName {
if pck.Errors != nil && len(pck.Errors) > 0 {
log.Info("Error ", "count", len(pck.Errors), "App Import Path", pck.ID, "errors", pck.Errors)
}
//a,_ := pck.MarshalJSON()
log.Info("Found ", "count", len(pck.GoFiles), "App Import Path", pck.ID, "apppath", appPath)
sourcePathsmap[packageName] = filepath.Dir(pck.GoFiles[0])
found = true
}
}
if !found {
if packageName == "github.com/revel/revel" {
err = NO_REVEL_FOUND
} else {
err = NO_APP_FOUND
}
missingList = append(missingList, packageName)
}
}
return return
} }

View File

@@ -6,7 +6,7 @@ package cmd
const ( const (
// Version current Revel version // Version current Revel version
Version = "0.21.1" Version = "1.0.0-dev"
// BuildDate latest commit/release date // BuildDate latest commit/release date
BuildDate = "2018-10-30" BuildDate = "2018-10-30"

View File

@@ -12,7 +12,7 @@ import (
"github.com/revel/cmd/model" "github.com/revel/cmd/model"
"github.com/revel/cmd/utils" "github.com/revel/cmd/utils"
"gopkg.in/fsnotify/fsnotify.v1" "github.com/fsnotify/fsnotify"
"time" "time"
) )
@@ -20,7 +20,7 @@ import (
type Listener interface { type Listener interface {
// Refresh is invoked by the watcher on relevant filesystem events. // Refresh is invoked by the watcher on relevant filesystem events.
// If the listener returns an error, it is served to the user on the current request. // If the listener returns an error, it is served to the user on the current request.
Refresh() *utils.Error Refresh() *utils.SourceError
} }
// DiscerningListener allows the receiver to selectively watch files. // DiscerningListener allows the receiver to selectively watch files.
@@ -44,7 +44,7 @@ type Watcher struct {
paths *model.RevelContainer paths *model.RevelContainer
refreshTimer *time.Timer // The timer to countdown the next refresh refreshTimer *time.Timer // The timer to countdown the next refresh
timerMutex *sync.Mutex // A mutex to prevent concurrent updates timerMutex *sync.Mutex // A mutex to prevent concurrent updates
refreshChannel chan *utils.Error refreshChannel chan *utils.SourceError
refreshChannelCount int refreshChannelCount int
refreshTimerMS time.Duration // The number of milliseconds between refreshing builds refreshTimerMS time.Duration // The number of milliseconds between refreshing builds
} }
@@ -61,7 +61,7 @@ func NewWatcher(paths *model.RevelContainer, eagerRefresh bool) *Watcher {
paths.Config.BoolDefault("watch", true) && paths.Config.BoolDefault("watch", true) &&
paths.Config.StringDefault("watch.mode", "normal") == "eager", paths.Config.StringDefault("watch.mode", "normal") == "eager",
timerMutex: &sync.Mutex{}, timerMutex: &sync.Mutex{},
refreshChannel: make(chan *utils.Error, 10), refreshChannel: make(chan *utils.SourceError, 10),
refreshChannelCount: 0, refreshChannelCount: 0,
} }
} }
@@ -178,7 +178,7 @@ func (w *Watcher) NotifyWhenUpdated(listener Listener, watcher *fsnotify.Watcher
// Notify causes the watcher to forward any change events to listeners. // Notify causes the watcher to forward any change events to listeners.
// It returns the first (if any) error returned. // It returns the first (if any) error returned.
func (w *Watcher) Notify() *utils.Error { func (w *Watcher) Notify() *utils.SourceError {
if w.serial { if w.serial {
// Serialize Notify() calls. // Serialize Notify() calls.
w.notifyMutex.Lock() w.notifyMutex.Lock()
@@ -207,7 +207,7 @@ func (w *Watcher) Notify() *utils.Error {
utils.Logger.Info("Watcher:Notify refresh state", "Current Index", i, " last error index", w.lastError) utils.Logger.Info("Watcher:Notify refresh state", "Current Index", i, " last error index", w.lastError)
if w.forceRefresh || refresh || w.lastError == i { if w.forceRefresh || refresh || w.lastError == i {
var err *utils.Error var err *utils.SourceError
if w.serial { if w.serial {
err = listener.Refresh() err = listener.Refresh()
} else { } else {
@@ -229,7 +229,7 @@ func (w *Watcher) Notify() *utils.Error {
// Build a queue for refresh notifications // Build a queue for refresh notifications
// this will not return until one of the queue completes // this will not return until one of the queue completes
func (w *Watcher) notifyInProcess(listener Listener) (err *utils.Error) { func (w *Watcher) notifyInProcess(listener Listener) (err *utils.SourceError) {
shouldReturn := false shouldReturn := false
// This code block ensures that either a timer is created // This code block ensures that either a timer is created
// or that a process would be added the the h.refreshChannel // or that a process would be added the the h.refreshChannel