Skip to content

Commit

Permalink
feat(webhooks): gitea webhooks implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
jkuri committed Jul 30, 2020
1 parent 7a00bd4 commit e1a3ee7
Show file tree
Hide file tree
Showing 11 changed files with 1,528 additions and 58 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ clean:
@rm -rf build/ web/abstruse/dist server/ui/ worker/data pb/api.pb.go

dev:
@reflex -sr '\.go$$' -R '^web/' -R '^server/ui' -R '^worker/' -R '^configs/' -- sh -c 'make server && ./build/abstruse-server --log-level debug'
@reflex -sr '\.go$$' -R '^web/' -R '^server/ui' -R '^worker/' -R '^configs/' -R '^tests/' -- sh -c 'make server && ./build/abstruse-server --log-level debug'

dev_worker:
@reflex -sr '\.go$$' -R '^web/' -R '^server/' -R '^configs/' -- sh -c 'make worker && ./build/abstruse-worker --log-level debug'
@reflex -sr '\.go$$' -R '^web/' -R '^server/' -R '^configs/' -R '^tests/' -- sh -c 'make worker && ./build/abstruse-worker --log-level debug'

protoc:
@protoc ./pb/api.proto --go_out=plugins=grpc:./pb/
Expand Down
2 changes: 1 addition & 1 deletion server/api/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ func (r *router) workersRouter() *chi.Mux {

func (r *router) webhooksRouter() *chi.Mux {
router := chi.NewRouter()
webhooks := newWebhooks(r.logger)
webhooks := newWebhooks(r.logger, r.app)

router.Post("/", webhooks.hook())

Expand Down
239 changes: 189 additions & 50 deletions server/api/webhook.go
Original file line number Diff line number Diff line change
@@ -1,74 +1,213 @@
package api

import (
"context"
"fmt"
"net/http"
"path"

"github.com/bleenco/abstruse/internal/common"
"github.com/bleenco/abstruse/pkg/render"
"github.com/bleenco/abstruse/pkg/scm"
"github.com/bleenco/abstruse/server/core"
"github.com/bleenco/abstruse/server/db/model"
"github.com/bleenco/abstruse/server/webhook"
goscm "github.com/drone/go-scm/scm"
"go.uber.org/zap"
)

type webhooks struct {
logger *zap.SugaredLogger
app *core.App
}

func newWebhooks(logger *zap.Logger) webhooks {
func newWebhooks(logger *zap.Logger, app *core.App) webhooks {
return webhooks{
logger: logger.With(zap.String("type", "webhooks")).Sugar(),
app: app,
}
}

func (h *webhooks) hook() http.HandlerFunc {
// dir := "/Users/jan/Desktop/webhooks"

// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// headers := r.Header.Clone()
// data, err := ioutil.ReadAll(r.Body)
// if err != nil {
// h.logger.Errorf("error: %v", err)
// }
// r.Body.Close()
// r.Body = ioutil.NopCloser(bytes.NewBuffer(data))

// var body interface{}
// if err := json.Unmarshal(data, &body); err != nil {
// h.logger.Errorf("error: %v", err)
// }

// file, err := json.MarshalIndent(body, "", " ")
// if err != nil {
// h.logger.Errorf("error: %v", err)
// }

// now := time.Now().Format(time.RFC3339)

// if err := fs.MakeDir(filepath.Join(dir, now)); err != nil {
// h.logger.Errorf("error: %v", err)
// }

// filePath := filepath.Join(dir, now, "webhook.json")
// headersPath := filepath.Join(dir, now, "headers.txt")

// err = ioutil.WriteFile(filePath, file, 0644)
// if err != nil {
// h.logger.Errorf("error: %v", err)
// }

// var header string
// for h, v := range headers {
// for _, v := range v {
// header += fmt.Sprintf("%s %s\n", h, v)
// }
// }
// err = ioutil.WriteFile(headersPath, []byte(header), 0644)
// if err != nil {
// h.logger.Errorf("error: %v", err)
// }

// render.JSON(w, http.StatusOK, render.Empty{})
// })

return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
repo, err := webhook.ParseWebhook(r)
if err != nil {
render.JSON(w, http.StatusBadRequest, render.Error{Message: err.Error()})
return
}

cli, err := scm.NewSCM(context.Background(), repo.Provider.Name, repo.URL, repo.Provider.AccessToken)
if err != nil {
render.JSON(w, http.StatusNotImplemented, render.Error{Message: err.Error()})
return
}

client := cli.Client()
fn := func(webhook goscm.Webhook) (string, error) {
return "", nil
}
webhook, err := client.Webhooks.Parse(r, fn)

switch event := webhook.(type) {
case *goscm.PushHook:
if err := h.app.StartBuild(generateBuild(event)); err != nil {
render.JSON(w, http.StatusInternalServerError, render.Error{Message: err.Error()})
return
}
case *goscm.PullRequestHook:
if event.PullRequest.Closed {
render.JSON(w, http.StatusOK, render.Empty{})
return
}
if err := h.app.StartBuild(generatePR(event, repo)); err != nil {
render.JSON(w, http.StatusInternalServerError, render.Error{Message: err.Error()})
return
}
case *goscm.TagHook:
if err := h.app.StartBuild(generateTagBuild(event)); err != nil {
render.JSON(w, http.StatusNotImplemented, render.Error{Message: err.Error()})
return
}
case *goscm.BranchHook:
if err := h.app.StartBuild(generateBranchBuild(event)); err != nil {
render.JSON(w, http.StatusNotImplemented, render.Error{Message: err.Error()})
return
}
}

render.JSON(w, http.StatusOK, render.Empty{})
})
}

func generateBuild(event *goscm.PushHook) common.Build {
return common.Build{
Branch: event.Repo.Branch,
Ref: event.Ref,
CommitSHA: event.Commit.Sha,
CommitMessage: event.Commit.Message,
RepoURL: event.Repo.Clone,
RepoName: fmt.Sprintf("%s/%s", event.Repo.Namespace, event.Repo.Name),
AuthorName: event.Commit.Author.Name,
AuthorEmail: event.Commit.Author.Email,
AuthorAvatar: event.Commit.Author.Avatar,
AuthorLogin: event.Commit.Author.Login,
SenderName: event.Sender.Name,
SenderEmail: event.Sender.Email,
SenderAvatar: event.Sender.Avatar,
SenderLogin: event.Sender.Login,
}
}

func generatePR(event *goscm.PullRequestHook, repo model.Repository) common.Build {
ref := event.PullRequest.Ref
if repo.Provider.Name == "gitlab" {
ref = fmt.Sprintf("refs/merge-requests/%d/head", event.PullRequest.Number)
} else if repo.Provider.Name == "github" || repo.Provider.Name == "gitea" {
ref = fmt.Sprintf("refs/pull/%d/head", event.PullRequest.Number)
}

return common.Build{
Branch: event.Repo.Branch,
PrNumber: event.PullRequest.Number,
Ref: ref,
CommitSHA: event.PullRequest.Sha,
PrTitle: event.PullRequest.Title,
PrBody: event.PullRequest.Body,
RepoURL: event.Repo.Clone,
RepoName: fmt.Sprintf("%s/%s", event.Repo.Namespace, event.Repo.Name),
AuthorName: event.PullRequest.Author.Name,
AuthorEmail: event.PullRequest.Author.Email,
AuthorAvatar: event.PullRequest.Author.Avatar,
AuthorLogin: event.PullRequest.Author.Login,
SenderName: event.Sender.Name,
SenderEmail: event.Sender.Email,
SenderAvatar: event.Sender.Avatar,
SenderLogin: event.Sender.Login,
}
}

func generateTagBuild(event *goscm.TagHook) common.Build {
return common.Build{
Branch: event.Repo.Branch,
Ref: event.Ref.Path,
CommitSHA: event.Ref.Sha,
CommitMessage: path.Base(event.Ref.Path),
RepoURL: event.Repo.Clone,
RepoName: fmt.Sprintf("%s/%s", event.Repo.Namespace, event.Repo.Name),
AuthorName: event.Sender.Name,
AuthorEmail: event.Sender.Email,
AuthorAvatar: event.Sender.Avatar,
AuthorLogin: event.Sender.Login,
SenderName: event.Sender.Name,
SenderEmail: event.Sender.Email,
SenderAvatar: event.Sender.Avatar,
SenderLogin: event.Sender.Login,
}
}

func generateBranchBuild(event *goscm.BranchHook) common.Build {
return common.Build{
Branch: event.Ref.Name,
Ref: fmt.Sprintf("refs/heads/%s", event.Ref.Name),
CommitSHA: event.Ref.Sha,
RepoURL: event.Repo.Clone,
RepoName: fmt.Sprintf("%s/%s", event.Repo.Namespace, event.Repo.Name),
AuthorName: event.Sender.Name,
AuthorEmail: event.Sender.Email,
AuthorAvatar: event.Sender.Avatar,
AuthorLogin: event.Sender.Login,
SenderName: event.Sender.Name,
SenderEmail: event.Sender.Email,
SenderAvatar: event.Sender.Avatar,
SenderLogin: event.Sender.Login,
}
}

// dir := "/Users/jan/Desktop/webhooks"

// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// headers := r.Header.Clone()
// data, err := ioutil.ReadAll(r.Body)
// if err != nil {
// h.logger.Errorf("error: %v", err)
// }
// r.Body.Close()
// r.Body = ioutil.NopCloser(bytes.NewBuffer(data))

// var body interface{}
// if err := json.Unmarshal(data, &body); err != nil {
// h.logger.Errorf("error: %v", err)
// }

// file, err := json.MarshalIndent(body, "", " ")
// if err != nil {
// h.logger.Errorf("error: %v", err)
// }

// now := time.Now().Format(time.RFC3339)

// if err := fs.MakeDir(filepath.Join(dir, now)); err != nil {
// h.logger.Errorf("error: %v", err)
// }

// filePath := filepath.Join(dir, now, "webhook.json")
// headersPath := filepath.Join(dir, now, "headers.txt")

// err = ioutil.WriteFile(filePath, file, 0644)
// if err != nil {
// h.logger.Errorf("error: %v", err)
// }

// var header string
// for h, v := range headers {
// for _, v := range v {
// header += fmt.Sprintf("%s %s\n", h, v)
// }
// }
// err = ioutil.WriteFile(headersPath, []byte(header), 0644)
// if err != nil {
// h.logger.Errorf("error: %v", err)
// }

// render.JSON(w, http.StatusOK, render.Empty{})
// })
10 changes: 7 additions & 3 deletions server/core/builds.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
// StartBuild temp func.
func (app *App) StartBuild(b common.Build) error {
app.logBuild(b)
repo, err := app.repo.Repo.FindByURL(b.RepoURL)
repo, err := app.repo.Repo.FindByClone(b.RepoURL)
if err != nil {
return err
}
Expand All @@ -28,7 +28,11 @@ func (app *App) StartBuild(b common.Build) error {
if err != nil {
return err
}
if b.CommitSHA == "" || b.CommitMessage == "" {
reference := ref.Path
if b.PrNumber != 0 {
reference = b.Ref
}
if b.CommitSHA == "" || (b.CommitMessage == "" && b.PrNumber == 0) {
commit, err := scm.LastCommit(repo.FullName, b.Branch)
if err != nil {
return err
Expand All @@ -55,7 +59,7 @@ func (app *App) StartBuild(b common.Build) error {

buildModel := model.Build{
Branch: b.Branch,
Ref: ref.Path,
Ref: reference,
Commit: b.CommitSHA,
CommitMessage: b.CommitMessage,
PR: b.PrNumber,
Expand Down
11 changes: 11 additions & 0 deletions server/db/repository/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,17 @@ func (r RepoRepository) FindByURL(url string) (model.Repository, error) {
return repo, err
}

// FindByClone returns repo by clone url.
func (r RepoRepository) FindByClone(clone string) (model.Repository, error) {
var repo model.Repository
db, err := db.Instance()
if err != nil {
return repo, err
}
err = db.Model(&repo).Where("clone = ?", clone).Preload("Provider").First(&repo).Error
return repo, err
}

// CreateOrUpdate inserts new repository into db or updates it if already exists.
func (r RepoRepository) CreateOrUpdate(data model.Repository) (model.Repository, error) {
db, err := db.Instance()
Expand Down
Loading

0 comments on commit e1a3ee7

Please sign in to comment.