From 895dfe92e0d440c6f8aa3675c88e17282a5df333 Mon Sep 17 00:00:00 2001 From: justusbunsi <61625851+justusbunsi@users.noreply.github.com> Date: Sun, 10 Oct 2021 17:59:28 +0200 Subject: [PATCH] Add pending status on PR synchronize event Signed-off-by: Steven Kriegler <61625851+justusbunsi@users.noreply.github.com> --- internal/api/gitea.go | 19 +++- internal/clients/gitea/status.go | 1 + .../webhooks/gitea/{webhook.go => comment.go} | 12 +-- internal/webhooks/gitea/pull.go | 87 +++++++++++++++++++ 4 files changed, 111 insertions(+), 8 deletions(-) rename internal/webhooks/gitea/{webhook.go => comment.go} (84%) create mode 100644 internal/webhooks/gitea/pull.go diff --git a/internal/api/gitea.go b/internal/api/gitea.go index 1b7d1ab..c96ffbc 100644 --- a/internal/api/gitea.go +++ b/internal/api/gitea.go @@ -37,13 +37,28 @@ func (h *GiteaWebhookHandler) parseBody(rw http.ResponseWriter, r *http.Request) func (h *GiteaWebhookHandler) HandleSynchronize(rw http.ResponseWriter, r *http.Request) { rw.Header().Set("Content-Type", "application/json") - _, err := h.parseBody(rw, r) + raw, err := h.parseBody(rw, r) if err != nil { return } + w, ok := webhook.NewPullWebhook(raw) + if !ok { + rw.WriteHeader(http.StatusUnprocessableEntity) + io.WriteString(rw, `{"message": "Error parsing POST body."}`) + return + } + + if err := w.Validate(); err != nil { + rw.WriteHeader(http.StatusOK) + io.WriteString(rw, fmt.Sprintf(`{"message": "%s"}`, err.Error())) + return + } + rw.WriteHeader(http.StatusOK) io.WriteString(rw, `{"message": "Processing data. See bot logs for details."}`) + + w.ProcessData(h.giteaSdk, h.sqSdk) } func (h *GiteaWebhookHandler) HandleComment(rw http.ResponseWriter, r *http.Request) { @@ -54,7 +69,7 @@ func (h *GiteaWebhookHandler) HandleComment(rw http.ResponseWriter, r *http.Requ return } - w, ok := webhook.New(raw) + w, ok := webhook.NewCommentWebhook(raw) if !ok { rw.WriteHeader(http.StatusUnprocessableEntity) io.WriteString(rw, `{"message": "Error parsing POST body."}`) diff --git a/internal/clients/gitea/status.go b/internal/clients/gitea/status.go index 68dff31..5c5b59f 100644 --- a/internal/clients/gitea/status.go +++ b/internal/clients/gitea/status.go @@ -6,6 +6,7 @@ type State gitea.StatusState const ( StatusOK State = State(gitea.StatusSuccess) + StatusPending State = State(gitea.StatusPending) StatusFailure State = State(gitea.StatusFailure) ) diff --git a/internal/webhooks/gitea/webhook.go b/internal/webhooks/gitea/comment.go similarity index 84% rename from internal/webhooks/gitea/webhook.go rename to internal/webhooks/gitea/comment.go index e920242..c7a5436 100644 --- a/internal/webhooks/gitea/webhook.go +++ b/internal/webhooks/gitea/comment.go @@ -21,7 +21,7 @@ type comment struct { Body string `json:"body"` } -type Webhook struct { +type CommentWebhook struct { Action string `json:"action"` IsPR bool `json:"is_pull"` Issue issue `json:"issue"` @@ -29,7 +29,7 @@ type Webhook struct { ConfiguredProject settings.Project } -func (w *Webhook) inProjectsMapping(p []settings.Project) (bool, int) { +func (w *CommentWebhook) inProjectsMapping(p []settings.Project) (bool, int) { owner := w.Issue.Repository.Owner name := w.Issue.Repository.Name for idx, proj := range p { @@ -41,7 +41,7 @@ func (w *Webhook) inProjectsMapping(p []settings.Project) (bool, int) { return false, 0 } -func (w *Webhook) Validate() error { +func (w *CommentWebhook) Validate() error { if !w.IsPR { return fmt.Errorf("ignore non-PR hook") } @@ -64,7 +64,7 @@ func (w *Webhook) Validate() error { return nil } -func (w *Webhook) ProcessData(gSDK giteaSdk.GiteaSdkInterface, sqSDK sqSdk.SonarQubeSdkInterface) { +func (w *CommentWebhook) ProcessData(gSDK giteaSdk.GiteaSdkInterface, sqSDK sqSdk.SonarQubeSdkInterface) { headRef, err := gSDK.DetermineHEAD(w.ConfiguredProject.Gitea, w.Issue.Number) if err != nil { log.Printf("Error retrieving HEAD ref: %s", err.Error()) @@ -79,8 +79,8 @@ func (w *Webhook) ProcessData(gSDK giteaSdk.GiteaSdkInterface, sqSDK sqSdk.Sonar }) } -func New(raw []byte) (*Webhook, bool) { - w := &Webhook{} +func NewCommentWebhook(raw []byte) (*CommentWebhook, bool) { + w := &CommentWebhook{} err := json.Unmarshal(raw, &w) if err != nil { log.Printf("Error parsing Gitea webhook: %s", err.Error()) diff --git a/internal/webhooks/gitea/pull.go b/internal/webhooks/gitea/pull.go new file mode 100644 index 0000000..8d7ff6a --- /dev/null +++ b/internal/webhooks/gitea/pull.go @@ -0,0 +1,87 @@ +package gitea + +import ( + "encoding/json" + "fmt" + "log" + + giteaSdk "gitea-sonarqube-pr-bot/internal/clients/gitea" + sqSdk "gitea-sonarqube-pr-bot/internal/clients/sonarqube" + "gitea-sonarqube-pr-bot/internal/settings" +) + +type pullRequest struct { + Number int64 `json:"number"` + Head struct { + Sha string `json:"sha"` + } `json:"head"` +} + +type repoOwner struct { + Login string `json:"login"` +} + +type rawRepository struct { + Name string `json:"name"` + Owner repoOwner `json:"owner"` +} + +type PullWebhook struct { + Action string `json:"action"` + PullRequest pullRequest `json:"pull_request"` + RawRepository rawRepository `json:"repository"` + Repository settings.GiteaRepository + ConfiguredProject settings.Project +} + +func (w *PullWebhook) inProjectsMapping(p []settings.Project) (bool, int) { + owner := w.RawRepository.Owner.Login + name := w.RawRepository.Name + for idx, proj := range p { + if proj.Gitea.Owner == owner && proj.Gitea.Name == name { + return true, idx + } + } + + return false, 0 +} + +func (w *PullWebhook) Validate() error { + found, pIdx := w.inProjectsMapping(settings.Projects) + owner := w.RawRepository.Owner.Login + name := w.RawRepository.Name + if !found { + return fmt.Errorf("ignore hook for non-configured project '%s/%s'", owner, name) + } + + if w.Action != "synchronized" { + return fmt.Errorf("ignore hook for action others than synchronized") + } + + w.Repository = settings.GiteaRepository{ + Owner: owner, + Name: name, + } + w.ConfiguredProject = settings.Projects[pIdx] + + return nil +} + +func (w *PullWebhook) ProcessData(gSDK giteaSdk.GiteaSdkInterface, sqSDK sqSdk.SonarQubeSdkInterface) { + _ = gSDK.UpdateStatus(w.ConfiguredProject.Gitea, w.PullRequest.Head.Sha, giteaSdk.StatusDetails{ + Url: "", + Message: "Analysis pending...", + State: giteaSdk.StatusPending, + }) +} + +func NewPullWebhook(raw []byte) (*PullWebhook, bool) { + w := &PullWebhook{} + err := json.Unmarshal(raw, &w) + if err != nil { + log.Printf("Error parsing Gitea webhook: %s", err.Error()) + return w, false + } + + return w, true +}