upgrade_common.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. // Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
  2. // All rights reserved. Use of this source code is governed by an MIT-style
  3. // license that can be found in the LICENSE file.
  4. // Package upgrade downloads and compares releases, and upgrades the running binary.
  5. package upgrade
  6. import (
  7. "errors"
  8. "strconv"
  9. "strings"
  10. )
  11. type Release struct {
  12. Tag string `json:"tag_name"`
  13. Prerelease bool `json:"prerelease"`
  14. Assets []Asset `json:"assets"`
  15. }
  16. type Asset struct {
  17. URL string `json:"url"`
  18. Name string `json:"name"`
  19. }
  20. var (
  21. ErrVersionUpToDate = errors.New("current version is up to date")
  22. ErrVersionUnknown = errors.New("couldn't fetch release information")
  23. ErrUpgradeUnsupported = errors.New("upgrade unsupported")
  24. ErrUpgradeInProgress = errors.New("upgrade already in progress")
  25. upgradeUnlocked = make(chan bool, 1)
  26. )
  27. func init() {
  28. upgradeUnlocked <- true
  29. }
  30. // A wrapper around actual implementations
  31. func UpgradeTo(rel Release, archExtra string) error {
  32. select {
  33. case <-upgradeUnlocked:
  34. err := upgradeTo(rel, archExtra)
  35. // If we've failed to upgrade, unlock so that another attempt could be made
  36. if err != nil {
  37. upgradeUnlocked <- true
  38. }
  39. return err
  40. default:
  41. return ErrUpgradeInProgress
  42. }
  43. }
  44. // Returns 1 if a>b, -1 if a<b and 0 if they are equal
  45. func CompareVersions(a, b string) int {
  46. arel, apre := versionParts(a)
  47. brel, bpre := versionParts(b)
  48. minlen := len(arel)
  49. if l := len(brel); l < minlen {
  50. minlen = l
  51. }
  52. // First compare major-minor-patch versions
  53. for i := 0; i < minlen; i++ {
  54. if arel[i] < brel[i] {
  55. return -1
  56. }
  57. if arel[i] > brel[i] {
  58. return 1
  59. }
  60. }
  61. // Longer version is newer, when the preceding parts are equal
  62. if len(arel) < len(brel) {
  63. return -1
  64. }
  65. if len(arel) > len(brel) {
  66. return 1
  67. }
  68. // Prerelease versions are older, if the versions are the same
  69. if len(apre) == 0 && len(bpre) > 0 {
  70. return 1
  71. }
  72. if len(apre) > 0 && len(bpre) == 0 {
  73. return -1
  74. }
  75. minlen = len(apre)
  76. if l := len(bpre); l < minlen {
  77. minlen = l
  78. }
  79. // Compare prerelease strings
  80. for i := 0; i < minlen; i++ {
  81. switch av := apre[i].(type) {
  82. case int:
  83. switch bv := bpre[i].(type) {
  84. case int:
  85. if av < bv {
  86. return -1
  87. }
  88. if av > bv {
  89. return 1
  90. }
  91. case string:
  92. return -1
  93. }
  94. case string:
  95. switch bv := bpre[i].(type) {
  96. case int:
  97. return 1
  98. case string:
  99. if av < bv {
  100. return -1
  101. }
  102. if av > bv {
  103. return 1
  104. }
  105. }
  106. }
  107. }
  108. // If all else is equal, longer prerelease string is newer
  109. if len(apre) < len(bpre) {
  110. return -1
  111. }
  112. if len(apre) > len(bpre) {
  113. return 1
  114. }
  115. // Looks like they're actually the same
  116. return 0
  117. }
  118. // Split a version into parts.
  119. // "1.2.3-beta.2" -> []int{1, 2, 3}, []interface{}{"beta", 2}
  120. func versionParts(v string) ([]int, []interface{}) {
  121. parts := strings.SplitN(v, "+", 2)
  122. parts = strings.SplitN(parts[0], "-", 2)
  123. fields := strings.Split(parts[0], ".")
  124. release := make([]int, len(fields))
  125. for i, s := range fields {
  126. v, _ := strconv.Atoi(s)
  127. release[i] = v
  128. }
  129. var prerelease []interface{}
  130. if len(parts) > 1 {
  131. fields = strings.Split(parts[1], ".")
  132. prerelease = make([]interface{}, len(fields))
  133. for i, s := range fields {
  134. v, err := strconv.Atoi(s)
  135. if err == nil {
  136. prerelease[i] = v
  137. } else {
  138. prerelease[i] = s
  139. }
  140. }
  141. }
  142. return release, prerelease
  143. }