tools/version-tracker/pkg/commands/display/display.go (154 lines of code) (raw):
package display
import (
"context"
"fmt"
"os"
"path/filepath"
"slices"
"strings"
gogithub "github.com/google/go-github/v53/github"
"github.com/rodaine/table"
"gopkg.in/yaml.v3"
"github.com/aws/eks-anywhere-build-tooling/tools/version-tracker/pkg/constants"
"github.com/aws/eks-anywhere-build-tooling/tools/version-tracker/pkg/ecrpublic"
"github.com/aws/eks-anywhere-build-tooling/tools/version-tracker/pkg/git"
"github.com/aws/eks-anywhere-build-tooling/tools/version-tracker/pkg/github"
"github.com/aws/eks-anywhere-build-tooling/tools/version-tracker/pkg/types"
)
// 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 {
return fmt.Errorf("GITHUB_TOKEN environment variable is not set")
}
client := gogithub.NewTokenClient(context.Background(), githubToken)
cwd, err := os.Getwd()
if err != nil {
return fmt.Errorf("retrieving current working directory: %v", err)
}
// Get base repository owner environment variable if set.
baseRepoOwner := os.Getenv(constants.BaseRepoOwnerEnvvar)
if baseRepoOwner == "" {
baseRepoOwner = constants.AWSOrgName
}
// Clone the eks-anywhere-build-tooling repository.
buildToolingRepoPath := filepath.Join(cwd, constants.BuildToolingRepoName)
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) {
return fmt.Errorf("invalid project name %s", displayOptions.ProjectName)
}
}
// Load upstream projects tracker file.
upstreamProjectsTrackerFilePath := filepath.Join(buildToolingRepoPath, constants.UpstreamProjectsTrackerFile)
contents, err := os.ReadFile(upstreamProjectsTrackerFilePath)
if err != nil {
return fmt.Errorf("reading upstream projects tracker file: %v", err)
}
// Unmarshal upstream projects tracker file
var projectsList types.ProjectsList
err = yaml.Unmarshal(contents, &projectsList)
if err != nil {
return fmt.Errorf("unmarshalling upstream projects tracker file: %v", err)
}
var projectVersionInfoList []types.ProjectVersionInfo
for _, project := range projectsList.Projects {
org := project.Org
for _, repo := range project.Repos {
var currentVersionList, latestVersionList, upToDateList []string
repoName := repo.Name
fullRepoName := fmt.Sprintf("%s/%s", org, repoName)
if displayOptions.ProjectName != "" && displayOptions.ProjectName != fullRepoName {
continue
}
releaseBranched := false
var currentVersion types.Version
if len(repo.Versions) > 1 {
releaseBranched = true
}
supportedReleaseBranches, err := getSupportedReleaseBranches(buildToolingRepoPath)
if err != nil {
return fmt.Errorf("getting supported EKS Distro release branches: %v", err)
}
for _, releaseBranch := range supportedReleaseBranches {
err := os.Setenv(constants.ReleaseBranchEnvvar, releaseBranch)
releaseBranchIndex := slices.Index(supportedReleaseBranches, releaseBranch)
if !releaseBranched && releaseBranchIndex > 0 {
break
}
currentVersion = repo.Versions[releaseBranchIndex]
var isTrackedByCommitHash bool
var currentRevision string
if currentVersion.Tag != "" {
currentRevision = currentVersion.Tag
} else if currentVersion.Commit != "" {
currentRevision = currentVersion.Commit
isTrackedByCommitHash = true
}
currentVersionList = append(currentVersionList, currentRevision)
var latestRevision string
if fullRepoName == "cilium/cilium" || fullRepoName == "envoyproxy/envoy" {
latestRevision, _, err = ecrpublic.GetLatestRevision(constants.ECRImageRepositories[fullRepoName], currentRevision, branchName)
if err != nil {
return fmt.Errorf("getting latest revision from ECR Public: %v", err)
}
} else {
// Get latest revision for the project from GitHub.
latestRevision, _, err = github.GetLatestRevision(client, org, repoName, currentRevision, branchName, isTrackedByCommitHash, releaseBranched)
if err != nil {
return fmt.Errorf("getting latest revision from GitHub for project %s: %v", fullRepoName, err)
}
}
latestVersionList = append(latestVersionList, latestRevision)
upToDateList = append(upToDateList, fmt.Sprintf("%t", currentRevision == latestRevision))
}
var paddedOrgName, paddedRepoName string
if len(currentVersionList) > 2 {
padding := strings.Repeat("\n", len(currentVersionList)/2)
smallerPadding := strings.Repeat("\n", len(currentVersionList)/2-1)
if len(currentVersionList)%2 == 0 {
paddedOrgName = fmt.Sprintf("%s%s%s", padding, org, smallerPadding)
paddedRepoName = fmt.Sprintf("%s%s%s", padding, repoName, smallerPadding)
} else {
paddedOrgName = fmt.Sprintf("%s%s%s", padding, org, padding)
paddedRepoName = fmt.Sprintf("%s%s%s", padding, repoName, padding)
}
} else {
paddedOrgName = org
paddedRepoName = repoName
}
projectVersionInfoList = append(projectVersionInfoList, types.ProjectVersionInfo{Org: paddedOrgName, Repo: paddedRepoName, CurrentVersion: strings.Join(currentVersionList, "\n"), LatestVersion: strings.Join(latestVersionList, "\n"), UpToDate: strings.Join(upToDateList, "\n")})
}
}
// Create a new table with the required column names in uppercase.
tbl := table.New("Organization", "Repository", "Current Version", "Latest Version", "Up-To-Date").WithHeaderFormatter(func(format string, vals ...interface{}) string {
return strings.ToUpper(fmt.Sprintf(format, vals...))
})
// Add rows to the table for each project in the list.
for _, versionInfo := range projectVersionInfoList {
tbl.AddRow(versionInfo.Org, versionInfo.Repo, versionInfo.CurrentVersion, versionInfo.LatestVersion, versionInfo.UpToDate)
}
// Print the table contents to standard output.
tbl.Print()
return nil
}
func getSupportedReleaseBranches(buildToolingRepoPath string) ([]string, error) {
supportedReleaseBranchesFilepath := filepath.Join(buildToolingRepoPath, constants.SupportedReleaseBranchesFile)
supportedReleaseBranchesFileContents, err := os.ReadFile(supportedReleaseBranchesFilepath)
if err != nil {
return nil, fmt.Errorf("reading supported release branches file: %v", err)
}
supportedK8sVersions := strings.Split(strings.TrimRight(string(supportedReleaseBranchesFileContents), "\n"), "\n")
return supportedK8sVersions, nil
}