vcs.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "os"
  6. "strings"
  7. "time"
  8. gosrc "github.com/Morganamilo/go-srcinfo"
  9. rpc "github.com/mikkeloscar/aur"
  10. )
  11. // Info contains the last commit sha of a repo
  12. type vcsInfo map[string]shaInfos
  13. type shaInfos map[string]shaInfo
  14. type shaInfo struct {
  15. Protocols []string `json:"protocols"`
  16. Branch string `json:"branch"`
  17. SHA string `json:"sha"`
  18. }
  19. // createDevelDB forces yay to create a DB of the existing development packages
  20. func createDevelDB() error {
  21. infoMap := make(map[string]*rpc.Pkg)
  22. srcinfosStale := make(map[string]*gosrc.Srcinfo)
  23. _, _, _, remoteNames, err := filterPackages()
  24. if err != nil {
  25. return err
  26. }
  27. info, err := aurInfoPrint(remoteNames)
  28. if err != nil {
  29. return err
  30. }
  31. for _, pkg := range info {
  32. infoMap[pkg.Name] = pkg
  33. }
  34. bases := getBases(infoMap)
  35. toSkip := pkgBuildsToSkip(info, sliceToStringSet(remoteNames))
  36. downloadPkgBuilds(info, bases, toSkip)
  37. tryParsesrcinfosFile(info, srcinfosStale, bases)
  38. for _, pkg := range info {
  39. pkgbuild, ok := srcinfosStale[pkg.PackageBase]
  40. if !ok {
  41. continue
  42. }
  43. for _, pkg := range bases[pkg.PackageBase] {
  44. updateVCSData(pkg.Name, pkgbuild.Source)
  45. }
  46. }
  47. fmt.Println(bold(yellow(arrow) + bold(" GenDB finished. No packages were installed")))
  48. return err
  49. }
  50. // parseSource returns the git url, default branch and protocols it supports
  51. func parseSource(source string) (url string, branch string, protocols []string) {
  52. if !(strings.Contains(source, "git://") ||
  53. strings.Contains(source, ".git") ||
  54. strings.Contains(source, "git+https://")) {
  55. return "", "", nil
  56. }
  57. split := strings.Split(source, "::")
  58. source = split[len(split)-1]
  59. split = strings.SplitN(source, "://", 2)
  60. if len(split) != 2 {
  61. return "", "", nil
  62. }
  63. protocols = strings.Split(split[0], "+")
  64. split = strings.SplitN(split[1], "#", 2)
  65. if len(split) == 2 {
  66. secondSplit := strings.SplitN(split[1], "=", 2)
  67. if secondSplit[0] != "branch" {
  68. //source has #commit= or #tag= which makes them not vcs
  69. //packages because they reference a specific point
  70. return "", "", nil
  71. }
  72. if len(secondSplit) == 2 {
  73. url = split[0]
  74. branch = secondSplit[1]
  75. }
  76. } else {
  77. url = split[0]
  78. branch = "HEAD"
  79. }
  80. url = strings.Split(url, "?")[0]
  81. branch = strings.Split(branch, "?")[0]
  82. return
  83. }
  84. func updateVCSData(pkgName string, sources []gosrc.ArchString) {
  85. if savedInfo == nil {
  86. savedInfo = make(vcsInfo)
  87. }
  88. info := make(shaInfos)
  89. for _, source := range sources {
  90. url, branch, protocols := parseSource(source.Value)
  91. if url == "" || branch == "" {
  92. continue
  93. }
  94. commit := getCommit(url, branch, protocols)
  95. if commit == "" {
  96. continue
  97. }
  98. info[url] = shaInfo{
  99. protocols,
  100. branch,
  101. commit,
  102. }
  103. savedInfo[pkgName] = info
  104. fmt.Println(bold(yellow(arrow)) + " Found git repo: " + cyan(url))
  105. saveVCSInfo()
  106. }
  107. }
  108. func getCommit(url string, branch string, protocols []string) string {
  109. for _, protocol := range protocols {
  110. cmd := passToGit("ls-remote", protocol+"://"+url, branch)
  111. cmd.Env = append(cmd.Env, "GIT_TERMINAL_PROMPT=0")
  112. stdout, _, err := capture(cmd)
  113. if err != nil {
  114. continue
  115. }
  116. //for some reason
  117. //git://bitbucket.org/volumesoffun/polyvox.git` hangs on my
  118. //machine but using http:// instead of git does not hang.
  119. //Introduce a time out so this can not hang
  120. timer := time.AfterFunc(5*time.Second, func() {
  121. cmd.Process.Kill()
  122. })
  123. err = cmd.Wait()
  124. timer.Stop()
  125. if err != nil {
  126. continue
  127. }
  128. split := strings.Fields(stdout)
  129. if len(split) < 2 {
  130. continue
  131. }
  132. commit := split[0]
  133. return commit
  134. }
  135. return ""
  136. }
  137. func (infos shaInfos) needsUpdate() bool {
  138. //used to signal we have gone through all sources and found nothing
  139. finished := make(chan struct{})
  140. alive := 0
  141. //if we find an update we use this to exit early and return true
  142. hasUpdate := make(chan struct{})
  143. checkHash := func(url string, info shaInfo) {
  144. hash := getCommit(url, info.Branch, info.Protocols)
  145. if hash != "" && hash != info.SHA {
  146. hasUpdate <- struct{}{}
  147. } else {
  148. finished <- struct{}{}
  149. }
  150. }
  151. for url, info := range infos {
  152. alive++
  153. go checkHash(url, info)
  154. }
  155. for {
  156. select {
  157. case <-hasUpdate:
  158. return true
  159. case <-finished:
  160. alive--
  161. if alive == 0 {
  162. return false
  163. }
  164. }
  165. }
  166. }
  167. func saveVCSInfo() error {
  168. marshalledinfo, err := json.MarshalIndent(savedInfo, "", "\t")
  169. if err != nil || string(marshalledinfo) == "null" {
  170. return err
  171. }
  172. in, err := os.OpenFile(vcsFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
  173. if err != nil {
  174. return err
  175. }
  176. defer in.Close()
  177. _, err = in.Write(marshalledinfo)
  178. if err != nil {
  179. return err
  180. }
  181. err = in.Sync()
  182. return err
  183. }