Revel tool enhancements

* run Command will choose CWD if no additional arguments are supplied
* Added Revel version check, compatible lists are in model/version
This commit is contained in:
NotZippy
2018-10-10 20:53:23 -07:00
parent 031fde6009
commit 8c21a56302
8 changed files with 236 additions and 35 deletions

View File

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

View File

@@ -1,17 +1,22 @@
package model
// The constants
import (
"fmt"
"github.com/revel/cmd"
"github.com/revel/cmd/logger"
"github.com/revel/cmd/utils"
"go/ast"
"go/build"
"go/parser"
"go/token"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
)
// The constants
const (
NEW COMMAND = iota + 1
RUN
@@ -28,17 +33,19 @@ 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
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
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"`
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
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"`
@@ -57,7 +64,7 @@ type (
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" `
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
@@ -138,6 +145,16 @@ func (c *CommandConfig) UpdateImportPath() bool {
c.ImportPath = importPath
utils.Logger.Info("Returned import path", "path", importPath, "buildpath", build.Default.GOPATH)
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 {
utils.Logger.Fatal("Compatibility Error", "message", err,
"Revel framework version", c.FrameworkVersion.String(), "Revel tool version", c.CommandVersion.String())
}
utils.Logger.Info("Revel versions", "revel-tool", c.CommandVersion.String(), "Revel Framework", c.FrameworkVersion.String())
}
return (len(importPath) > 0 || !required)
}
@@ -185,7 +202,7 @@ func (c *CommandConfig) InitPackageResolver() {
getCmd = exec.Command(depPath, "ensure", "-add", pkgName)
} else {
utils.Logger.Info("No vendor folder detected, not using dependency manager to import package", "package", pkgName)
getCmd = exec.Command(c.GoCmd, "get", pkgName)
getCmd = exec.Command(c.GoCmd, "get", "-u", pkgName)
}
utils.CmdInit(getCmd, c.AppPath)
@@ -237,7 +254,7 @@ func (c *CommandConfig) InitGoPaths() {
}
}
if len(c.SrcRoot)==0 && len(bestpath) > 0 {
if len(c.SrcRoot) == 0 && len(bestpath) > 0 {
c.SrcRoot = bestpath
}
utils.Logger.Info("Source root", "path", c.SrcRoot, "cwd", workingDir, "gopath", c.GoPath)
@@ -256,3 +273,48 @@ func (c *CommandConfig) InitGoPaths() {
c.AppPath = filepath.Join(c.SrcRoot, filepath.FromSlash(c.ImportPath))
utils.Logger.Info("Set application path", "path", c.AppPath)
}
// 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)
if err == nil {
utils.Logger.Info("Fullpath to revel", "dir", revelPath)
fset := token.NewFileSet() // positions are relative to fset
versionData, err := ioutil.ReadFile(filepath.Join(revelPath, RevelImportPath, "version.go"))
if err != nil {
utils.Logger.Error("Failed to find Revel version:", "error", err, "path", revelPath)
}
// Parse src but stop after processing the imports.
f, err := parser.ParseFile(fset, "", versionData, parser.ParseComments)
if err != nil {
return utils.NewBuildError("Failed to parse Revel version error:", "error", err)
}
// Print the imports from the file's AST.
for _, s := range f.Decls {
genDecl, ok := s.(*ast.GenDecl)
if !ok {
continue
}
if genDecl.Tok != token.CONST {
continue
}
for _, a := range genDecl.Specs {
spec := a.(*ast.ValueSpec)
r := spec.Values[0].(*ast.BasicLit)
if spec.Names[0].Name == "Version" {
c.FrameworkVersion, err = ParseVersion(strings.Replace(r.Value, `"`, ``, -1))
if err != nil {
utils.Logger.Errorf("Failed to parse version")
} else {
utils.Logger.Info("Parsed revel version", "version", c.FrameworkVersion.String())
}
}
}
}
}
return
}

View File

@@ -10,15 +10,15 @@ import (
func TestEventHandler(t *testing.T) {
counter := 0
newListener := func(typeOf revel.Event, value interface{}) (responseOf revel.EventResponse) {
if typeOf == revel.REVEL_FAILURE {
if typeOf == revel.ENGINE_SHUTDOWN_REQUEST {
counter++
}
return
}
// Attach the same handlder twice so we expect to see the response twice as well
// Attach the same handler twice so we expect to see the response twice as well
revel.AddInitEventHandler(newListener)
revel.AddInitEventHandler(newListener)
revel.RaiseEvent(revel.REVEL_AFTER_MODULES_LOADED, nil)
revel.RaiseEvent(revel.REVEL_FAILURE, nil)
revel.StopServer(1)
assert.Equal(t, counter, 2, "Expected event handler to have been called")
}

110
model/version.go Normal file
View File

@@ -0,0 +1,110 @@
package model
import (
"fmt"
"github.com/pkg/errors"
"regexp"
"strconv"
)
type Version struct {
Prefix string
Major int
Minor int
Maintenance int
Suffix string
}
// The compatibility list
var frameworkCompatibleList = [][]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
}
// Parses a version like v1.2.3a or 1.2
var versionRegExp = regexp.MustCompile(`([^\d]*)?([0-9]*)\.([0-9]*)(\.([0-9]*))?(.*)`)
// Parse the version and return it as a Version object
func ParseVersion(version string) (v *Version, err error) {
parsedResult := versionRegExp.FindAllStringSubmatch(version, -1)
if len(parsedResult) != 1 {
err = errors.Errorf("Invalid version %s", version)
return
}
if len(parsedResult[0]) != 7 {
err = errors.Errorf("Invalid version %s", version)
return
}
v = &Version{}
v.Prefix = parsedResult[0][1]
v.Major = v.intOrZero(parsedResult[0][2])
v.Minor = v.intOrZero(parsedResult[0][3])
v.Maintenance = v.intOrZero(parsedResult[0][5])
v.Suffix = parsedResult[0][6]
return
}
// Returns 0 or an int value for the string, errors are returned as 0
func (v *Version) intOrZero(input string) (value int) {
if input != "" {
value, _ = strconv.Atoi(input)
}
return value
}
// Returns true if this major revision is compatible
func (v *Version) CompatibleFramework(c *CommandConfig) error {
for i, rv := range frameworkCompatibleList {
start, _ := ParseVersion(rv[0])
end, _ := ParseVersion(rv[1])
if !v.Newer(start) || v.Newer(end) {
continue
}
// Framework is older then 0.20, turn on historic mode
if i == 0 {
c.HistoricMode = true
}
return nil
}
return errors.New("Unknown versions - do a 'go get -u github.com/revel/cmd/revel github.com/revel/modules github.com/revel/revel'")
}
// Returns true if this major revision is newer then the passed in
func (v *Version) MajorNewer(o *Version) bool {
if v.Major != o.Major {
return v.Major > o.Major
}
return false
}
// Returns true if this major or major and minor revision is newer then the value passed in
func (v *Version) MinorNewer(o *Version) bool {
if v.Major != o.Major {
return v.Major > o.Major
}
if v.Minor != o.Minor {
return v.Minor > o.Minor
}
return false
}
// Returns true if the version is newer then the current on
func (v *Version) Newer(o *Version) bool {
if v.Major != o.Major {
return v.Major > o.Major
}
if v.Minor != o.Minor {
return v.Minor > o.Minor
}
if v.Maintenance != o.Maintenance {
return v.Maintenance > o.Maintenance
}
return false
}
// Convert the version to a string
func (v *Version) String() string {
return fmt.Sprintf("%s%d.%d.%d%s", v.Prefix, v.Major, v.Minor, v.Maintenance, v.Suffix)
}

33
model/version_test.go Normal file
View File

@@ -0,0 +1,33 @@
package model_test
import (
"github.com/stretchr/testify/assert"
"testing"
"github.com/revel/cmd/model"
)
var versionTests = [][]string{
{"v0.20.0-dev", "v0.20.0-dev"},
{"v0.20-dev", "v0.20.0-dev"},
{"v0.20.", "v0.20.0"},
{"2.0", "2.0.0"},
}
// Test that the event handler can be attached and it dispatches the event received
func TestVersion(t *testing.T) {
for _, v:= range versionTests {
p,e:=model.ParseVersion(v[0])
assert.Nil(t,e,"Should have parsed %s",v)
assert.Equal(t,p.String(),v[1], "Should be equal %s==%s",p.String(),v)
}
}
// test the ranges
func TestVersionRange(t *testing.T) {
a,_ := model.ParseVersion("0.1.2")
b,_ := model.ParseVersion("0.2.1")
c,_ := model.ParseVersion("1.0.1")
assert.True(t, b.MinorNewer(a), "B is newer then A")
assert.False(t, b.MajorNewer(a), "B is not major newer then A")
assert.False(t, b.MajorNewer(c), "B is not major newer then A")
assert.True(t, c.MajorNewer(b), "C is major newer then b")
}

View File

@@ -119,6 +119,8 @@ func newApp(c *model.CommandConfig) (err error) {
if err = setApplicationPath(c); err != nil {
return err
}
// At this point the versions can be set
c.SetVersions()
// checking and setting skeleton
if err=setSkeletonPath(c);err!=nil {
@@ -189,16 +191,6 @@ func setApplicationPath(c *model.CommandConfig) (err error) {
c.AppName = filepath.Base(c.AppPath)
//if c.BasePath == "." {
// // we need to remove the a single '.' when
// // the app is in the $GOROOT/src directory
// c.BasePath = ""
//} else {
// // we need to append a '/' when the app is
// // is a subdirectory such as $GOROOT/src/path/to/revelapp
// c.BasePath += "/"
//}
return nil
}

View File

@@ -12,6 +12,7 @@ import (
"github.com/revel/cmd/model"
"github.com/revel/cmd/utils"
"go/build"
"os"
)
var cmdRun = &Command{
@@ -108,7 +109,8 @@ func updateRunConfig(c *model.CommandConfig, args []string) bool {
c.Run.Mode = args[0]
}
case 0:
return false
// Attempt to set the import path to the current working director.
c.Run.ImportPath,_ = os.Getwd()
}
c.Index = model.RUN
return true

View File

@@ -118,3 +118,4 @@ func versionApp(c *model.CommandConfig) (err error) {
return
}