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