github.com/mutagen-io/mutagen@v0.18.0-rc1/pkg/mutagen/version.go (about) 1 //go:build go1.21 2 3 package mutagen 4 5 import ( 6 "encoding/binary" 7 "errors" 8 "fmt" 9 "io" 10 ) 11 12 const ( 13 // VersionMajor represents the current major version of Mutagen. 14 VersionMajor = 0 15 // VersionMinor represents the current minor version of Mutagen. 16 VersionMinor = 18 17 // VersionPatch represents the current patch version of Mutagen. 18 VersionPatch = 0 19 // VersionTag represents a tag to be appended to the Mutagen version string. 20 // It must not contain spaces. If empty, no tag is appended to the version 21 // string. 22 VersionTag = "rc1" 23 ) 24 25 // DevelopmentModeEnabled indicates that development mode is active. This is 26 // regulated via VersionTag and should not be set or updated explicitly. 27 const DevelopmentModeEnabled = VersionTag == "dev" 28 29 // Version provides a stringified version of the current Mutagen version. 30 var Version string 31 32 // init performs global initialization. 33 func init() { 34 // Compute the stringified version. 35 if VersionTag != "" { 36 Version = fmt.Sprintf("%d.%d.%d-%s", VersionMajor, VersionMinor, VersionPatch, VersionTag) 37 } else { 38 Version = fmt.Sprintf("%d.%d.%d", VersionMajor, VersionMinor, VersionPatch) 39 } 40 } 41 42 // versionBytes is a type that can be used to send and receive version 43 // information over the wire. 44 type versionBytes [12]byte 45 46 // sendVersion writes the current version to the specified writer. Version tag 47 // components are neither transmitted nor received. 48 func sendVersion(writer io.Writer) error { 49 // Compute the version bytes. 50 var data versionBytes 51 binary.BigEndian.PutUint32(data[:4], VersionMajor) 52 binary.BigEndian.PutUint32(data[4:8], VersionMinor) 53 binary.BigEndian.PutUint32(data[8:], VersionPatch) 54 55 // Transmit the bytes. 56 _, err := writer.Write(data[:]) 57 return err 58 } 59 60 // receiveVersion reads version information from the specified reader. Version 61 // tag components are neither transmitted nor received. 62 func receiveVersion(reader io.Reader) (uint32, uint32, uint32, error) { 63 // Read the bytes. 64 var data versionBytes 65 if _, err := io.ReadFull(reader, data[:]); err != nil { 66 return 0, 0, 0, err 67 } 68 69 // Decode components. 70 major := binary.BigEndian.Uint32(data[:4]) 71 minor := binary.BigEndian.Uint32(data[4:8]) 72 patch := binary.BigEndian.Uint32(data[8:]) 73 74 // Done. 75 return major, minor, patch, nil 76 } 77 78 // ClientVersionHandshake performs the client side of a version handshake, 79 // returning an error if the received server version is not compatible with the 80 // client version. 81 // 82 // TODO: Add some ability to support version skew in this function. 83 func ClientVersionHandshake(stream io.ReadWriteCloser) error { 84 // Receive the server's version. 85 serverMajor, serverMinor, serverPatch, err := receiveVersion(stream) 86 if err != nil { 87 return fmt.Errorf("unable to receive server version: %w", err) 88 } 89 90 // Send our version to the server. 91 if err := sendVersion(stream); err != nil { 92 return fmt.Errorf("unable to send client version: %w", err) 93 } 94 95 // Ensure that our Mutagen versions are compatible. For now, we enforce that 96 // they're equal. 97 // TODO: Once we lock-in an internal protocol that we're going to support 98 // for some time, we can allow some version skew. On the client side in 99 // particular, we'll probably want to look out for the specific "locked-in" 100 // server protocol that we support and instantiate some frozen client 101 // implementation from that version. 102 versionMatch := serverMajor == VersionMajor && 103 serverMinor == VersionMinor && 104 serverPatch == VersionPatch 105 if !versionMatch { 106 return errors.New("version mismatch") 107 } 108 109 // Success. 110 return nil 111 } 112 113 // ServerVersionHandshake performs the server side of a version handshake, 114 // returning an error if the received client version is not compatible with the 115 // server version. 116 // 117 // TODO: Add some ability to support version skew in this function. 118 func ServerVersionHandshake(stream io.ReadWriteCloser) error { 119 // Send our version to the client. 120 if err := sendVersion(stream); err != nil { 121 return fmt.Errorf("unable to send server version: %w", err) 122 } 123 124 // Receive the client's version. 125 clientMajor, clientMinor, clientPatch, err := receiveVersion(stream) 126 if err != nil { 127 return fmt.Errorf("unable to receive client version: %w", err) 128 } 129 130 // Ensure that our versions are compatible. For now, we enforce that they're 131 // equal. 132 versionMatch := clientMajor == VersionMajor && 133 clientMinor == VersionMinor && 134 clientPatch == VersionPatch 135 if !versionMatch { 136 return errors.New("version mismatch") 137 } 138 139 // Success. 140 return nil 141 }