Repository: openshift/osdctl
Pull request #750: OSD-30081: Fix issues with `osdctl promote saas`
Author: login: joshbranham
Labels: name: lgtm
description: Indicates that a PR is ready to be merged.
name: approved
description: Indicates a PR has been approved by an approver from all required OWNERS files.
name: jira/valid-reference
description: Indicates that this PR references a valid Jira ticket of any type.
Created: 2025-05-14T15:37:28Z | Merged: 2025-05-14T16:38:34Z

In https://github.com/openshift/osdctl/pull/723 a refactor was done to make testing easier for this package. However, it turns out that was not tested functionally and there were a few issues. This fixes those issues.


Diff:
diff --git a/cmd/promote/git/app_interface.go b/cmd/promote/git/app_interface.go
index cafc68789..a2a82c54e 100644
--- a/cmd/promote/git/app_interface.go
+++ b/cmd/promote/git/app_interface.go
@@ -77,8 +77,9 @@ func DefaultAppInterfaceDirectory() string {
 	return filepath.Join(os.Getenv("HOME"), "git", "app-interface")
 }
 
-func BootstrapOsdCtlForAppInterfaceAndServicePromotions(appInterfaceCheckoutDir string) AppInterface {
+func BootstrapOsdCtlForAppInterfaceAndServicePromotions(appInterfaceCheckoutDir string, gitExecutor iexec.Exec) AppInterface {
 	a := AppInterface{}
+	a.GitExecutor = gitExecutor
 	if appInterfaceCheckoutDir != "" {
 		a.GitDirectory = appInterfaceCheckoutDir
 		err := a.checkAppInterfaceCheckout()
@@ -109,19 +110,18 @@ func BootstrapOsdCtlForAppInterfaceAndServicePromotions(appInterfaceCheckoutDir
 }
 
 // checkAppInterfaceCheckout checks if the script is running in the checkout of app-interface
-func (a AppInterface) checkAppInterfaceCheckout() error {
+func (a *AppInterface) checkAppInterfaceCheckout() error {
 	output, err := a.GitExecutor.Output(a.GitDirectory, "git", "remote", "-v")
 	if err != nil {
 		return fmt.Errorf("error executing 'git remote -v': %v", err)
 	}
 
-	outputString := string(output)
+	outputString := output
 
 	// Check if the output contains the app-interface repository URL
 	if !strings.Contains(outputString, "gitlab.cee.redhat.com") && !strings.Contains(outputString, "app-interface") {
 		return fmt.Errorf("not running in checkout of app-interface")
 	}
-	//fmt.Println("Running in checkout of app-interface.")
 
 	return nil
 }
@@ -247,7 +247,7 @@ func GetCurrentPackageTagFromAppInterface(saasFile string) (string, error) {
 	return currentPackageTag, nil
 }
 
-func (a AppInterface) UpdateAppInterface(serviceName, saasFile, currentGitHash, promotionGitHash, branchName string) error {
+func (a *AppInterface) UpdateAppInterface(_, saasFile, currentGitHash, promotionGitHash, branchName string) error {
 
 	if err := a.GitExecutor.Run(a.GitDirectory, "git", "checkout", "master"); err != nil {
 		return fmt.Errorf("failed to checkout master: branch %v", err)
@@ -286,7 +286,7 @@ func (a AppInterface) UpdateAppInterface(serviceName, saasFile, currentGitHash,
 	return nil
 }
 
-func (a AppInterface) UpdatePackageTag(saasFile, oldTag, promotionTag, branchName string) error {
+func (a *AppInterface) UpdatePackageTag(saasFile, oldTag, promotionTag, branchName string) error {
 
 	if err := a.GitExecutor.Run(a.GitDirectory, "git", "checkout", "master"); err != nil {
 		return fmt.Errorf("failed to checkout master branch: %v", err)
@@ -312,7 +312,7 @@ func (a AppInterface) UpdatePackageTag(saasFile, oldTag, promotionTag, branchNam
 	return nil
 }
 
-func (a AppInterface) CommitSaasFile(saasFile, commitMessage string) error {
+func (a *AppInterface) CommitSaasFile(saasFile, commitMessage string) error {
 	// Commit the change
 	if err := a.GitExecutor.Run(a.GitDirectory, "git", "add", saasFile); err != nil {
 		return fmt.Errorf("failed to add file %s: %v", saasFile, err)
diff --git a/cmd/promote/git/git_cmd.go b/cmd/promote/git/git_cmd.go
index 2e6b3e8eb..0476cf905 100644
--- a/cmd/promote/git/git_cmd.go
+++ b/cmd/promote/git/git_cmd.go
@@ -22,50 +22,8 @@ func getBaseDir(exec iexec.IExec) (string, error) {
 			return
 		}
 
-		BaseDir = strings.TrimSpace(string(baseDirOutput))
+		BaseDir = strings.TrimSpace(baseDirOutput)
 	})
 
 	return BaseDir, baseDirErr
 }
-
-/*
-// Not in use
-func checkBehindMaster(dir string) error {
-	fmt.Printf("### Checking 'master' branch is up to date ###\n")
-
-	cmd := exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD")
-	cmd.Dir = dir
-	output, err := cmd.Output()
-	if err != nil {
-		return fmt.Errorf("error executing git rev-parse command: %v", err)
-	}
-
-	branch := strings.TrimSpace(string(output))
-	if branch != "master" {
-		return fmt.Errorf("you are not on the 'master' branch")
-	}
-
-	// Fetch the latest changes from the upstream repository
-	cmd = exec.Command("git", "fetch", "upstream")
-	cmd.Dir = dir
-	err = cmd.Run()
-	if err != nil {
-		return fmt.Errorf("error executing git fetch command: %v", err)
-	}
-
-	cmd = exec.Command("git", "rev-list", "--count", "HEAD..upstream/master")
-	cmd.Dir = dir
-	output, err = cmd.Output()
-	if err != nil {
-		return fmt.Errorf("error executing git rev-list command: %v", err)
-	}
-
-	behindCount := strings.TrimSpace(string(output))
-	if behindCount != "0" {
-		return fmt.Errorf("you are behind 'master' by this many commits: %s", behindCount)
-	}
-	fmt.Printf("### 'master' branch is up to date ###\n\n")
-
-	return nil
-}
-*/
diff --git a/cmd/promote/git/service_repo.go b/cmd/promote/git/service_repo.go
index 0d45bf6f7..daedf33c2 100644
--- a/cmd/promote/git/service_repo.go
+++ b/cmd/promote/git/service_repo.go
@@ -9,7 +9,7 @@ import (
 	"github.com/openshift/osdctl/cmd/promote/iexec"
 )
 
-func CheckoutAndCompareGitHash(gitexecuter iexec.IExec, gitURL, gitHash, currentGitHash string) (string, string, error) {
+func CheckoutAndCompareGitHash(gitExecutor iexec.IExec, gitURL, gitHash, currentGitHash string) (string, string, error) {
 	tempDir, err := ioutil.TempDir("", "")
 	if err != nil {
 		return "", "", fmt.Errorf("failed to create temporary directory: %v", err)
@@ -21,7 +21,7 @@ func CheckoutAndCompareGitHash(gitexecuter iexec.IExec, gitURL, gitHash, current
 		return "", "", fmt.Errorf("failed to change directory to temporary directory: %v", err)
 	}
 
-	err = gitexecuter.Run(tempDir, "git", "clone", gitURL, "source-dir")
+	err = gitExecutor.Run(tempDir, "git", "clone", gitURL, "source-dir")
 	if err != nil {
 		return "", "", fmt.Errorf("failed to clone git repository: %v", err)
 	}
@@ -32,21 +32,21 @@ func CheckoutAndCompareGitHash(gitexecuter iexec.IExec, gitURL, gitHash, current
 
 	if gitHash == "" {
 		fmt.Printf("No git hash provided. Using HEAD.\n")
-		output, err := gitexecuter.Output("git", "rev-parse", "HEAD")
+		output, err := gitExecutor.Output("", "git", "rev-parse", "HEAD")
 		if err != nil {
 			return "", "", fmt.Errorf("failed to get git hash: %v", err)
 		}
-		gitHash = strings.TrimSpace(string(output))
+		gitHash = strings.TrimSpace(output)
 		fmt.Printf("The head githash is %s\n", gitHash)
 	}
 
 	if currentGitHash == gitHash {
 		return "", "", fmt.Errorf("git hash %s is already at HEAD", gitHash)
 	} else {
-		commitLog, err := gitexecuter.Output("git", "log", "--no-merges", fmt.Sprintf("%s..%s", currentGitHash, gitHash))
+		commitLog, err := gitExecutor.Output("", "git", "log", "--no-merges", fmt.Sprintf("%s..%s", currentGitHash, gitHash))
 		if err != nil {
 			return "", "", err
 		}
-		return gitHash, string(commitLog), nil
+		return gitHash, commitLog, nil
 	}
 }
diff --git a/cmd/promote/pko/pko.go b/cmd/promote/pko/pko.go
index 61eb9477d..17dea43c0 100644
--- a/cmd/promote/pko/pko.go
+++ b/cmd/promote/pko/pko.go
@@ -28,9 +28,7 @@ func NewCmdPKO() *cobra.Command {
 			}
 
 			cmdutil.CheckErr(ops.ValidatePKOOptions())
-			appInterface := git.BootstrapOsdCtlForAppInterfaceAndServicePromotions(ops.appInterfaceCheckoutDir)
-			// ovverriding appInterface.GitExecuter to iexec.Exec{}
-			appInterface.GitExecutor = iexec.Exec{}
+			appInterface := git.BootstrapOsdCtlForAppInterfaceAndServicePromotions(ops.appInterfaceCheckoutDir, iexec.Exec{})
 			cmdutil.CheckErr(PromotePackage(appInterface, ops.serviceName, ops.packageTag, ops.hcp))
 		},
 	}
diff --git a/cmd/promote/saas/saas.go b/cmd/promote/saas/saas.go
index 0eea79c0e..5a4f5ea7d 100644
--- a/cmd/promote/saas/saas.go
+++ b/cmd/promote/saas/saas.go
@@ -36,8 +36,7 @@ func NewCmdSaas() *cobra.Command {
 		osdctl promote saas --serviceName <service-name> --gitHash <git-hash> --hcp`,
 		RunE: func(cmd *cobra.Command, args []string) error {
 			ops.validateSaasFlow()
-			appInterface := git.BootstrapOsdCtlForAppInterfaceAndServicePromotions(ops.appInterfaceCheckoutDir)
-			appInterface.GitExecutor = iexec.Exec{}
+			appInterface := git.BootstrapOsdCtlForAppInterfaceAndServicePromotions(ops.appInterfaceCheckoutDir, iexec.Exec{})
 			if ops.list {
 				if ops.serviceName != "" || ops.gitHash != "" || ops.osd || ops.hcp {
 					fmt.Printf("Error: --list cannot be used with any other flags\n\n")


Reviews:
user: login: typeid
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-14T16:29:18Z
user: login: typeid
body: /lgtm
/approve
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-14T16:29:30Z
user: login: joshbranham
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T16:30:35Z

Review comments:
diff_hunk: @@ -22,50 +22,8 @@ func getBaseDir(exec iexec.IExec) (string, error) {
 			return
 		}
 
-		BaseDir = strings.TrimSpace(string(baseDirOutput))
+		BaseDir = strings.TrimSpace(baseDirOutput)
 	})
 
 	return BaseDir, baseDirErr
 }
-
-/*
-// Not in use
path: cmd/promote/git/git_cmd.go
user: login: typeid
body: hm
created_at: 2025-05-14T16:29:18Z
author_association: MEMBER
diff_hunk: @@ -22,50 +22,8 @@ func getBaseDir(exec iexec.IExec) (string, error) {
 			return
 		}
 
-		BaseDir = strings.TrimSpace(string(baseDirOutput))
+		BaseDir = strings.TrimSpace(baseDirOutput)
 	})
 
 	return BaseDir, baseDirErr
 }
-
-/*
-// Not in use
path: cmd/promote/git/git_cmd.go
user: login: joshbranham
body: Unsure why this was commented out as "not in use" versus just being removed.
created_at: 2025-05-14T16:30:35Z
author_association: CONTRIBUTOR

PR comments:
user: login: openshift-ci-robot
created_at: 2025-05-14T15:49:07Z
body: @joshbranham: This pull request references [OSD-30081](https://issues.redhat.com//browse/OSD-30081) which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the bug to target the "4.20.0" version, but no target version was set.

<details>

In response to [this](https://github.com/openshift/osdctl/pull/750):

>In https://github.com/openshift/osdctl/pull/723 a refactor was done to make testing easier for this package. However, it turns out that was not tested functionally and there were a few issues. This fixes those issues.


Instructions for interacting with me using PR comments are available [here](https://prow.ci.openshift.org/command-help?repo=openshift%2Fosdctl).  If you have questions or suggestions related to my behavior, please file an issue against the [openshift-eng/jira-lifecycle-plugin](https://github.com/openshift-eng/jira-lifecycle-plugin/issues/new) repository.
</details>
author_association: NONE
user: login: openshift-ci[bot]
created_at: 2025-05-14T16:03:20Z
body: @joshbranham: all tests **passed!**


[Full PR test history](https://prow.ci.openshift.org/pr-history?org=openshift&repo=osdctl&pr=750). [Your PR dashboard](https://prow.ci.openshift.org/pr?query=is:pr+state:open+author:joshbranham).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
author_association: CONTRIBUTOR
user: login: openshift-ci[bot]
created_at: 2025-05-14T16:31:14Z
body: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/openshift/osdctl/pull/750#" title="Author self-approved">joshbranham</a>*, *<a href="https://github.com/openshift/osdctl/pull/750#pullrequestreview-2840829595" title="Approved">typeid</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=openshift%2Fosdctl).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/openshift/osdctl/blob/master/OWNERS)~~ [joshbranham,typeid]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
author_association: CONTRIBUTOR
user: login: joshbranham
created_at: 2025-05-14T16:34:27Z
body: Btw, I ran this locally with the fixes and it produced a valid branch and pushed it 👍 
author_association: CONTRIBUTOR