github.com/bytom/bytom@v1.1.2-0.20221014091027-bbcba3df6075/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 = "2.0.6"
    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  }