github.com/verrazzano/verrazzano@v1.7.0/ci/tools/derive_upgrade_version.go (about) 1 // Copyright (c) 2022, 2023, Oracle and/or its affiliates. 2 // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 package main 4 5 import ( 6 "flag" 7 "fmt" 8 "github.com/verrazzano/verrazzano/pkg/semver" 9 "os" 10 "os/exec" 11 "sort" 12 "strings" 13 ) 14 15 const ( 16 VersionForInstall = "install-version" 17 InterimVersionForUpgrade = "interim-version" 18 LatestVersionForCurrentBranch = "latest-version-for-branch" 19 VersionsGTE = "versions-gte" 20 VersionsLT = "versions-lt" 21 ) 22 23 var ( 24 workspace, versionType, developmentVersion string 25 excludeReleaseTags []string 26 ) 27 28 func main() { 29 30 //Parse command line arguments to extract params 31 help := false 32 flag.BoolVar(&help, "help", false, "Display usage help") 33 flag.Parse() 34 if help { 35 printUsage() 36 os.Exit(0) 37 } 38 parseCliArgs(flag.Args()) 39 40 //Extract release tags from git tag command. 41 releaseTags := getReleaseTags(workspace, excludeReleaseTags) 42 switch versionType { 43 case InterimVersionForUpgrade: 44 interimRelease := getInterimRelease(releaseTags) 45 fmt.Print(interimRelease) 46 case VersionForInstall: 47 installRelease := getInstallRelease(releaseTags) 48 fmt.Print(installRelease) 49 case LatestVersionForCurrentBranch: 50 latestRelease := getLatestReleaseForCurrentBranch(releaseTags) 51 fmt.Println(latestRelease) 52 case VersionsGTE: 53 tagsAfter, err := getTagsGTE(releaseTags, excludeReleaseTags[0]) 54 if err != nil { 55 panic(err) 56 } 57 fmt.Println(tagsAfter) 58 case VersionsLT: 59 tagsBefore, err := getTagsLT(releaseTags, excludeReleaseTags[0]) 60 if err != nil { 61 panic(err) 62 } 63 fmt.Println(tagsBefore) 64 default: 65 fmt.Printf("invalid command line argument for derive version type \n") 66 os.Exit(1) 67 } 68 } 69 70 func parseCliArgs(args []string) { 71 72 if len(args) < 1 { 73 fmt.Printf("\nno command line arguments were specified\n") 74 printUsage() 75 os.Exit(1) 76 } 77 78 if len(args) > 0 { 79 // Receive working directory as a command line argument. 80 workspace = args[0] 81 // Receive version type such as interimVersionForUpgrade or versionForInstall argument 82 versionType = args[1] 83 } else { 84 fmt.Printf("no worspace path and version type line arguments were specified\n") 85 os.Exit(1) 86 } 87 88 if len(args) > 2 { 89 for index, arg := range args { 90 // Remove any ',' or ']' suffixes and remove any '[' prefix 91 trimArg := strings.TrimPrefix(strings.TrimSuffix(strings.TrimSuffix(arg, ","), "]"), "[") 92 if index > 1 { 93 if versionType == LatestVersionForCurrentBranch { 94 developmentVersion = trimArg 95 return 96 } 97 excludeReleaseTags = append(excludeReleaseTags, trimArg) 98 } 99 } 100 } 101 } 102 103 func getReleaseTags(workspace string, excludeReleaseTags []string) []string { 104 // Change the working directory to the verrazzano workspace 105 err := os.Chdir(workspace) 106 if err != nil { 107 fmt.Printf("\nunable to change the current working directory %v", err.Error()) 108 } 109 // Execute git tag command. 110 cmd := exec.Command("git", "tag") 111 out, err := cmd.Output() 112 if err != nil { 113 fmt.Printf("\nunable to execute git tag command %v", err.Error()) 114 } 115 116 // Split the output by newline and store it in a slice 117 gitTags := strings.Split(string(out), "\n") 118 119 // Extract release tags from gitTags 120 var releaseTags []string 121 122 for _, tag := range gitTags { 123 if strings.HasPrefix(tag, "v") && !strings.HasPrefix(tag, "v0") { 124 // Exclude the release tags if tag exists in excludeReleaseTags 125 if !DoesTagExistsInExcludeList(tag, excludeReleaseTags) { 126 releaseTags = append(releaseTags, tag) 127 } 128 } 129 } 130 return releaseTags 131 } 132 133 // DoesTagExistsInExcludeList returns true if the tag exists in excludeReleasetag 134 func DoesTagExistsInExcludeList(releaseTag string, excludeReleaseTags []string) bool { 135 majorMinorReleaseTag := removePatchVersion(releaseTag) 136 builder := strings.Builder{} 137 if !strings.HasPrefix(majorMinorReleaseTag, strings.ToLower("v")) { 138 builder.WriteString("v" + majorMinorReleaseTag) 139 majorMinorReleaseTag = builder.String() 140 } 141 for _, excludeTag := range excludeReleaseTags { 142 builder.Reset() 143 majorMinorExcludeTag := removePatchVersion(excludeTag) 144 if !strings.HasPrefix(majorMinorExcludeTag, strings.ToLower("v")) { 145 builder.WriteString("v" + majorMinorExcludeTag) 146 majorMinorExcludeTag = builder.String() 147 } 148 if majorMinorExcludeTag == majorMinorReleaseTag { 149 return true 150 } 151 } 152 return false 153 } 154 155 func getLatestReleaseForCurrentBranch(releaseTags []string) string { 156 157 builder := strings.Builder{} 158 var latestForCurrentBranch *semver.SemVersion 159 160 o, err := semver.NewSemVersion(developmentVersion) 161 o.Patch = 0 162 if err != nil { 163 return "" 164 } 165 for _, tag := range releaseTags { 166 var t = tag 167 tagVersion, err := semver.NewSemVersion(t) 168 if err != nil { 169 return "" 170 } 171 if tagVersion.IsLessThan(o) { 172 latestForCurrentBranch = tagVersion 173 } 174 } 175 builder.WriteString("v" + latestForCurrentBranch.ToString()) 176 177 return builder.String() 178 } 179 180 // Derives interim version which is the latest git release tag - 1 minor version. 181 // If there are only two unique minor versions then latest patch is derived. 182 func getInterimRelease(releaseTags []string) string { 183 minorAndPatchesVersionMap, uniqueMinorVersions := getUniqueMajorMinorAndPatchVersionMap(releaseTags) 184 uniqueMinorReleaseCount := len(uniqueMinorVersions) 185 186 // Handles edge cases such as having less than 2 minor releases. 187 var interimRelease string 188 var installReleasePatchVersions []string 189 if uniqueMinorReleaseCount == 1 { 190 installReleasePatchVersions = minorAndPatchesVersionMap[uniqueMinorVersions[0]] 191 interimRelease = installReleasePatchVersions[len(releaseTags)-1] 192 } else if uniqueMinorReleaseCount == 2 { 193 secondLatestRelease := uniqueMinorVersions[len(uniqueMinorVersions)-2] 194 installReleasePatchVersions = minorAndPatchesVersionMap[secondLatestRelease] 195 interimRelease = installReleasePatchVersions[len(installReleasePatchVersions)-1] 196 } else if uniqueMinorReleaseCount > 2 { 197 secondLatestRelease := uniqueMinorVersions[len(uniqueMinorVersions)-2] 198 installReleasePatchVersions = minorAndPatchesVersionMap[secondLatestRelease] 199 interimRelease = installReleasePatchVersions[len(installReleasePatchVersions)-1] 200 } 201 return interimRelease 202 } 203 204 // Derives install version which is the latest git release tag - 2 minor version. 205 // If there are only two unique minor versions then oldest patch is derived. 206 func getInstallRelease(releaseTags []string) string { 207 minorAndPatchesVersionMap, uniqueMinorVersions := getUniqueMajorMinorAndPatchVersionMap(releaseTags) 208 uniqueMinorReleaseCount := len(uniqueMinorVersions) 209 210 // Handles edge cases such as having less than 2 minor releases. 211 var installRelease string 212 var installReleasePatchVersions []string 213 if uniqueMinorReleaseCount == 1 { 214 installReleasePatchVersions = minorAndPatchesVersionMap[uniqueMinorVersions[0]] 215 installRelease = installReleasePatchVersions[0] 216 } else if uniqueMinorReleaseCount == 2 { 217 thirdLatestRelease := uniqueMinorVersions[len(uniqueMinorVersions)-2] 218 installReleasePatchVersions = minorAndPatchesVersionMap[thirdLatestRelease] 219 installRelease = installReleasePatchVersions[0] 220 } else if uniqueMinorReleaseCount > 2 { 221 thirdLatestRelease := uniqueMinorVersions[len(uniqueMinorVersions)-3] 222 installReleasePatchVersions = minorAndPatchesVersionMap[thirdLatestRelease] 223 installRelease = installReleasePatchVersions[len(installReleasePatchVersions)-1] 224 } 225 return installRelease 226 } 227 228 func getTagsLT(tags []string, oldestAllowedVersion string) (string, error) { 229 builder := strings.Builder{} 230 o, err := semver.NewSemVersion(oldestAllowedVersion) 231 if err != nil { 232 return "", err 233 } 234 235 for _, tag := range tags { 236 var t = tag 237 if tag[0] == 'v' || tag[0] == 'V' { 238 t = tag[1:] 239 } 240 tagVersion, err := semver.NewSemVersion(t) 241 if err != nil { 242 return "", err 243 } 244 if tagVersion.IsLessThan(o) { 245 builder.WriteString(tag) 246 builder.WriteString(" ") 247 } 248 } 249 250 return builder.String(), nil 251 } 252 253 func getTagsGTE(tags []string, oldestAllowedVersion string) (string, error) { 254 builder := strings.Builder{} 255 256 o, err := semver.NewSemVersion(oldestAllowedVersion) 257 if err != nil { 258 return "", err 259 } 260 261 for _, tag := range tags { 262 var t = tag 263 if tag[0] == 'v' || tag[0] == 'V' { 264 t = tag[1:] 265 } 266 tagVersion, err := semver.NewSemVersion(t) 267 if err != nil { 268 return "", err 269 } 270 if tagVersion.IsGreaterThanOrEqualTo(o) { 271 builder.WriteString(tag) 272 builder.WriteString("\n") 273 } 274 } 275 276 return builder.String(), nil 277 } 278 279 func removePatchVersion(tag string) string { 280 split := strings.Split(tag, ".") 281 return strings.Join(split[:2], ".") 282 } 283 284 func compareVersions(v1, v2 string) bool { 285 v1Split := strings.Split(v1, ".") 286 v2Split := strings.Split(v2, ".") 287 288 for i := 0; i < len(v1Split) && i < len(v2Split); i++ { 289 var num int 290 v1Num, _ := fmt.Sscanf(v1Split[i], "%d", &num) 291 v2Num, _ := fmt.Sscanf(v2Split[i], "%d", &num) 292 293 if v1Num != v2Num { 294 return v1Num < v2Num 295 } 296 } 297 return false 298 } 299 300 func getUniqueMajorMinorAndPatchVersionMap(releaseTags []string) (map[string][]string, []string) { 301 // Remove patch minorVersion 302 majorMinorVersions := make([]string, len(releaseTags)) 303 for i, tag := range releaseTags { 304 majorMinorVersions[i] = removePatchVersion(tag) 305 } 306 307 // Sort based on the major and minor minorVersion 308 sort.SliceStable(majorMinorVersions, func(i, j int) bool { 309 return compareVersions(majorMinorVersions[i], majorMinorVersions[j]) 310 }) 311 312 // Remove duplicates 313 uniqueMinorVersions := make([]string, 0) 314 seen := make(map[string]bool) 315 for _, minorVersion := range majorMinorVersions { 316 if !seen[minorVersion] { 317 seen[minorVersion] = true 318 uniqueMinorVersions = append(uniqueMinorVersions, minorVersion) 319 } 320 } 321 322 // Create a map of unique majorMinorVersions and related patch versions 323 minorAndPatchesVersionMap := make(map[string][]string) 324 for _, version := range majorMinorVersions { 325 if _, ok := minorAndPatchesVersionMap[version]; !ok { 326 minorAndPatchesVersionMap[version] = make([]string, 0) 327 } 328 } 329 330 for _, tag := range releaseTags { 331 version := removePatchVersion(tag) 332 minorAndPatchesVersionMap[version] = append(minorAndPatchesVersionMap[version], tag) 333 } 334 335 return minorAndPatchesVersionMap, uniqueMinorVersions 336 } 337 338 // printUsage Prints the help for this program 339 func printUsage() { 340 usageString := ` 341 342 go run derive_upgrade_version.go [args] workspace version-type exclude-releases 343 344 Args: 345 [workspace] Uses the workspace path to retrieve the list of release tags using git tag command 346 [version-type] Specify version to derive 347 [exclude-releases] list of release tags to exclude 348 Options: 349 --help prints usage 350 ` 351 fmt.Print(usageString) 352 }