Skip to content

Commit

Permalink
Support Sonar PR type analysis & boolean flag to comment without requ…
Browse files Browse the repository at this point in the history
…esting changes (#5)

Fixes #4
  • Loading branch information
juldou authored Mar 23, 2022
1 parent e95ade2 commit 04a741c
Show file tree
Hide file tree
Showing 11 changed files with 81 additions and 14 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# :robot: Sonarqube PR Issues Review
[![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] [![Go Report Card][report-card-img]][report-card]

Simple Webhook for Sonarqube which publishes the issues found in the PR as review requesting changes.
Simple Webhook for Sonarqube which publishes the issues found in the PR.


![Review screenshot](assets/review_screenshot.png)
Expand Down
2 changes: 2 additions & 0 deletions cmd/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ var project string
var branch string
var publishReview bool
var markAsPublished bool
var requestChanges bool

func init() {
CliCmd.PersistentFlags().StringVar(&project, "project", "my-project", "Sonarqube project name")
CliCmd.PersistentFlags().StringVar(&branch, "branch", "my-branch", "SCM branch name")
CliCmd.PersistentFlags().BoolVar(&publishReview, "publish", false, "Publish review in the SCM")
CliCmd.PersistentFlags().BoolVar(&markAsPublished, "mark", false, "Mark the issue as published to avoid sending it again")
CliCmd.PersistentFlags().BoolVar(&requestChanges, "request-changes", true, "When issue is found, mark PR as changes requested")

CliCmd.AddCommand(RunCmd)
}
2 changes: 1 addition & 1 deletion cmd/cli/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func Run(cmd *cobra.Command, args []string) {
var gh scm2.SCM = scm2.NewGithub(ctx, sonar, ghToken)

// Publish review
err = gh.PublishIssuesReviewFor(ctx, issues.Issues, pr)
err = gh.PublishIssuesReviewFor(ctx, issues.Issues, pr, requestChanges)
if err != nil {
logrus.WithError(err).Panicln("Failed to publish issues review")

Expand Down
21 changes: 15 additions & 6 deletions cmd/server/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ func WebhookHandler(webhookSecret string, sonar *sonarqube2.Sonarqube, gh scm2.S
queue <- func() error {
logrus.Infoln("Processing", webhook.Project.Key, "->", webhook.Branch.Name)

if err := PublishIssues(context.Background(), sonar, gh, webhook.Project.Key, webhook.Branch.Name); err != nil {
if err := PublishIssues(context.Background(), sonar, gh, webhook.Project.Key, webhook.Branch.Name, webhook.Branch.Type); err != nil {
return err
}

Expand All @@ -153,11 +153,20 @@ func ProcessQueue(queue <-chan func() error) {
}

// PublishIssues publishes the issues in the PR for the given project branch
func PublishIssues(ctx context.Context, sonar *sonarqube2.Sonarqube, projectScm scm2.SCM, project string, branch string) error {
func PublishIssues(ctx context.Context, sonar *sonarqube2.Sonarqube, projectScm scm2.SCM, project string, branch string, branchType string) error {
// Find PR
pr, err := sonar.FindPRForBranch(project, branch)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("failed to find PR for branch %s of the project %s", branch, project))
var pr *sonarqube2.PullRequest
var err error
if branchType == sonarqube2.BRANCH_TYPE_PULL_REQUEST {
pr, err = sonar.FindPRForKey(project, branch)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("failed to find PR for key %s of the project %s", branch, project))
}
} else {
pr, err = sonar.FindPRForBranch(project, branch)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("failed to find PR for branch %s of the project %s", branch, project))
}
}

// List issues
Expand All @@ -175,7 +184,7 @@ func PublishIssues(ctx context.Context, sonar *sonarqube2.Sonarqube, projectScm
}

// Publish review
err = projectScm.PublishIssuesReviewFor(ctx, issues.Issues, pr)
err = projectScm.PublishIssuesReviewFor(ctx, issues.Issues, pr, requestChanges)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("Failed to publish issues review for branch %s of the project %s", branch, project))
}
Expand Down
2 changes: 2 additions & 0 deletions cmd/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

var serverPort int
var workers int
var requestChanges bool

var ServerCmd = &cobra.Command{
Use: "server",
Expand All @@ -15,5 +16,6 @@ var ServerCmd = &cobra.Command{
func init() {
ServerCmd.PersistentFlags().IntVarP(&serverPort, "port", "p", 8080, "Server port")
ServerCmd.PersistentFlags().IntVarP(&workers, "workers", "w", 30, "Workers count")
ServerCmd.PersistentFlags().BoolVar(&requestChanges, "request-changes", true, "When issue is found, mark PR as changes requested")
ServerCmd.AddCommand(RunCmd)
}
17 changes: 14 additions & 3 deletions pkg/scm/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ import (
"github.com/herlon214/sonarqube-pr-issues/pkg/sonarqube"
)

const (
REVIEW_EVENT_COMMENT = "COMMENT"
REVIEW_EVENT_REQUEST_CHANGES = "REQUEST_CHANGES"
)

type Github struct {
client *github.Client
sonar *sonarqube.Sonarqube
Expand Down Expand Up @@ -43,8 +48,14 @@ func NewGithub(ctx context.Context, sonar *sonarqube.Sonarqube, token string) *G
}

// PublishIssuesReviewFor publishes a review with a comment for each issue
func (g *Github) PublishIssuesReviewFor(ctx context.Context, issues []sonarqube.Issue, pr *sonarqube.PullRequest) error {
event := "REQUEST_CHANGES"
func (g *Github) PublishIssuesReviewFor(ctx context.Context, issues []sonarqube.Issue, pr *sonarqube.PullRequest, requestChanges bool) error {
var reviewEvent string
if requestChanges {
reviewEvent = REVIEW_EVENT_REQUEST_CHANGES
} else {
reviewEvent = REVIEW_EVENT_COMMENT
}

comments := make([]*github.DraftReviewComment, 0)

// Create a comment for each issue
Expand All @@ -67,7 +78,7 @@ func (g *Github) PublishIssuesReviewFor(ctx context.Context, issues []sonarqube.

reviewRequest := &github.PullRequestReviewRequest{
Body: &body,
Event: &event,
Event: &reviewEvent,
Comments: comments,
}

Expand Down
4 changes: 3 additions & 1 deletion pkg/scm/github_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ func TestGithubPublishIssuesReview(t *testing.T) {
},
}

err := gh.PublishIssuesReviewFor(ctx, issues, pr)
reviewEvent := REVIEW_EVENT_REQUEST_CHANGES

err := gh.PublishIssuesReviewFor(ctx, issues, pr, reviewEvent)
assert.NoError(t, err)

}
Expand Down
2 changes: 1 addition & 1 deletion pkg/scm/scm.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ import (
)

type SCM interface {
PublishIssuesReviewFor(ctx context.Context, issues []sonarqube.Issue, pr *sonarqube.PullRequest) error
PublishIssuesReviewFor(ctx context.Context, issues []sonarqube.Issue, pr *sonarqube.PullRequest, requestChanges bool) error
}
21 changes: 20 additions & 1 deletion pkg/sonarqube/sonarqube.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import (
)

const (
TAG_PUBLISHED = "published"
TAG_PUBLISHED = "published"
BRANCH_TYPE_PULL_REQUEST = "PULL_REQUEST"
)

type BulkActionResponse struct {
Expand Down Expand Up @@ -77,6 +78,24 @@ func (s *Sonarqube) ProjectPullRequests(projectId string) (*ProjectPullRequests,
return &data, nil
}

// FindPRForKey searches the pull request for the given project and key
func (s *Sonarqube) FindPRForKey(project string, key string) (*PullRequest, error) {
// Fetch project pull requests
pullRequests, err := s.ProjectPullRequests(project)
if err != nil {
return nil, err
}

// Filter by key
for _, item := range pullRequests.PullRequests {
if item.Key == key {
return &item, nil
}
}

return nil, errors.New("not found")
}

// FindPRForBranch searches the pull request for the given project and branch
func (s *Sonarqube) FindPRForBranch(project string, branch string) (*PullRequest, error) {
// Fetch project pull requests
Expand Down
21 changes: 21 additions & 0 deletions pkg/sonarqube/sonarqube_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,27 @@ func TestSonarqubeProjectPRs(t *testing.T) {
assert.Equal(t, "https://github.com/myorg/myproject/pull/2", prs.PullRequests[1].URL)
}

func TestSonarqubeFindPRForKey(t *testing.T) {
// Mock response
expected := `{"pullRequests":[{"key":"3","title":"Feat/newtest","branch":"feat/newtest","base":"feat/mvp","status":{"qualityGateStatus":"ERROR","bugs":2,"vulnerabilities":0,"codeSmells":0},"analysisDate":"2021-12-04T15:44:18+0000","url":"https://github.com/myorg/myproject/pull/3","target":"feat/mvp"},{"key":"2","title":"test PR","branch":"feat/test","base":"feat/mvp","status":{"qualityGateStatus":"ERROR","bugs":1,"vulnerabilities":0,"codeSmells":2},"analysisDate":"2021-12-03T18:10:59+0000","url":"https://github.com/myorg/myproject/pull/2","target":"feat/mvp"}]}`
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(expected))
}))
defer svr.Close()

// New sonar
sonar := New(svr.URL, "myapikey")

// Read PRs
pr, err := sonar.FindPRForKey("myproject", "2")
assert.NoError(t, err)

assert.NotNil(t, pr)
assert.Equal(t, "feat/test", pr.Branch)
assert.Equal(t, "2", pr.Key)
assert.Equal(t, "https://github.com/myorg/myproject/pull/2", pr.URL)
}

func TestSonarqubeFindPRForBranch(t *testing.T) {
// Mock response
expected := `{"pullRequests":[{"key":"3","title":"Feat/newtest","branch":"feat/newtest","base":"feat/mvp","status":{"qualityGateStatus":"ERROR","bugs":2,"vulnerabilities":0,"codeSmells":0},"analysisDate":"2021-12-04T15:44:18+0000","url":"https://github.com/myorg/myproject/pull/3","target":"feat/mvp"},{"key":"2","title":"test PR","branch":"feat/test","base":"feat/mvp","status":{"qualityGateStatus":"ERROR","bugs":1,"vulnerabilities":0,"codeSmells":2},"analysisDate":"2021-12-03T18:10:59+0000","url":"https://github.com/myorg/myproject/pull/2","target":"feat/mvp"}]}`
Expand Down
1 change: 1 addition & 0 deletions pkg/sonarqube/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ type WebhookData struct {
} `json:"project"`
Branch struct {
Name string `json:"name"`
Type string `json:"type"`
} `json:"branch"`
}

0 comments on commit 04a741c

Please sign in to comment.