From ee53d2f39916551a24edecc1467a1522659ab1bc Mon Sep 17 00:00:00 2001 From: NotZippy Date: Mon, 29 Oct 2018 12:04:19 -0700 Subject: [PATCH] Patchset for 21 Added Version -u to update the checked out libaraies Enhanced new skeleton toto support http https and git schemas when cloning the skeleton repo. Enhanced version command to fetch server version from master branch Enhanced version command to update local repository if a new version exists on the server --- .codebeatsettings | 1 + model/command_config.go | 32 +++-- model/revel_container.go | 3 +- model/version.go | 20 ++- revel/new.go | 25 ++-- revel/version.go | 267 ++++++++++++++++++++++++++++++--------- version.go | 2 +- 7 files changed, 256 insertions(+), 94 deletions(-) diff --git a/.codebeatsettings b/.codebeatsettings index edce06a..a99f39e 100644 --- a/.codebeatsettings +++ b/.codebeatsettings @@ -1,6 +1,7 @@ { "GOLANG": { "ABC":[25, 35, 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], diff --git a/model/command_config.go b/model/command_config.go index 41f82d6..e46366a 100644 --- a/model/command_config.go +++ b/model/command_config.go @@ -44,6 +44,7 @@ type ( 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 @@ -87,6 +88,7 @@ type ( // 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"` } ) @@ -124,7 +126,6 @@ func (c *CommandConfig) UpdateImportPath() error { } // For an absolute path currentPath, _ = filepath.Abs(importPath) - } if err == nil { @@ -136,7 +137,10 @@ func (c *CommandConfig) UpdateImportPath() error { if len(importPath) > 4 && strings.ToLower(importPath[0:4]) == "src/" { importPath = importPath[4:] } else if importPath == "src" { - return fmt.Errorf("Invlaid import path, working dir is in GOPATH root") + if c.Index != VERSION { + return fmt.Errorf("Invlaid import path, working dir is in GOPATH root") + } + importPath = "" } utils.Logger.Info("Updated import path", "path", importPath) } @@ -167,18 +171,19 @@ func (c *CommandConfig) UpdateImportPath() error { // Used to initialize the package resolver func (c *CommandConfig) InitPackageResolver() { - useVendor := utils.DirExists(filepath.Join(c.AppPath, "vendor")) + c.Vendored = utils.DirExists(filepath.Join(c.AppPath, "vendor")) if c.Index == NEW && c.New.Vendored { - useVendor = true + c.Vendored = true } - utils.Logger.Info("InitPackageResolver", "useVendor", useVendor, "path", c.AppPath) + + utils.Logger.Info("InitPackageResolver", "useVendor", c.Vendored, "path", c.AppPath) var ( depPath string err error ) - if useVendor { + if c.Vendored { utils.Logger.Info("Vendor folder detected, scanning for deps in path") depPath, err = exec.LookPath("dep") if err != nil { @@ -194,8 +199,8 @@ func (c *CommandConfig) InitPackageResolver() { //useVendor := utils.DirExists(filepath.Join(c.AppPath, "vendor")) var getCmd *exec.Cmd - utils.Logger.Info("Request for package ", "package", pkgName, "use vendor", useVendor) - if useVendor { + 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 == "" { @@ -206,7 +211,15 @@ func (c *CommandConfig) InitPackageResolver() { utils.Logger.Error("Missing package", "package", pkgName) return fmt.Errorf("Missing package %s", pkgName) } - getCmd = exec.Command(depPath, "ensure", "-add", 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) @@ -225,6 +238,7 @@ func (c *CommandConfig) InitPackageResolver() { // lookup and set Go related variables func (c *CommandConfig) InitGoPaths() { + utils.Logger.Info("InitGoPaths") // lookup go path c.GoPath = build.Default.GOPATH if c.GoPath == "" { diff --git a/model/revel_container.go b/model/revel_container.go index 1cba47c..f15a665 100644 --- a/model/revel_container.go +++ b/model/revel_container.go @@ -92,6 +92,7 @@ func (w *WrappedRevelCallback) PackageResolver(pkgName string) error { // RevelImportPath Revel framework import path var RevelImportPath = "github.com/revel/revel" +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. @@ -226,7 +227,7 @@ func (rp *RevelContainer) loadModules(callback RevelCallback) (err error) { modulePath, err := rp.ResolveImportPath(moduleImportPath) if err != nil { - utils.Logger.Info("Missing module ", "module", moduleImportPath, "error",err) + utils.Logger.Info("Missing module ", "module_import_path", moduleImportPath, "error",err) callback.PackageResolver(moduleImportPath) modulePath, err = rp.ResolveImportPath(moduleImportPath) if err != nil { diff --git a/model/version.go b/model/version.go index 502cbb4..60ee3b0 100644 --- a/model/version.go +++ b/model/version.go @@ -13,6 +13,8 @@ type Version struct { Minor int Maintenance int Suffix string + BuildDate string + MinGoVersion string } // The compatibility list @@ -27,6 +29,13 @@ 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) { + v = &Version{} + return v, v.ParseVersion(version) +} + +// Parse the version and return it as a Version object +func (v *Version)ParseVersion(version string) (err error) { + parsedResult := versionRegExp.FindAllStringSubmatch(version, -1) if len(parsedResult) != 1 { err = errors.Errorf("Invalid version %s", version) @@ -36,7 +45,7 @@ func ParseVersion(version string) (v *Version, err error) { 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]) @@ -45,7 +54,6 @@ func ParseVersion(version string) (v *Version, err error) { 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 != "" { @@ -105,6 +113,12 @@ func (v *Version) Newer(o *Version) bool { } // Convert the version to a string -func (v *Version) String() string { +func (v *Version) VersionString() string { return fmt.Sprintf("%s%d.%d.%d%s", v.Prefix, v.Major, v.Minor, v.Maintenance, v.Suffix) } + +// Convert the version build date and go version to a string +func (v *Version) String() string { + return fmt.Sprintf("Version: %s%d.%d.%d%s\nBuild Date: %s\n Minimium Go Version: %s", + v.Prefix, v.Major, v.Minor, v.Maintenance, v.Suffix, v.BuildDate, v.MinGoVersion) +} diff --git a/revel/new.go b/revel/new.go index abe299d..84f848e 100644 --- a/revel/new.go +++ b/revel/new.go @@ -16,9 +16,6 @@ import ( "github.com/revel/cmd/model" "github.com/revel/cmd/utils" "net/url" - "github.com/kr/pty" - "io" - "bytes" ) var cmdNew = &Command{ @@ -117,16 +114,11 @@ func newApp(c *model.CommandConfig) (err error) { getCmd := exec.Command("dep", "ensure", "-v") utils.CmdInit(getCmd, c.AppPath) - f, err := pty.Start(getCmd); - stdout := new(bytes.Buffer) - io.Copy(io.MultiWriter(stdout, os.Stdout), f) - if err = getCmd.Wait(); err != nil { - } utils.Logger.Info("Exec:", "args", getCmd.Args, "env", getCmd.Env, "workingdir",getCmd.Dir) - // getOutput, err := getCmd.CombinedOutput() + getOutput, err := getCmd.CombinedOutput() if err != nil { - return utils.NewBuildIfError(err, stdout.String()) + return utils.NewBuildIfError(err, string(getOutput)) } } @@ -207,7 +199,7 @@ func setApplicationPath(c *model.CommandConfig) (err error) { // Set the skeleton path func setSkeletonPath(c *model.CommandConfig) (err error) { if len(c.New.SkeletonPath) == 0 { - c.New.SkeletonPath = "git://" + RevelSkeletonsImportPath + ":basic/bootstrap4" + c.New.SkeletonPath = "https://" + RevelSkeletonsImportPath + ":basic/bootstrap4" } // First check to see the protocol of the string @@ -216,7 +208,7 @@ func setSkeletonPath(c *model.CommandConfig) (err error) { utils.Logger.Info("Detected skeleton path", "path", sp) switch strings.ToLower(sp.Scheme) { - // TODO Add support for https, http, ftp + // TODO Add support for ftp, sftp, scp ?? case "" : sp.Scheme="file" fallthrough @@ -234,14 +226,13 @@ func setSkeletonPath(c *model.CommandConfig) (err error) { return fmt.Errorf("Failed to find skeleton in filepath %s %s", fullpath, sp.String()) } case "git": + fallthrough + case "http": + fallthrough + case "https": if err := newLoadFromGit(c, sp); err != nil { return err } - //case "": - - //if err := newLoadFromGo(c, sp); err != nil { - // return err - //} default: utils.Logger.Fatal("Unsupported skeleton schema ", "path", c.New.SkeletonPath) diff --git a/revel/version.go b/revel/version.go index de3e584..1000693 100644 --- a/revel/version.go +++ b/revel/version.go @@ -10,19 +10,30 @@ package main import ( "fmt" - "runtime" "github.com/revel/cmd" "github.com/revel/cmd/model" "github.com/revel/cmd/utils" "go/ast" - "go/build" "go/parser" "go/token" "io/ioutil" + "net/http" "os" "os/exec" "path/filepath" + "strings" + "bytes" +) + +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 + } ) var cmdVersion = &Command{ @@ -38,12 +49,13 @@ For example: } func init() { - cmdVersion.RunWith = versionApp - cmdVersion.UpdateConfig = updateVersionConfig + v := &VersionCommand{} + cmdVersion.UpdateConfig = v.UpdateConfig + cmdVersion.RunWith = v.RunWith } // Update the version -func updateVersionConfig(c *model.CommandConfig, args []string) bool { +func (v *VersionCommand) UpdateConfig(c *model.CommandConfig, args []string) bool { if len(args) > 0 { c.Version.ImportPath = args[0] } @@ -51,71 +63,200 @@ func updateVersionConfig(c *model.CommandConfig, args []string) bool { } // Displays the version of go and Revel -func versionApp(c *model.CommandConfig) (err error) { +func (v *VersionCommand) RunWith(c *model.CommandConfig) (err error) { + utils.Logger.Info("Requesting version information", "config", c) + v.Command = c - var revelPath, appPath string + // Update the versions with the local values + v.updateLocalVersions() - - appPath, revelPath, err = utils.FindSrcPaths(c.ImportPath, model.RevelImportPath, c.PackageResolver) - if err != nil { - return utils.NewBuildError("Failed to import "+c.ImportPath+" with error:", "error", err) + needsUpdates := true + versionInfo := "" + for x := 0; x < 2 && needsUpdates; x++ { + needsUpdates = false + versionInfo, needsUpdates = v.doRepoCheck(x==0) } - revelPath = revelPath + model.RevelImportPath - fmt.Println("\nRevel Framework",revelPath, appPath ) - if err != nil { - utils.Logger.Info("Failed to find Revel in GOPATH with error:", "error", err, "gopath", build.Default.GOPATH) - fmt.Println("Information not available (not on GOPATH)") + fmt.Println(versionInfo) + cmd := exec.Command(c.GoCmd, "version") + cmd.Stdout = os.Stdout + if e := cmd.Start(); e != nil { + fmt.Println("Go command error ", e) } else { - utils.Logger.Info("Fullpath to revel", "dir", revelPath) - fset := token.NewFileSet() // positions are relative to fset - - version, err := ioutil.ReadFile(filepath.Join(revelPath, "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, "", version, 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) - fmt.Printf("Revel %s = %s\n", spec.Names[0].Name, r.Value) - } - } - } - fmt.Println("\nRevel Command Utility Tool") - fmt.Println("Version", cmd.Version) - fmt.Println("Build Date", cmd.BuildDate) - fmt.Println("Minimum Go Version", cmd.MinimumGoVersion) - - fmt.Printf("Compiled By %s %s/%s\n\n", runtime.Version(), runtime.GOOS, runtime.GOARCH) - // Extract the goversion detected - if len(c.GoCmd) > 0 { - cmd := exec.Command(c.GoCmd, "version") - cmd.Stdout = os.Stdout - if e := cmd.Start(); e != nil { - fmt.Println("Go command error ", e) - } else { - cmd.Wait() - } - } else { - fmt.Println("Go command not found ") + cmd.Wait() } return } +// Checks the Revel repos for the latest version +func (v *VersionCommand) doRepoCheck(updateLibs bool) (versionInfo string, needsUpdate bool) { + var ( + title string + localVersion *model.Version + ) + for _, repo := range []string{"revel", "cmd", "modules"} { + versonFromRepo, err := v.versionFromRepo(repo, "", "version.go") + if err != nil { + utils.Logger.Info("Failed to get version from repo", "repo", repo, "error", err) + } + switch repo { + case "revel": + title, repo, localVersion = "Revel Framework", "github.com/revel/revel", v.revelVersion + case "cmd": + title, repo, localVersion = "Revel Cmd", "github.com/revel/cmd/revel", v.cmdVersion + case "modules": + title, repo, localVersion = "Revel Modules", "github.com/revel/modules", v.modulesVersion + } + + // 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)) { + needsUpdate = true + if shouldUpdate { + v.doUpdate(title, repo, localVersion, versonFromRepo) + v.updateLocalVersions() + } + } + } + versionInfo = versionInfo + v.outputVersion(title, repo, localVersion, versonFromRepo) + } + return +} + +// 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) + 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) + } else if repo == "github.com/revel/cmd/revel" { + // One extra step required here to run the install for the command + utils.Logger.Fatal("Revel command tool was updated, you must manually run the following command before continuing\ngo install github.com/revel/cmd/revel") + } + return +} + +// Prints out the local and remote versions, calls update if needed +func (v *VersionCommand) outputVersion(title, repo string, local, remote *model.Version) (output string) { + buffer := &bytes.Buffer{} + remoteVersion := "Unknown" + if remote != nil { + remoteVersion = remote.VersionString() + } + localVersion := "Unknown" + if local != nil { + localVersion = local.VersionString() + } + + fmt.Fprintf(buffer, "%s\t:\t%s\t(%s remote master branch)\n", title, localVersion, remoteVersion) + return buffer.String() +} + +// Returns the version from the repository +func (v *VersionCommand) versionFromRepo(repoName, branchName, fileName string) (version *model.Version, err error) { + if branchName == "" { + branchName = "master" + } + // Try to download the version of file from the repo, just use an http connection to retrieve the source + // Assuming that the repo is github + fullurl := "https://raw.githubusercontent.com/revel/" + repoName + "/" + branchName + "/" + fileName + resp, err := http.Get(fullurl) + if err != nil { + return + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return + } + utils.Logger.Info("Got version file", "from", fullurl, "content", string(body)) + + return v.versionFromBytes(body) +} + +// Returns version information from a file called version on the gopath +func (v *VersionCommand) compareAndUpdateVersion(remoteVersion *model.Version, localVersion *model.Version) (err error) { + return +} +func (v *VersionCommand) versionFromFilepath(sourcePath string) (version *model.Version, err error) { + utils.Logger.Info("Fullpath to revel", "dir", sourcePath) + + sourceStream, err := ioutil.ReadFile(filepath.Join(sourcePath, "version.go")) + if err != nil { + return + } + return v.versionFromBytes(sourceStream) +} + +// Returns version information from a file called version on the gopath +func (v *VersionCommand) versionFromBytes(sourceStream []byte) (version *model.Version, err error) { + fset := token.NewFileSet() // positions are relative to fset + + // Parse src but stop after processing the imports. + f, err := parser.ParseFile(fset, "", sourceStream, parser.ParseComments) + if err != nil { + err = utils.NewBuildError("Failed to parse Revel version error:", "error", err) + return + } + version = &model.Version{} + + // 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) + switch spec.Names[0].Name { + case "Version": + version.ParseVersion(strings.Replace(r.Value, `"`, "", -1)) + case "BuildDate": + version.BuildDate = r.Value + case "MinimumGoVersion": + version.MinGoVersion = r.Value + } + } + } + return +} + +// Fetch the local version of revel from the file system +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) + if err != nil { + utils.Logger.Warn("Unable to extract version information from Revel library", "error,err") + return + } + revelPath = revelPath + model.RevelImportPath + utils.Logger.Info("Fullpath to revel", "dir", revelPath) + v.revelVersion, err = v.versionFromFilepath(revelPath) + 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) + 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) + } + + return +} diff --git a/version.go b/version.go index c4793c7..f9c4944 100644 --- a/version.go +++ b/version.go @@ -6,7 +6,7 @@ package cmd const ( // Version current Revel Command version - Version = "0.20.2" + Version = "0.21.0-dev" // BuildDate latest commit/release date BuildDate = "2018-10-02"