mirror of
https://github.com/kevin-DL/revel-cmd.git
synced 2026-01-11 18:54:31 +00:00
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:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
110
model/version.go
Normal 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
33
model/version_test.go
Normal 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")
|
||||
}
|
||||
Reference in New Issue
Block a user