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