github.com/wiselike/revel-cmd@v1.2.1/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/wiselike/revel-cmd" 25 "github.com/wiselike/revel-cmd/model" 26 "github.com/wiselike/revel-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/wiselike/revel", v.revelVersion 107 case "cmd": 108 title, repo, localVersion = "Revel Cmd", "github.com/wiselike/revel-cmd/revel", v.cmdVersion 109 case "modules": 110 title, repo, localVersion = "Revel Modules", "github.com/wiselike/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 }