diff --git a/go.mod b/go.mod index 6ff086e..05f7060 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/aws/aws-sdk-go-v2/config v1.27.7 github.com/aws/aws-sdk-go-v2/service/sns v1.29.2 github.com/aws/aws-sdk-go-v2/service/sqs v1.31.2 + github.com/google/go-github/v60 v60.0.0 github.com/gruntwork-io/terratest v0.46.11 github.com/hashicorp/terraform-json v0.13.0 github.com/stretchr/testify v1.8.4 @@ -47,7 +48,8 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/gnostic-models v0.6.8 // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.3.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect diff --git a/go.sum b/go.sum index 00fe232..ca2c767 100644 --- a/go.sum +++ b/go.sum @@ -354,8 +354,13 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-github/v60 v60.0.0 h1:oLG98PsLauFvvu4D/YPxq374jhSxFYdzQGNCyONLfn8= +github.com/google/go-github/v60 v60.0.0/go.mod h1:ByhX2dP9XT9o/ll2yXAu2VD8l5eNVg8hD4Cr0S/LmQk= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= diff --git a/pkg/gh/release.go b/pkg/gh/release.go new file mode 100644 index 0000000..292ac75 --- /dev/null +++ b/pkg/gh/release.go @@ -0,0 +1,58 @@ +package gh + +import ( + "context" + "fmt" + "net/url" + "os" + "strings" + + "github.com/google/go-github/v60/github" + "golang.org/x/oauth2" +) + +// FetchReleases fetches GitHub releases for a given repo URL. +// If `onlyLatest` is true, it returns the most recent release. +// Otherwise, it returns all releases. +func FetchReleases(repoURL string, onlyLatest bool) ([]*github.RepositoryRelease, error) { + ctx := context.Background() + + // Setup GitHub client with authentication + tc := oauth2.NewClient(ctx, oauth2.StaticTokenSource( + &oauth2.Token{AccessToken: os.Getenv("GITHUB_TOKEN")}, + )) + client := github.NewClient(tc) + + owner, repo, err := GetOwnerAndRepoFromURL(repoURL) + if err != nil { + return nil, fmt.Errorf("failed to parse GitHub URL: %w", err) + } + + if onlyLatest { + release, _, err := client.Repositories.GetLatestRelease(ctx, owner, repo) + if err != nil { + return nil, fmt.Errorf("error fetching the latest release: %w", err) + } + return []*github.RepositoryRelease{release}, nil + } + + releases, _, err := client.Repositories.ListReleases(ctx, owner, repo, &github.ListOptions{}) + if err != nil { + return nil, fmt.Errorf("error fetching releases: %w", err) + } + + return releases, nil +} + +// GetOwnerAndRepoFromURL extracts the owner and repo name from a GitHub URL +func GetOwnerAndRepoFromURL(repoURL string) (string, string, error) { + u, err := url.Parse(repoURL) + if err != nil { + return "", "", fmt.Errorf("error parsing URL: %w", err) + } + parts := strings.Split(strings.Trim(u.Path, "/"), "/") + if len(parts) != 2 { + return "", "", fmt.Errorf("invalid GitHub repo URL format") + } + return parts[0], parts[1], nil +} diff --git a/pkg/utils/git.go b/pkg/utils/git.go index 6e618d7..75b3d6e 100644 --- a/pkg/utils/git.go +++ b/pkg/utils/git.go @@ -6,24 +6,43 @@ import ( "path/filepath" ) -// IsAGitRepository checks if the given directory is a git repository. -func IsAGitRepository(repoRoot string) error { +// IsAGitRepository checks if the given directory or any of its parent directories up to `levels` is a git repository. +// It returns the git root directory, the subdirectory passed relative to the git root, and any error encountered. +func IsAGitRepository(repoRoot string, levels int) (gitRoot string, subDir string, err error) { if repoRoot == "" { - return fmt.Errorf("directory path cannot be empty") + return "", "", fmt.Errorf("directory path cannot be empty") } - if err := DirExistAndHasContent(repoRoot); err != nil { - return err + originalPath, err := filepath.Abs(repoRoot) + if err != nil { + return "", "", fmt.Errorf("failed to resolve absolute path for %s: %v", repoRoot, err) } - _, err := os.Stat(filepath.Join(repoRoot, ".git")) - if err != nil { - if os.IsNotExist(err) { - return fmt.Errorf("directory %s is not a git repository", repoRoot) + if err := DirExistAndHasContent(originalPath); err != nil { + return "", "", err + } + + currentPath := originalPath + for i := 0; i <= levels; i++ { + _, err := os.Stat(filepath.Join(currentPath, ".git")) + if err == nil { + relPath, _ := filepath.Rel(currentPath, originalPath) + return currentPath, relPath, nil } - return fmt.Errorf("unexpected error when checking the directory %s: %v", repoRoot, err) + if !os.IsNotExist(err) { + // If the error is not because the .git directory doesn't exist, return it. + return "", "", fmt.Errorf("unexpected error when checking the directory %s: %v", currentPath, err) + } + + // Move up one directory level + parentDir := filepath.Dir(currentPath) + if parentDir == currentPath { + // If the parent directory is the same as the current one, we've reached the filesystem root + break + } + currentPath = parentDir } - return nil + return "", "", fmt.Errorf("no git repository found within %d levels of directory %s", levels, repoRoot) }