OSDN Git Service

11bfdd615b1435dbdf98ef0565288d3d8c1bc88c
[bytom/bytom.git] / version / version.go
1 // package version provide the version info for the node, and also provide
2 // support for version compatibility check and update notification.
3 //
4 // The version format should follow Semantic Versioning (https://semver.org/):
5 // MAJOR.MINOR.PATCH
6 //  1. MAJOR version when you make incompatible API changes,
7 //  2. MINOR version when you add functionality in a backwards-compatible manner, and
8 //      3. PATCH version when you make backwards-compatible bug fixes.
9 //
10 // A pre-release version MAY be denoted by appending a hyphen and a series of
11 // dot separated identifiers immediately following the patch version.
12 // Examples:
13 // 1.0.0-alpha, 1.0.0-alpha.1, 1.0.0-0.3.7, 1.0.0-x.7.z.92.
14 // Precedence:
15 // 1. Pre-release versions have a lower precedence than the associated normal version!
16 //    Numeric identifiers always have lower precedence than non-numeric identifiers.
17 // 2. A larger set of pre-release fields has a higher precedence than a smaller set,
18 //    if all of the preceding identifiers are equal.
19 // 3. Example:
20 //    1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0.
21 //
22 // Build metadata MAY be denoted by appending a plus sign and a series of dot
23 // separated identifiers immediately following the patch or pre-release version.
24 // Build metadata SHOULD be ignored when determining version precedence. Thus
25 // two versions that differ only in the build metadata, have the same precedence.
26
27 package version
28
29 import (
30         "sync"
31
32         gover "github.com/hashicorp/go-version"
33         log "github.com/sirupsen/logrus"
34         "gopkg.in/fatih/set.v0"
35 )
36
37 const (
38         // If needing to edit the iota, please ensure the following:
39         // noUpdate = 0
40         // hasUpdate = 1
41         // hasMUpdate = 2
42         noUpdate uint16 = iota
43         hasUpdate
44         hasMUpdate
45         logModule = "version"
46 )
47
48 var (
49         // The full version string
50         Version = "2.0.7"
51         // GitCommit is set with --ldflags "-X main.gitCommit=$(git rev-parse HEAD)"
52         GitCommit string
53         Status    *UpdateStatus
54 )
55
56 func init() {
57         if GitCommit != "" {
58                 Version += "+" + GitCommit[:8]
59         }
60
61         Status = &UpdateStatus{
62                 maxVerSeen:    Version,
63                 notified:      false,
64                 seedSet:       set.New(),
65                 versionStatus: noUpdate,
66         }
67 }
68
69 type UpdateStatus struct {
70         sync.RWMutex
71         maxVerSeen    string
72         notified      bool
73         seedSet       *set.Set
74         versionStatus uint16
75 }
76
77 func (s *UpdateStatus) AddSeed(seedAddr string) {
78         s.Lock()
79         defer s.Unlock()
80         s.seedSet.Add(seedAddr)
81 }
82
83 // CheckUpdate checks whether there is a newer version to update.
84 // If there is, it set the "Status" variable to a proper value.
85 //      params:
86 //              localVerStr: the version of the node itself
87 //              remoteVerStr: the version received from a seed node.
88 //              remoteAddr: the version received from a seed node.
89 // current rule:
90 //              1. small update: seed version is higher than the node itself
91 //              2. significant update: seed mojor version is higher than the node itself
92 func (s *UpdateStatus) CheckUpdate(localVerStr string, remoteVerStr string, remoteAddr string) error {
93         s.Lock()
94         defer s.Unlock()
95
96         if !s.seedSet.Has(remoteAddr) {
97                 return nil
98         }
99
100         localVersion, err := gover.NewVersion(localVerStr)
101         if err != nil {
102                 return err
103         }
104         remoteVersion, err := gover.NewVersion(remoteVerStr)
105         if err != nil {
106                 return err
107         }
108         if remoteVersion.GreaterThan(localVersion) {
109                 if s.versionStatus == noUpdate {
110                         s.versionStatus = hasUpdate
111                 }
112
113                 maxVersion, err := gover.NewVersion(s.maxVerSeen)
114                 if err != nil {
115                         return err
116                 }
117
118                 if remoteVersion.GreaterThan(maxVersion) {
119                         s.maxVerSeen = remoteVerStr
120                 }
121         }
122         if remoteVersion.Segments()[0] > localVersion.Segments()[0] {
123                 s.versionStatus = hasMUpdate
124         }
125         if s.versionStatus != noUpdate {
126                 log.WithFields(log.Fields{
127                         "module":          logModule,
128                         "Current version": localVerStr,
129                         "Newer version":   remoteVerStr,
130                         "seed":            remoteAddr,
131                 }).Warn("Please update your bytomd via https://github.com/Bytom/bytom/releases/ or http://bytom.io/wallet/")
132                 s.notified = true
133         }
134         return nil
135 }
136
137 func (s *UpdateStatus) MaxVerSeen() string {
138         s.RLock()
139         defer s.RUnlock()
140         return s.maxVerSeen
141 }
142
143 func (s *UpdateStatus) VersionStatus() uint16 {
144         s.RLock()
145         defer s.RUnlock()
146         return s.versionStatus
147 }
148
149 // CompatibleWith checks whether the remote peer version is compatible with the
150 // node itself.
151 // RULES:
152 // | local |           remote           |
153 // |   -   |             -              |
154 // | 1.0.3 | same major&moinor version. |
155 // | 1.0.4 |     same major version.    |
156 func CompatibleWith(remoteVerStr string) (bool, error) {
157         localVersion, err := gover.NewVersion(Version)
158         if err != nil {
159                 return false, err
160         }
161         remoteVersion, err := gover.NewVersion(remoteVerStr)
162         if err != nil {
163                 return false, err
164         }
165         return (localVersion.Segments()[0] == remoteVersion.Segments()[0]), nil
166 }