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 }