github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/version/version.go (about)

     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 = "1.1.1"
    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  	Status = &UpdateStatus{
    61  		maxVerSeen:    Version,
    62  		notified:      false,
    63  		seedSet:       set.New(),
    64  		versionStatus: noUpdate,
    65  	}
    66  }
    67  
    68  type UpdateStatus struct {
    69  	sync.RWMutex
    70  	maxVerSeen    string
    71  	notified      bool
    72  	seedSet       *set.Set
    73  	versionStatus uint16
    74  }
    75  
    76  func (s *UpdateStatus) AddSeed(seedAddr string) {
    77  	s.Lock()
    78  	defer s.Unlock()
    79  	s.seedSet.Add(seedAddr)
    80  }
    81  
    82  // CheckUpdate checks whether there is a newer version to update.
    83  // If there is, it set the "Status" variable to a proper value.
    84  // 	params:
    85  // 		localVerStr: the version of the node itself
    86  // 		remoteVerStr: the version received from a seed node.
    87  // 		remoteAddr: the version received from a seed node.
    88  // current rule:
    89  // 		1. small update: seed version is higher than the node itself
    90  // 		2. significant update: seed mojor version is higher than the node itself
    91  func (s *UpdateStatus) CheckUpdate(localVerStr string, remoteVerStr string, remoteAddr string) error {
    92  	s.Lock()
    93  	defer s.Unlock()
    94  
    95  	if !s.seedSet.Has(remoteAddr) {
    96  		return nil
    97  	}
    98  
    99  	localVersion, err := gover.NewVersion(localVerStr)
   100  	if err != nil {
   101  		return err
   102  	}
   103  	remoteVersion, err := gover.NewVersion(remoteVerStr)
   104  	if err != nil {
   105  		return err
   106  	}
   107  	if remoteVersion.GreaterThan(localVersion) {
   108  		if s.versionStatus == noUpdate {
   109  			s.versionStatus = hasUpdate
   110  		}
   111  
   112  		maxVersion, err := gover.NewVersion(s.maxVerSeen)
   113  		if err != nil {
   114  			return err
   115  		}
   116  
   117  		if remoteVersion.GreaterThan(maxVersion) {
   118  			s.maxVerSeen = remoteVerStr
   119  		}
   120  	}
   121  	if remoteVersion.Segments()[0] > localVersion.Segments()[0] {
   122  		s.versionStatus = hasMUpdate
   123  	}
   124  	if s.versionStatus != noUpdate {
   125  		log.WithFields(log.Fields{
   126  			"module":          logModule,
   127  			"Current version": localVerStr,
   128  			"Newer version":   remoteVerStr,
   129  			"seed":            remoteAddr,
   130  		}).Warn("Please update your bytomd via https://github.com/Bytom/bytom/releases/ or http://bytom.io/wallet/")
   131  		s.notified = true
   132  	}
   133  	return nil
   134  }
   135  
   136  func (s *UpdateStatus) MaxVerSeen() string {
   137  	s.RLock()
   138  	defer s.RUnlock()
   139  	return s.maxVerSeen
   140  }
   141  
   142  func (s *UpdateStatus) VersionStatus() uint16 {
   143  	s.RLock()
   144  	defer s.RUnlock()
   145  	return s.versionStatus
   146  }
   147  
   148  // CompatibleWith checks whether the remote peer version is compatible with the
   149  // node itself.
   150  // RULES:
   151  // | local |           remote           |
   152  // |   -   |             -              |
   153  // | 1.0.3 | same major&moinor version. |
   154  // | 1.0.4 |     same major version.    |
   155  func CompatibleWith(remoteVerStr string) (bool, error) {
   156  	localVersion, err := gover.NewVersion(Version)
   157  	if err != nil {
   158  		return false, err
   159  	}
   160  	remoteVersion, err := gover.NewVersion(remoteVerStr)
   161  	if err != nil {
   162  		return false, err
   163  	}
   164  	return (localVersion.Segments()[0] == remoteVersion.Segments()[0]), nil
   165  }