github.com/jd-ly/cmd@v1.0.10/revel/version.go (about)

     1  // Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
     2  // Revel Framework source code and usage is governed by a MIT style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
     6  // Revel Framework source code and usage is governed by a MIT style
     7  // license that can be found in the LICENSE file.
     8  
     9  package main
    10  
    11  import (
    12  	"fmt"
    13  
    14  	"github.com/jd-ly/cmd"
    15  	"github.com/jd-ly/cmd/model"
    16  	"github.com/jd-ly/cmd/utils"
    17  	"go/ast"
    18  	"go/parser"
    19  	"go/token"
    20  	"io/ioutil"
    21  	"net/http"
    22  	"os"
    23  	"os/exec"
    24  	"path/filepath"
    25  	"strings"
    26  	"bytes"
    27  )
    28  
    29  type (
    30  	// The version container
    31  	VersionCommand struct {
    32  		Command        *model.CommandConfig // The command
    33  		revelVersion   *model.Version       // The Revel framework version
    34  		modulesVersion *model.Version       // The Revel modules version
    35  		cmdVersion     *model.Version       // The tool version
    36  	}
    37  )
    38  
    39  var cmdVersion = &Command{
    40  	UsageLine: "revel version",
    41  	Short:     "displays the Revel Framework and Go version",
    42  	Long: `
    43  Displays the Revel Framework and Go version.
    44  
    45  For example:
    46  
    47      revel version [<application path>]
    48  `,
    49  }
    50  
    51  func init() {
    52  	v := &VersionCommand{}
    53  	cmdVersion.UpdateConfig = v.UpdateConfig
    54  	cmdVersion.RunWith = v.RunWith
    55  }
    56  
    57  // Update the version
    58  func (v *VersionCommand) UpdateConfig(c *model.CommandConfig, args []string) bool {
    59  	if len(args) > 0 {
    60  		c.Version.ImportPath = args[0]
    61  	}
    62  	return true
    63  }
    64  
    65  // Displays the version of go and Revel
    66  func (v *VersionCommand) RunWith(c *model.CommandConfig) (err error) {
    67  	utils.Logger.Info("Requesting version information", "config", c)
    68  	v.Command = c
    69  
    70  	// Update the versions with the local values
    71  	v.updateLocalVersions()
    72  
    73  	needsUpdates := true
    74  	versionInfo := ""
    75  	for x := 0; x < 2 && needsUpdates; x++ {
    76  		needsUpdates = false
    77  		versionInfo, needsUpdates = v.doRepoCheck(x == 0)
    78  	}
    79  
    80  	fmt.Printf("%s\n\nGo Location:%s\n\n", versionInfo, c.GoCmd)
    81  	cmd := exec.Command(c.GoCmd, "version")
    82  	cmd.Stdout = os.Stdout
    83  	if e := cmd.Start(); e != nil {
    84  		fmt.Println("Go command error ", e)
    85  	} else {
    86  		cmd.Wait()
    87  	}
    88  
    89  	return
    90  }
    91  
    92  // Checks the Revel repos for the latest version
    93  func (v *VersionCommand) doRepoCheck(updateLibs bool) (versionInfo string, needsUpdate bool) {
    94  	var (
    95  		title string
    96  		localVersion *model.Version
    97  	)
    98  	for _, repo := range []string{"revel", "cmd", "modules"} {
    99  		versonFromRepo, err := v.versionFromRepo(repo, "", "version.go")
   100  		if err != nil {
   101  			utils.Logger.Info("Failed to get version from repo", "repo", repo, "error", err)
   102  		}
   103  		switch repo {
   104  		case "revel":
   105  			title, repo, localVersion = "Revel Framework", "github.com/revel/revel", v.revelVersion
   106  		case "cmd":
   107  			title, repo, localVersion = "Revel Cmd", "github.com/jd-ly/cmd/revel", v.cmdVersion
   108  		case "modules":
   109  			title, repo, localVersion = "Revel Modules", "github.com/revel/modules", v.modulesVersion
   110  		}
   111  
   112  		// Only do an update on the first loop, and if specified to update
   113  		versionInfo = versionInfo + v.outputVersion(title, repo, localVersion, versonFromRepo)
   114  	}
   115  	return
   116  }
   117  
   118  // Checks for updates if needed
   119  func (v *VersionCommand) doUpdate(title, repo string, local, remote *model.Version) {
   120  	utils.Logger.Info("Updating package", "package", title, "repo", repo)
   121  	fmt.Println("Attempting to update package", title)
   122  	if err := v.Command.PackageResolver(repo); err != nil {
   123  		utils.Logger.Error("Unable to update repo", "repo", repo, "error", err)
   124  	} else if repo == "github.com/jd-ly/cmd/revel" {
   125  		// One extra step required here to run the install for the command
   126  		utils.Logger.Fatal("Revel command tool was updated, you must manually run the following command before continuing\ngo install github.com/jd-ly/cmd/revel")
   127  	}
   128  	return
   129  }
   130  
   131  // Prints out the local and remote versions, calls update if needed
   132  func (v *VersionCommand) outputVersion(title, repo string, local, remote *model.Version) (output string) {
   133  	buffer := &bytes.Buffer{}
   134  	remoteVersion := "Unknown"
   135  	if remote != nil {
   136  		remoteVersion = remote.VersionString()
   137  	}
   138  	localVersion := "Unknown"
   139  	if local != nil {
   140  		localVersion = local.VersionString()
   141  	}
   142  
   143  	fmt.Fprintf(buffer, "%s\t:\t%s\t(%s remote master branch)\n", title, localVersion, remoteVersion)
   144  	return buffer.String()
   145  }
   146  
   147  // Returns the version from the repository
   148  func (v *VersionCommand) versionFromRepo(repoName, branchName, fileName string) (version *model.Version, err error) {
   149  	if branchName == "" {
   150  		branchName = "master"
   151  	}
   152  	// Try to download the version of file from the repo, just use an http connection to retrieve the source
   153  	// Assuming that the repo is github
   154  	fullurl := "https://raw.githubusercontent.com/revel/" + repoName + "/" + branchName + "/" + fileName
   155  	resp, err := http.Get(fullurl)
   156  	if err != nil {
   157  		return
   158  	}
   159  	defer resp.Body.Close()
   160  	body, err := ioutil.ReadAll(resp.Body)
   161  	if err != nil {
   162  		return
   163  	}
   164  	utils.Logger.Info("Got version file", "from", fullurl, "content", string(body))
   165  
   166  	return v.versionFromBytes(body)
   167  }
   168  
   169  // Returns version information from a file called version on the gopath
   170  func (v *VersionCommand) compareAndUpdateVersion(remoteVersion *model.Version, localVersion *model.Version) (err error) {
   171  	return
   172  }
   173  func (v *VersionCommand) versionFromFilepath(sourcePath string) (version *model.Version, err error) {
   174  	utils.Logger.Info("Fullpath to revel", "dir", sourcePath)
   175  
   176  	sourceStream, err := ioutil.ReadFile(filepath.Join(sourcePath, "version.go"))
   177  	if err != nil {
   178  		return
   179  	}
   180  	return v.versionFromBytes(sourceStream)
   181  }
   182  
   183  // Returns version information from a file called version on the gopath
   184  func (v *VersionCommand) versionFromBytes(sourceStream []byte) (version *model.Version, err error) {
   185  	fset := token.NewFileSet() // positions are relative to fset
   186  
   187  	// Parse src but stop after processing the imports.
   188  	f, err := parser.ParseFile(fset, "", sourceStream, parser.ParseComments)
   189  	if err != nil {
   190  		err = utils.NewBuildError("Failed to parse Revel version error:", "error", err)
   191  		return
   192  	}
   193  	version = &model.Version{}
   194  
   195  	// Print the imports from the file's AST.
   196  	for _, s := range f.Decls {
   197  		genDecl, ok := s.(*ast.GenDecl)
   198  		if !ok {
   199  			continue
   200  		}
   201  		if genDecl.Tok != token.CONST {
   202  			continue
   203  		}
   204  		for _, a := range genDecl.Specs {
   205  			spec := a.(*ast.ValueSpec)
   206  			r := spec.Values[0].(*ast.BasicLit)
   207  			switch spec.Names[0].Name {
   208  			case "Version":
   209  				version.ParseVersion(strings.Replace(r.Value, `"`, "", -1))
   210  			case "BuildDate":
   211  				version.BuildDate = r.Value
   212  			case "MinimumGoVersion":
   213  				version.MinGoVersion = r.Value
   214  			}
   215  		}
   216  	}
   217  	return
   218  }
   219  
   220  // Fetch the local version of revel from the file system
   221  func (v *VersionCommand) updateLocalVersions() {
   222  	v.cmdVersion = &model.Version{}
   223  	v.cmdVersion.ParseVersion(cmd.Version)
   224  	v.cmdVersion.BuildDate = cmd.BuildDate
   225  	v.cmdVersion.MinGoVersion = cmd.MinimumGoVersion
   226  
   227  	if v.Command.Version.ImportPath=="" {
   228  		return
   229  	}
   230  
   231  	pathMap, err := utils.FindSrcPaths(v.Command.AppPath, []string{model.RevelImportPath, model.RevelModulesImportPath}, v.Command.PackageResolver)
   232  	if err != nil {
   233  		utils.Logger.Warn("Unable to extract version information from Revel library", "path", pathMap[model.RevelImportPath], "error", err)
   234  		return
   235  	}
   236  	utils.Logger.Info("Fullpath to revel modules", "dir", pathMap[model.RevelImportPath])
   237  	v.revelVersion, err = v.versionFromFilepath(pathMap[model.RevelImportPath])
   238  	if err != nil {
   239  		utils.Logger.Warn("Unable to extract version information from Revel", "error,err")
   240  	}
   241  
   242  	v.modulesVersion, err = v.versionFromFilepath(pathMap[model.RevelModulesImportPath])
   243  	if err != nil {
   244  		utils.Logger.Warn("Unable to extract version information from Revel Modules", "path", pathMap[model.RevelModulesImportPath], "error", err)
   245  	}
   246  
   247  	return
   248  }