From e66c81ad3046272b9ddfb0907a012f3b17cb3903 Mon Sep 17 00:00:00 2001 From: galactica Date: Tue, 12 Nov 2024 16:14:01 +0200 Subject: [PATCH 01/13] Add pr decoration in BB --- cmd/main.go | 4 +- internal/commands/util/pr.go | 179 ++++++++++++++++++++++++++++--- internal/params/binds.go | 2 + internal/params/envs.go | 2 + internal/params/flags.go | 8 +- internal/params/keys.go | 2 + internal/wrappers/pr-http.go | 58 +++++++++- internal/wrappers/pr.go | 20 ++++ test/integration/util_command.go | 4 +- 9 files changed, 258 insertions(+), 21 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 8e6c70cfb..2b3a1a305 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -42,6 +42,8 @@ func main() { bfl := viper.GetString(params.BflPathKey) prDecorationGithubPath := viper.GetString(params.PRDecorationGithubPathKey) prDecorationGitlabPath := viper.GetString(params.PRDecorationGitlabPathKey) + bitbucketServerPath := viper.GetString(params.PRDecorationBitbucketServerPathKey) + bitbucketCloudPath := viper.GetString(params.PRDecorationBitbucketCloudPathKey) descriptionsPath := viper.GetString(params.DescriptionsPathKey) tenantConfigurationPath := viper.GetString(params.TenantConfigurationPathKey) resultsPdfPath := viper.GetString(params.ResultsPdfReportPathKey) @@ -72,7 +74,7 @@ func main() { bitBucketServerWrapper := bitbucketserver.NewBitbucketServerWrapper() gitLabWrapper := wrappers.NewGitLabWrapper() bflWrapper := wrappers.NewBflHTTPWrapper(bfl) - prWrapper := wrappers.NewHTTPPRWrapper(prDecorationGithubPath, prDecorationGitlabPath) + prWrapper := wrappers.NewHTTPPRWrapper(prDecorationGithubPath, prDecorationGitlabPath, bitbucketCloudPath, bitbucketServerPath) learnMoreWrapper := wrappers.NewHTTPLearnMoreWrapper(descriptionsPath) tenantConfigurationWrapper := wrappers.NewHTTPTenantConfigurationWrapper(tenantConfigurationPath) jwtWrapper := wrappers.NewJwtWrapper() diff --git a/internal/commands/util/pr.go b/internal/commands/util/pr.go index ab5207d0d..eb69a583b 100644 --- a/internal/commands/util/pr.go +++ b/internal/commands/util/pr.go @@ -3,6 +3,7 @@ package util import ( "fmt" "log" + "strings" "github.com/MakeNowJust/heredoc" "github.com/checkmarx/ast-cli/internal/commands/policymanagement" @@ -15,18 +16,20 @@ import ( ) const ( - failedCreatingGithubPrDecoration = "Failed creating github PR Decoration" - failedCreatingGitlabPrDecoration = "Failed creating gitlab MR Decoration" - errorCodeFormat = "%s: CODE: %d, %s\n" - policyErrorFormat = "%s: Failed to get scanID policy information" - waitDelayDefault = 5 - resultPolicyDefaultTimeout = 1 - failedGettingScanError = "Failed showing a scan" - noPRDecorationCreated = "A PR couldn't be created for this scan because it is still in progress." - githubOnPremURLSuffix = "/api/v3/repos/" - gitlabOnPremURLSuffix = "/api/v4/" - githubCloudURL = "https://api.github.com/repos/" - gitlabCloudURL = "https://gitlab.com" + gitlabOnPremURLSuffix + failedCreatingGithubPrDecoration = "Failed creating github PR Decoration" + failedCreatingGitlabPrDecoration = "Failed creating gitlab MR Decoration" + failedCreatingBitbucketPrDecoration = "Failed creating bitbucket PR Decoration" + errorCodeFormat = "%s: CODE: %d, %s\n" + policyErrorFormat = "%s: Failed to get scanID policy information" + waitDelayDefault = 5 + resultPolicyDefaultTimeout = 1 + failedGettingScanError = "Failed showing a scan" + noPRDecorationCreated = "A PR couldn't be created for this scan because it is still in progress." + githubOnPremURLSuffix = "/api/v3/repos/" + gitlabOnPremURLSuffix = "/api/v4/" + githubCloudURL = "https://api.github.com/repos/" + gitlabCloudURL = "https://gitlab.com" + gitlabOnPremURLSuffix + bitbucketCloudURL = "bitbucket.org" ) func NewPRDecorationCommand(prWrapper wrappers.PRWrapper, policyWrapper wrappers.PolicyWrapper, scansWrapper wrappers.ScansWrapper) *cobra.Command { @@ -42,9 +45,11 @@ func NewPRDecorationCommand(prWrapper wrappers.PRWrapper, policyWrapper wrappers prDecorationGithub := PRDecorationGithub(prWrapper, policyWrapper, scansWrapper) prDecorationGitlab := PRDecorationGitlab(prWrapper, policyWrapper, scansWrapper) + prDecorationBitbucket := PRDecorationBitbucket(prWrapper, policyWrapper, scansWrapper) cmd.AddCommand(prDecorationGithub) cmd.AddCommand(prDecorationGitlab) + cmd.AddCommand(prDecorationBitbucket) return cmd } @@ -159,6 +164,46 @@ func PRDecorationGitlab(prWrapper wrappers.PRWrapper, policyWrapper wrappers.Pol return prDecorationGitlab } +func PRDecorationBitbucket(prWrapper wrappers.PRWrapper, policyWrapper wrappers.PolicyWrapper, scansWrapper wrappers.ScansWrapper) *cobra.Command { + prDecorationBitbucket := &cobra.Command{ + Use: "bitbucket ", + Short: "Decorate bitbucket PR with vulnerabilities", + Long: "Decorate bitbucket PR with vulnerabilities", + Example: heredoc.Doc( + ` + $ cx utils pr bitbucket --scan-id --token --namespace --repo-name + --pr-id --code-repository-url + `, + ), + Annotations: map[string]string{ + "command:doc": heredoc.Doc( + ` + `, + ), + }, + RunE: runPRDecorationBitbucket(prWrapper, policyWrapper, scansWrapper), + } + + prDecorationBitbucket.Flags().String(params.ScanIDFlag, "", "Scan ID to retrieve results from") + prDecorationBitbucket.Flags().String(params.SCMTokenFlag, "", params.BitbucketTokenUsage) + prDecorationBitbucket.Flags().String(params.NamespaceFlag, "", fmt.Sprintf(params.NamespaceFlagUsage, "Bitbucket")) + prDecorationBitbucket.Flags().String(params.RepoNameFlag, "", fmt.Sprintf(params.RepoNameFlagUsage, "Bitbucket")) + prDecorationBitbucket.Flags().Int(params.PRBBIDFlag, 0, params.PRBBIDFlagUsage) + prDecorationBitbucket.Flags().String(params.ProjectKeyFlag, "", params.ProjectKeyFlagUsage) //int or string? + prDecorationBitbucket.Flags().String(params.CodeRepositoryFlag, "", params.CodeRepositoryFlagUsage) + + // Set the value for token to mask the scm token + _ = viper.BindPFlag(params.SCMTokenFlag, prDecorationBitbucket.Flags().Lookup(params.SCMTokenFlag)) + + // mark all fields as required\ + _ = prDecorationBitbucket.MarkFlagRequired(params.ScanIDFlag) + _ = prDecorationBitbucket.MarkFlagRequired(params.SCMTokenFlag) + _ = prDecorationBitbucket.MarkFlagRequired(params.RepoNameFlag) + _ = prDecorationBitbucket.MarkFlagRequired(params.PRBBIDFlag) + + return prDecorationBitbucket +} + func runPRDecoration(prWrapper wrappers.PRWrapper, policyWrapper wrappers.PolicyWrapper, scansWrapper wrappers.ScansWrapper) func(cmd *cobra.Command, args []string) error { return func(cmd *cobra.Command, args []string) error { scanID, _ := cmd.Flags().GetString(params.ScanIDFlag) @@ -283,6 +328,116 @@ func runPRDecorationGitlab(prWrapper wrappers.PRWrapper, policyWrapper wrappers. } } +func runPRDecorationBitbucket(prWrapper wrappers.PRWrapper, policyWrapper wrappers.PolicyWrapper, scansWrapper wrappers.ScansWrapper) func(cmd *cobra.Command, args []string) error { + return func(cmd *cobra.Command, args []string) error { + scanID, _ := cmd.Flags().GetString(params.ScanIDFlag) + scmTokenFlag, _ := cmd.Flags().GetString(params.SCMTokenFlag) + namespaceFlag, _ := cmd.Flags().GetString(params.NamespaceFlag) + repoNameFlag, _ := cmd.Flags().GetString(params.RepoNameFlag) + prIDFlag, _ := cmd.Flags().GetInt(params.PRBBIDFlag) + apiURL, _ := cmd.Flags().GetString(params.CodeRepositoryFlag) + projectKey, _ := cmd.Flags().GetString(params.ProjectKeyFlag) + + isCloud, err := isBitbucketCloud(apiURL) + if err != nil { + return err + } + + flagRequiredErr := validateBitbucketFlags(isCloud, namespaceFlag, projectKey, apiURL) + if flagRequiredErr != nil { + return flagRequiredErr + } + + scanRunningOrQueued, err := IsScanRunningOrQueued(scansWrapper, scanID) + + if err != nil { + return err + } + + if scanRunningOrQueued { + log.Println(noPRDecorationCreated) + return nil + } + + // Retrieve policies related to the scan and project to include in the PR decoration + policies, policyError := getScanViolatedPolicies(scansWrapper, policyWrapper, scanID, cmd) + if policyError != nil { + return errors.Errorf(policyErrorFormat, failedCreatingBitbucketPrDecoration) + } + + repoSlugFormatBB := repoSlugFormatBB(repoNameFlag) + + var prModel interface{} + if isCloud { + prModel = &wrappers.BitbucketCloudPRModel{ + ScanID: scanID, + ScmToken: scmTokenFlag, + Namespace: namespaceFlag, + RepoName: repoSlugFormatBB, + PRID: prIDFlag, + Policies: policies, + } + } else { + prModel = &wrappers.BitbucketServerPRModel{ + ScanID: scanID, + ScmToken: scmTokenFlag, + ProjectKey: projectKey, + RepoName: repoSlugFormatBB, + PRID: prIDFlag, + Policies: policies, + ServerUrl: apiURL, + } + } + var prResponse string + var errorModel *wrappers.WebError + if isCloud { + prResponse, errorModel, err = prWrapper.PostBitbucketCloudPRDecoration(prModel.(*wrappers.BitbucketCloudPRModel)) + } else { + prResponse, errorModel, err = prWrapper.PostBitbucketServerPRDecoration(prModel.(*wrappers.BitbucketServerPRModel)) + } + + if err != nil { + return err + } + + if errorModel != nil { + return errors.Errorf(errorCodeFormat, failedCreatingBitbucketPrDecoration, errorModel.Code, errorModel.Message) + } + + logger.Print(prResponse) + return nil + } +} + +func repoSlugFormatBB(repoNameFlag string) string { + repoSlug := strings.Replace(repoNameFlag, " ", "-", -1) + return repoSlug +} + +func validateBitbucketFlags(isCloud bool, namespaceFlag, projectKey, apiURL string) error { + if isCloud { + if namespaceFlag == "" { + return errors.New("Namespace is required for Bitbucket Cloud") + } + } else { + if projectKey == "" { + return errors.New("Project key is required for Bitbucket Server") + } + if apiURL == "" { + return errors.New("API URL is required for Bitbucket Server") + } + } + return nil +} + +func isBitbucketCloud(apiURL string) (bool, error) { + if apiURL == "" || strings.Contains(apiURL, bitbucketCloudURL) { + return true, nil + } else { + return false, nil + } +} + func getScanViolatedPolicies(scansWrapper wrappers.ScansWrapper, policyWrapper wrappers.PolicyWrapper, scanID string, cmd *cobra.Command) ([]wrappers.PrPolicy, error) { // retrieve scan model to get the projectID scanResponseModel, errorScanModel, err := scansWrapper.GetByID(scanID) diff --git a/internal/params/binds.go b/internal/params/binds.go index 4a5b10e0b..0a8e95d47 100644 --- a/internal/params/binds.go +++ b/internal/params/binds.go @@ -28,6 +28,8 @@ var EnvVarsBinds = []struct { {BflPathKey, BflPathEnv, "api/bfl"}, {PRDecorationGithubPathKey, PRDecorationGithubPathEnv, "api/flow-publisher/pr/github"}, {PRDecorationGitlabPathKey, PRDecorationGitlabPathEnv, "api/flow-publisher/pr/gitlab"}, + {PRDecorationBitbucketCloudPathKey, PRDecorationBitbucketCloudPathEnv, "api/flow-publisher/pr/bitbucket"}, + {PRDecorationBitbucketServerPathKey, PRDecorationBitbucketServerPathEnv, "api/flow-publisher/pr/bitbucket-server"}, {DescriptionsPathKey, DescriptionsPathEnv, "api/queries/descriptions"}, {TenantConfigurationPathKey, TenantConfigurationPathEnv, "api/configuration/tenant"}, {UploadsPathKey, UploadsPathEnv, "api/uploads"}, diff --git a/internal/params/envs.go b/internal/params/envs.go index 9ea114d98..9789e2da1 100644 --- a/internal/params/envs.go +++ b/internal/params/envs.go @@ -30,6 +30,8 @@ const ( BflPathEnv = "CX_BFL_PATH" PRDecorationGithubPathEnv = "CX_PR_DECORATION_GITHUB_PATH" PRDecorationGitlabPathEnv = "CX_PR_DECORATION_GITLAB_PATH" + PRDecorationBitbucketCloudPathEnv = "CX_PR_DECORATION_BITBUCKET_CLOUD_PATH" + PRDecorationBitbucketServerPathEnv = "CX_PR_DECORATION_BITBUCKET_SERVER_PATH" SastRmPathEnv = "CX_SAST_RM_PATH" UploadsPathEnv = "CX_UPLOADS_PATH" TokenExpirySecondsEnv = "CX_TOKEN_EXPIRY_SECONDS" diff --git a/internal/params/flags.go b/internal/params/flags.go index b52903aee..936104777 100644 --- a/internal/params/flags.go +++ b/internal/params/flags.go @@ -34,7 +34,7 @@ const ( BranchFlagSh = "b" ScanIDFlag = "scan-id" CodeRepositoryFlag = "code-repository-url" - CodeRepositoryFlagUsage = "Code repository URL (optional for self-hosted SCMs)" + CodeRepositoryFlagUsage = "Code repository URL (required for self-hosted SCMs)" BranchFlagUsage = "Branch to scan" MainBranchFlag = "branch" ScaResolverFlag = "sca-resolver" @@ -121,6 +121,7 @@ const ( AzureTokenUsage = "Azure DevOps personal access token. Requires “Connected server” and “Code“ scope." GithubTokenUsage = "GitHub OAuth token. Requires “Repo” scope and organization SSO authorization, if enforced by the organization" GitLabTokenUsage = "GitLab OAuth token" + BitbucketTokenUsage = "Bitbucket OAuth token" BotCount = "Note: dependabot is not counted but other bots might be considered as contributors." DisabledReposCount = "Note: Disabled repositories are not counted." URLFlag = "url" @@ -171,7 +172,10 @@ const ( PRIidFlagUsage = "Gitlab IID (internal ID) of the merge request" PRGitlabProjectFlag = "gitlab-project-id" PRGitlabProjectFlagUsage = "Gitlab project ID" - + ProjectKeyFlag = "project-key" + ProjectKeyFlagUsage = "Key of the project containing the repository" + PRBBIDFlag = "pr-id" + PRBBIDFlagUsage = "Bitbucket PR ID" // Chat (General) ChatAPIKey = "chat-apikey" ChatConversationID = "conversation-id" diff --git a/internal/params/keys.go b/internal/params/keys.go index 1da512e43..e49f2526f 100644 --- a/internal/params/keys.go +++ b/internal/params/keys.go @@ -28,6 +28,8 @@ var ( BflPathKey = strings.ToLower(BflPathEnv) PRDecorationGithubPathKey = strings.ToLower(PRDecorationGithubPathEnv) PRDecorationGitlabPathKey = strings.ToLower(PRDecorationGitlabPathEnv) + PRDecorationBitbucketCloudPathKey = strings.ToLower(PRDecorationBitbucketCloudPathEnv) + PRDecorationBitbucketServerPathKey = strings.ToLower(PRDecorationBitbucketServerPathEnv) UploadsPathKey = strings.ToLower(UploadsPathEnv) SastRmPathKey = strings.ToLower(SastRmPathEnv) AccessKeyIDConfigKey = strings.ToLower(AccessKeyIDEnv) diff --git a/internal/wrappers/pr-http.go b/internal/wrappers/pr-http.go index 8756d74ac..80467e074 100644 --- a/internal/wrappers/pr-http.go +++ b/internal/wrappers/pr-http.go @@ -16,14 +16,18 @@ const ( ) type PRHTTPWrapper struct { - githubPath string - gitlabPath string + githubPath string + gitlabPath string + bitbucketCloudPath string + bitbucketServerPath string } -func NewHTTPPRWrapper(githubPath, gitlabPath string) PRWrapper { +func NewHTTPPRWrapper(githubPath, gitlabPath, bitbucketCloudPath, bitbucketServerPath string) PRWrapper { return &PRHTTPWrapper{ - githubPath: githubPath, - gitlabPath: gitlabPath, + githubPath: githubPath, + gitlabPath: gitlabPath, + bitbucketCloudPath: bitbucketCloudPath, + bitbucketServerPath: bitbucketServerPath, } } @@ -71,6 +75,50 @@ func (r *PRHTTPWrapper) PostGitlabPRDecoration(model *GitlabPRModel) ( return handlePRResponseWithBody(resp, err) } +func (r *PRHTTPWrapper) PostBitbucketCloudPRDecoration(model *BitbucketCloudPRModel) ( + string, + *WebError, + error, +) { + clientTimeout := viper.GetUint(commonParams.ClientTimeoutKey) + jsonBytes, err := json.Marshal(model) + if err != nil { + return "", nil, err + } + resp, err := SendHTTPRequestWithJSONContentType(http.MethodPost, r.bitbucketCloudPath, bytes.NewBuffer(jsonBytes), true, clientTimeout) + if err != nil { + return "", nil, err + } + defer func() { + if err == nil { + _ = resp.Body.Close() + } + }() + return handlePRResponseWithBody(resp, err) +} + +func (r *PRHTTPWrapper) PostBitbucketServerPRDecoration(model *BitbucketServerPRModel) ( + string, + *WebError, + error, +) { + clientTimeout := viper.GetUint(commonParams.ClientTimeoutKey) + jsonBytes, err := json.Marshal(model) + if err != nil { + return "", nil, err + } + resp, err := SendHTTPRequestWithJSONContentType(http.MethodPost, r.bitbucketServerPath, bytes.NewBuffer(jsonBytes), true, clientTimeout) + if err != nil { + return "", nil, err + } + defer func() { + if err == nil { + _ = resp.Body.Close() + } + }() + return handlePRResponseWithBody(resp, err) +} + func handlePRResponseWithBody(resp *http.Response, err error) (string, *WebError, error) { if err != nil { return "", nil, err diff --git a/internal/wrappers/pr.go b/internal/wrappers/pr.go index 40ccdb159..74ec829c2 100644 --- a/internal/wrappers/pr.go +++ b/internal/wrappers/pr.go @@ -25,7 +25,27 @@ type GitlabPRModel struct { APIURL string `json:"apiUrl"` } +type BitbucketCloudPRModel struct { + ScanID string `json:"scanId"` + ScmToken string `json:"scmToken"` + Namespace string `json:"namespace"` + RepoName string `json:"repoName"` + PRID int `json:"prId"` + Policies []PrPolicy `json:"violatedPolicyList"` +} + +type BitbucketServerPRModel struct { + ScanID string `json:"scanId"` + ScmToken string `json:"scmToken"` + ServerUrl string `json:"apiUrl"` + ProjectKey string `json:"namespace"` + RepoName string `json:"repoName"` + PRID int `json:"prNumber"` + Policies []PrPolicy `json:"violatedPolicyList"` +} type PRWrapper interface { PostPRDecoration(model *PRModel) (string, *WebError, error) PostGitlabPRDecoration(model *GitlabPRModel) (string, *WebError, error) + PostBitbucketCloudPRDecoration(model *BitbucketCloudPRModel) (string, *WebError, error) + PostBitbucketServerPRDecoration(model *BitbucketServerPRModel) (string, *WebError, error) } diff --git a/test/integration/util_command.go b/test/integration/util_command.go index 8c91f45ec..e1b67092e 100644 --- a/test/integration/util_command.go +++ b/test/integration/util_command.go @@ -74,6 +74,8 @@ func createASTIntegrationTestCommand(t *testing.T) *cobra.Command { learnMore := viper.GetString(params.DescriptionsPathKey) prDecorationGithubPath := viper.GetString(params.PRDecorationGithubPathKey) prDecorationGitlabPath := viper.GetString(params.PRDecorationGitlabPathKey) + prDecorationBitbucketCloudPath := viper.GetString(params.PRDecorationBitbucketCloudPathKey) + prDecorationBitbucketServerPath := viper.GetString(params.PRDecorationBitbucketServerPathKey) tenantConfigurationPath := viper.GetString(params.TenantConfigurationPathKey) resultsPdfPath := viper.GetString(params.ResultsPdfReportPathKey) exportPath := viper.GetString(params.ExportPathKey) @@ -104,7 +106,7 @@ func createASTIntegrationTestCommand(t *testing.T) *cobra.Command { bitBucketWrapper := wrappers.NewBitbucketWrapper() bflWrapper := wrappers.NewBflHTTPWrapper(bfl) learnMoreWrapper := wrappers.NewHTTPLearnMoreWrapper(learnMore) - prWrapper := wrappers.NewHTTPPRWrapper(prDecorationGithubPath, prDecorationGitlabPath) + prWrapper := wrappers.NewHTTPPRWrapper(prDecorationGithubPath, prDecorationGitlabPath, prDecorationBitbucketCloudPath, prDecorationBitbucketServerPath) tenantConfigurationWrapper := wrappers.NewHTTPTenantConfigurationWrapper(tenantConfigurationPath) jwtWrapper := wrappers.NewJwtWrapper() scaRealtimeWrapper := wrappers.NewHTTPScaRealTimeWrapper() From fb46e4be617214a94de33c940ed08c2a0f5e66ef Mon Sep 17 00:00:00 2001 From: galactica Date: Tue, 12 Nov 2024 16:36:34 +0200 Subject: [PATCH 02/13] refactor pr http wrapper --- internal/wrappers/mock/pr-mock.go | 20 +++++--- internal/wrappers/pr-http.go | 80 ++++++------------------------- test/integration/pr_test.go | 4 +- 3 files changed, 31 insertions(+), 73 deletions(-) diff --git a/internal/wrappers/mock/pr-mock.go b/internal/wrappers/mock/pr-mock.go index 4d5a464aa..7d6dad35d 100644 --- a/internal/wrappers/mock/pr-mock.go +++ b/internal/wrappers/mock/pr-mock.go @@ -2,19 +2,27 @@ package mock import ( "github.com/checkmarx/ast-cli/internal/wrappers" + "github.com/pkg/errors" ) type PRMockWrapper struct { } -func (pr *PRMockWrapper) PostPRDecoration(model *wrappers.PRModel) ( +func (pr *PRMockWrapper) PostPRDecoration(model interface{}) ( string, *wrappers.WebError, error, ) { - return "PR comment created successfully.", nil, nil -} - -func (pr *PRMockWrapper) PostGitlabPRDecoration(model *wrappers.GitlabPRModel) (string, *wrappers.WebError, error) { - return "MR comment created successfully.", nil, nil + switch model.(type) { + case *wrappers.PRModel: + return "PR comment created successfully.", nil, nil + case *wrappers.GitlabPRModel: + return "MR comment created successfully.", nil, nil + case *wrappers.BitbucketCloudPRModel: + return "Bitbucket Cloud PR comment created successfully.", nil, nil + case *wrappers.BitbucketServerPRModel: + return "Bitbucket Server PR comment created successfully.", nil, nil + default: + return "", nil, errors.New("unsupported model type") + } } diff --git a/internal/wrappers/pr-http.go b/internal/wrappers/pr-http.go index 80467e074..ce374fe14 100644 --- a/internal/wrappers/pr-http.go +++ b/internal/wrappers/pr-http.go @@ -31,61 +31,18 @@ func NewHTTPPRWrapper(githubPath, gitlabPath, bitbucketCloudPath, bitbucketServe } } -func (r *PRHTTPWrapper) PostPRDecoration(model *PRModel) ( - string, - *WebError, - error, -) { - clientTimeout := viper.GetUint(commonParams.ClientTimeoutKey) - jsonBytes, err := json.Marshal(model) - if err != nil { - return "", nil, err - } - resp, err := SendHTTPRequestWithJSONContentType(http.MethodPost, r.githubPath, bytes.NewBuffer(jsonBytes), true, clientTimeout) - if err != nil { - return "", nil, err - } - defer func() { - if err == nil { - _ = resp.Body.Close() - } - }() - return handlePRResponseWithBody(resp, err) -} - -func (r *PRHTTPWrapper) PostGitlabPRDecoration(model *GitlabPRModel) ( - string, - *WebError, - error, -) { - clientTimeout := viper.GetUint(commonParams.ClientTimeoutKey) - jsonBytes, err := json.Marshal(model) - if err != nil { - return "", nil, err - } - resp, err := SendHTTPRequestWithJSONContentType(http.MethodPost, r.gitlabPath, bytes.NewBuffer(jsonBytes), true, clientTimeout) +func (r *PRHTTPWrapper) PostPRDecoration(model interface{}) (string, *WebError, error) { + url, err := r.getPRDecorationURL(model) if err != nil { return "", nil, err } - defer func() { - if err == nil { - _ = resp.Body.Close() - } - }() - return handlePRResponseWithBody(resp, err) -} -func (r *PRHTTPWrapper) PostBitbucketCloudPRDecoration(model *BitbucketCloudPRModel) ( - string, - *WebError, - error, -) { clientTimeout := viper.GetUint(commonParams.ClientTimeoutKey) jsonBytes, err := json.Marshal(model) if err != nil { return "", nil, err } - resp, err := SendHTTPRequestWithJSONContentType(http.MethodPost, r.bitbucketCloudPath, bytes.NewBuffer(jsonBytes), true, clientTimeout) + resp, err := SendHTTPRequestWithJSONContentType(http.MethodPost, url, bytes.NewBuffer(jsonBytes), true, clientTimeout) if err != nil { return "", nil, err } @@ -97,26 +54,19 @@ func (r *PRHTTPWrapper) PostBitbucketCloudPRDecoration(model *BitbucketCloudPRMo return handlePRResponseWithBody(resp, err) } -func (r *PRHTTPWrapper) PostBitbucketServerPRDecoration(model *BitbucketServerPRModel) ( - string, - *WebError, - error, -) { - clientTimeout := viper.GetUint(commonParams.ClientTimeoutKey) - jsonBytes, err := json.Marshal(model) - if err != nil { - return "", nil, err - } - resp, err := SendHTTPRequestWithJSONContentType(http.MethodPost, r.bitbucketServerPath, bytes.NewBuffer(jsonBytes), true, clientTimeout) - if err != nil { - return "", nil, err +func (r *PRHTTPWrapper) getPRDecorationURL(model interface{}) (string, error) { + switch model.(type) { + case *PRModel: + return r.githubPath, nil + case *GitlabPRModel: + return r.gitlabPath, nil + case *BitbucketCloudPRModel: + return r.bitbucketCloudPath, nil + case *BitbucketServerPRModel: + return r.bitbucketServerPath, nil + default: + return "", errors.New("unsupported model type") } - defer func() { - if err == nil { - _ = resp.Body.Close() - } - }() - return handlePRResponseWithBody(resp, err) } func handlePRResponseWithBody(resp *http.Response, err error) (string, *WebError, error) { diff --git a/test/integration/pr_test.go b/test/integration/pr_test.go index 932273862..a498095b1 100644 --- a/test/integration/pr_test.go +++ b/test/integration/pr_test.go @@ -184,10 +184,10 @@ func TestPRGitlabDecoration_WhenUseCodeRepositoryFlag_ShouldSuccess(t *testing.T "https://gitlab.example.com", } - monkey.Patch((*wrappers.PRHTTPWrapper).PostGitlabPRDecoration, func(*wrappers.PRHTTPWrapper, *wrappers.GitlabPRModel) (string, *wrappers.WebError, error) { + monkey.Patch((*wrappers.PRHTTPWrapper).PostPRDecoration, func(*wrappers.PRHTTPWrapper, *wrappers.GitlabPRModel) (string, *wrappers.WebError, error) { return gitlabPRCommentCreated, nil, nil }) - defer monkey.Unpatch((*wrappers.PRHTTPWrapper).PostGitlabPRDecoration) + defer monkey.Unpatch((*wrappers.PRHTTPWrapper).PostPRDecoration) file := createOutputFile(t, outputFileName) defer deleteOutputFile(t, file) From 0da08ab666f1eedfe72edd2b4e00022844f5222d Mon Sep 17 00:00:00 2001 From: galactica Date: Tue, 12 Nov 2024 16:55:43 +0200 Subject: [PATCH 03/13] refactor pr http wrapper --- internal/commands/util/pr.go | 10 ++-------- internal/wrappers/pr.go | 5 +---- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/internal/commands/util/pr.go b/internal/commands/util/pr.go index eb69a583b..162fd6912 100644 --- a/internal/commands/util/pr.go +++ b/internal/commands/util/pr.go @@ -312,7 +312,7 @@ func runPRDecorationGitlab(prWrapper wrappers.PRWrapper, policyWrapper wrappers. APIURL: updatedAPIURL, } - prResponse, errorModel, err := prWrapper.PostGitlabPRDecoration(prModel) + prResponse, errorModel, err := prWrapper.PostPRDecoration(prModel) if err != nil { return err @@ -388,13 +388,7 @@ func runPRDecorationBitbucket(prWrapper wrappers.PRWrapper, policyWrapper wrappe ServerUrl: apiURL, } } - var prResponse string - var errorModel *wrappers.WebError - if isCloud { - prResponse, errorModel, err = prWrapper.PostBitbucketCloudPRDecoration(prModel.(*wrappers.BitbucketCloudPRModel)) - } else { - prResponse, errorModel, err = prWrapper.PostBitbucketServerPRDecoration(prModel.(*wrappers.BitbucketServerPRModel)) - } + prResponse, errorModel, err := prWrapper.PostPRDecoration(prModel) if err != nil { return err diff --git a/internal/wrappers/pr.go b/internal/wrappers/pr.go index 74ec829c2..e4a047f90 100644 --- a/internal/wrappers/pr.go +++ b/internal/wrappers/pr.go @@ -44,8 +44,5 @@ type BitbucketServerPRModel struct { Policies []PrPolicy `json:"violatedPolicyList"` } type PRWrapper interface { - PostPRDecoration(model *PRModel) (string, *WebError, error) - PostGitlabPRDecoration(model *GitlabPRModel) (string, *WebError, error) - PostBitbucketCloudPRDecoration(model *BitbucketCloudPRModel) (string, *WebError, error) - PostBitbucketServerPRDecoration(model *BitbucketServerPRModel) (string, *WebError, error) + PostPRDecoration(model interface{}) (string, *WebError, error) } From 1b6773172b515f2bda79a58c4c45003cf71cc7b5 Mon Sep 17 00:00:00 2001 From: galactica Date: Wed, 13 Nov 2024 10:30:34 +0200 Subject: [PATCH 04/13] fix lint errors and monkey tests --- internal/commands/util/pr.go | 20 ++++++++------------ internal/wrappers/pr.go | 2 +- test/integration/pr_test.go | 4 ++-- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/internal/commands/util/pr.go b/internal/commands/util/pr.go index 162fd6912..6a3227ffe 100644 --- a/internal/commands/util/pr.go +++ b/internal/commands/util/pr.go @@ -189,7 +189,7 @@ func PRDecorationBitbucket(prWrapper wrappers.PRWrapper, policyWrapper wrappers. prDecorationBitbucket.Flags().String(params.NamespaceFlag, "", fmt.Sprintf(params.NamespaceFlagUsage, "Bitbucket")) prDecorationBitbucket.Flags().String(params.RepoNameFlag, "", fmt.Sprintf(params.RepoNameFlagUsage, "Bitbucket")) prDecorationBitbucket.Flags().Int(params.PRBBIDFlag, 0, params.PRBBIDFlagUsage) - prDecorationBitbucket.Flags().String(params.ProjectKeyFlag, "", params.ProjectKeyFlagUsage) //int or string? + prDecorationBitbucket.Flags().String(params.ProjectKeyFlag, "", params.ProjectKeyFlagUsage) prDecorationBitbucket.Flags().String(params.CodeRepositoryFlag, "", params.CodeRepositoryFlagUsage) // Set the value for token to mask the scm token @@ -338,10 +338,7 @@ func runPRDecorationBitbucket(prWrapper wrappers.PRWrapper, policyWrapper wrappe apiURL, _ := cmd.Flags().GetString(params.CodeRepositoryFlag) projectKey, _ := cmd.Flags().GetString(params.ProjectKeyFlag) - isCloud, err := isBitbucketCloud(apiURL) - if err != nil { - return err - } + isCloud := isBitbucketCloud(apiURL) flagRequiredErr := validateBitbucketFlags(isCloud, namespaceFlag, projectKey, apiURL) if flagRequiredErr != nil { @@ -385,7 +382,7 @@ func runPRDecorationBitbucket(prWrapper wrappers.PRWrapper, policyWrapper wrappe RepoName: repoSlugFormatBB, PRID: prIDFlag, Policies: policies, - ServerUrl: apiURL, + ServerURL: apiURL, } } prResponse, errorModel, err := prWrapper.PostPRDecoration(prModel) @@ -411,11 +408,11 @@ func repoSlugFormatBB(repoNameFlag string) string { func validateBitbucketFlags(isCloud bool, namespaceFlag, projectKey, apiURL string) error { if isCloud { if namespaceFlag == "" { - return errors.New("Namespace is required for Bitbucket Cloud") + return errors.New("namespace is required for Bitbucket Cloud") } } else { if projectKey == "" { - return errors.New("Project key is required for Bitbucket Server") + return errors.New("project key is required for Bitbucket Server") } if apiURL == "" { return errors.New("API URL is required for Bitbucket Server") @@ -424,12 +421,11 @@ func validateBitbucketFlags(isCloud bool, namespaceFlag, projectKey, apiURL stri return nil } -func isBitbucketCloud(apiURL string) (bool, error) { +func isBitbucketCloud(apiURL string) bool { if apiURL == "" || strings.Contains(apiURL, bitbucketCloudURL) { - return true, nil - } else { - return false, nil + return true } + return false } func getScanViolatedPolicies(scansWrapper wrappers.ScansWrapper, policyWrapper wrappers.PolicyWrapper, scanID string, cmd *cobra.Command) ([]wrappers.PrPolicy, error) { diff --git a/internal/wrappers/pr.go b/internal/wrappers/pr.go index e4a047f90..d23d95647 100644 --- a/internal/wrappers/pr.go +++ b/internal/wrappers/pr.go @@ -37,7 +37,7 @@ type BitbucketCloudPRModel struct { type BitbucketServerPRModel struct { ScanID string `json:"scanId"` ScmToken string `json:"scmToken"` - ServerUrl string `json:"apiUrl"` + ServerURL string `json:"apiUrl"` ProjectKey string `json:"namespace"` RepoName string `json:"repoName"` PRID int `json:"prNumber"` diff --git a/test/integration/pr_test.go b/test/integration/pr_test.go index 00997ac95..1d5752530 100644 --- a/test/integration/pr_test.go +++ b/test/integration/pr_test.go @@ -99,7 +99,7 @@ func TestPRGithubDecoration_WhenUseCodeRepositoryFlag_ShouldSuccess(t *testing.T "https://github.example.com", } - monkey.Patch((*wrappers.PRHTTPWrapper).PostPRDecoration, func(*wrappers.PRHTTPWrapper, *wrappers.PRModel) (string, *wrappers.WebError, error) { + monkey.Patch((*wrappers.PRHTTPWrapper).PostPRDecoration, func(*wrappers.PRHTTPWrapper, interface{}) (string, *wrappers.WebError, error) { return githubPRCommentCreated, nil, nil }) defer monkey.Unpatch((*wrappers.PRHTTPWrapper).PostPRDecoration) @@ -182,7 +182,7 @@ func TestPRGitlabDecoration_WhenUseCodeRepositoryFlag_ShouldSuccess(t *testing.T "https://gitlab.example.com", } - monkey.Patch((*wrappers.PRHTTPWrapper).PostPRDecoration, func(*wrappers.PRHTTPWrapper, *wrappers.GitlabPRModel) (string, *wrappers.WebError, error) { + monkey.Patch((*wrappers.PRHTTPWrapper).PostPRDecoration, func(*wrappers.PRHTTPWrapper, interface{}) (string, *wrappers.WebError, error) { return gitlabPRCommentCreated, nil, nil }) defer monkey.Unpatch((*wrappers.PRHTTPWrapper).PostPRDecoration) From 1d64108c11cd43da3dc97e2135875b7f034a3c5e Mon Sep 17 00:00:00 2001 From: galactica Date: Wed, 13 Nov 2024 21:45:15 +0200 Subject: [PATCH 05/13] add test. --- .github/workflows/ci.yml | 4 + .github/workflows/manual-integration-test.yml | 4 + internal/commands/util/pr.go | 64 +++++----- internal/commands/util/pr_test.go | 103 +++++++++++++++ test/integration/pr_test.go | 118 ++++++++++++++++++ 5 files changed, 263 insertions(+), 30 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1d9484f42..e37a0d27a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -80,6 +80,10 @@ jobs: BITBUCKET_USERNAME: ${{ secrets.BITBUCKET_USERNAME }} BITBUCKET_PASSWORD: ${{ secrets.BITBUCKET_PASSWORD }} GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }} + PR_BITBUCKET_TOKEN: ${{ secrets.PR_BITBUCKET_TOKEN }} + PR_BITBUCKET_NAMESPACE: "AstSystemTest" + PR_BITBUCKET_REPO_NAME: "cliIntegrationTest", + PR_BITBUCKET_ID: 1 run: | sudo chmod +x ./internal/commands/.scripts/integration_up.sh ./internal/commands/.scripts/integration_down.sh ./internal/commands/.scripts/integration_up.sh diff --git a/.github/workflows/manual-integration-test.yml b/.github/workflows/manual-integration-test.yml index 81219f263..80e88c7db 100644 --- a/.github/workflows/manual-integration-test.yml +++ b/.github/workflows/manual-integration-test.yml @@ -83,6 +83,10 @@ jobs: BITBUCKET_USERNAME: ${{ secrets.BITBUCKET_USERNAME }} BITBUCKET_PASSWORD: ${{ secrets.BITBUCKET_PASSWORD }} GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }} + PR_BITBUCKET_TOKEN: ${{ secrets.PR_BITBUCKET_TOKEN }} + PR_BITBUCKET_NAMESPACE: "AstSystemTest" + PR_BITBUCKET_REPO_NAME: "cliIntegrationTest" + PR_BITBUCKET_ID: 1 run: | sudo chmod +x ./internal/commands/.scripts/integration_up.sh ./internal/commands/.scripts/integration_down.sh ./internal/commands/.scripts/integration_up.sh diff --git a/internal/commands/util/pr.go b/internal/commands/util/pr.go index 6a3227ffe..8abdeb98b 100644 --- a/internal/commands/util/pr.go +++ b/internal/commands/util/pr.go @@ -338,9 +338,7 @@ func runPRDecorationBitbucket(prWrapper wrappers.PRWrapper, policyWrapper wrappe apiURL, _ := cmd.Flags().GetString(params.CodeRepositoryFlag) projectKey, _ := cmd.Flags().GetString(params.ProjectKeyFlag) - isCloud := isBitbucketCloud(apiURL) - - flagRequiredErr := validateBitbucketFlags(isCloud, namespaceFlag, projectKey, apiURL) + isCloud, flagRequiredErr := checkIsCloudAndValidateFlag(apiURL, namespaceFlag, projectKey) if flagRequiredErr != nil { return flagRequiredErr } @@ -356,35 +354,12 @@ func runPRDecorationBitbucket(prWrapper wrappers.PRWrapper, policyWrapper wrappe return nil } - // Retrieve policies related to the scan and project to include in the PR decoration policies, policyError := getScanViolatedPolicies(scansWrapper, policyWrapper, scanID, cmd) if policyError != nil { return errors.Errorf(policyErrorFormat, failedCreatingBitbucketPrDecoration) } - repoSlugFormatBB := repoSlugFormatBB(repoNameFlag) - - var prModel interface{} - if isCloud { - prModel = &wrappers.BitbucketCloudPRModel{ - ScanID: scanID, - ScmToken: scmTokenFlag, - Namespace: namespaceFlag, - RepoName: repoSlugFormatBB, - PRID: prIDFlag, - Policies: policies, - } - } else { - prModel = &wrappers.BitbucketServerPRModel{ - ScanID: scanID, - ScmToken: scmTokenFlag, - ProjectKey: projectKey, - RepoName: repoSlugFormatBB, - PRID: prIDFlag, - Policies: policies, - ServerURL: apiURL, - } - } + prModel := createPRModel(isCloud, scanID, scmTokenFlag, namespaceFlag, repoNameFlag, prIDFlag, apiURL, projectKey, policies) prResponse, errorModel, err := prWrapper.PostPRDecoration(prModel) if err != nil { @@ -405,6 +380,12 @@ func repoSlugFormatBB(repoNameFlag string) string { return repoSlug } +func checkIsCloudAndValidateFlag(apiURL, namespaceFlag, projectKey string) (bool, error) { + isCloud := isBitbucketCloud(apiURL) + flagRequiredErr := validateBitbucketFlags(isCloud, namespaceFlag, projectKey, apiURL) + return isCloud, flagRequiredErr +} + func validateBitbucketFlags(isCloud bool, namespaceFlag, projectKey, apiURL string) error { if isCloud { if namespaceFlag == "" { @@ -414,9 +395,6 @@ func validateBitbucketFlags(isCloud bool, namespaceFlag, projectKey, apiURL stri if projectKey == "" { return errors.New("project key is required for Bitbucket Server") } - if apiURL == "" { - return errors.New("API URL is required for Bitbucket Server") - } } return nil } @@ -428,6 +406,32 @@ func isBitbucketCloud(apiURL string) bool { return false } +func createPRModel(isCloud bool, scanID, scmTokenFlag, namespaceFlag, repoNameFlag string, prIDFlag int, apiURL, projectKey string, policies []wrappers.PrPolicy) interface{} { + + repoSlugFormatBB := repoSlugFormatBB(repoNameFlag) + + if isCloud { + return &wrappers.BitbucketCloudPRModel{ + ScanID: scanID, + ScmToken: scmTokenFlag, + Namespace: namespaceFlag, + RepoName: repoSlugFormatBB, + PRID: prIDFlag, + Policies: policies, + } + } else { + return &wrappers.BitbucketServerPRModel{ + ScanID: scanID, + ScmToken: scmTokenFlag, + ProjectKey: projectKey, + RepoName: repoSlugFormatBB, + PRID: prIDFlag, + Policies: policies, + ServerURL: apiURL, + } + } +} + func getScanViolatedPolicies(scansWrapper wrappers.ScansWrapper, policyWrapper wrappers.PolicyWrapper, scanID string, cmd *cobra.Command) ([]wrappers.PrPolicy, error) { // retrieve scan model to get the projectID scanResponseModel, errorScanModel, err := scansWrapper.GetByID(scanID) diff --git a/internal/commands/util/pr_test.go b/internal/commands/util/pr_test.go index 9e11b8385..54a0b5eb2 100644 --- a/internal/commands/util/pr_test.go +++ b/internal/commands/util/pr_test.go @@ -66,3 +66,106 @@ func TestUpdateAPIURLForGitlabOnPrem_whenAPIURLIsNotSet_ShouldReturnCloudAPIURL( cloudAPIURL := updateAPIURLForGitlabOnPrem("") asserts.Equal(t, gitlabCloudURL, cloudAPIURL) } + +func TestCheckIsCloudAndValidateFlag(t *testing.T) { + tests := []struct { + name string + apiURL string + namespaceFlag string + projectKey string + expectedCloud bool + expectedError string + }{ + { + name: "Bitbucket Cloud", + apiURL: "", + namespaceFlag: "namespace", + projectKey: "", + expectedCloud: true, + expectedError: "", + }, + { + name: "Bitbucket Cloud with namespace", + apiURL: "https://bitbucket.org", + namespaceFlag: "namespace", + projectKey: "", + expectedCloud: true, + expectedError: "", + }, + { + name: "Bitbucket Cloud without namespace", + apiURL: "https://bitbucket.org", + namespaceFlag: "", + projectKey: "", + expectedCloud: true, + expectedError: "namespace is required for Bitbucket Cloud", + }, + { + name: "Bitbucket Server with project key and API URL", + apiURL: "https://bitbucket.example.com", + namespaceFlag: "", + projectKey: "projectKey", + expectedCloud: false, + expectedError: "", + }, + { + name: "Bitbucket Server without project key", + apiURL: "https://bitbucket.example.com", + namespaceFlag: "", + projectKey: "", + expectedCloud: false, + expectedError: "project key is required for Bitbucket Server", + }, + { + name: "Bitbucket Cloud with URL and project key", + apiURL: "https://bitbucket.org", + namespaceFlag: "", + projectKey: "projectKey", + expectedCloud: true, + expectedError: "namespace is required for Bitbucket Cloud", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + isCloud, err := checkIsCloudAndValidateFlag(tt.apiURL, tt.namespaceFlag, tt.projectKey) + asserts.Equal(t, tt.expectedCloud, isCloud) + if tt.expectedError != "" { + asserts.EqualError(t, err, tt.expectedError) + } else { + asserts.NoError(t, err) + } + }) + } +} + +func TestRepoSlugFormatBB(t *testing.T) { + tests := []struct { + name string + repoNameFlag string + expectedSlug string + }{ + { + name: "Single word repo name", + repoNameFlag: "repository", + expectedSlug: "repository", + }, + { + name: "Repo name with spaces", + repoNameFlag: "my repository", + expectedSlug: "my-repository", + }, + { + name: "Repo name with multiple spaces", + repoNameFlag: "my awesome repository", + expectedSlug: "my-awesome-repository", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + slug := repoSlugFormatBB(tt.repoNameFlag) + asserts.Equal(t, tt.expectedSlug, slug) + }) + } +} diff --git a/test/integration/pr_test.go b/test/integration/pr_test.go index 1d5752530..7f756aafa 100644 --- a/test/integration/pr_test.go +++ b/test/integration/pr_test.go @@ -27,10 +27,15 @@ const ( prGitlabNamespace = "PR_GITLAB_NAMESPACE" prGitlabProjectId = "PR_GITLAB_PROJECT_ID" prGitlabIid = "PR_GITLAB_IID" + prBBToken = "PR_BITBUCKET_TOKEN" + prBBNamespace = "PR_BITBUCKET_NAMESPACE" + prBBId = "PR_BITBUCKET_ID" + prBBRepoName = "PR_BITBUCKET_REPO_NAME" prdDecorationForbiddenMessage = "A PR couldn't be created for this scan because it is still in progress." failedGettingScanError = "Failed showing a scan" githubPRCommentCreated = "github PR comment created successfully." gitlabPRCommentCreated = "gitlab PR comment created successfully." + BBPRCommentCreated = "bitbucket PR comment created successfully." outputFileName = "test_output.log" scans = "api/scans" ) @@ -305,3 +310,116 @@ func deleteOutputFile(t *testing.T, file *os.File) { logger.Printf("Failed to remove log file: %v", err) } } + +func TestPRBBOnCloudDecorationSuccessCase(t *testing.T) { + //scanID, _ := getRootScan(t, params.SastType) + args := []string{ + "utils", + "pr", + "bitbucket", + flag(params.ScanIDFlag), + getCompletedScanID(t), + flag(params.SCMTokenFlag), + os.Getenv(prBBToken), + flag(params.NamespaceFlag), + os.Getenv(prBBNamespace), + flag(params.PRBBIDFlag), + os.Getenv(prBBId), + flag(params.RepoNameFlag), + os.Getenv(prBBRepoName), + "--debug", + } + + err, _ := executeCommand(t, args...) + assert.NilError(t, err, "Error should be nil") +} + +func TestPRBBBDecorationFailure(t *testing.T) { + args := []string{ + "utils", + "pr", + "bitbucket", + flag(params.ScanIDFlag), + "fakeScanID", + flag(params.SCMTokenFlag), + os.Getenv(prBBToken), + flag(params.NamespaceFlag), + os.Getenv(prBBNamespace), + flag(params.PRBBIDFlag), + os.Getenv(prBBId), + flag(params.RepoNameFlag), + os.Getenv(prBBRepoName), + "--debug", + } + + err, _ := executeCommand(t, args...) + assert.ErrorContains(t, err, "scan not found") +} + +func TestPRBBDecoration_WhenUseCodeRepositoryFlag_ShouldSuccess(t *testing.T) { + args := []string{ + "utils", + "pr", + "bitbucket", + flag(params.ScanIDFlag), + getCompletedScanID(t), + flag(params.SCMTokenFlag), + "Token", + flag(params.ProjectKeyFlag), + "PROJECTKEY", + flag(params.PRBBIDFlag), + os.Getenv(prBBId), + flag(params.RepoNameFlag), + os.Getenv(prBBRepoName), + flag(params.CodeRepositoryFlag), + "https://bitbucket.example.com", + } + + monkey.Patch((*wrappers.PRHTTPWrapper).PostPRDecoration, func(*wrappers.PRHTTPWrapper, interface{}) (string, *wrappers.WebError, error) { + return BBPRCommentCreated, nil, nil + }) + defer monkey.Unpatch((*wrappers.PRHTTPWrapper).PostPRDecoration) + + file := createOutputFile(t, outputFileName) + defer deleteOutputFile(t, file) + defer logger.SetOutput(os.Stdout) + + err, _ := executeCommand(t, args...) + assert.NilError(t, err, "Error should be nil") + + stdoutString, err := util.ReadFileAsString(file.Name()) + if err != nil { + t.Fatalf("Failed to read log file: %v", err) + } + + assert.Equal(t, strings.Contains(stdoutString, BBPRCommentCreated), true, "Expected output: %s", BBPRCommentCreated) +} +func TestPRBitbucketDecoration_WhenScanIsRunning_ShouldAvoidPRDecorationCommand(t *testing.T) { + scanID, _ := createScanNoWait(t, Zip, Tags, getProjectNameForScanTests()) + args := []string{ + "utils", + "pr", + "bitbucket", + flag(params.ScanIDFlag), + scanID, + flag(params.SCMTokenFlag), + os.Getenv(prBBToken), + flag(params.PRBBIDFlag), + os.Getenv(prBBId), + flag(params.RepoNameFlag), + os.Getenv(prBBRepoName), + flag(params.NamespaceFlag), + os.Getenv(prBBNamespace), + } + + file := createOutputFile(t, outputFileName) + _, _ = executeCommand(t, args...) + stdoutString, err := util.ReadFileAsString(file.Name()) + if err != nil { + t.Fatalf("Failed to read log file: %v", err) + } + assert.Equal(t, strings.Contains(stdoutString, prdDecorationForbiddenMessage), true, "Expected output: %s", prdDecorationForbiddenMessage) + + defer deleteOutputFile(t, file) + defer logger.SetOutput(os.Stdout) +} From 37f0fc9ad4f1e92725e1270a0ac98f8f1650986c Mon Sep 17 00:00:00 2001 From: galactica Date: Wed, 13 Nov 2024 22:46:26 +0200 Subject: [PATCH 06/13] fix --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e37a0d27a..e3e9600a7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -82,7 +82,7 @@ jobs: GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }} PR_BITBUCKET_TOKEN: ${{ secrets.PR_BITBUCKET_TOKEN }} PR_BITBUCKET_NAMESPACE: "AstSystemTest" - PR_BITBUCKET_REPO_NAME: "cliIntegrationTest", + PR_BITBUCKET_REPO_NAME: "cliIntegrationTest" PR_BITBUCKET_ID: 1 run: | sudo chmod +x ./internal/commands/.scripts/integration_up.sh ./internal/commands/.scripts/integration_down.sh From 7d5f2215073d11adcca1eb654964c472943bf130 Mon Sep 17 00:00:00 2001 From: galactica Date: Wed, 13 Nov 2024 22:55:11 +0200 Subject: [PATCH 07/13] fix lint --- internal/commands/util/pr.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/commands/util/pr.go b/internal/commands/util/pr.go index 8abdeb98b..9593bf10e 100644 --- a/internal/commands/util/pr.go +++ b/internal/commands/util/pr.go @@ -382,11 +382,11 @@ func repoSlugFormatBB(repoNameFlag string) string { func checkIsCloudAndValidateFlag(apiURL, namespaceFlag, projectKey string) (bool, error) { isCloud := isBitbucketCloud(apiURL) - flagRequiredErr := validateBitbucketFlags(isCloud, namespaceFlag, projectKey, apiURL) + flagRequiredErr := validateBitbucketFlags(isCloud, namespaceFlag, projectKey) return isCloud, flagRequiredErr } -func validateBitbucketFlags(isCloud bool, namespaceFlag, projectKey, apiURL string) error { +func validateBitbucketFlags(isCloud bool, namespaceFlag, projectKey string) error { if isCloud { if namespaceFlag == "" { return errors.New("namespace is required for Bitbucket Cloud") From 43c5ea02106e54a654a2760167fb65d55a3db43c Mon Sep 17 00:00:00 2001 From: galactica Date: Wed, 13 Nov 2024 23:06:17 +0200 Subject: [PATCH 08/13] fix lint --- internal/commands/util/pr.go | 20 +++++++++----------- internal/commands/util/pr_test.go | 1 + 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/internal/commands/util/pr.go b/internal/commands/util/pr.go index 9593bf10e..64429d9a7 100644 --- a/internal/commands/util/pr.go +++ b/internal/commands/util/pr.go @@ -407,7 +407,6 @@ func isBitbucketCloud(apiURL string) bool { } func createPRModel(isCloud bool, scanID, scmTokenFlag, namespaceFlag, repoNameFlag string, prIDFlag int, apiURL, projectKey string, policies []wrappers.PrPolicy) interface{} { - repoSlugFormatBB := repoSlugFormatBB(repoNameFlag) if isCloud { @@ -419,16 +418,15 @@ func createPRModel(isCloud bool, scanID, scmTokenFlag, namespaceFlag, repoNameFl PRID: prIDFlag, Policies: policies, } - } else { - return &wrappers.BitbucketServerPRModel{ - ScanID: scanID, - ScmToken: scmTokenFlag, - ProjectKey: projectKey, - RepoName: repoSlugFormatBB, - PRID: prIDFlag, - Policies: policies, - ServerURL: apiURL, - } + } + return &wrappers.BitbucketServerPRModel{ + ScanID: scanID, + ScmToken: scmTokenFlag, + ProjectKey: projectKey, + RepoName: repoSlugFormatBB, + PRID: prIDFlag, + Policies: policies, + ServerURL: apiURL, } } diff --git a/internal/commands/util/pr_test.go b/internal/commands/util/pr_test.go index 54a0b5eb2..46749bdb9 100644 --- a/internal/commands/util/pr_test.go +++ b/internal/commands/util/pr_test.go @@ -127,6 +127,7 @@ func TestCheckIsCloudAndValidateFlag(t *testing.T) { } for _, tt := range tests { + tt := tt t.Run(tt.name, func(t *testing.T) { isCloud, err := checkIsCloudAndValidateFlag(tt.apiURL, tt.namespaceFlag, tt.projectKey) asserts.Equal(t, tt.expectedCloud, isCloud) From 4d5ccc5853e8ba611d8f66b35acc36c5f9d10bbe Mon Sep 17 00:00:00 2001 From: galactica Date: Wed, 13 Nov 2024 23:10:30 +0200 Subject: [PATCH 09/13] fix lint --- internal/commands/util/pr_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/commands/util/pr_test.go b/internal/commands/util/pr_test.go index 46749bdb9..f080ec820 100644 --- a/internal/commands/util/pr_test.go +++ b/internal/commands/util/pr_test.go @@ -164,6 +164,7 @@ func TestRepoSlugFormatBB(t *testing.T) { } for _, tt := range tests { + tt := tt t.Run(tt.name, func(t *testing.T) { slug := repoSlugFormatBB(tt.repoNameFlag) asserts.Equal(t, tt.expectedSlug, slug) From 4996652878902c9756d3e26ce953690f7377b690 Mon Sep 17 00:00:00 2001 From: galactica Date: Thu, 14 Nov 2024 12:22:27 +0200 Subject: [PATCH 10/13] fix CR --- internal/commands/util/pr.go | 14 +++++++------- internal/commands/util/pr_test.go | 15 ++++++++++++++- test/integration/pr_test.go | 1 - 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/internal/commands/util/pr.go b/internal/commands/util/pr.go index 64429d9a7..0bd91c8c4 100644 --- a/internal/commands/util/pr.go +++ b/internal/commands/util/pr.go @@ -359,7 +359,7 @@ func runPRDecorationBitbucket(prWrapper wrappers.PRWrapper, policyWrapper wrappe return errors.Errorf(policyErrorFormat, failedCreatingBitbucketPrDecoration) } - prModel := createPRModel(isCloud, scanID, scmTokenFlag, namespaceFlag, repoNameFlag, prIDFlag, apiURL, projectKey, policies) + prModel := createBBPRModel(isCloud, scanID, scmTokenFlag, namespaceFlag, repoNameFlag, prIDFlag, apiURL, projectKey, policies) prResponse, errorModel, err := prWrapper.PostPRDecoration(prModel) if err != nil { @@ -375,8 +375,8 @@ func runPRDecorationBitbucket(prWrapper wrappers.PRWrapper, policyWrapper wrappe } } -func repoSlugFormatBB(repoNameFlag string) string { - repoSlug := strings.Replace(repoNameFlag, " ", "-", -1) +func formatRepoNameSlugBB(repoName string) string { + repoSlug := strings.Replace(strings.TrimSpace(repoName), " ", "-", -1) return repoSlug } @@ -406,15 +406,15 @@ func isBitbucketCloud(apiURL string) bool { return false } -func createPRModel(isCloud bool, scanID, scmTokenFlag, namespaceFlag, repoNameFlag string, prIDFlag int, apiURL, projectKey string, policies []wrappers.PrPolicy) interface{} { - repoSlugFormatBB := repoSlugFormatBB(repoNameFlag) +func createBBPRModel(isCloud bool, scanID, scmTokenFlag, namespaceFlag, repoNameFlag string, prIDFlag int, apiURL, projectKey string, policies []wrappers.PrPolicy) interface{} { + formattedRepoNameSlug := formatRepoNameSlugBB(repoNameFlag) if isCloud { return &wrappers.BitbucketCloudPRModel{ ScanID: scanID, ScmToken: scmTokenFlag, Namespace: namespaceFlag, - RepoName: repoSlugFormatBB, + RepoName: formattedRepoNameSlug, PRID: prIDFlag, Policies: policies, } @@ -423,7 +423,7 @@ func createPRModel(isCloud bool, scanID, scmTokenFlag, namespaceFlag, repoNameFl ScanID: scanID, ScmToken: scmTokenFlag, ProjectKey: projectKey, - RepoName: repoSlugFormatBB, + RepoName: formattedRepoNameSlug, PRID: prIDFlag, Policies: policies, ServerURL: apiURL, diff --git a/internal/commands/util/pr_test.go b/internal/commands/util/pr_test.go index f080ec820..147c6bc89 100644 --- a/internal/commands/util/pr_test.go +++ b/internal/commands/util/pr_test.go @@ -84,6 +84,14 @@ func TestCheckIsCloudAndValidateFlag(t *testing.T) { expectedCloud: true, expectedError: "", }, + { + name: "Bitbucket Cloud without https", + apiURL: "bitbucket.org", + namespaceFlag: "namespace", + projectKey: "", + expectedCloud: true, + expectedError: "", + }, { name: "Bitbucket Cloud with namespace", apiURL: "https://bitbucket.org", @@ -161,12 +169,17 @@ func TestRepoSlugFormatBB(t *testing.T) { repoNameFlag: "my awesome repository", expectedSlug: "my-awesome-repository", }, + { + name: "Repo name with leading and trailing spaces", + repoNameFlag: " my repository ", + expectedSlug: "my-repository", + }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { - slug := repoSlugFormatBB(tt.repoNameFlag) + slug := formatRepoNameSlugBB(tt.repoNameFlag) asserts.Equal(t, tt.expectedSlug, slug) }) } diff --git a/test/integration/pr_test.go b/test/integration/pr_test.go index 7f756aafa..31253261d 100644 --- a/test/integration/pr_test.go +++ b/test/integration/pr_test.go @@ -312,7 +312,6 @@ func deleteOutputFile(t *testing.T, file *os.File) { } func TestPRBBOnCloudDecorationSuccessCase(t *testing.T) { - //scanID, _ := getRootScan(t, params.SastType) args := []string{ "utils", "pr", From 603e594a0be5e8ce3185a77a58b02eb625fa70cd Mon Sep 17 00:00:00 2001 From: galactica Date: Thu, 14 Nov 2024 16:26:56 +0200 Subject: [PATCH 11/13] merge with main --- internal/commands/util/pr.go | 73 +++++++------------------------ internal/wrappers/mock/pr-mock.go | 7 ++- internal/wrappers/pr-http.go | 36 ++++----------- test/integration/pr_test.go | 4 +- 4 files changed, 29 insertions(+), 91 deletions(-) diff --git a/internal/commands/util/pr.go b/internal/commands/util/pr.go index c097e222a..e5b2dcdfc 100644 --- a/internal/commands/util/pr.go +++ b/internal/commands/util/pr.go @@ -16,25 +16,23 @@ import ( ) const ( - failedCreatingGithubPrDecoration = "Failed creating github PR Decoration" - failedCreatingGitlabPrDecoration = "Failed creating gitlab MR Decoration" + failedCreatingGithubPrDecoration = "Failed creating github PR Decoration" + failedCreatingGitlabPrDecoration = "Failed creating gitlab MR Decoration" failedCreatingBitbucketPrDecoration = "Failed creating bitbucket PR Decoration" - failedCreatingAzurePrDecoration = "Failed creating azure PR Decoration" - failedCreatingGitlabPrDecoration = "Failed creating gitlab MR Decoration" - errorCodeFormat = "%s: CODE: %d, %s\n" - policyErrorFormat = "%s: Failed to get scanID policy information" - waitDelayDefault = 5 - resultPolicyDefaultTimeout = 1 - failedGettingScanError = "Failed showing a scan" - noPRDecorationCreated = "A PR couldn't be created for this scan because it is still in progress." - githubOnPremURLSuffix = "/api/v3/repos/" - gitlabOnPremURLSuffix = "/api/v4/" - githubCloudURL = "https://api.github.com/repos/" - gitlabCloudURL = "https://gitlab.com" + gitlabOnPremURLSuffix - azureCloudURL = "https://dev.azure.com/" + failedCreatingAzurePrDecoration = "Failed creating azure PR Decoration" + errorCodeFormat = "%s: CODE: %d, %s\n" + policyErrorFormat = "%s: Failed to get scanID policy information" + waitDelayDefault = 5 + resultPolicyDefaultTimeout = 1 + failedGettingScanError = "Failed showing a scan" + noPRDecorationCreated = "A PR couldn't be created for this scan because it is still in progress." + githubOnPremURLSuffix = "/api/v3/repos/" + gitlabOnPremURLSuffix = "/api/v4/" + githubCloudURL = "https://api.github.com/repos/" + gitlabCloudURL = "https://gitlab.com" + gitlabOnPremURLSuffix + azureCloudURL = "https://dev.azure.com/" bitbucketCloudURL = "bitbucket.org" - errorAzureOnPremParams = "code-repository-url must be set when code-repository-username is set" - + errorAzureOnPremParams = "code-repository-url must be set when code-repository-username is set" ) func NewPRDecorationCommand(prWrapper wrappers.PRWrapper, policyWrapper wrappers.PolicyWrapper, scansWrapper wrappers.ScansWrapper) *cobra.Command { @@ -304,45 +302,6 @@ func runPRDecoration(prWrapper wrappers.PRWrapper, policyWrapper wrappers.Policy return nil } } -func PRDecorationBitbucket(prWrapper wrappers.PRWrapper, policyWrapper wrappers.PolicyWrapper, scansWrapper wrappers.ScansWrapper) *cobra.Command { - prDecorationBitbucket := &cobra.Command{ - Use: "bitbucket ", - Short: "Decorate bitbucket PR with vulnerabilities", - Long: "Decorate bitbucket PR with vulnerabilities", - Example: heredoc.Doc( - ` - $ cx utils pr bitbucket --scan-id --token --namespace --repo-name - --pr-id --code-repository-url - `, - ), - Annotations: map[string]string{ - "command:doc": heredoc.Doc( - ` - `, - ), - }, - RunE: runPRDecorationBitbucket(prWrapper, policyWrapper, scansWrapper), - } - - prDecorationBitbucket.Flags().String(params.ScanIDFlag, "", "Scan ID to retrieve results from") - prDecorationBitbucket.Flags().String(params.SCMTokenFlag, "", params.BitbucketTokenUsage) - prDecorationBitbucket.Flags().String(params.NamespaceFlag, "", fmt.Sprintf(params.NamespaceFlagUsage, "Bitbucket")) - prDecorationBitbucket.Flags().String(params.RepoNameFlag, "", fmt.Sprintf(params.RepoNameFlagUsage, "Bitbucket")) - prDecorationBitbucket.Flags().Int(params.PRBBIDFlag, 0, params.PRBBIDFlagUsage) - prDecorationBitbucket.Flags().String(params.ProjectKeyFlag, "", params.ProjectKeyFlagUsage) - prDecorationBitbucket.Flags().String(params.CodeRepositoryFlag, "", params.CodeRepositoryFlagUsage) - - // Set the value for token to mask the scm token - _ = viper.BindPFlag(params.SCMTokenFlag, prDecorationBitbucket.Flags().Lookup(params.SCMTokenFlag)) - - // mark all fields as required\ - _ = prDecorationBitbucket.MarkFlagRequired(params.ScanIDFlag) - _ = prDecorationBitbucket.MarkFlagRequired(params.SCMTokenFlag) - _ = prDecorationBitbucket.MarkFlagRequired(params.RepoNameFlag) - _ = prDecorationBitbucket.MarkFlagRequired(params.PRBBIDFlag) - - return prDecorationBitbucket -} func updateAPIURLForGithubOnPrem(apiURL string) string { if apiURL != "" { @@ -514,7 +473,7 @@ func runPRDecorationAzure(prWrapper wrappers.PRWrapper, policyWrapper wrappers.P Policies: policies, APIURL: updatedAPIURL, } - prResponse, errorModel, err := prWrapper.PostAzurePRDecoration(prModel) + prResponse, errorModel, err := prWrapper.PostPRDecoration(prModel) if err != nil { return err } diff --git a/internal/wrappers/mock/pr-mock.go b/internal/wrappers/mock/pr-mock.go index 12cf1f9d4..cca405358 100644 --- a/internal/wrappers/mock/pr-mock.go +++ b/internal/wrappers/mock/pr-mock.go @@ -22,11 +22,10 @@ func (pr *PRMockWrapper) PostPRDecoration(model interface{}) ( return "Bitbucket Cloud PR comment created successfully.", nil, nil case *wrappers.BitbucketServerPRModel: return "Bitbucket Server PR comment created successfully.", nil, nil + case *wrappers.AzurePRModel: + return "PR comment created successfully.", nil, nil + default: return "", nil, errors.New("unsupported model type") } } - -func (pr *PRMockWrapper) PostAzurePRDecoration(model *wrappers.AzurePRModel) (string, *wrappers.WebError, error) { - return "PR comment created successfully.", nil, nil -} diff --git a/internal/wrappers/pr-http.go b/internal/wrappers/pr-http.go index 62f40999a..c74fe8be7 100644 --- a/internal/wrappers/pr-http.go +++ b/internal/wrappers/pr-http.go @@ -16,18 +16,18 @@ const ( ) type PRHTTPWrapper struct { - githubPath string - gitlabPath string - azurePath string + githubPath string + gitlabPath string + azurePath string bitbucketCloudPath string bitbucketServerPath string } func NewHTTPPRWrapper(githubPath, gitlabPath, bitbucketCloudPath, bitbucketServerPath, azurePath string) PRWrapper { return &PRHTTPWrapper{ - githubPath: githubPath, - gitlabPath: gitlabPath, - azurePath: azurePath, + githubPath: githubPath, + gitlabPath: gitlabPath, + azurePath: azurePath, bitbucketCloudPath: bitbucketCloudPath, bitbucketServerPath: bitbucketServerPath, } @@ -56,28 +56,6 @@ func (r *PRHTTPWrapper) PostPRDecoration(model interface{}) (string, *WebError, return handlePRResponseWithBody(resp, err) } -func (r *PRHTTPWrapper) PostAzurePRDecoration(model *AzurePRModel) ( - string, - *WebError, - error, -) { - clientTimeout := viper.GetUint(commonParams.ClientTimeoutKey) - jsonBytes, err := json.Marshal(model) - if err != nil { - return "", nil, err - } - resp, err := SendHTTPRequestWithJSONContentType(http.MethodPost, r.azurePath, bytes.NewBuffer(jsonBytes), true, clientTimeout) - if err != nil { - return "", nil, err - } - defer func() { - if err == nil { - _ = resp.Body.Close() - } - }() - return handlePRResponseWithBody(resp, err) -} - func (r *PRHTTPWrapper) getPRDecorationURL(model interface{}) (string, error) { switch model.(type) { case *PRModel: @@ -88,6 +66,8 @@ func (r *PRHTTPWrapper) getPRDecorationURL(model interface{}) (string, error) { return r.bitbucketCloudPath, nil case *BitbucketServerPRModel: return r.bitbucketServerPath, nil + case *AzurePRModel: + return r.azurePath, nil default: return "", errors.New("unsupported model type") } diff --git a/test/integration/pr_test.go b/test/integration/pr_test.go index f51bbaccf..b95c6125e 100644 --- a/test/integration/pr_test.go +++ b/test/integration/pr_test.go @@ -289,10 +289,10 @@ func TestPRAzureDecoration_WhenUseCodeRepositoryFlag_ShouldSuccess(t *testing.T) "https://azure.example.com", } - monkey.Patch((*wrappers.PRHTTPWrapper).PostAzurePRDecoration, func(*wrappers.PRHTTPWrapper, *wrappers.AzurePRModel) (string, *wrappers.WebError, error) { + monkey.Patch((*wrappers.PRHTTPWrapper).PostPRDecoration, func(*wrappers.PRHTTPWrapper, interface{}) (string, *wrappers.WebError, error) { return azurePRCommentCreated, nil, nil }) - defer monkey.Unpatch((*wrappers.PRHTTPWrapper).PostAzurePRDecoration) + defer monkey.Unpatch((*wrappers.PRHTTPWrapper).PostPRDecoration) file := createOutputFile(t, outputFileName) defer deleteOutputFile(t, file) From 3c9ef93a65ace17139f937e43218359222ee31bd Mon Sep 17 00:00:00 2001 From: galactica Date: Thu, 14 Nov 2024 17:20:23 +0200 Subject: [PATCH 12/13] fix lint --- internal/params/flags.go | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/internal/params/flags.go b/internal/params/flags.go index 7265e6c38..c1c00d6ab 100644 --- a/internal/params/flags.go +++ b/internal/params/flags.go @@ -162,24 +162,24 @@ const ( ScaFilterUsage = "SCA filter" // PR decoration flags - NamespaceFlag = "namespace" - NamespaceFlagUsage = "%s namespace is required to post the comments" - RepoNameFlag = "repo-name" - RepoNameFlagUsage = "%s repository details" - PRNumberFlag = "pr-number" - PRNumberFlagUsage = "Pull Request number for posting notifications and comments" - PRIidFlag = "mr-iid" - PRIidFlagUsage = "Gitlab IID (internal ID) of the merge request" - PRGitlabProjectFlag = "gitlab-project-id" - PRGitlabProjectFlagUsage = "Gitlab project ID" + NamespaceFlag = "namespace" + NamespaceFlagUsage = "%s namespace is required to post the comments" + RepoNameFlag = "repo-name" + RepoNameFlagUsage = "%s repository details" + PRNumberFlag = "pr-number" + PRNumberFlagUsage = "Pull Request number for posting notifications and comments" + PRIidFlag = "mr-iid" + PRIidFlagUsage = "Gitlab IID (internal ID) of the merge request" + PRGitlabProjectFlag = "gitlab-project-id" + PRGitlabProjectFlagUsage = "Gitlab project ID" AzureProjectFlag = "project" AzureProjectFlagUsage = "Azure project name or project ID" CodeRespositoryUsernameFlag = "code-repository-username" CodeRespositoryUsernameFlagUsage = "Azure username for code repository" - ProjectKeyFlag = "project-key" - ProjectKeyFlagUsage = "Key of the project containing the repository" - PRBBIDFlag = "pr-id" - PRBBIDFlagUsage = "Bitbucket PR ID" + ProjectKeyFlag = "project-key" + ProjectKeyFlagUsage = "Key of the project containing the repository" + PRBBIDFlag = "pr-id" + PRBBIDFlagUsage = "Bitbucket PR ID" // Chat (General) ChatAPIKey = "chat-apikey" From 343ab904efc8474f2be4ea6ca87eaf17ab7e64e0 Mon Sep 17 00:00:00 2001 From: galactica Date: Thu, 14 Nov 2024 17:49:46 +0200 Subject: [PATCH 13/13] fix lint --- internal/wrappers/mock/pr-mock.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/internal/wrappers/mock/pr-mock.go b/internal/wrappers/mock/pr-mock.go index cca405358..00289ae09 100644 --- a/internal/wrappers/mock/pr-mock.go +++ b/internal/wrappers/mock/pr-mock.go @@ -5,6 +5,8 @@ import ( "github.com/pkg/errors" ) +const prCommentSuccess = "PR comment created successfully." + type PRMockWrapper struct { } @@ -15,7 +17,7 @@ func (pr *PRMockWrapper) PostPRDecoration(model interface{}) ( ) { switch model.(type) { case *wrappers.PRModel: - return "PR comment created successfully.", nil, nil + return prCommentSuccess, nil, nil case *wrappers.GitlabPRModel: return "MR comment created successfully.", nil, nil case *wrappers.BitbucketCloudPRModel: @@ -23,7 +25,7 @@ func (pr *PRMockWrapper) PostPRDecoration(model interface{}) ( case *wrappers.BitbucketServerPRModel: return "Bitbucket Server PR comment created successfully.", nil, nil case *wrappers.AzurePRModel: - return "PR comment created successfully.", nil, nil + return prCommentSuccess, nil, nil default: return "", nil, errors.New("unsupported model type")