mirror of
https://github.com/kevin-DL/revel-cmd.git
synced 2026-01-12 02:55:16 +00:00
Compare commits
32 Commits
release/v0
...
go-mod
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d9df256a0 | ||
|
|
ad694c0fb0 | ||
|
|
fb4b56513a | ||
|
|
20d5766eb6 | ||
|
|
0920905a0c | ||
|
|
31cb64e496 | ||
|
|
33abc47c7a | ||
|
|
86736d6e43 | ||
|
|
07d67846c1 | ||
|
|
c1aee24445 | ||
|
|
f2b54f5a69 | ||
|
|
3f54665d4e | ||
|
|
548cbc1764 | ||
|
|
9a9511d28f | ||
|
|
acb8fb631b | ||
|
|
d2014633af | ||
|
|
773f6889b4 | ||
|
|
ca4cfa567e | ||
|
|
436869049c | ||
|
|
cf2e617618 | ||
|
|
424474a035 | ||
|
|
531aa1e209 | ||
|
|
83dfdb8ad2 | ||
|
|
6d8fcd90c1 | ||
|
|
aa459c1b66 | ||
|
|
0b23b3e494 | ||
|
|
3f65e1ef41 | ||
|
|
7dce3d8967 | ||
|
|
149f9f992b | ||
|
|
dfb08d9bd2 | ||
|
|
98e771cd01 | ||
|
|
5c8d5bca7f |
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"GOLANG": {
|
||||
"ABC":[25, 35, 50, 70],
|
||||
"ABC":[33, 38, 50, 70],
|
||||
"ARITY":[5,6,7,8],
|
||||
"BLOCK_NESTING":[7, 9, 11, 13],
|
||||
"CYCLO":[20, 30, 45, 60],
|
||||
"TOO_MANY_IVARS": [20, 25, 40, 45],
|
||||
"BLOCK_NESTING":[9, 10, 12, 13],
|
||||
"CYCLO":[30, 35, 45, 60],
|
||||
"TOO_MANY_IVARS": [28, 30, 40, 45],
|
||||
"TOO_MANY_FUNCTIONS": [20, 30, 40, 50],
|
||||
"TOTAL_COMPLEXITY": [150, 250, 400, 500],
|
||||
"LOC": [100, 175, 250, 320],
|
||||
"TOTAL_LOC": [300, 400, 500, 600]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
75
.travis.yml
75
.travis.yml
@@ -1,16 +1,15 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- "1.8.x"
|
||||
- "1.9.x"
|
||||
- "1.10.x"
|
||||
- "1.11.x"
|
||||
- "tip"
|
||||
- "1.12.x"
|
||||
# - "1.13.x"
|
||||
# - "1.14.x"
|
||||
# - "tip"
|
||||
|
||||
os:
|
||||
- osx
|
||||
# - osx
|
||||
- linux
|
||||
- windows
|
||||
# - windows
|
||||
|
||||
sudo: false
|
||||
|
||||
@@ -19,6 +18,8 @@ branches:
|
||||
- master
|
||||
- develop
|
||||
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
|
||||
install:
|
||||
# Setting environments variables
|
||||
@@ -26,43 +27,43 @@ install:
|
||||
- export REVEL_BRANCH="develop"
|
||||
- 'if [[ "$TRAVIS_BRANCH" == "master" ]]; then export REVEL_BRANCH="master"; fi'
|
||||
- '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/modules ../modules/
|
||||
#- git clone -b $REVEL_BRANCH git://github.com/revel/revel ../revel/
|
||||
#- 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 -u github.com/golang/dep/cmd/dep
|
||||
- echo $GOPATH
|
||||
- echo $PATH
|
||||
- pwd
|
||||
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/...
|
||||
|
||||
# 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:
|
||||
allow_failures:
|
||||
- go: tip
|
||||
|
||||
29
go.mod
Normal file
29
go.mod
Normal 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
|
||||
)
|
||||
@@ -15,20 +15,22 @@ import (
|
||||
|
||||
"github.com/revel/cmd/model"
|
||||
"github.com/revel/cmd/utils"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// App contains the configuration for running a Revel app. (Not for the app itself)
|
||||
// Its only purpose is constructing the command to execute.
|
||||
type App struct {
|
||||
BinaryPath string // Path to the app executable
|
||||
Port int // Port to pass as a command line argument.
|
||||
cmd AppCmd // The last cmd returned.
|
||||
Paths *model.RevelContainer
|
||||
BinaryPath string // Path to the app executable
|
||||
Port int // Port to pass as a command line argument.
|
||||
cmd AppCmd // The last cmd returned.
|
||||
PackagePathMap map[string]string // Package to directory path map
|
||||
Paths *model.RevelContainer
|
||||
}
|
||||
|
||||
// NewApp returns app instance with binary path in it
|
||||
func NewApp(binPath string, paths *model.RevelContainer) *App {
|
||||
return &App{BinaryPath: binPath, Paths: paths, Port: paths.HTTPPort}
|
||||
func NewApp(binPath string, paths *model.RevelContainer, packagePathMap map[string]string) *App {
|
||||
return &App{BinaryPath: binPath, Paths: paths, Port: paths.HTTPPort, PackagePathMap:packagePathMap}
|
||||
}
|
||||
|
||||
// Cmd returns a command to run the app server using the current configuration.
|
||||
@@ -63,7 +65,7 @@ func (cmd AppCmd) Start(c *model.CommandConfig) error {
|
||||
listeningWriter := &startupListeningWriter{os.Stdout, make(chan bool), c, &bytes.Buffer{}}
|
||||
cmd.Stdout = listeningWriter
|
||||
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 {
|
||||
utils.Logger.Fatal("Error running:", "error", err)
|
||||
}
|
||||
@@ -71,9 +73,9 @@ func (cmd AppCmd) Start(c *model.CommandConfig) error {
|
||||
select {
|
||||
case exitState := <-cmd.waitChan():
|
||||
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,"")
|
||||
// TODO pretiffy command line output
|
||||
// err.MetaError = listeningWriter.getLastOutput()
|
||||
err := utils.NewError("", "Revel Run Error", "starting your application there was an exception. See terminal output, " + exitState, "")
|
||||
// TODO pretiffy command line output
|
||||
// err.MetaError = listeningWriter.getLastOutput()
|
||||
return err
|
||||
|
||||
case <-time.After(60 * time.Second):
|
||||
@@ -101,11 +103,30 @@ func (cmd AppCmd) Run() {
|
||||
func (cmd AppCmd) Kill() {
|
||||
|
||||
if cmd.Cmd != nil && (cmd.ProcessState == nil || !cmd.ProcessState.Exited()) {
|
||||
// Windows appears to send the kill to all threads, shutting down the
|
||||
// server before this can, this check will ensure the process is still running
|
||||
if _, err := os.FindProcess(int(cmd.Process.Pid));err!=nil {
|
||||
// Server has already exited
|
||||
utils.Logger.Info("Killing revel server pid", "pid", cmd.Process.Pid)
|
||||
return
|
||||
}
|
||||
|
||||
// Send an interrupt signal to allow for a graceful shutdown
|
||||
utils.Logger.Info("Killing revel server pid", "pid", cmd.Process.Pid)
|
||||
err := cmd.Process.Signal(os.Interrupt)
|
||||
var err error
|
||||
if runtime.GOOS == "windows" {
|
||||
// os.Interrupt is not available on windows
|
||||
err = cmd.Process.Signal(os.Kill)
|
||||
} else {
|
||||
err = cmd.Process.Signal(os.Interrupt)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
utils.Logger.Fatal("Failed to kill revel server:", "error", err)
|
||||
utils.Logger.Error(
|
||||
"Revel app failed to kill process.",
|
||||
"processid", cmd.Process.Pid,"error",err,
|
||||
"killerror", cmd.Process.Kill())
|
||||
return
|
||||
}
|
||||
|
||||
// Wait for the shutdown
|
||||
@@ -129,7 +150,7 @@ func (cmd AppCmd) Kill() {
|
||||
case <-ch:
|
||||
return
|
||||
case <-time.After(60 * time.Second):
|
||||
// Kill the process
|
||||
// Kill the process
|
||||
utils.Logger.Error(
|
||||
"Revel app failed to exit in 60 seconds - killing.",
|
||||
"processid", cmd.Process.Pid,
|
||||
@@ -178,7 +199,7 @@ func (w *startupListeningWriter) Write(p []byte) (int, error) {
|
||||
w.notifyReady = nil
|
||||
}
|
||||
}
|
||||
if w.notifyReady!=nil {
|
||||
if w.notifyReady != nil {
|
||||
w.buffer.Write(p)
|
||||
}
|
||||
return w.dest.Write(p)
|
||||
|
||||
106
harness/build.go
106
harness/build.go
@@ -19,17 +19,25 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/revel/cmd/model"
|
||||
"github.com/revel/cmd/parser"
|
||||
_ "github.com/revel/cmd/parser"
|
||||
"github.com/revel/cmd/utils"
|
||||
"github.com/revel/cmd/parser2"
|
||||
"github.com/revel/cmd/parser"
|
||||
)
|
||||
|
||||
var importErrorPattern = regexp.MustCompile("cannot find package \"([^\"]+)\"")
|
||||
|
||||
type ByString []*model.TypeInfo
|
||||
|
||||
func (c ByString) Len() int { return len(c) }
|
||||
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() }
|
||||
func (c ByString) Len() int {
|
||||
return len(c)
|
||||
}
|
||||
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:
|
||||
// 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).
|
||||
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 {
|
||||
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)
|
||||
if err != nil {
|
||||
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))
|
||||
// Binary path is a combination of BasePath/target/app directory, app's import path and its name.
|
||||
binName := filepath.Join(paths.BasePath, "target", "app", paths.ImportPath, filepath.Base(paths.BasePath))
|
||||
|
||||
// Change binary path for Windows build
|
||||
goos := runtime.GOOS
|
||||
@@ -135,8 +144,25 @@ func Build(c *model.CommandConfig, paths *model.RevelContainer) (_ *App, err err
|
||||
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 {
|
||||
appVersion := getAppVersion(paths)
|
||||
if appVersion == "" {
|
||||
appVersion = "noVersionProvided"
|
||||
}
|
||||
|
||||
buildTime := time.Now().UTC().Format(time.RFC3339)
|
||||
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 {
|
||||
flags = []string{
|
||||
"build",
|
||||
"-i",
|
||||
"-ldflags", versionLinkerFlags,
|
||||
"-tags", buildTags,
|
||||
"-o", binName}
|
||||
@@ -157,7 +182,12 @@ func Build(c *model.CommandConfig, paths *model.RevelContainer) (_ *App, err err
|
||||
}
|
||||
flags = append(flags, c.BuildFlags...)
|
||||
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") {
|
||||
flags = append(flags, "-tags", buildTags)
|
||||
@@ -170,31 +200,35 @@ func Build(c *model.CommandConfig, paths *model.RevelContainer) (_ *App, err err
|
||||
// Add in build flags
|
||||
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
|
||||
flags = append(flags, path.Join(paths.ImportPath, "app", "tmp"))
|
||||
|
||||
buildCmd := exec.Command(goPath, flags...)
|
||||
buildCmd.Env = append(os.Environ(),
|
||||
"GOPATH="+gopath,
|
||||
)
|
||||
utils.CmdInit(buildCmd, c.AppPath)
|
||||
utils.Logger.Info("Exec:", "args", buildCmd.Args)
|
||||
if !c.Vendored {
|
||||
// This is Go main path
|
||||
gopath := c.GoPath
|
||||
for _, o := range paths.ModulePathMap {
|
||||
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()
|
||||
|
||||
// If the build succeeded, we're done.
|
||||
if err == nil {
|
||||
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
|
||||
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.
|
||||
matches := importErrorPattern.FindAllStringSubmatch(stOutput, -1)
|
||||
@@ -246,7 +280,7 @@ func getAppVersion(paths *model.RevelContainer) string {
|
||||
if (err != nil && os.IsNotExist(err)) || !info.IsDir() {
|
||||
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)
|
||||
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.
|
||||
// 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+:)? (.*)$`).
|
||||
FindSubmatch(output)
|
||||
if errorMatch == nil {
|
||||
@@ -384,7 +418,7 @@ func newCompileError(paths *model.RevelContainer, output []byte) *utils.Error {
|
||||
|
||||
if errorMatch == nil {
|
||||
utils.Logger.Error("Failed to parse build errors", "error", string(output))
|
||||
return &utils.Error{
|
||||
return &utils.SourceError{
|
||||
SourceType: "Go code",
|
||||
Title: "Go Compilation 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
|
||||
gopaths := filepath.SplitList(build.Default.GOPATH)
|
||||
for _, gp := range gopaths {
|
||||
newPath := filepath.Join(gp,"src", paths.ImportPath, relFilename)
|
||||
newPath := filepath.Join(gp, "src", paths.ImportPath, relFilename)
|
||||
println(newPath)
|
||||
if utils.Exists(newPath) {
|
||||
return newPath
|
||||
@@ -414,11 +448,11 @@ func newCompileError(paths *model.RevelContainer, output []byte) *utils.Error {
|
||||
|
||||
// Read the source for the offending file.
|
||||
var (
|
||||
relFilename = string(errorMatch[1]) // e.g. "src/revel/sample/app/controllers/app.go"
|
||||
absFilename = findInPaths(relFilename)
|
||||
line, _ = strconv.Atoi(string(errorMatch[2]))
|
||||
description = string(errorMatch[4])
|
||||
compileError = &utils.Error{
|
||||
relFilename = string(errorMatch[1]) // e.g. "src/revel/sample/app/controllers/app.go"
|
||||
absFilename = findInPaths(relFilename)
|
||||
line, _ = strconv.Atoi(string(errorMatch[2]))
|
||||
description = string(errorMatch[4])
|
||||
compileError = &utils.SourceError{
|
||||
SourceType: "Go code",
|
||||
Title: "Go Compilation Error",
|
||||
Path: relFilename,
|
||||
@@ -436,7 +470,7 @@ func newCompileError(paths *model.RevelContainer, output []byte) *utils.Error {
|
||||
fileStr, err := utils.ReadLines(absFilename)
|
||||
if err != nil {
|
||||
compileError.MetaError = absFilename + ": " + err.Error()
|
||||
utils.Logger.Info("Unable to readlines "+compileError.MetaError, "error", err)
|
||||
utils.Logger.Info("Unable to readlines " + compileError.MetaError, "error", err)
|
||||
return compileError
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ import (
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"sync"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
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())
|
||||
return
|
||||
}
|
||||
var revelError *utils.Error
|
||||
var revelError *utils.SourceError
|
||||
switch e := err.(type) {
|
||||
case *utils.Error:
|
||||
case *utils.SourceError:
|
||||
revelError = e
|
||||
case error:
|
||||
revelError = &utils.Error{
|
||||
revelError = &utils.SourceError{
|
||||
Title: "Server Error",
|
||||
Description: e.Error(),
|
||||
}
|
||||
@@ -161,6 +162,7 @@ func NewHarness(c *model.CommandConfig, paths *model.RevelContainer, runMode str
|
||||
addr := paths.HTTPAddr
|
||||
port := paths.Config.IntDefault("harness.port", 0)
|
||||
scheme := "http"
|
||||
|
||||
if paths.HTTPSsl {
|
||||
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.
|
||||
// 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
|
||||
// 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
|
||||
@@ -217,10 +219,10 @@ func (h *Harness) Refresh() (err *utils.Error) {
|
||||
h.app, newErr = Build(h.config, h.paths)
|
||||
if newErr != nil {
|
||||
utils.Logger.Error("Build detected an error", "error", newErr)
|
||||
if castErr, ok := newErr.(*utils.Error); ok {
|
||||
if castErr, ok := newErr.(*utils.SourceError); ok {
|
||||
return castErr
|
||||
}
|
||||
err = &utils.Error{
|
||||
err = &utils.SourceError{
|
||||
Title: "App failed to start up",
|
||||
Description: err.Error(),
|
||||
}
|
||||
@@ -229,12 +231,22 @@ func (h *Harness) Refresh() (err *utils.Error) {
|
||||
|
||||
if h.useProxy {
|
||||
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)
|
||||
if err,k :=err2.(*utils.Error);k {
|
||||
if err,k :=err2.(*utils.SourceError);k {
|
||||
return err
|
||||
}
|
||||
return &utils.Error{
|
||||
return &utils.SourceError{
|
||||
Title: "App failed to start up",
|
||||
Description: err2.Error(),
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ func (rl *RevelLogger) SetStackDepth(amount int) MultiLogger {
|
||||
// Create a new logger
|
||||
func New(ctx ...interface{}) MultiLogger {
|
||||
r := &RevelLogger{Logger: log15.New(ctx...)}
|
||||
r.SetStackDepth(1)
|
||||
r.SetStackDepth(0)
|
||||
return r
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ func (c callHandler) Log(log *log15.Record) error {
|
||||
ctx := log.Ctx
|
||||
var ctxMap ContextMap
|
||||
if len(ctx) > 0 {
|
||||
ctxMap = make(ContextMap, len(ctx)/2)
|
||||
ctxMap = make(ContextMap, len(ctx) / 2)
|
||||
|
||||
for i := 0; i < len(ctx); i += 2 {
|
||||
v := ctx[i]
|
||||
@@ -108,8 +108,8 @@ func (c callHandler) Log(log *log15.Record) error {
|
||||
key = fmt.Sprintf("LOGGER_INVALID_KEY %v", v)
|
||||
}
|
||||
var value interface{}
|
||||
if len(ctx) > i+1 {
|
||||
value = ctx[i+1]
|
||||
if len(ctx) > i + 1 {
|
||||
value = ctx[i + 1]
|
||||
} else {
|
||||
value = "LOGGER_VALUE_MISSING"
|
||||
}
|
||||
|
||||
10
model/command/build.go
Normal file
10
model/command/build.go
Normal 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
6
model/command/clean.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package command
|
||||
type (
|
||||
Clean struct {
|
||||
ImportCommand
|
||||
}
|
||||
)
|
||||
7
model/command/import_command.go
Normal file
7
model/command/import_command.go
Normal 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
14
model/command/new.go
Normal 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
9
model/command/package.go
Normal 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
9
model/command/run.go
Normal 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"`
|
||||
}
|
||||
)
|
||||
9
model/command/test_command.go
Normal file
9
model/command/test_command.go
Normal 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
8
model/command/version.go
Normal 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"`
|
||||
}
|
||||
)
|
||||
@@ -3,7 +3,6 @@ package model
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/revel/cmd"
|
||||
"github.com/revel/cmd/logger"
|
||||
"github.com/revel/cmd/utils"
|
||||
"go/ast"
|
||||
"go/build"
|
||||
@@ -14,6 +13,7 @@ import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"github.com/revel/cmd/model/command"
|
||||
)
|
||||
|
||||
// The constants
|
||||
@@ -33,63 +33,29 @@ type (
|
||||
|
||||
// The Command config for the line input
|
||||
CommandConfig struct {
|
||||
Index COMMAND // The index
|
||||
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
|
||||
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
|
||||
ImportPath string // The import path (relative to a GOPATH)
|
||||
GoPath string // The GoPath
|
||||
GoCmd string // The full path to the go executable
|
||||
SrcRoot string // The source root
|
||||
AppPath string // The application path (absolute)
|
||||
AppName string // The application name
|
||||
Vendored bool // True if the application is vendored
|
||||
PackageResolver func(pkgName string) error // a packge resolver for the config
|
||||
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"`
|
||||
// The new command
|
||||
New struct {
|
||||
ImportPath string `short:"a" long:"application-path" description:"Path to application folder" required:"false"`
|
||||
SkeletonPath string `short:"s" long:"skeleton" description:"Path to skeleton folder (Must exist on GO PATH)" required:"false"`
|
||||
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 bool `short:"r" long:"run" description:"True if you want to run the application right away"`
|
||||
} `command:"new"`
|
||||
// The build command
|
||||
Build struct {
|
||||
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 the framework and modules" required:"false"`
|
||||
} `command:"version"`
|
||||
Index COMMAND // The index
|
||||
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
|
||||
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
|
||||
ImportPath string // The import path (relative to a GOPATH)
|
||||
GoPath string // The GoPath
|
||||
GoCmd string // The full path to the go executable
|
||||
SrcRoot string // The source root
|
||||
AppPath string // The application path (absolute)
|
||||
AppName string // The application name
|
||||
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
|
||||
Vendored bool // True if the application is vendored
|
||||
PackageResolver func(pkgName string) error // a package resolver for the config
|
||||
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"`
|
||||
GoModFlags []string `long:"gomod-flags" description:"These flags will execut go mod commands for each flag, this happens during the build process"`
|
||||
New command.New `command:"new"`
|
||||
Build command.Build `command:"build"`
|
||||
Run command.Run `command:"run"`
|
||||
Package command.Package `command:"package"`
|
||||
Clean command.Clean `command:"clean"`
|
||||
Test command.Test `command:"test"`
|
||||
Version command.Version `command:"version"`
|
||||
}
|
||||
)
|
||||
|
||||
@@ -102,14 +68,19 @@ func (c *CommandConfig) UpdateImportPath() error {
|
||||
importPath = c.New.ImportPath
|
||||
case RUN:
|
||||
importPath = c.Run.ImportPath
|
||||
c.Vendored = utils.Exists(filepath.Join(importPath, "go.mod"))
|
||||
case BUILD:
|
||||
importPath = c.Build.ImportPath
|
||||
c.Vendored = utils.Exists(filepath.Join(importPath, "go.mod"))
|
||||
case PACKAGE:
|
||||
importPath = c.Package.ImportPath
|
||||
c.Vendored = utils.Exists(filepath.Join(importPath, "go.mod"))
|
||||
case CLEAN:
|
||||
importPath = c.Clean.ImportPath
|
||||
c.Vendored = utils.Exists(filepath.Join(importPath, "go.mod"))
|
||||
case TEST:
|
||||
importPath = c.Test.ImportPath
|
||||
c.Vendored = utils.Exists(filepath.Join(importPath, "go.mod"))
|
||||
case VERSION:
|
||||
importPath = c.Version.ImportPath
|
||||
required = false
|
||||
@@ -131,10 +102,10 @@ func (c *CommandConfig) UpdateImportPath() error {
|
||||
if err == nil {
|
||||
for _, path := range strings.Split(build.Default.GOPATH, string(filepath.ListSeparator)) {
|
||||
utils.Logger.Infof("Checking import path %s with %s", currentPath, path)
|
||||
if strings.HasPrefix(currentPath, path) && len(currentPath) > len(path)+1 {
|
||||
importPath = currentPath[len(path)+1:]
|
||||
if strings.HasPrefix(currentPath, path) && len(currentPath) > len(path) + 1 {
|
||||
importPath = currentPath[len(path) + 1:]
|
||||
// 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:]
|
||||
} else if importPath == "src" {
|
||||
if c.Index != VERSION {
|
||||
@@ -149,12 +120,14 @@ func (c *CommandConfig) UpdateImportPath() error {
|
||||
}
|
||||
|
||||
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 err := c.SetVersions(); err != nil {
|
||||
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,
|
||||
"Revel framework version", c.FrameworkVersion.String(), "Revel tool version", c.CommandVersion.String())
|
||||
}
|
||||
@@ -163,81 +136,149 @@ func (c *CommandConfig) UpdateImportPath() error {
|
||||
if !required {
|
||||
return nil
|
||||
}
|
||||
if len(importPath) == 0 {
|
||||
if len(importPath) == 0 {
|
||||
return fmt.Errorf("Unable to determine import path from : %s", importPath)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Used to initialize the package resolver
|
||||
func (c *CommandConfig) InitPackageResolver() {
|
||||
c.Vendored = utils.DirExists(filepath.Join(c.AppPath, "vendor"))
|
||||
if c.Index == NEW && c.New.Vendored {
|
||||
c.Vendored = true
|
||||
func (c *CommandConfig) initAppFolder() (err error) {
|
||||
utils.Logger.Info("initAppFolder", "vendored", c.Vendored)
|
||||
|
||||
// check for go executable
|
||||
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 (
|
||||
depPath string
|
||||
err error
|
||||
)
|
||||
utils.Logger.Info("Determined app folder to be", "appfolder", appFolder, "working", wd, "importPath", c.ImportPath)
|
||||
|
||||
if c.Vendored {
|
||||
utils.Logger.Info("Vendor folder detected, scanning for deps in path")
|
||||
depPath, err = exec.LookPath("dep")
|
||||
// Use app folder to read the go.mod if it exists and extract the package information
|
||||
goModFile := filepath.Join(appFolder, "go.mod")
|
||||
if utils.Exists(goModFile) {
|
||||
c.Vendored = true
|
||||
file, err := ioutil.ReadFile(goModFile)
|
||||
if err != nil {
|
||||
// Do not halt build unless a new package needs to be imported
|
||||
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. " +
|
||||
"You can install the `dep` tool by doing a `go get -u github.com/golang/dep/cmd/dep`")
|
||||
return err
|
||||
}
|
||||
for _, line := range strings.Split(string(file), "\n") {
|
||||
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
|
||||
c.PackageResolver = func(pkgName string) error {
|
||||
//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)
|
||||
if c.Vendored {
|
||||
utils.Logger.Info("Using dependency manager to import package", "package", pkgName)
|
||||
|
||||
if depPath == "" {
|
||||
utils.Logger.Error("Build: Vendor folder found, but the `dep` tool was not found, " +
|
||||
"if you use a different vendoring (package management) tool please add the following packages by hand, " +
|
||||
"or install the `dep` tool into your gopath by doing a `go get -u github.com/golang/dep/cmd/dep`. " +
|
||||
"For more information and usage of the tool please see http://github.com/golang/dep")
|
||||
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)
|
||||
goModCmd := exec.Command("go", "mod", "tidy")
|
||||
utils.CmdInit(goModCmd, !c.Vendored, c.AppPath)
|
||||
goModCmd.Run()
|
||||
return nil
|
||||
}
|
||||
|
||||
utils.CmdInit(getCmd, c.AppPath)
|
||||
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
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// lookup and set Go related variables
|
||||
func (c *CommandConfig) InitGoPaths() {
|
||||
func (c *CommandConfig) InitGoPathsOld() {
|
||||
utils.Logger.Info("InitGoPaths")
|
||||
// lookup go path
|
||||
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 {
|
||||
c.SrcRoot = bestpath
|
||||
}
|
||||
|
||||
// If source root is empty and this isn't a version then skip it
|
||||
if len(c.SrcRoot) == 0 {
|
||||
if c.Index != VERSION {
|
||||
utils.Logger.Fatal("Abort: could not create a Revel application outside of GOPATH.")
|
||||
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
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// set go src path
|
||||
@@ -298,14 +343,14 @@ func (c *CommandConfig) InitGoPaths() {
|
||||
// Sets the versions on the command config
|
||||
func (c *CommandConfig) SetVersions() (err error) {
|
||||
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 {
|
||||
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
|
||||
|
||||
versionData, err := ioutil.ReadFile(filepath.Join(revelPath, RevelImportPath, "version.go"))
|
||||
versionData, err := ioutil.ReadFile(filepath.Join(pathMap[RevelImportPath], "version.go"))
|
||||
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.
|
||||
|
||||
@@ -4,13 +4,12 @@ package model
|
||||
import (
|
||||
"github.com/revel/cmd/utils"
|
||||
"github.com/revel/config"
|
||||
"go/build"
|
||||
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"golang.org/x/tools/go/packages"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -65,7 +64,11 @@ type (
|
||||
CookieSecure bool // True if cookie is secure
|
||||
SecretStr string // The secret string
|
||||
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 {
|
||||
@@ -96,30 +99,22 @@ var RevelModulesImportPath = "github.com/revel/modules"
|
||||
|
||||
// This function returns a container object describing the revel application
|
||||
// eventually this type of function will replace the global variables.
|
||||
func NewRevelPaths(mode, importPath, srcPath string, callback RevelCallback) (rp *RevelContainer, err error) {
|
||||
rp = &RevelContainer{ModulePathMap: map[string]string{}}
|
||||
func NewRevelPaths(mode, importPath, appSrcPath string, callback RevelCallback) (rp *RevelContainer, err error) {
|
||||
rp = &RevelContainer{ModulePathMap: map[string]*ModuleInfo{}}
|
||||
// Ignore trailing slashes.
|
||||
rp.ImportPath = strings.TrimRight(importPath, "/")
|
||||
rp.SourcePath = srcPath
|
||||
rp.SourcePath = appSrcPath
|
||||
rp.RunMode = mode
|
||||
|
||||
// If the SourcePath is not specified, find it using build.Import.
|
||||
var revelSourcePath string // may be different from the app source path
|
||||
if rp.SourcePath == "" {
|
||||
rp.SourcePath, revelSourcePath, err = utils.FindSrcPaths(importPath, RevelImportPath, callback.PackageResolver)
|
||||
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
|
||||
// We always need to determine the paths for files
|
||||
pathMap, err := utils.FindSrcPaths(appSrcPath, []string{importPath+"/app", RevelImportPath}, callback.PackageResolver)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
rp.AppPath, rp.RevelPath = pathMap[importPath], pathMap[RevelImportPath]
|
||||
// Setup paths for application
|
||||
rp.RevelPath = filepath.Join(revelSourcePath, filepath.FromSlash(RevelImportPath))
|
||||
rp.BasePath = filepath.Join(rp.SourcePath, filepath.FromSlash(importPath))
|
||||
rp.PackageInfo.Vendor = utils.Exists(filepath.Join(rp.BasePath, "vendor"))
|
||||
rp.BasePath = rp.SourcePath
|
||||
rp.PackageInfo.Vendor = utils.Exists(filepath.Join(rp.BasePath, "go.mod"))
|
||||
rp.AppPath = filepath.Join(rp.BasePath, "app")
|
||||
|
||||
// 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", "")
|
||||
|
||||
callback.FireEvent(REVEL_BEFORE_MODULES_LOADED, nil)
|
||||
utils.Logger.Info("Loading modules")
|
||||
if err := rp.loadModules(callback); err != nil {
|
||||
return rp, err
|
||||
}
|
||||
@@ -248,9 +244,10 @@ func (rp *RevelContainer) loadModules(callback RevelCallback) (err error) {
|
||||
|
||||
// Adds a module paths to the container object
|
||||
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) {
|
||||
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) {
|
||||
rp.TemplatePaths = append(rp.TemplatePaths, viewsPath)
|
||||
}
|
||||
@@ -273,13 +270,21 @@ func (rp *RevelContainer) ResolveImportPath(importPath string) (string, error) {
|
||||
if rp.Packaged {
|
||||
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 {
|
||||
return "", err
|
||||
}
|
||||
if rp.PackageInfo.Vendor && !strings.HasPrefix(modPkg.Dir,rp.BasePath) {
|
||||
return "", fmt.Errorf("Module %s was found outside of path %s.",importPath, modPkg.Dir)
|
||||
if len(pkgs[0].GoFiles)>0 {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -12,14 +12,14 @@ import (
|
||||
type SourceInfo struct {
|
||||
// StructSpecs lists type info for all structs found under the code paths.
|
||||
// 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:
|
||||
// 1. The fully-qualified function name,
|
||||
// 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.
|
||||
// e.g. the line returned from runtime.Caller()
|
||||
// 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.
|
||||
// Revel notices files with an init() function and imports that package.
|
||||
InitImportPaths []string
|
||||
@@ -28,7 +28,9 @@ type SourceInfo struct {
|
||||
// app/controllers/... that embed (directly or indirectly) revel.Controller
|
||||
controllerSpecs []*TypeInfo
|
||||
// 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
|
||||
@@ -74,7 +76,7 @@ func (s *SourceInfo) TypesThatEmbed(targetType, packageFilter string) (filtered
|
||||
utils.Logger.Info("Debug: Skipping adding spec for unexported type",
|
||||
"type", filteredItem.StructName,
|
||||
"package", filteredItem.ImportPath)
|
||||
filtered = append(filtered[:i], filtered[i+1:]...)
|
||||
filtered = append(filtered[:i], filtered[i + 1:]...)
|
||||
exit = false
|
||||
break
|
||||
}
|
||||
@@ -97,8 +99,8 @@ func (s *SourceInfo) TypesThatEmbed(targetType, packageFilter string) (filtered
|
||||
|
||||
// Report non controller structures in controller folder.
|
||||
if !found && !strings.HasPrefix(spec.StructName, "Test") {
|
||||
utils.Logger.Warn("Type found in package: "+packageFilter+
|
||||
", but did not embed from: "+filepath.Base(targetType),
|
||||
utils.Logger.Warn("Type found in package: " + packageFilter +
|
||||
", but did not embed from: " + filepath.Base(targetType),
|
||||
"name", spec.StructName, "importpath", spec.ImportPath, "foundstructures", unfoundNames)
|
||||
}
|
||||
}
|
||||
@@ -110,7 +112,7 @@ func (s *SourceInfo) TypesThatEmbed(targetType, packageFilter string) (filtered
|
||||
// `revel.Controller`
|
||||
func (s *SourceInfo) ControllerSpecs() []*TypeInfo {
|
||||
if s.controllerSpecs == nil {
|
||||
s.controllerSpecs = s.TypesThatEmbed(RevelImportPath+".Controller", "controllers")
|
||||
s.controllerSpecs = s.TypesThatEmbed(RevelImportPath + ".Controller", "controllers")
|
||||
}
|
||||
return s.controllerSpecs
|
||||
}
|
||||
@@ -119,7 +121,19 @@ func (s *SourceInfo) ControllerSpecs() []*TypeInfo {
|
||||
// `testing.TestSuite`
|
||||
func (s *SourceInfo) TestSuites() []*TypeInfo {
|
||||
if s.testSuites == nil {
|
||||
s.testSuites = s.TypesThatEmbed(RevelImportPath+"/testing.TestSuite", "testsuite")
|
||||
s.testSuites = s.TypesThatEmbed(RevelImportPath + "/testing.TestSuite", "testsuite")
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -8,19 +8,20 @@ import (
|
||||
)
|
||||
|
||||
type Version struct {
|
||||
Prefix string
|
||||
Major int
|
||||
Minor int
|
||||
Maintenance int
|
||||
Suffix string
|
||||
BuildDate string
|
||||
Prefix string
|
||||
Major int
|
||||
Minor int
|
||||
Maintenance int
|
||||
Suffix string
|
||||
BuildDate string
|
||||
MinGoVersion string
|
||||
}
|
||||
|
||||
// The compatibility list
|
||||
var frameworkCompatibleRangeList = [][]string{
|
||||
{"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
|
||||
@@ -70,6 +71,7 @@ func (v *Version) CompatibleFramework(c *CommandConfig) error {
|
||||
if !v.Newer(start) || v.Newer(end) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Framework is older then 0.20, turn on historic mode
|
||||
if i == 0 {
|
||||
c.HistoricMode = true
|
||||
@@ -109,7 +111,7 @@ func (v *Version) Newer(o *Version) bool {
|
||||
if v.Maintenance != o.Maintenance {
|
||||
return v.Maintenance > o.Maintenance
|
||||
}
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
// Convert the version to a string
|
||||
|
||||
@@ -134,7 +134,7 @@ func appendAction(fset *token.FileSet, mm methodMap, decl ast.Decl, pkgImportPat
|
||||
var importPath string
|
||||
typeExpr := model.NewTypeExprFromAst(pkgName, field.Type)
|
||||
if !typeExpr.Valid {
|
||||
utils.Logger.Warn("Warn: Didn't understand argument '%s' of action %s. Ignoring.", name, getFuncName(funcDecl))
|
||||
utils.Logger.Warnf("Warn: Didn't understand argument '%s' of action %s. Ignoring.", name, getFuncName(funcDecl))
|
||||
return // We didn't understand one of the args. Ignore this action.
|
||||
}
|
||||
// Local object
|
||||
|
||||
@@ -29,7 +29,10 @@ func addImports(imports map[string]string, decl ast.Decl, srcDir string) {
|
||||
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
|
||||
|
||||
// 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.
|
||||
// 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.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
|
||||
} else {
|
||||
@@ -60,9 +64,10 @@ func addImports(imports map[string]string, decl ast.Decl, srcDir string) {
|
||||
|
||||
// Returns a valid import string from the path
|
||||
// using the build.Defaul.GOPATH to determine the root
|
||||
func importPathFromPath(root string) string {
|
||||
if vendorIdx := strings.Index(root, "/vendor/"); vendorIdx != -1 {
|
||||
return filepath.ToSlash(root[vendorIdx+8:])
|
||||
func importPathFromPath(root, basePath string) string {
|
||||
vendorTest := filepath.Join(basePath, "vendor")
|
||||
if len(root) > len(vendorTest) && root[:len(vendorTest)] == vendorTest {
|
||||
return filepath.ToSlash(root[len(vendorTest)+1:])
|
||||
}
|
||||
for _, gopath := range filepath.SplitList(build.Default.GOPATH) {
|
||||
srcPath := filepath.Join(gopath, "src")
|
||||
|
||||
@@ -37,7 +37,7 @@ type methodMap map[string][]*model.MethodSpec
|
||||
func ProcessSource(paths *model.RevelContainer) (_ *model.SourceInfo, compileError error) {
|
||||
pc := &processContainer{paths: paths}
|
||||
for _, root := range paths.CodePaths {
|
||||
rootImportPath := importPathFromPath(root)
|
||||
rootImportPath := importPathFromPath(root, paths.BasePath)
|
||||
if rootImportPath == "" {
|
||||
utils.Logger.Info("Skipping empty code path", "path", root)
|
||||
continue
|
||||
@@ -85,7 +85,7 @@ func (pc *processContainer) processPath(path string, info os.FileInfo, err error
|
||||
if err != nil {
|
||||
if errList, ok := err.(scanner.ErrorList); ok {
|
||||
var pos = errList[0].Pos
|
||||
newError := &utils.Error{
|
||||
newError := &utils.SourceError{
|
||||
SourceType: ".go source",
|
||||
Title: "Go Compilation Error",
|
||||
Path: pos.Filename,
|
||||
|
||||
379
parser2/source_info_processor.go
Normal file
379
parser2/source_info_processor.go
Normal 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
217
parser2/source_processor.go
Normal 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
|
||||
// }
|
||||
//}
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"github.com/revel/cmd/harness"
|
||||
"github.com/revel/cmd/model"
|
||||
"github.com/revel/cmd/utils"
|
||||
"go/build"
|
||||
)
|
||||
|
||||
var cmdBuild = &Command{
|
||||
@@ -38,6 +37,12 @@ func init() {
|
||||
// The update config updates the configuration command so that it can run
|
||||
func updateBuildConfig(c *model.CommandConfig, args []string) bool {
|
||||
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 len(args) < 2 {
|
||||
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.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 {
|
||||
return
|
||||
}
|
||||
|
||||
buildSafetyCheck(destPath)
|
||||
if err = buildSafetyCheck(destPath); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure the application can be built, this generates the main file
|
||||
app, err := harness.Build(c, revel_paths)
|
||||
@@ -87,7 +94,7 @@ func buildApp(c *model.CommandConfig) (err error) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = buildCopyModules(c, revel_paths, packageFolders)
|
||||
err = buildCopyModules(c, revel_paths, packageFolders, app)
|
||||
if err != nil {
|
||||
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
|
||||
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")
|
||||
// Find all the modules used and copy them over.
|
||||
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 will default to prod
|
||||
moduleImportList := []string{}
|
||||
for _, section := range config.Sections() {
|
||||
// If the runmode is defined we will only import modules defined for that run mode
|
||||
if c.Build.Mode != "" && c.Build.Mode != section {
|
||||
@@ -169,17 +176,14 @@ func buildCopyModules(c *model.CommandConfig, revel_paths *model.RevelContainer,
|
||||
if moduleImportPath == "" {
|
||||
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
|
||||
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)
|
||||
if c.Build.CopySource {
|
||||
err = utils.CopyDir(filepath.Join(destPath, importPath), fsPath, nil)
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/revel/cmd/model"
|
||||
"github.com/revel/cmd/utils"
|
||||
"go/build"
|
||||
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
@@ -37,6 +37,9 @@ func init() {
|
||||
// Update the clean command configuration, using old method
|
||||
func updateCleanConfig(c *model.CommandConfig, args []string) bool {
|
||||
c.Index = model.CLEAN
|
||||
if len(args) == 0 && c.Clean.ImportPath != "" {
|
||||
return true
|
||||
}
|
||||
if len(args) == 0 {
|
||||
fmt.Fprintf(os.Stderr, cmdClean.Long)
|
||||
return false
|
||||
@@ -47,14 +50,10 @@ func updateCleanConfig(c *model.CommandConfig, args []string) bool {
|
||||
|
||||
// Clean the source directory of generated files
|
||||
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{
|
||||
filepath.Join(appPkg.Dir, "app", "tmp"),
|
||||
filepath.Join(appPkg.Dir, "app", "routes"),
|
||||
filepath.Join(c.AppPath, "app", "tmp"),
|
||||
filepath.Join(c.AppPath, "app", "routes"),
|
||||
}
|
||||
|
||||
for _, dir := range purgeDirs {
|
||||
|
||||
@@ -22,12 +22,12 @@ func TestClean(t *testing.T) {
|
||||
main.Commands[model.NEW].RunWith(c)
|
||||
c.Index = model.TEST
|
||||
main.Commands[model.TEST].RunWith(c)
|
||||
a.True(utils.Exists(filepath.Join(gopath, "src", "clean-test", "app", "tmp", "main.go")),
|
||||
"Missing main from path "+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, "clean-test", "app", "tmp", "main.go"))
|
||||
c.Clean.ImportPath = c.ImportPath
|
||||
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")),
|
||||
"Did not remove main from path "+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, "clean-test", "app", "tmp", "main.go"))
|
||||
})
|
||||
if !t.Failed() {
|
||||
if err := os.RemoveAll(gopath); err != nil {
|
||||
|
||||
@@ -7,7 +7,9 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go/build"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// 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
|
||||
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 {
|
||||
case model.NEW:
|
||||
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:
|
||||
c.Build.ImportPath = name
|
||||
case model.TEST:
|
||||
@@ -68,7 +86,7 @@ func newApp(name string, command model.COMMAND, precall func(c *model.CommandCon
|
||||
if c.UpdateImportPath()!=nil {
|
||||
a.Fail("Unable to update import path")
|
||||
}
|
||||
c.InitGoPaths()
|
||||
|
||||
c.InitPackageResolver()
|
||||
return c
|
||||
}
|
||||
|
||||
161
revel/new.go
161
revel/new.go
@@ -46,14 +46,23 @@ func init() {
|
||||
// Called when unable to parse the command line automatically and assumes an old launch
|
||||
func updateNewConfig(c *model.CommandConfig, args []string) bool {
|
||||
c.Index = model.NEW
|
||||
if len(c.New.Package) > 0 {
|
||||
c.New.NotVendored = false
|
||||
}
|
||||
c.Vendored = !c.New.NotVendored
|
||||
|
||||
if len(args) == 0 {
|
||||
fmt.Fprintf(os.Stderr, cmdNew.Long)
|
||||
return false
|
||||
if len(c.New.ImportPath) == 0 {
|
||||
fmt.Fprintf(os.Stderr, cmdNew.Long)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
c.New.ImportPath = args[0]
|
||||
if len(args) > 1 {
|
||||
c.New.SkeletonPath = args[1]
|
||||
}
|
||||
|
||||
return true
|
||||
|
||||
}
|
||||
@@ -67,7 +76,7 @@ func newApp(c *model.CommandConfig) (err error) {
|
||||
}
|
||||
|
||||
// checking and setting skeleton
|
||||
if err=setSkeletonPath(c);err!=nil {
|
||||
if err = setSkeletonPath(c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -76,72 +85,26 @@ func newApp(c *model.CommandConfig) (err error) {
|
||||
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
|
||||
if err = setApplicationPath(c); err != nil {
|
||||
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
|
||||
if err = copyNewAppFiles(c);err != nil {
|
||||
if err = copyNewAppFiles(c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Rerun the dep tool if vendored
|
||||
if c.New.Vendored {
|
||||
getCmd := exec.Command("dep", "ensure", "-v")
|
||||
utils.CmdInit(getCmd, c.AppPath)
|
||||
utils.Logger.Info("Exec:", "args", getCmd.Args)
|
||||
getOutput, err := getCmd.CombinedOutput()
|
||||
if err != nil {
|
||||
utils.Logger.Fatal(string(getOutput))
|
||||
// Run the vendor tool if needed
|
||||
if c.Vendored {
|
||||
if err = createModVendor(c); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,6 +119,72 @@ func newApp(c *model.CommandConfig) (err error) {
|
||||
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
|
||||
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 !c.New.Vendored {
|
||||
if !c.Vendored {
|
||||
_, err = build.Import(model.RevelImportPath, "", build.FindOnly)
|
||||
if err != nil {
|
||||
//// Go get the revel project
|
||||
err = c.PackageResolver(model.RevelImportPath)
|
||||
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) {
|
||||
// TODO Add support for ftp, sftp, scp ??
|
||||
case "" :
|
||||
sp.Scheme="file"
|
||||
sp.Scheme = "file"
|
||||
fallthrough
|
||||
case "file" :
|
||||
fullpath := sp.String()[7:]
|
||||
if !filepath.IsAbs(fullpath) {
|
||||
fullpath, err = filepath.Abs(fullpath)
|
||||
if err!=nil {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -251,11 +280,11 @@ func newLoadFromGit(c *model.CommandConfig, sp *url.URL) (err error) {
|
||||
targetPath := filepath.Join(os.TempDir(), "revel", "skeleton")
|
||||
os.RemoveAll(targetPath)
|
||||
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)
|
||||
getOutput, err := getCmd.CombinedOutput()
|
||||
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
|
||||
if len(pathpart) > 1 {
|
||||
|
||||
@@ -3,95 +3,42 @@ package main_test
|
||||
import (
|
||||
"github.com/revel/cmd/model"
|
||||
"github.com/revel/cmd/revel"
|
||||
"github.com/revel/cmd/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// test the commands
|
||||
func TestNew(t *testing.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) {
|
||||
a := assert.New(t)
|
||||
c := newApp("onlyone/v/a", model.NEW, precall, a)
|
||||
c.New.Vendored = true
|
||||
c := newApp("new-test", model.NEW, nil, a)
|
||||
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)
|
||||
c := newApp("onlyone/v/a", model.TEST, nil, a)
|
||||
a.Nil(main.Commands[model.TEST].RunWith(c), "Test failed")
|
||||
c := newApp("new/test/a", model.NEW, nil, a)
|
||||
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)
|
||||
c := newApp("onlyone/v/a", model.BUILD, nil, a)
|
||||
c.Index = model.BUILD
|
||||
c.Build.TargetPath = filepath.Join(gopath, "src/onlyone/v/a", "target")
|
||||
a.Nil(main.Commands[model.BUILD].RunWith(c), " Build failed")
|
||||
a.True(utils.DirExists(c.Build.TargetPath), "Target folder not made", c.Build.TargetPath)
|
||||
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("Package", func(t *testing.T) {
|
||||
t.Run("Skeleton-Git", func(t *testing.T) {
|
||||
a := assert.New(t)
|
||||
c := newApp("onlyone/v/a", model.PACKAGE, nil, a)
|
||||
c.Package.TargetPath = filepath.Join(gopath, "src/onlyone/v/a", "target.tar.gz")
|
||||
a.Nil(main.Commands[model.PACKAGE].RunWith(c), "Package Failed")
|
||||
a.True(utils.Exists(c.Package.TargetPath), "Target package not made", c.Package.TargetPath)
|
||||
})
|
||||
t.Run("TestVendorDir", func(t *testing.T) {
|
||||
// Check to see that no additional packages were downloaded outside the vendor folder
|
||||
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"))
|
||||
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 {
|
||||
@@ -99,3 +46,4 @@ func TestNewVendor(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,9 @@ func init() {
|
||||
// Called when unable to parse the command line automatically and assumes an old launch
|
||||
func updatePackageConfig(c *model.CommandConfig, args []string) bool {
|
||||
c.Index = model.PACKAGE
|
||||
if len(args)==0 && c.Package.ImportPath!="" {
|
||||
return true
|
||||
}
|
||||
c.Package.ImportPath = args[0]
|
||||
if len(args) > 1 {
|
||||
c.Package.Mode = args[1]
|
||||
@@ -58,7 +61,7 @@ func packageApp(c *model.CommandConfig) (err error) {
|
||||
}
|
||||
|
||||
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 {
|
||||
return
|
||||
}
|
||||
@@ -86,7 +89,9 @@ func packageApp(c *model.CommandConfig) (err error) {
|
||||
}
|
||||
c.Build.TargetPath = tmpDir
|
||||
c.Build.CopySource = c.Package.CopySource
|
||||
buildApp(c)
|
||||
if err = buildApp(c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Create the zip file.
|
||||
|
||||
|
||||
@@ -71,27 +71,27 @@ func main() {
|
||||
wd, _ := os.Getwd()
|
||||
|
||||
utils.InitLogger(wd, logger.LvlError)
|
||||
parser := flags.NewParser(c, flags.HelpFlag|flags.PassDoubleDash)
|
||||
if len(os.Args)<2 {
|
||||
parser := flags.NewParser(c, flags.HelpFlag | flags.PassDoubleDash)
|
||||
if len(os.Args) < 2 {
|
||||
parser.WriteHelp(os.Stdout)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// Switch based on the verbose flag
|
||||
if len(c.Verbose)>1 {
|
||||
if len(c.Verbose) > 1 {
|
||||
utils.InitLogger(wd, logger.LvlDebug)
|
||||
} else if len(c.Verbose)>0 {
|
||||
} else if len(c.Verbose) > 0 {
|
||||
utils.InitLogger(wd, logger.LvlInfo)
|
||||
} else {
|
||||
utils.InitLogger(wd, logger.LvlWarn)
|
||||
}
|
||||
|
||||
if err := c.UpdateImportPath();err!=nil {
|
||||
if err := c.UpdateImportPath(); err != nil {
|
||||
utils.Logger.Error(err.Error())
|
||||
parser.WriteHelp(os.Stdout)
|
||||
os.Exit(1)
|
||||
@@ -101,13 +101,13 @@ func main() {
|
||||
println("Revel executing:", command.Short)
|
||||
|
||||
// Setting go paths
|
||||
c.InitGoPaths()
|
||||
// c.InitGoPaths()
|
||||
|
||||
// Setup package resolver
|
||||
c.InitPackageResolver()
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -142,13 +142,10 @@ func ParseArgs(c *model.CommandConfig, parser *flags.Parser, args []string) (err
|
||||
}
|
||||
}
|
||||
|
||||
if len(extraArgs) > 0 {
|
||||
utils.Logger.Info("Found additional arguements, setting them")
|
||||
if !Commands[c.Index].UpdateConfig(c, extraArgs) {
|
||||
buffer := &bytes.Buffer{}
|
||||
parser.WriteHelp(buffer)
|
||||
err = fmt.Errorf("Invalid command line arguements %v\n%s", extraArgs, buffer.String())
|
||||
}
|
||||
if !Commands[c.Index].UpdateConfig(c, extraArgs) {
|
||||
buffer := &bytes.Buffer{}
|
||||
parser.WriteHelp(buffer)
|
||||
err = fmt.Errorf("Invalid command line arguements %v\n%s", extraArgs, buffer.String())
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
16
revel/run.go
16
revel/run.go
@@ -6,7 +6,7 @@ package main
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/revel/cmd/harness"
|
||||
"github.com/revel/cmd/model"
|
||||
@@ -106,7 +106,9 @@ func updateRunConfig(c *model.CommandConfig, args []string) bool {
|
||||
}
|
||||
case 0:
|
||||
// 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
|
||||
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
|
||||
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 filepath.IsAbs(pathToCheck)
|
||||
@@ -126,7 +128,7 @@ func runApp(c *model.CommandConfig) (err error) {
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
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 {
|
||||
runMode = revel_path.RunMode
|
||||
}
|
||||
|
||||
@@ -55,6 +55,10 @@ func init() {
|
||||
// Called to update the config command with from the older stype
|
||||
func updateTestConfig(c *model.CommandConfig, args []string) bool {
|
||||
c.Index = model.TEST
|
||||
if len(args) == 0 && c.Test.ImportPath != "" {
|
||||
return true
|
||||
}
|
||||
|
||||
// The full test runs
|
||||
// revel test <import path> (run mode) (suite(.function))
|
||||
if len(args) < 1 {
|
||||
@@ -78,7 +82,7 @@ func testApp(c *model.CommandConfig) (err error) {
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return
|
||||
}
|
||||
@@ -95,7 +99,7 @@ func testApp(c *model.CommandConfig) (err error) {
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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 {
|
||||
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 {
|
||||
runMode = app.Paths.RunMode
|
||||
}
|
||||
cmd := app.Cmd(runMode)
|
||||
cmd.Dir = c.AppPath
|
||||
|
||||
cmd.Stderr = 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.
|
||||
func getTestsList(baseURL string) (*[]tests.TestSuiteDesc, error) {
|
||||
var (
|
||||
err error
|
||||
err error
|
||||
resp *http.Response
|
||||
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) {
|
||||
|
||||
// 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 (
|
||||
overallSuccess = true
|
||||
|
||||
@@ -29,10 +29,10 @@ import (
|
||||
type (
|
||||
// The version container
|
||||
VersionCommand struct {
|
||||
Command *model.CommandConfig // The command
|
||||
revelVersion *model.Version // The Revel framework version
|
||||
modulesVersion *model.Version // The Revel modules version
|
||||
cmdVersion *model.Version // The tool version
|
||||
Command *model.CommandConfig // The command
|
||||
revelVersion *model.Version // The Revel framework version
|
||||
modulesVersion *model.Version // The Revel modules version
|
||||
cmdVersion *model.Version // The tool version
|
||||
}
|
||||
)
|
||||
|
||||
@@ -74,10 +74,10 @@ func (v *VersionCommand) RunWith(c *model.CommandConfig) (err error) {
|
||||
versionInfo := ""
|
||||
for x := 0; x < 2 && needsUpdates; x++ {
|
||||
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.Stdout = os.Stdout
|
||||
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
|
||||
shouldUpdate := updateLibs && 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
|
||||
if shouldUpdate {
|
||||
v.doUpdate(title, repo, localVersion, versonFromRepo)
|
||||
@@ -127,7 +127,7 @@ func (v *VersionCommand) doRepoCheck(updateLibs bool) (versionInfo string, needs
|
||||
|
||||
// Checks for updates if needed
|
||||
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)
|
||||
if err := v.Command.PackageResolver(repo); err != nil {
|
||||
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
|
||||
func (v *VersionCommand) updateLocalVersions() {
|
||||
func (v *VersionCommand) updateLocalVersions() {
|
||||
v.cmdVersion = &model.Version{}
|
||||
v.cmdVersion.ParseVersion(cmd.Version)
|
||||
v.cmdVersion.BuildDate = cmd.BuildDate
|
||||
v.cmdVersion.MinGoVersion = cmd.MinimumGoVersion
|
||||
|
||||
var modulePath, revelPath string
|
||||
_, revelPath, err := utils.FindSrcPaths(v.Command.ImportPath, model.RevelImportPath, v.Command.PackageResolver)
|
||||
pathMap, err := utils.FindSrcPaths(v.Command.AppPath, []string{model.RevelImportPath, model.RevelModulesImportPath}, v.Command.PackageResolver)
|
||||
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
|
||||
}
|
||||
revelPath = revelPath + model.RevelImportPath
|
||||
utils.Logger.Info("Fullpath to revel", "dir", revelPath)
|
||||
v.revelVersion, err = v.versionFromFilepath(revelPath)
|
||||
utils.Logger.Info("Fullpath to revel modules", "dir", pathMap[model.RevelImportPath])
|
||||
v.revelVersion, err = v.versionFromFilepath(pathMap[model.RevelImportPath])
|
||||
if err != nil {
|
||||
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 {
|
||||
utils.Logger.Warn("Unable to extract version information from Revel library", "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)
|
||||
utils.Logger.Warn("Unable to extract version information from Revel Modules", "path", pathMap[model.RevelModulesImportPath], "error", err)
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
@@ -107,7 +107,7 @@ func describeSuite(testSuite interface{}) TestSuiteDesc {
|
||||
}
|
||||
|
||||
// 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)"
|
||||
actualPrefix := "(actual)"
|
||||
errDesc := err.Description
|
||||
|
||||
@@ -3,6 +3,8 @@ package utils
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/revel/cmd/logger"
|
||||
"strconv"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -43,3 +45,60 @@ func NewBuildIfError(err error, message string, args ...interface{}) (b error) {
|
||||
func (b *BuildError) Error() string {
|
||||
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
|
||||
}
|
||||
@@ -10,25 +10,27 @@ import (
|
||||
)
|
||||
|
||||
// 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
|
||||
// Dep does not like paths that are not real, convert all paths in go to real paths
|
||||
realPath := &bytes.Buffer{}
|
||||
for _, p := range filepath.SplitList(build.Default.GOPATH) {
|
||||
rp,_ := filepath.EvalSymlinks(p)
|
||||
if realPath.Len() > 0 {
|
||||
realPath.WriteString(string(filepath.ListSeparator))
|
||||
if addGoPath {
|
||||
for _, p := range filepath.SplitList(build.Default.GOPATH) {
|
||||
rp, _ := filepath.EvalSymlinks(p)
|
||||
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
|
||||
for _, e := range os.Environ() {
|
||||
pair := strings.Split(e, "=")
|
||||
if pair[0]=="GOPATH" || pair[0]=="GOROOT" {
|
||||
if pair[0] == "GOPATH" || pair[0] == "GOROOT" {
|
||||
continue
|
||||
}
|
||||
c.Env = append(c.Env,e)
|
||||
c.Env = append(c.Env, e)
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
// The error is a wrapper for the
|
||||
type (
|
||||
Error struct {
|
||||
SourceError struct {
|
||||
SourceType string // The type of source that failed to build.
|
||||
Title, Path, Description string // Description of the error, as presented to the user.
|
||||
Line, Column int // Where the error was encountered.
|
||||
@@ -24,8 +24,8 @@ type (
|
||||
}
|
||||
)
|
||||
// Return a new error object
|
||||
func NewError(source, title,path,description string) *Error {
|
||||
return &Error {
|
||||
func NewError(source, title, path, description string) *SourceError {
|
||||
return &SourceError{
|
||||
SourceType:source,
|
||||
Title:title,
|
||||
Path:path,
|
||||
@@ -34,7 +34,7 @@ func NewError(source, title,path,description string) *Error {
|
||||
}
|
||||
|
||||
// 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, "{{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
|
||||
// account that fields are optionally set. Returns e.g. Compilation Error
|
||||
// (in views/header.html:51): expected right delim in end; got "}"
|
||||
func (e *Error) Error() string {
|
||||
func (e *SourceError) Error() string {
|
||||
if e == nil {
|
||||
panic("opps")
|
||||
}
|
||||
@@ -69,7 +69,7 @@ func (e *Error) Error() string {
|
||||
|
||||
// ContextSource method returns a snippet of the source around
|
||||
// where the error occurred.
|
||||
func (e *Error) ContextSource() []SourceLine {
|
||||
func (e *SourceError) ContextSource() []SourceLine {
|
||||
if e.SourceLines == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -82,7 +82,7 @@ func (e *Error) ContextSource() []SourceLine {
|
||||
end = len(e.SourceLines)
|
||||
}
|
||||
|
||||
lines := make([]SourceLine, end-start)
|
||||
lines := make([]SourceLine, end - start)
|
||||
for i, src := range e.SourceLines[start:end] {
|
||||
fileLine := start + i + 1
|
||||
lines[i] = SourceLine{src, fileLine, fileLine == e.Line}
|
||||
|
||||
114
utils/file.go
114
utils/file.go
@@ -4,15 +4,15 @@ import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"errors"
|
||||
"html/template"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"golang.org/x/tools/go/packages"
|
||||
)
|
||||
|
||||
// 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) {
|
||||
tmpl, err := template.ParseFiles(srcPath)
|
||||
if err != nil {
|
||||
return NewBuildIfError(err, "Failed to parse template "+srcPath)
|
||||
return NewBuildIfError(err, "Failed to parse template " + srcPath)
|
||||
}
|
||||
|
||||
f, err := os.Create(destPath)
|
||||
@@ -119,12 +119,12 @@ func RenderTemplate(destPath, srcPath string, data interface{}) (err error) {
|
||||
|
||||
err = tmpl.Execute(f, data)
|
||||
if err != nil {
|
||||
return NewBuildIfError(err, "Failed to Render template "+srcPath)
|
||||
return NewBuildIfError(err, "Failed to Render template " + srcPath)
|
||||
}
|
||||
|
||||
err = f.Close()
|
||||
if err != nil {
|
||||
return NewBuildIfError(err, "Failed to close file stream "+destPath)
|
||||
return NewBuildIfError(err, "Failed to close file stream " + destPath)
|
||||
}
|
||||
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) {
|
||||
tmpl, err := template.ParseFiles(srcPath...)
|
||||
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)
|
||||
if err != nil {
|
||||
return NewBuildIfError(err, "Failed to render template "+srcPath[0])
|
||||
return NewBuildIfError(err, "Failed to render template " + srcPath[0])
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -150,7 +150,7 @@ func MustChmod(filename string, mode os.FileMode) {
|
||||
|
||||
// Called if panic
|
||||
func PanicOnError(err error, msg string) {
|
||||
if revErr, ok := err.(*Error); (ok && revErr != nil) || (!ok && err != nil) {
|
||||
if revErr, ok := err.(*SourceError); (ok && revErr != nil) || (!ok && err != nil) {
|
||||
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() {
|
||||
err := os.MkdirAll(filepath.Join(destDir, relSrcPath), 0777)
|
||||
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
|
||||
}
|
||||
@@ -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 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.
|
||||
@@ -218,7 +218,7 @@ func fsWalk(fname string, linkName string, walkFn filepath.WalkFunc) error {
|
||||
|
||||
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
|
||||
symlinkPath, err = filepath.EvalSymlinks(path)
|
||||
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
|
||||
func FindSrcPaths(appImportPath, revelImportPath string, packageResolver func(pkgName string) error) (appSourcePath, revelSourcePath string, err error) {
|
||||
var (
|
||||
gopaths = filepath.SplitList(build.Default.GOPATH)
|
||||
goroot = build.Default.GOROOT
|
||||
)
|
||||
|
||||
if len(gopaths) == 0 {
|
||||
err = errors.New("GOPATH environment variable is not set. " +
|
||||
"Please refer to http://golang.org/doc/code.html to configure your Go environment.")
|
||||
return
|
||||
}
|
||||
|
||||
if ContainsString(gopaths, goroot) {
|
||||
err = fmt.Errorf("GOPATH (%s) must not include your GOROOT (%s). "+
|
||||
"Please refer to http://golang.org/doc/code.html to configure your Go environment. ",
|
||||
build.Default.GOPATH, goroot)
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
appPkgDir := ""
|
||||
appPkgSrcDir := ""
|
||||
if len(appImportPath)>0 {
|
||||
Logger.Info("Seeking app package","app",appImportPath)
|
||||
appPkg, err := build.Import(appImportPath, "", build.FindOnly)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Failed to import " + appImportPath + " with error %s", err.Error())
|
||||
return "","",err
|
||||
func FindSrcPaths(appPath string, packageList []string, packageResolver func(pkgName string) error) (sourcePathsmap map[string]string, err error) {
|
||||
sourcePathsmap, missingList, err := findSrcPaths(appPath, packageList)
|
||||
if err != nil && packageResolver != nil {
|
||||
for _, item := range missingList {
|
||||
if err = packageResolver(item); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
appPkgDir,appPkgSrcDir =appPkg.Dir, appPkg.SrcRoot
|
||||
sourcePathsmap, missingList, err = findSrcPaths(appPath, packageList)
|
||||
}
|
||||
Logger.Info("Seeking remote package","using",appImportPath, "remote",revelImportPath)
|
||||
revelPkg, err := build.Default.Import(revelImportPath, appPkgDir, build.FindOnly)
|
||||
if err != nil {
|
||||
Logger.Info("Resolved called Seeking remote package","using",appImportPath, "remote",revelImportPath)
|
||||
packageResolver(revelImportPath)
|
||||
revelPkg, err = build.Import(revelImportPath, appPkgDir, build.FindOnly)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Failed to find Revel with error: %s", err.Error())
|
||||
return
|
||||
if err != nil && len(missingList) > 0 {
|
||||
for _, missing := range missingList {
|
||||
Logger.Error("Unable to import this package", "package", missing)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ package cmd
|
||||
|
||||
const (
|
||||
// Version current Revel version
|
||||
Version = "0.21.0"
|
||||
Version = "1.0.0-dev"
|
||||
|
||||
// BuildDate latest commit/release date
|
||||
BuildDate = "2018-10-30"
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
"github.com/revel/cmd/model"
|
||||
"github.com/revel/cmd/utils"
|
||||
"gopkg.in/fsnotify/fsnotify.v1"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
type Listener interface {
|
||||
// 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.
|
||||
Refresh() *utils.Error
|
||||
Refresh() *utils.SourceError
|
||||
}
|
||||
|
||||
// DiscerningListener allows the receiver to selectively watch files.
|
||||
@@ -44,7 +44,7 @@ type Watcher struct {
|
||||
paths *model.RevelContainer
|
||||
refreshTimer *time.Timer // The timer to countdown the next refresh
|
||||
timerMutex *sync.Mutex // A mutex to prevent concurrent updates
|
||||
refreshChannel chan *utils.Error
|
||||
refreshChannel chan *utils.SourceError
|
||||
refreshChannelCount int
|
||||
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.StringDefault("watch.mode", "normal") == "eager",
|
||||
timerMutex: &sync.Mutex{},
|
||||
refreshChannel: make(chan *utils.Error, 10),
|
||||
refreshChannel: make(chan *utils.SourceError, 10),
|
||||
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.
|
||||
// It returns the first (if any) error returned.
|
||||
func (w *Watcher) Notify() *utils.Error {
|
||||
func (w *Watcher) Notify() *utils.SourceError {
|
||||
if w.serial {
|
||||
// Serialize Notify() calls.
|
||||
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)
|
||||
if w.forceRefresh || refresh || w.lastError == i {
|
||||
var err *utils.Error
|
||||
var err *utils.SourceError
|
||||
if w.serial {
|
||||
err = listener.Refresh()
|
||||
} else {
|
||||
@@ -229,7 +229,7 @@ func (w *Watcher) Notify() *utils.Error {
|
||||
|
||||
// Build a queue for refresh notifications
|
||||
// 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
|
||||
// This code block ensures that either a timer is created
|
||||
// or that a process would be added the the h.refreshChannel
|
||||
|
||||
Reference in New Issue
Block a user