upgrade_common.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  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. "fmt"
  20. "runtime"
  21. "strconv"
  22. "strings"
  23. "github.com/calmh/osext"
  24. )
  25. type Release struct {
  26. Tag string `json:"tag_name"`
  27. Prerelease bool `json:"prerelease"`
  28. Assets []Asset `json:"assets"`
  29. }
  30. type Asset struct {
  31. URL string `json:"url"`
  32. Name string `json:"name"`
  33. }
  34. var (
  35. ErrVersionUpToDate = errors.New("current version is up to date")
  36. ErrVersionUnknown = errors.New("couldn't fetch release information")
  37. ErrUpgradeUnsupported = errors.New("upgrade unsupported")
  38. ErrUpgradeInProgress = errors.New("upgrade already in progress")
  39. upgradeUnlocked = make(chan bool, 1)
  40. )
  41. func init() {
  42. upgradeUnlocked <- true
  43. }
  44. // A wrapper around actual implementations
  45. func To(rel Release) error {
  46. select {
  47. case <-upgradeUnlocked:
  48. path, err := osext.Executable()
  49. if err != nil {
  50. upgradeUnlocked <- true
  51. return err
  52. }
  53. err = upgradeTo(path, rel)
  54. // If we've failed to upgrade, unlock so that another attempt could be made
  55. if err != nil {
  56. upgradeUnlocked <- true
  57. }
  58. return err
  59. default:
  60. return ErrUpgradeInProgress
  61. }
  62. }
  63. // A wrapper around actual implementations
  64. func ToURL(url string) error {
  65. select {
  66. case <-upgradeUnlocked:
  67. path, err := osext.Executable()
  68. if err != nil {
  69. upgradeUnlocked <- true
  70. return err
  71. }
  72. err = upgradeToURL(path, url)
  73. // If we've failed to upgrade, unlock so that another attempt could be made
  74. if err != nil {
  75. upgradeUnlocked <- true
  76. }
  77. return err
  78. default:
  79. return ErrUpgradeInProgress
  80. }
  81. }
  82. type Relation int
  83. const (
  84. MajorOlder Relation = -2 // Older by a major version (x in x.y.z or 0.x.y).
  85. Older = -1 // Older by a minor version (y or z in x.y.z, or y in 0.x.y)
  86. Equal = 0 // Versions are semantically equal
  87. Newer = 1 // Newer by a minor version (y or z in x.y.z, or y in 0.x.y)
  88. MajorNewer = 2 // Newer by a major version (x in x.y.z or 0.x.y).
  89. )
  90. // Returns a relation describing how a compares to b.
  91. func CompareVersions(a, b string) Relation {
  92. arel, apre := versionParts(a)
  93. brel, bpre := versionParts(b)
  94. minlen := len(arel)
  95. if l := len(brel); l < minlen {
  96. minlen = l
  97. }
  98. // First compare major-minor-patch versions
  99. for i := 0; i < minlen; i++ {
  100. if arel[i] < brel[i] {
  101. if i == 0 {
  102. return MajorOlder
  103. }
  104. if i == 1 && arel[0] == 0 {
  105. return MajorOlder
  106. }
  107. return Older
  108. }
  109. if arel[i] > brel[i] {
  110. if i == 0 {
  111. return MajorNewer
  112. }
  113. if i == 1 && arel[0] == 0 {
  114. return MajorNewer
  115. }
  116. return Newer
  117. }
  118. }
  119. // Longer version is newer, when the preceding parts are equal
  120. if len(arel) < len(brel) {
  121. return Older
  122. }
  123. if len(arel) > len(brel) {
  124. return Newer
  125. }
  126. // Prerelease versions are older, if the versions are the same
  127. if len(apre) == 0 && len(bpre) > 0 {
  128. return Newer
  129. }
  130. if len(apre) > 0 && len(bpre) == 0 {
  131. return Older
  132. }
  133. minlen = len(apre)
  134. if l := len(bpre); l < minlen {
  135. minlen = l
  136. }
  137. // Compare prerelease strings
  138. for i := 0; i < minlen; i++ {
  139. switch av := apre[i].(type) {
  140. case int:
  141. switch bv := bpre[i].(type) {
  142. case int:
  143. if av < bv {
  144. return Older
  145. }
  146. if av > bv {
  147. return Newer
  148. }
  149. case string:
  150. return Older
  151. }
  152. case string:
  153. switch bv := bpre[i].(type) {
  154. case int:
  155. return Newer
  156. case string:
  157. if av < bv {
  158. return Older
  159. }
  160. if av > bv {
  161. return Newer
  162. }
  163. }
  164. }
  165. }
  166. // If all else is equal, longer prerelease string is newer
  167. if len(apre) < len(bpre) {
  168. return Older
  169. }
  170. if len(apre) > len(bpre) {
  171. return Newer
  172. }
  173. // Looks like they're actually the same
  174. return Equal
  175. }
  176. // Split a version into parts.
  177. // "1.2.3-beta.2" -> []int{1, 2, 3}, []interface{}{"beta", 2}
  178. func versionParts(v string) ([]int, []interface{}) {
  179. if strings.HasPrefix(v, "v") || strings.HasPrefix(v, "V") {
  180. // Strip initial 'v' or 'V' prefix if present.
  181. v = v[1:]
  182. }
  183. parts := strings.SplitN(v, "+", 2)
  184. parts = strings.SplitN(parts[0], "-", 2)
  185. fields := strings.Split(parts[0], ".")
  186. release := make([]int, len(fields))
  187. for i, s := range fields {
  188. v, _ := strconv.Atoi(s)
  189. release[i] = v
  190. }
  191. var prerelease []interface{}
  192. if len(parts) > 1 {
  193. fields = strings.Split(parts[1], ".")
  194. prerelease = make([]interface{}, len(fields))
  195. for i, s := range fields {
  196. v, err := strconv.Atoi(s)
  197. if err == nil {
  198. prerelease[i] = v
  199. } else {
  200. prerelease[i] = s
  201. }
  202. }
  203. }
  204. return release, prerelease
  205. }
  206. func releaseName(tag string) string {
  207. switch runtime.GOOS {
  208. case "darwin":
  209. return fmt.Sprintf("syncthing-macosx-%s-%s.", runtime.GOARCH, tag)
  210. default:
  211. return fmt.Sprintf("syncthing-%s-%s-%s.", runtime.GOOS, runtime.GOARCH, tag)
  212. }
  213. }