Skip to content

Commit

Permalink
added cobra cli
Browse files Browse the repository at this point in the history
  • Loading branch information
herlon214 committed Dec 4, 2021
1 parent bd392bc commit 884baa5
Show file tree
Hide file tree
Showing 11 changed files with 860 additions and 37 deletions.
18 changes: 12 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,23 @@ First of all you need to setup the environment variables:
SONAR_API_KEY=SONAR_API_TOKEN
GH_TOKEN=GITHUB_API_TOKEN
SONAR_ROOT_URL=https://sonar-url-without-trailing-slash
WEBHOOK_SECRET=my-hook-secret
PORT=8080
WEBHOOK_SECRET=my-hook-secret # Not necessary if CLI
PORT=8080 # Not necessary if CLI
```
There are currently two ways to use this project:

### Webhook
To use the webhook you need to start the server by running:
```shell
$ sqpr
```
There are currently two options to use this project:

### CLI
This option is mostly to test, ideally you should use the webhook.

To use in the command line you can see the available flags by running `sqpr --help` (or `go run cmd/cli/main.go`):
```
$ sqpr --help
$ sqpr -server --help
Usage of sqpr:
-branch string
Expand Down Expand Up @@ -77,8 +85,6 @@ INFO[0000] 0 issues failed
INFO[0000] --------------------------
```

### Webhook

[doc-img]: http://img.shields.io/badge/GoDoc-Reference-blue.svg
[doc]: https://godoc.org/go.uber.org/fx

Expand Down
24 changes: 24 additions & 0 deletions cmd/cli/cli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package cli

import (
"github.com/spf13/cobra"
)

var CliCmd = &cobra.Command{
Use: "cli",
Short: "Executes CLI commands",
}

var project string
var branch string
var publishReview bool
var markAsPublished 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.AddCommand(RunCmd)
}
45 changes: 25 additions & 20 deletions cmd/cli/main.go → cmd/cli/run.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
package main
package cli

import (
"context"
"flag"
"fmt"
"github.com/herlon214/sonarqube-pr-issues/scm"
"github.com/herlon214/sonarqube-pr-issues/sonarqube"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"os"
"time"
)

"github.com/sirupsen/logrus"
var RunCmd = &cobra.Command{
Use: "run",
Short: "Process the given project and branch",
Run: Run,
TraverseChildren: true,
}

"github.com/herlon214/sonarqube-pr-issues/sonarqube"
)
func Run(cmd *cobra.Command, args []string) {
logrus.Infoln("Processing", project, "->", branch)

func main() {
// Environment
ghToken := os.Getenv("GH_TOKEN")
apiKey := os.Getenv("SONAR_API_KEY")
Expand All @@ -29,14 +36,7 @@ func main() {
return
}

// Flags
project := flag.String("project", "my-project", "Sonarqube project name")
branch := flag.String("branch", "my-branch", "SCM Branch name")
publishReview := flag.Bool("publish", false, "Publish review")
markAsPublished := flag.Bool("markaspublished", true, "Mark the issue as published to avoid sending it again")
flag.Parse()

if project == nil || *project == "" || branch == nil || *branch == "" {
if project == "" || branch == "" {
logrus.Panicln("Project / branch can't be empty")

return
Expand All @@ -50,29 +50,34 @@ func main() {
sonar := sonarqube.New(sonarRootURL, apiKey)

// Find PR
pr, err := sonar.FindPRForBranch(*project, *branch)
pr, err := sonar.FindPRForBranch(project, branch)
if err != nil {
logrus.WithError(err).Panicln("Failed to find PR for the given branch:", *branch)
logrus.WithError(err).Panicln("Failed to find PR for the given branch:", branch)

return
}

// List issues
issues, err := sonar.ListIssuesForPR(*project, pr.Key)
issues, err := sonar.ListIssuesForPR(project, pr.Key)
if err != nil {
logrus.WithError(err).Panicln("Failed to find PR for the given PR:", pr.Key)
logrus.WithError(err).Panicln("Failed to list issues for the given PR:", pr.Key)

return
}

// Filter issues
issues = issues.FilterByStatus("OPEN").FilterOutByTag(sonarqube.TAG_PUBLISHED)
if len(issues.Issues) == 0 {
logrus.Infoln("No issues found!")

return
}

// Print issues
printIssues(sonar, issues.Issues)

// Check if should publish the review
if *publishReview {
if publishReview {
// Check if token is set
if ghToken == "" {
logrus.Panicln("GH_TOKEN environment variable is missing")
Expand All @@ -94,7 +99,7 @@ func main() {
logrus.Infoln("Issues review published!")

// Check if should update the issues
if *markAsPublished {
if markAsPublished {
bulkActionRes, err := sonar.TagIssues(issues.Issues, sonarqube.TAG_PUBLISHED)
if err != nil {
logrus.WithError(err).Panicln("Failed to mark issues as published")
Expand Down
30 changes: 30 additions & 0 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package cmd

import (
"github.com/herlon214/sonarqube-pr-issues/cmd/cli"
"github.com/herlon214/sonarqube-pr-issues/cmd/server"
"github.com/spf13/cobra"
"os"

"github.com/sirupsen/logrus"
)

var rootCmd = &cobra.Command{
Use: "sqpr",
Short: "SQPR publishes Sonarqube the issues into your PRs",
}

// Flags
var mode string

func init() {
rootCmd.AddCommand(server.ServerCmd)
rootCmd.AddCommand(cli.CliCmd)
}

func Execute() {
if err := rootCmd.Execute(); err != nil {
logrus.WithError(err)
os.Exit(1)
}
}
177 changes: 177 additions & 0 deletions cmd/server/run.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
package server

import (
"context"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"os"

"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"

"github.com/herlon214/sonarqube-pr-issues/scm"
"github.com/herlon214/sonarqube-pr-issues/sonarqube"
)

var RunCmd = &cobra.Command{
Use: "run",
Short: "Starts the webhook server",
Run: Run,
}

func Run(cmd *cobra.Command, args []string) {
// Context
ctx := context.Background()

// Environment
port := os.Getenv("PORT")
if port == "" {
logrus.Panicln("PORT is required")

return
}
apiKey := os.Getenv("SONAR_API_KEY")
if apiKey == "" {
logrus.Panicln("SONAR_API_KEY environment variable is missing")

return
}
sonarRootURL := os.Getenv("SONAR_ROOT_URL")
if sonarRootURL == "" {
logrus.Panicln("SONAR_ROOT_URL environment variable is missing")

return
}
ghToken := os.Getenv("GH_TOKEN")
if ghToken == "" {
logrus.Panicln("GH_TOKEN environment variable is missing")

return
}
webhookSecret := os.Getenv("WEBHOOK_SECRET")
if webhookSecret == "" {
logrus.Panicln("WEBHOOK_SECRET environment variable is missing")

return
}

// Sonarqube
sonar := sonarqube.New(sonarRootURL, apiKey)
var gh scm.SCM = scm.NewGithub(ctx, sonar, ghToken)

// Listen
http.HandleFunc("/webhook", WebhookHandler(webhookSecret, sonar, gh))

logrus.Infoln("Listening on port", port)
if err := http.ListenAndServe(fmt.Sprintf(":%s", port), nil); err != nil {
panic(err)
}
}

func WebhookHandler(webhookSecret string, sonar *sonarqube.Sonarqube, gh scm.SCM) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) {
// Read webhook secret
reqSecret := req.Header.Get("X-Sonar-Webhook-HMAC-SHA256")
if reqSecret == "" {
w.WriteHeader(http.StatusUnauthorized)

return
}

// Read request body
body, err := io.ReadAll(req.Body)
if err != nil {
w.WriteHeader(http.StatusBadRequest)

return
}

// Generate hmac hash
h := hmac.New(sha256.New, []byte(webhookSecret))

// Write Data to it
h.Write(body)

// Get result and encode as hexadecimal string
sha := hex.EncodeToString(h.Sum(nil))

// Compare hashes
if sha != reqSecret {
w.WriteHeader(http.StatusUnauthorized)

return
}

// Unmarshal data
var webhook sonarqube.WebhookData
err = json.Unmarshal(body, &webhook)
if err != nil {
w.WriteHeader(http.StatusBadRequest)

return
}

logrus.Infoln("Processing", webhook.Project.Key, "->", webhook.Branch.Name)

// Process the event
err = PublishIssues(req.Context(), sonar, gh, webhook.Project.Key, webhook.Branch.Name)
if err != nil {
logrus.WithError(err).Errorln("Failed to publish issues for", webhook.Project.Key, webhook.Branch.Name)

return
}

logrus.Infoln("Issues published for", webhook.Project.Key, webhook.Branch.Name)
}
}

// PublishIssues publishes the issues in the PR for the given project branch
func PublishIssues(ctx context.Context, sonar *sonarqube.Sonarqube, projectScm scm.SCM, project string, branch string) error {
// Find PR
pr, err := sonar.FindPRForBranch(project, branch)
if err != nil {
return errors.Wrap(err, "failed to find PR for the given branch")
}

// List issues
issues, err := sonar.ListIssuesForPR(project, pr.Key)
if err != nil {
return errors.Wrap(err, "failed to list issues for the given PR")
}

// Filter issues
issues = issues.FilterByStatus("OPEN").FilterOutByTag(sonarqube.TAG_PUBLISHED)

// No issues found
if len(issues.Issues) == 0 {
return nil
}

// Publish review
err = projectScm.PublishIssuesReviewFor(ctx, issues.Issues, pr)
if err != nil {
return errors.Wrap(err, "Failed to publish issues review")
}

// Tag published issues
bulkActionRes, err := sonar.TagIssues(issues.Issues, sonarqube.TAG_PUBLISHED)
if err != nil {
return errors.Wrap(err, "failed to mark issues as published")
}

logrus.Infoln("--------------------------")
logrus.Infoln("Mark as published result:")
logrus.Infoln(bulkActionRes.Success, "issues marked")
logrus.Infoln(bulkActionRes.Ignored, "issues ignored")
logrus.Infoln(bulkActionRes.Failures, "issues failed")
logrus.Infoln("--------------------------")

return nil

}
14 changes: 14 additions & 0 deletions cmd/server/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package server

import (
"github.com/spf13/cobra"
)

var ServerCmd = &cobra.Command{
Use: "server",
Short: "Executes the server commands",
}

func init() {
ServerCmd.AddCommand(RunCmd)
}
Loading

0 comments on commit 884baa5

Please sign in to comment.