diff --git a/internal/env/env.go b/internal/env/env.go index 9b4c93c9..48c46da4 100644 --- a/internal/env/env.go +++ b/internal/env/env.go @@ -8,6 +8,7 @@ import ( "os" "os/exec" "path/filepath" + "regexp" "strings" "sync" "time" @@ -314,28 +315,43 @@ func fetchAPIResources() ([]string, error) { return lines, nil } -func IsInStackroxRepository() bool { - // TODO(#91): This assumes that: - // - origin is the name of the upstream remote (not true if someone cloned their fork) - // - the git transport was used - // How about instead looking for "# StackRox Kubernetes Security Platform" in README? - cmd := exec.Command("git", "remote", "get-url", "origin") - outputBytes, err := cmd.Output() - if err != nil { +func IsInStackroxRepository(log *logger.Logger) bool { + out, err := exec.Command("git", "remote", "-v").Output() + if exitErr, ok := errors.AsType[*exec.ExitError](err); ok { + log.Dimf("Not a git repository, ignoring ('git remote' returned %d)", exitErr.ExitCode()) return false } - outputLines := strings.Split(string(outputBytes), "\n") - if len(outputLines) == 0 { + if err != nil { + log.Dimf("Invoking 'git remote' failed: %v", err) return false } - return outputLines[0] == "git@github.com:stackrox/stackrox.git" + for line := range strings.SplitSeq(string(out), "\n") { + fields := strings.Fields(line) + if len(fields) < 2 { + continue + } + remote := fields[1] + if isStackRoxRepositoryRemote(remote) { + log.Dimf("Identified repository as a clone of stackrox/stackrox based on the configured git remote %q", remote) + return true + } + } + log.Dim("Repository does not seem to be a clone of stackrox/stackrox based on the configured git remotes") + return false +} + +var stackroxRepoPattern = regexp.MustCompile(`[/:]stackrox/stackrox(\.git)?$`) + +func isStackRoxRepositoryRemote(remote string) bool { + return stackroxRepoPattern.MatchString(remote) } -func GetStackroxRepositoryTag() (string, error) { +func GetStackroxRepositoryTag(log *logger.Logger) (string, error) { topLevelDir, err := getStackRoxTopLevelDir() if err != nil { return "", fmt.Errorf("getting stackrox top level directory: %w", err) } + log.Dimf("Invoking 'make tag' in directory %q", topLevelDir) cmd := exec.Command("make", "-s", "-C", topLevelDir, "tag") outputBytes, err := cmd.Output() if err != nil { diff --git a/internal/env/env_test.go b/internal/env/env_test.go index b9ccdb7c..b5cf6c1d 100644 --- a/internal/env/env_test.go +++ b/internal/env/env_test.go @@ -325,6 +325,33 @@ func TestClusterTypeString(t *testing.T) { } } +func TestIsStackRoxRepositoryRemote(t *testing.T) { + tests := []struct { + remote string + want bool + }{ + {"git@github.com:stackrox/stackrox.git", true}, + {"git@github.com:stackrox/stackrox", true}, + {"https://github.com/stackrox/stackrox.git", true}, + {"https://github.com/stackrox/stackrox", true}, + {"ssh://git@github.com/stackrox/stackrox.git", true}, + {"ssh://git@github.com/stackrox/stackrox", true}, + {"git@github.com:someone/stackrox.git", false}, + {"git@github.com:stackrox/other.git", false}, + {"git@github.com:foo-stackrox/stackrox-bar.git", false}, + {"git@github.com:foo-stackrox/stackrox.git", false}, + {"git@github.com:stackrox/stackrox-bar.git", false}, + {"", false}, + } + + for _, tt := range tests { + t.Run(tt.remote, func(t *testing.T) { + got := isStackRoxRepositoryRemote(tt.remote) + assert.Equal(t, tt.want, got, "isStackRoxRepositoryRemote(%q)", tt.remote) + }) + } +} + func TestDefaultDetector_Detect(t *testing.T) { tests := []struct { name string diff --git a/internal/helpers/tag.go b/internal/helpers/tag.go index c7b8f692..6c2050b8 100644 --- a/internal/helpers/tag.go +++ b/internal/helpers/tag.go @@ -17,29 +17,32 @@ import ( ) func LookupMainImageTag(ctx context.Context, log *logger.Logger) (string, error) { - log.Info("Looking up main image tag") + log.Dim("Checking if main image tag is defined in the environment") if tag := os.Getenv("MAIN_IMAGE_TAG"); tag != "" { - log.Dimf("Using MAIN_IMAGE_TAG from environment: %s", tag) + log.Infof("Using MAIN_IMAGE_TAG from environment: %s", tag) return tag, nil } - if env.IsInStackroxRepository() { - tag, err := env.GetStackroxRepositoryTag() + log.Dim("Checking if current working directory is checkout of stackrox/stackrox repository") + if env.IsInStackroxRepository(log) { + tag, err := env.GetStackroxRepositoryTag(log) if err != nil { log.Dimf("Error retrieving stackrox repository tag: %v", err) return "", err } - log.Dimf("Using stackrox repository tag: %s", tag) + log.Infof("Using stackrox repository tag: %s", tag) return tag, nil } - log.Warningf("No MAIN_IMAGE_TAG found in the environment, looking up latest release tag on registry") - log.Warning("To use a different tag, set the MAIN_IMAGE_TAG environment variable") + log.Warning("No tag specific and no MAIN_IMAGE_TAG found in the environment, looking up latest release tag on registry") + log.Warning("To use a different tag, use `--tag` or set the MAIN_IMAGE_TAG environment variable") log.Warning("Alternatively, execute roxie from within the stackrox repository, in which case the currently checked out stackrox tag will be used") + log.Dim("Checking what the latest released version tag is") latestTag, err := LookupLatestTag(ctx, log) if err != nil { return "", fmt.Errorf("looking up latest release tag: %w", err) } + log.Infof("Using latest released tag %v", latestTag) return latestTag, nil }