github.com/linchen2chris/hugo@v0.0.0-20230307053224-cec209389705/common/hugo/version.go (about) 1 // Copyright 2018 The Hugo Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package hugo 15 16 import ( 17 "fmt" 18 "io" 19 "math" 20 "runtime" 21 "strconv" 22 "strings" 23 24 "github.com/gohugoio/hugo/compare" 25 "github.com/spf13/cast" 26 ) 27 28 // Version represents the Hugo build version. 29 type Version struct { 30 Major int 31 32 Minor int 33 34 // Increment this for bug releases 35 PatchLevel int 36 37 // HugoVersionSuffix is the suffix used in the Hugo version string. 38 // It will be blank for release versions. 39 Suffix string 40 } 41 42 var ( 43 _ compare.Eqer = (*VersionString)(nil) 44 _ compare.Comparer = (*VersionString)(nil) 45 ) 46 47 func (v Version) String() string { 48 return version(v.Major, v.Minor, v.PatchLevel, v.Suffix) 49 } 50 51 // Version returns the Hugo version. 52 func (v Version) Version() VersionString { 53 return VersionString(v.String()) 54 } 55 56 // Compare implements the compare.Comparer interface. 57 func (h Version) Compare(other any) int { 58 return compareVersions(h, other) 59 } 60 61 // VersionString represents a Hugo version string. 62 type VersionString string 63 64 func (h VersionString) String() string { 65 return string(h) 66 } 67 68 // Compare implements the compare.Comparer interface. 69 func (h VersionString) Compare(other any) int { 70 v := MustParseVersion(h.String()) 71 return compareVersions(v, other) 72 } 73 74 // Eq implements the compare.Eqer interface. 75 func (h VersionString) Eq(other any) bool { 76 s, err := cast.ToStringE(other) 77 if err != nil { 78 return false 79 } 80 return s == h.String() 81 } 82 83 var versionSuffixes = []string{"-test", "-DEV"} 84 85 // ParseVersion parses a version string. 86 func ParseVersion(s string) (Version, error) { 87 var vv Version 88 for _, suffix := range versionSuffixes { 89 if strings.HasSuffix(s, suffix) { 90 vv.Suffix = suffix 91 s = strings.TrimSuffix(s, suffix) 92 } 93 } 94 95 vv.Major, vv.Minor, vv.PatchLevel = parseVersion(s) 96 97 return vv, nil 98 } 99 100 // MustParseVersion parses a version string 101 // and panics if any error occurs. 102 func MustParseVersion(s string) Version { 103 vv, err := ParseVersion(s) 104 if err != nil { 105 panic(err) 106 } 107 return vv 108 } 109 110 // ReleaseVersion represents the release version. 111 func (v Version) ReleaseVersion() Version { 112 v.Suffix = "" 113 return v 114 } 115 116 // Next returns the next Hugo release version. 117 func (v Version) Next() Version { 118 return Version{Major: v.Major, Minor: v.Minor + 1} 119 } 120 121 // Prev returns the previous Hugo release version. 122 func (v Version) Prev() Version { 123 return Version{Major: v.Major, Minor: v.Minor - 1} 124 } 125 126 // NextPatchLevel returns the next patch/bugfix Hugo version. 127 // This will be a patch increment on the previous Hugo version. 128 func (v Version) NextPatchLevel(level int) Version { 129 prev := v.Prev() 130 prev.PatchLevel = level 131 return prev 132 } 133 134 // BuildVersionString creates a version string. This is what you see when 135 // running "hugo version". 136 func BuildVersionString() string { 137 // program := "Hugo Static Site Generator" 138 program := "hugo" 139 140 version := "v" + CurrentVersion.String() 141 142 bi := getBuildInfo() 143 if bi == nil { 144 return version 145 } 146 if bi.Revision != "" { 147 version += "-" + bi.Revision 148 } 149 if IsExtended { 150 version += "+extended" 151 } 152 153 osArch := bi.GoOS + "/" + bi.GoArch 154 155 date := bi.RevisionTime 156 if date == "" { 157 // Accept vendor-specified build date if .git/ is unavailable. 158 date = buildDate 159 } 160 if date == "" { 161 date = "unknown" 162 } 163 164 versionString := fmt.Sprintf("%s %s %s BuildDate=%s", 165 program, version, osArch, date) 166 167 if vendorInfo != "" { 168 versionString += " VendorInfo=" + vendorInfo 169 } 170 171 return versionString 172 } 173 174 func version(major, minor, patch int, suffix string) string { 175 if patch > 0 || minor > 53 { 176 return fmt.Sprintf("%d.%d.%d%s", major, minor, patch, suffix) 177 } 178 return fmt.Sprintf("%d.%d%s", major, minor, suffix) 179 } 180 181 // CompareVersion compares the given version string or number against the 182 // running Hugo version. 183 // It returns -1 if the given version is less than, 0 if equal and 1 if greater than 184 // the running version. 185 func CompareVersion(version any) int { 186 return compareVersions(CurrentVersion, version) 187 } 188 189 func compareVersions(inVersion Version, in any) int { 190 var c int 191 switch d := in.(type) { 192 case float64: 193 c = compareFloatWithVersion(d, inVersion) 194 case float32: 195 c = compareFloatWithVersion(float64(d), inVersion) 196 case int: 197 c = compareFloatWithVersion(float64(d), inVersion) 198 case int32: 199 c = compareFloatWithVersion(float64(d), inVersion) 200 case int64: 201 c = compareFloatWithVersion(float64(d), inVersion) 202 case Version: 203 if d.Major == inVersion.Major && d.Minor == inVersion.Minor && d.PatchLevel == inVersion.PatchLevel { 204 return strings.Compare(inVersion.Suffix, d.Suffix) 205 } 206 if d.Major > inVersion.Major { 207 return 1 208 } else if d.Major < inVersion.Major { 209 return -1 210 } 211 if d.Minor > inVersion.Minor { 212 return 1 213 } else if d.Minor < inVersion.Minor { 214 return -1 215 } 216 if d.PatchLevel > inVersion.PatchLevel { 217 return 1 218 } else if d.PatchLevel < inVersion.PatchLevel { 219 return -1 220 } 221 default: 222 s, err := cast.ToStringE(in) 223 if err != nil { 224 return -1 225 } 226 227 v, err := ParseVersion(s) 228 if err != nil { 229 return -1 230 } 231 return inVersion.Compare(v) 232 233 } 234 235 return c 236 } 237 238 func parseVersion(s string) (int, int, int) { 239 var major, minor, patch int 240 parts := strings.Split(s, ".") 241 if len(parts) > 0 { 242 major, _ = strconv.Atoi(parts[0]) 243 } 244 if len(parts) > 1 { 245 minor, _ = strconv.Atoi(parts[1]) 246 } 247 if len(parts) > 2 { 248 patch, _ = strconv.Atoi(parts[2]) 249 } 250 251 return major, minor, patch 252 } 253 254 // compareFloatWithVersion compares v1 with v2. 255 // It returns -1 if v1 is less than v2, 0 if v1 is equal to v2 and 1 if v1 is greater than v2. 256 func compareFloatWithVersion(v1 float64, v2 Version) int { 257 mf, minf := math.Modf(v1) 258 v1maj := int(mf) 259 v1min := int(minf * 100) 260 261 if v2.Major == v1maj && v2.Minor == v1min { 262 return 0 263 } 264 265 if v1maj > v2.Major { 266 return 1 267 268 } 269 270 if v1maj < v2.Major { 271 return -1 272 } 273 274 if v1min > v2.Minor { 275 return 1 276 } 277 278 return -1 279 280 } 281 282 func GoMinorVersion() int { 283 return goMinorVersion(runtime.Version()) 284 } 285 286 func goMinorVersion(version string) int { 287 if strings.HasPrefix(version, "devel") { 288 return 9999 // magic 289 } 290 var major, minor int 291 var trailing string 292 n, err := fmt.Sscanf(version, "go%d.%d%s", &major, &minor, &trailing) 293 if n == 2 && err == io.EOF { 294 // Means there were no trailing characters (i.e., not an alpha/beta) 295 err = nil 296 } 297 if err != nil { 298 return 0 299 } 300 return minor 301 }