diff --git a/tools/version-tracker/pkg/commands/display/display.go b/tools/version-tracker/pkg/commands/display/display.go index 0948c6797c..9f27ce04a4 100644 --- a/tools/version-tracker/pkg/commands/display/display.go +++ b/tools/version-tracker/pkg/commands/display/display.go @@ -20,6 +20,12 @@ import ( // Run contains the business logic to execute the `display` subcommand. func Run(displayOptions *types.DisplayOptions) error { + // Check if branch name environment variable has been set. + branchName, ok := os.LookupEnv(constants.BranchNameEnvVar) + if !ok { + branchName = constants.MainBranchName + } + // Check if GitHub token environment variable has been set. githubToken, ok := os.LookupEnv("GITHUB_TOKEN") if !ok { @@ -40,11 +46,30 @@ func Run(displayOptions *types.DisplayOptions) error { // Clone the eks-anywhere-build-tooling repository. buildToolingRepoPath := filepath.Join(cwd, constants.BuildToolingRepoName) - _, _, err = git.CloneRepo(fmt.Sprintf(constants.BuildToolingRepoURL, baseRepoOwner), buildToolingRepoPath, "") + repo, headCommit, err := git.CloneRepo(fmt.Sprintf(constants.BuildToolingRepoURL, baseRepoOwner), buildToolingRepoPath, "", branchName) if err != nil { return fmt.Errorf("cloning build-tooling repo: %v", err) } + // Get the worktree corresponding to the cloned repository. + worktree, err := repo.Worktree() + if err != nil { + return fmt.Errorf("getting repo's current worktree: %v", err) + } + + // Checkout the eks-anywhere-build-tooling repo at the provided branch name. + createBranch := (branchName != constants.MainBranchName) + err = git.Checkout(worktree, branchName, createBranch) + if err != nil { + return fmt.Errorf("checking out worktree at branch %s: %v", branchName, err) + } + + // Reset current worktree to get a clean index. + err = git.ResetToHEAD(worktree, headCommit) + if err != nil { + return fmt.Errorf("resetting new branch to [origin/%s] HEAD: %v", branchName, err) + } + if displayOptions.ProjectName != "" { // Validate if the project name provided exists in the repository. if _, err := os.Stat(filepath.Join(buildToolingRepoPath, "projects", displayOptions.ProjectName)); os.IsNotExist(err) { @@ -102,7 +127,7 @@ func Run(displayOptions *types.DisplayOptions) error { } // Get latest revision for the project from GitHub. - latestRevision, _, err := github.GetLatestRevision(client, org, repoName, currentRevision, isTrackedByCommitHash, releaseBranched) + latestRevision, _, err := github.GetLatestRevision(client, org, repoName, currentRevision, branchName, isTrackedByCommitHash, releaseBranched) if err != nil { return fmt.Errorf("getting latest revision from GitHub: %v", err) } diff --git a/tools/version-tracker/pkg/commands/listprojects/listprojects.go b/tools/version-tracker/pkg/commands/listprojects/listprojects.go index 80fbbebb10..4d494969c3 100644 --- a/tools/version-tracker/pkg/commands/listprojects/listprojects.go +++ b/tools/version-tracker/pkg/commands/listprojects/listprojects.go @@ -21,6 +21,12 @@ func Run() error { return fmt.Errorf("retrieving current working directory: %v", err) } + // Check if branch name environment variable has been set. + branchName, ok := os.LookupEnv(constants.BranchNameEnvVar) + if !ok { + branchName = constants.MainBranchName + } + // Get base repository owner environment variable if set. baseRepoOwner := os.Getenv(constants.BaseRepoOwnerEnvvar) if baseRepoOwner == "" { @@ -29,7 +35,7 @@ func Run() error { // Clone the eks-anywhere-build-tooling repository. buildToolingRepoPath := filepath.Join(cwd, constants.BuildToolingRepoName) - _, _, err = git.CloneRepo(fmt.Sprintf(constants.BuildToolingRepoURL, baseRepoOwner), buildToolingRepoPath, "") + _, _, err = git.CloneRepo(fmt.Sprintf(constants.BuildToolingRepoURL, baseRepoOwner), buildToolingRepoPath, "", branchName) if err != nil { return fmt.Errorf("cloning build-tooling repo: %v", err) } diff --git a/tools/version-tracker/pkg/commands/upgrade/upgrade.go b/tools/version-tracker/pkg/commands/upgrade/upgrade.go index 032e7064b9..e666c5e2fb 100644 --- a/tools/version-tracker/pkg/commands/upgrade/upgrade.go +++ b/tools/version-tracker/pkg/commands/upgrade/upgrade.go @@ -45,6 +45,12 @@ func Run(upgradeOptions *types.UpgradeOptions) error { projectOrg := strings.Split(projectName, "/")[0] projectRepo := strings.Split(projectName, "/")[1] + // Check if branch name environment variable has been set. + branchName, ok := os.LookupEnv(constants.BranchNameEnvVar) + if !ok { + branchName = constants.MainBranchName + } + // Check if base repository owner environment variable has been set. baseRepoOwner, ok := os.LookupEnv(constants.BaseRepoOwnerEnvvar) if !ok { @@ -82,7 +88,7 @@ func Run(upgradeOptions *types.UpgradeOptions) error { // Clone the eks-anywhere-build-tooling repository. buildToolingRepoPath := filepath.Join(cwd, constants.BuildToolingRepoName) - repo, headCommit, err := git.CloneRepo(fmt.Sprintf(constants.BuildToolingRepoURL, baseRepoOwner), buildToolingRepoPath, headRepoOwner) + repo, headCommit, err := git.CloneRepo(fmt.Sprintf(constants.BuildToolingRepoURL, baseRepoOwner), buildToolingRepoPath, headRepoOwner, branchName) if err != nil { return fmt.Errorf("cloning build-tooling repo: %v", err) } @@ -93,23 +99,36 @@ func Run(upgradeOptions *types.UpgradeOptions) error { return fmt.Errorf("getting repo's current worktree: %v", err) } + // Checkout the eks-anywhere-build-tooling repo at the provided branch name. + createBranch := (branchName != constants.MainBranchName) + err = git.Checkout(worktree, branchName, createBranch) + if err != nil { + return fmt.Errorf("checking out worktree at branch %s: %v", branchName, err) + } + + // Reset current worktree to get a clean index. + err = git.ResetToHEAD(worktree, headCommit) + if err != nil { + return fmt.Errorf("resetting new branch to [origin/%s] HEAD: %v", branchName, err) + } + var headBranchName, baseBranchName, commitMessage, pullRequestBody string if isEKSDistroUpgrade(projectName) { - headBranchName = "update-eks-distro-latest-releases" - baseBranchName = constants.MainBranchName + headBranchName = fmt.Sprintf("update-eks-distro-latest-releases-%s", branchName) + baseBranchName = branchName commitMessage = "Bump EKS Distro releases to latest" pullRequestBody = constants.EKSDistroUpgradePullRequestBody // Checkout a new branch to keep track of version upgrade chaneges. - err = git.Checkout(worktree, headBranchName) + err = git.Checkout(worktree, headBranchName, true) if err != nil { return fmt.Errorf("checking out worktree at branch %s: %v", headBranchName, err) } // Reset current worktree to get a clean index. - err = git.ResetToMain(worktree, headCommit) + err = git.ResetToHEAD(worktree, headCommit) if err != nil { - return fmt.Errorf("resetting new branch to [origin/main] HEAD: %v", err) + return fmt.Errorf("resetting new branch to [origin/%s] HEAD: %v", branchName, err) } isUpdated, err := updateEKSDistroReleasesFile(client, buildToolingRepoPath) @@ -182,11 +201,11 @@ func Run(upgradeOptions *types.UpgradeOptions) error { totalPatchCount = len(patchFiles) } - headBranchName = fmt.Sprintf("update-%s-%s", projectOrg, projectRepo) - baseBranchName = constants.MainBranchName + headBranchName = fmt.Sprintf("update-%s-%s-%s", projectOrg, projectRepo, branchName) + baseBranchName = branchName commitMessage = fmt.Sprintf("Bump %s to latest release", projectName) if isReleaseBranched { - headBranchName = fmt.Sprintf("update-%s-%s-%s", projectOrg, projectRepo, releaseBranch) + headBranchName = fmt.Sprintf("update-%s-%s-%s-%s", projectOrg, projectRepo, releaseBranch, branchName) commitMessage = fmt.Sprintf("Bump %s %s release branch to latest release", projectName, releaseBranch) } @@ -199,7 +218,7 @@ func Run(upgradeOptions *types.UpgradeOptions) error { } } else { // Get latest revision for the project from GitHub. - latestRevision, needsUpgrade, err = github.GetLatestRevision(client, projectOrg, projectRepo, currentRevision, isTrackedByCommitHash, isReleaseBranched) + latestRevision, needsUpgrade, err = github.GetLatestRevision(client, projectOrg, projectRepo, currentRevision, branchName, isTrackedByCommitHash, isReleaseBranched) if err != nil { return fmt.Errorf("getting latest revision from GitHub: %v", err) } @@ -211,15 +230,15 @@ func Run(upgradeOptions *types.UpgradeOptions) error { // greater than the semver of the current version. if needsUpgrade || slices.Contains(constants.ProjectsWithUnconventionalUpgradeFlows, projectName) { // Checkout a new branch to keep track of version upgrade chaneges. - err = git.Checkout(worktree, headBranchName) + err = git.Checkout(worktree, headBranchName, true) if err != nil { return fmt.Errorf("checking out worktree at branch %s: %v", headBranchName, err) } // Reset current worktree to get a clean index. - err = git.ResetToMain(worktree, headCommit) + err = git.ResetToHEAD(worktree, headCommit) if err != nil { - return fmt.Errorf("resetting new branch to [origin/main] HEAD: %v", err) + return fmt.Errorf("resetting new branch to [origin/%s] HEAD: %v", branchName, err) } if needsUpgrade { @@ -360,7 +379,7 @@ func Run(upgradeOptions *types.UpgradeOptions) error { } if projectName == "kubernetes-sigs/image-builder" { - currentBottlerocketVersion, latestBottlerocketVersion, updatedBRFiles, err := updateBottlerocketVersionFiles(client, projectRootFilepath, projectPath) + currentBottlerocketVersion, latestBottlerocketVersion, updatedBRFiles, err := updateBottlerocketVersionFiles(client, projectRootFilepath, projectPath, branchName) if err != nil { return fmt.Errorf("updating Bottlerocket version and metadata files: %v", err) } @@ -376,7 +395,7 @@ func Run(upgradeOptions *types.UpgradeOptions) error { pullRequestBody = fmt.Sprintf(constants.CombinedImageBuilderBottlerocketUpgradePullRequestBody, currentRevision, latestRevision, currentBottlerocketVersion, latestBottlerocketVersion) } - err = git.Checkout(worktree, headBranchName) + err = git.Checkout(worktree, headBranchName, true) if err != nil { return fmt.Errorf("checking out worktree at branch %s: %v", headBranchName, err) } @@ -698,7 +717,7 @@ func updateCiliumImageDigestFiles(projectRootFilepath, projectPath string) ([]st return updateCiliumFiles, nil } -func updateBottlerocketVersionFiles(client *gogithub.Client, projectRootFilepath, projectPath string) (string, string, []string, error) { +func updateBottlerocketVersionFiles(client *gogithub.Client, projectRootFilepath, projectPath, branchName string) (string, string, []string, error) { updatedBRFiles := []string{} var bottlerocketReleaseMap map[string]interface{} bottlerocketReleasesFilePath := filepath.Join(projectRootFilepath, constants.BottlerocketReleasesFile) @@ -727,7 +746,7 @@ func updateBottlerocketVersionFiles(client *gogithub.Client, projectRootFilepath } } - latestBottlerocketVersion, needsUpgrade, err := github.GetLatestRevision(client, "bottlerocket-os", "bottlerocket", currentBottlerocketVersion, false, false) + latestBottlerocketVersion, needsUpgrade, err := github.GetLatestRevision(client, "bottlerocket-os", "bottlerocket", currentBottlerocketVersion, branchName, false, false) if err != nil { return "", "", nil, fmt.Errorf("getting latest Bottlerocket version from GitHub: %v", err) } diff --git a/tools/version-tracker/pkg/constants/constants.go b/tools/version-tracker/pkg/constants/constants.go index 8538cd8415..47c5df9764 100644 --- a/tools/version-tracker/pkg/constants/constants.go +++ b/tools/version-tracker/pkg/constants/constants.go @@ -6,6 +6,7 @@ import ( // Constants used across the version-tracker source code. const ( + BranchNameEnvVar = "BRANCH_NAME" BaseRepoOwnerEnvvar = "BASE_REPO_OWNER" HeadRepoOwnerEnvvar = "HEAD_REPO_OWNER" GitHubTokenEnvvar = "GITHUB_TOKEN" @@ -42,7 +43,7 @@ const ( GithubPerPage = 100 datetimeFormat = "%Y-%m-%dT%H:%M:%SZ" MainBranchName = "main" - BaseRepoHeadRevision = "refs/remotes/origin/main" + BaseRepoHeadRevisionPattern = "refs/remotes/origin/%s" EKSDistroUpgradePullRequestBody = `This PR bumps EKS Distro releases to the latest available release versions. /hold diff --git a/tools/version-tracker/pkg/git/git.go b/tools/version-tracker/pkg/git/git.go index 6b1ba40949..8a7270e674 100644 --- a/tools/version-tracker/pkg/git/git.go +++ b/tools/version-tracker/pkg/git/git.go @@ -18,7 +18,7 @@ import ( ) // CloneRepo clones the remote repository to a destination folder and creates a Git remote. -func CloneRepo(cloneURL, destination, headRepoOwner string) (*git.Repository, string, error) { +func CloneRepo(cloneURL, destination, headRepoOwner, branch string) (*git.Repository, string, error) { logger.V(6).Info(fmt.Sprintf("Cloning repository [%s] to %s directory", cloneURL, destination)) progress := io.Discard if logger.Verbosity >= 6 { @@ -40,9 +40,9 @@ func CloneRepo(cloneURL, destination, headRepoOwner string) (*git.Repository, st } } - repoHeadCommit, err := repo.ResolveRevision(plumbing.Revision(constants.BaseRepoHeadRevision)) + repoHeadCommit, err := repo.ResolveRevision(plumbing.Revision(fmt.Sprintf(constants.BaseRepoHeadRevisionPattern, branch))) if err != nil { - return nil, "", fmt.Errorf("resolving revision [%s] to commit hash: %v", constants.BaseRepoHeadRevision, err) + return nil, "", fmt.Errorf("resolving revision [%s] to commit hash: %v", fmt.Sprintf(constants.BaseRepoHeadRevisionPattern, branch), err) } repoHeadCommitHash := strings.Split(repoHeadCommit.String(), " ")[0] @@ -63,8 +63,8 @@ func CloneRepo(cloneURL, destination, headRepoOwner string) (*git.Repository, st return repo, repoHeadCommitHash, nil } -// ResetToMain hard-resets the current working tree to point to the HEAD commit of the base repository. -func ResetToMain(worktree *git.Worktree, baseRepoHeadCommit string) error { +// ResetToHEAD hard-resets the current working tree to point to the HEAD commit of the base repository. +func ResetToHEAD(worktree *git.Worktree, baseRepoHeadCommit string) error { err := worktree.Reset(&git.ResetOptions{ Commit: plumbing.NewHash(baseRepoHeadCommit), Mode: git.HardReset, @@ -77,13 +77,13 @@ func ResetToMain(worktree *git.Worktree, baseRepoHeadCommit string) error { } // Checkout checks out the working tree at the given branch, creating a new branch if necessary. -func Checkout(worktree *git.Worktree, branch string) error { +func Checkout(worktree *git.Worktree, branch string, create bool) error { logger.V(6).Info(fmt.Sprintf("Checking out branch [%s] in local worktree", branch)) err := worktree.Checkout(&git.CheckoutOptions{ Branch: plumbing.NewBranchReferenceName(branch), Keep: true, - Create: true, + Create: create, }) if err != nil { return fmt.Errorf("checking out branch [%s]: %v", branch, err) diff --git a/tools/version-tracker/pkg/github/github.go b/tools/version-tracker/pkg/github/github.go index 55c72f16a8..0e30bf319e 100644 --- a/tools/version-tracker/pkg/github/github.go +++ b/tools/version-tracker/pkg/github/github.go @@ -97,7 +97,7 @@ func GetFileContents(client *github.Client, org, repo, filePath, ref string) ([] } // GetLatestRevision returns the latest revision (GitHub release or tag) for a given GitHub repository. -func GetLatestRevision(client *github.Client, org, repo, currentRevision string, isTrackedUsingCommitHash, releaseBranched bool) (string, bool, error) { +func GetLatestRevision(client *github.Client, org, repo, currentRevision, branchName string, isTrackedUsingCommitHash, releaseBranched bool) (string, bool, error) { logger.V(6).Info(fmt.Sprintf("Getting latest revision for [%s/%s] repository", org, repo)) var currentRevisionCommit, latestRevision string needsUpgrade := false @@ -158,7 +158,13 @@ func GetLatestRevision(client *github.Client, org, repo, currentRevision string, if releaseBranched { releaseBranch := os.Getenv(constants.ReleaseBranchEnvvar) releaseNumber := strings.Split(releaseBranch, "-")[1] - tagRegex := regexp.MustCompile(fmt.Sprintf(`^v?1.%s.\d$`, releaseNumber)) + tagRegex := regexp.MustCompile(fmt.Sprintf(`^v?1.%s.\d+$`, releaseNumber)) + if !tagRegex.MatchString(tagNameForSemver) { + continue + } + } + if branchName != constants.MainBranchName { + tagRegex := regexp.MustCompile(fmt.Sprintf(`^v%d.%d.\d+`, currentRevisionSemver.Major, currentRevisionSemver.Minor)) if !tagRegex.MatchString(tagNameForSemver) { continue }