github.com/decred/politeia@v1.4.0/util/version/version.go (about) 1 // Copyright (c) 2013-2014 The btcsuite developers 2 // Copyright (c) 2015-2022 The Decred developers 3 // Use of this source code is governed by an ISC 4 // license that can be found in the LICENSE file. 5 6 // Package version provides a single location to house the version information 7 // for dcrd and other utilities provided in the same repository. 8 package version 9 10 import ( 11 "fmt" 12 "regexp" 13 "strconv" 14 "strings" 15 ) 16 17 const ( 18 // semanticAlphabet defines the allowed characters for the pre-release and 19 // build metadata portions of a semantic version string. 20 semanticAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-." 21 ) 22 23 // semverRE is a regular expression used to parse a semantic version string 24 // into its constituent parts. 25 var semverRE = regexp.MustCompile(`^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)` + 26 `(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*` + 27 `[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`) 28 29 // These variables define the application version and follow the semantic 30 // versioning 2.0.0 spec (https://semver.org/). 31 var ( 32 // Note for maintainers: 33 // 34 // The expected process for setting the version in releases is as follows: 35 // - Create a release branch of the form 'release-vMAJOR.MINOR' 36 // - Modify the Version variable below on that branch to: 37 // - Remove the pre-release portion 38 // - Set the build metadata to 'release.local' 39 // - Update the Version variable below on the master branch to the next 40 // expected version while retaining a pre-release of 'pre' 41 // 42 // These steps ensure that building from source produces versions that are 43 // distinct from reproducible builds that override the Version via linker 44 // flags. 45 46 // Version is the application version per the semantic versioning 2.0.0 spec 47 // (https://semver.org/). 48 // 49 // It is defined as a variable so it can be overridden during the build 50 // process with: 51 // '-ldflags "-X main.Version=fullsemver"' 52 // if needed. 53 // 54 // It MUST be a full semantic version per the semantic versioning spec or 55 // the app will panic at runtime. Of particular note is the pre-release 56 // and build metadata portions MUST only contain characters from 57 // semanticAlphabet. 58 Version = "1.4.0-pre" 59 60 // NOTE: The following values are set via init by parsing the above Version 61 // string. 62 63 // These fields are the individual semantic version components that define 64 // the application version. 65 Major uint32 66 Minor uint32 67 Patch uint32 68 PreRelease string 69 BuildMetadata string 70 ) 71 72 // parseUint32 converts the passed string to an unsigned integer or returns an 73 // error if it is invalid. 74 func parseUint32(s string, fieldName string) (uint32, error) { 75 val, err := strconv.ParseUint(s, 10, 32) 76 if err != nil { 77 return 0, fmt.Errorf("malformed semver %s: %w", fieldName, err) 78 } 79 return uint32(val), err 80 } 81 82 // checkSemString returns an error if the passed string contains characters that 83 // are not in the provided alphabet. 84 func checkSemString(s, alphabet, fieldName string) error { 85 for _, r := range s { 86 if !strings.ContainsRune(alphabet, r) { 87 return fmt.Errorf("malformed semver %s: %q invalid", fieldName, r) 88 } 89 } 90 return nil 91 } 92 93 // parseSemVer parses various semver components from the provided string. 94 func parseSemVer(s string) (uint32, uint32, uint32, string, string, error) { 95 // Parse the various semver component from the version string via a regular 96 // expression. 97 m := semverRE.FindStringSubmatch(s) 98 if m == nil { 99 err := fmt.Errorf("malformed version string %q: does not conform to "+ 100 "semver specification", s) 101 return 0, 0, 0, "", "", err 102 } 103 104 major, err := parseUint32(m[1], "major") 105 if err != nil { 106 return 0, 0, 0, "", "", err 107 } 108 109 minor, err := parseUint32(m[2], "minor") 110 if err != nil { 111 return 0, 0, 0, "", "", err 112 } 113 114 patch, err := parseUint32(m[3], "patch") 115 if err != nil { 116 return 0, 0, 0, "", "", err 117 } 118 119 preRel := m[4] 120 err = checkSemString(preRel, semanticAlphabet, "pre-release") 121 if err != nil { 122 return 0, 0, 0, "", "", err 123 } 124 125 build := m[5] 126 err = checkSemString(build, semanticAlphabet, "buildmetadata") 127 if err != nil { 128 return 0, 0, 0, "", "", err 129 } 130 131 return major, minor, patch, preRel, build, nil 132 } 133 134 func init() { 135 var err error 136 Major, Minor, Patch, PreRelease, BuildMetadata, err = parseSemVer(Version) 137 if err != nil { 138 panic(err) 139 } 140 if BuildMetadata == "" { 141 BuildMetadata = vcsCommitID() 142 if BuildMetadata != "" { 143 Version = fmt.Sprintf("%d.%d.%d", Major, Minor, Patch) 144 if PreRelease != "" { 145 Version += "-" + PreRelease 146 } 147 Version += "+" + BuildMetadata 148 } 149 } 150 }