upgrade_common.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. // Copyright (C) 2014 The Syncthing Authors.
  2. //
  3. // This program is free software: you can redistribute it and/or modify it
  4. // under the terms of the GNU General Public License as published by the Free
  5. // Software Foundation, either version 3 of the License, or (at your option)
  6. // any later version.
  7. //
  8. // This program is distributed in the hope that it will be useful, but WITHOUT
  9. // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  10. // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  11. // more details.
  12. //
  13. // You should have received a copy of the GNU General Public License along
  14. // with this program. If not, see <http://www.gnu.org/licenses/>.
  15. // Package upgrade downloads and compares releases, and upgrades the running binary.
  16. package upgrade
  17. import (
  18. "errors"
  19. "strconv"
  20. "strings"
  21. "github.com/calmh/osext"
  22. )
  23. type Release struct {
  24. Tag string `json:"tag_name"`
  25. Prerelease bool `json:"prerelease"`
  26. Assets []Asset `json:"assets"`
  27. }
  28. type Asset struct {
  29. URL string `json:"url"`
  30. Name string `json:"name"`
  31. }
  32. var (
  33. ErrVersionUpToDate = errors.New("current version is up to date")
  34. ErrVersionUnknown = errors.New("couldn't fetch release information")
  35. ErrUpgradeUnsupported = errors.New("upgrade unsupported")
  36. ErrUpgradeInProgress = errors.New("upgrade already in progress")
  37. upgradeUnlocked = make(chan bool, 1)
  38. )
  39. func init() {
  40. upgradeUnlocked <- true
  41. }
  42. // A wrapper around actual implementations
  43. func To(rel Release) error {
  44. select {
  45. case <-upgradeUnlocked:
  46. path, err := osext.Executable()
  47. if err != nil {
  48. upgradeUnlocked <- true
  49. return err
  50. }
  51. err = upgradeTo(path, rel)
  52. // If we've failed to upgrade, unlock so that another attempt could be made
  53. if err != nil {
  54. upgradeUnlocked <- true
  55. }
  56. return err
  57. default:
  58. return ErrUpgradeInProgress
  59. }
  60. }
  61. // A wrapper around actual implementations
  62. func ToURL(url string) error {
  63. select {
  64. case <-upgradeUnlocked:
  65. path, err := osext.Executable()
  66. if err != nil {
  67. upgradeUnlocked <- true
  68. return err
  69. }
  70. err = upgradeToURL(path, url)
  71. // If we've failed to upgrade, unlock so that another attempt could be made
  72. if err != nil {
  73. upgradeUnlocked <- true
  74. }
  75. return err
  76. default:
  77. return ErrUpgradeInProgress
  78. }
  79. }
  80. type Relation int
  81. const (
  82. MajorOlder Relation = -2 // Older by a major version (x in x.y.z or 0.x.y).
  83. Older = -1 // Older by a minor version (y or z in x.y.z, or y in 0.x.y)
  84. Equal = 0 // Versions are semantically equal
  85. Newer = 1 // Newer by a minor version (y or z in x.y.z, or y in 0.x.y)
  86. MajorNewer = 2 // Newer by a major version (x in x.y.z or 0.x.y).
  87. )
  88. // Returns a relation describing how a compares to b.
  89. func CompareVersions(a, b string) Relation {
  90. arel, apre := versionParts(a)
  91. brel, bpre := versionParts(b)
  92. minlen := len(arel)
  93. if l := len(brel); l < minlen {
  94. minlen = l
  95. }
  96. // First compare major-minor-patch versions
  97. for i := 0; i < minlen; i++ {
  98. if arel[i] < brel[i] {
  99. if i == 0 {
  100. return MajorOlder
  101. }
  102. if i == 1 && arel[0] == 0 {
  103. return MajorOlder
  104. }
  105. return Older
  106. }
  107. if arel[i] > brel[i] {
  108. if i == 0 {
  109. return MajorNewer
  110. }
  111. if i == 1 && arel[0] == 0 {
  112. return MajorNewer
  113. }
  114. return Newer
  115. }
  116. }
  117. // Longer version is newer, when the preceding parts are equal
  118. if len(arel) < len(brel) {
  119. return Older
  120. }
  121. if len(arel) > len(brel) {
  122. return Newer
  123. }
  124. // Prerelease versions are older, if the versions are the same
  125. if len(apre) == 0 && len(bpre) > 0 {
  126. return Newer
  127. }
  128. if len(apre) > 0 && len(bpre) == 0 {
  129. return Older
  130. }
  131. minlen = len(apre)
  132. if l := len(bpre); l < minlen {
  133. minlen = l
  134. }
  135. // Compare prerelease strings
  136. for i := 0; i < minlen; i++ {
  137. switch av := apre[i].(type) {
  138. case int:
  139. switch bv := bpre[i].(type) {
  140. case int:
  141. if av < bv {
  142. return Older
  143. }
  144. if av > bv {
  145. return Newer
  146. }
  147. case string:
  148. return Older
  149. }
  150. case string:
  151. switch bv := bpre[i].(type) {
  152. case int:
  153. return Newer
  154. case string:
  155. if av < bv {
  156. return Older
  157. }
  158. if av > bv {
  159. return Newer
  160. }
  161. }
  162. }
  163. }
  164. // If all else is equal, longer prerelease string is newer
  165. if len(apre) < len(bpre) {
  166. return Older
  167. }
  168. if len(apre) > len(bpre) {
  169. return Newer
  170. }
  171. // Looks like they're actually the same
  172. return Equal
  173. }
  174. // Split a version into parts.
  175. // "1.2.3-beta.2" -> []int{1, 2, 3}, []interface{}{"beta", 2}
  176. func versionParts(v string) ([]int, []interface{}) {
  177. if strings.HasPrefix(v, "v") || strings.HasPrefix(v, "V") {
  178. // Strip initial 'v' or 'V' prefix if present.
  179. v = v[1:]
  180. }
  181. parts := strings.SplitN(v, "+", 2)
  182. parts = strings.SplitN(parts[0], "-", 2)
  183. fields := strings.Split(parts[0], ".")
  184. release := make([]int, len(fields))
  185. for i, s := range fields {
  186. v, _ := strconv.Atoi(s)
  187. release[i] = v
  188. }
  189. var prerelease []interface{}
  190. if len(parts) > 1 {
  191. fields = strings.Split(parts[1], ".")
  192. prerelease = make([]interface{}, len(fields))
  193. for i, s := range fields {
  194. v, err := strconv.Atoi(s)
  195. if err == nil {
  196. prerelease[i] = v
  197. } else {
  198. prerelease[i] = s
  199. }
  200. }
  201. }
  202. return release, prerelease
  203. }