From e1b647f660ffe3ef5d4a4b0f50026ea7d8e7f3d7 Mon Sep 17 00:00:00 2001 From: the-johnwick Date: Thu, 3 Oct 2024 07:45:10 +0000 Subject: [PATCH] feat: webhook events support for bitbucket server prebuild Signed-off-by: the-johnwick --- cmd/daytona/config/const.go | 4 + pkg/gitprovider/bitbucketserver.go | 133 +++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+) diff --git a/cmd/daytona/config/const.go b/cmd/daytona/config/const.go index ce64d5cfde..f9e388e72d 100644 --- a/cmd/daytona/config/const.go +++ b/cmd/daytona/config/const.go @@ -126,6 +126,8 @@ func GetPrebuildScopesFromGitProviderId(providerId string) string { return "admin:repo_hook" case "bitbucket": return "webhooks" + case "bitbucket-server": + return "webhooks" case "azure-devops": return "Work (Read, Write & Manage); Build (Read & Execute)" default: @@ -143,6 +145,8 @@ func GetWebhookEventHeaderKeyFromGitProvider(providerId string) string { return "X-Gitlab-Event" case "bitbucket": return "X-Event-Key" + case "bitbucket-server": + return "X-Event-Key" case "gitea": return "X-Gitea-Event" case "azure-devops": diff --git a/pkg/gitprovider/bitbucketserver.go b/pkg/gitprovider/bitbucketserver.go index 2ed83f5c24..e944ace927 100644 --- a/pkg/gitprovider/bitbucketserver.go +++ b/pkg/gitprovider/bitbucketserver.go @@ -14,7 +14,9 @@ import ( "net/http" "net/url" + "github.com/daytonaio/daytona/internal/util" bitbucketv1 "github.com/gfleury/go-bitbucket-v1" + bitbucketServerWebhook "github.com/go-playground/webhooks/v6/bitbucket-server" "github.com/mitchellh/mapstructure" ) @@ -524,3 +526,134 @@ func (b *BitbucketServerGitProvider) FormatError(statusCode int, message string) return fmt.Errorf("status code: %d err: Request failed with %s", statusCode, message) } + +func (b *BitbucketServerGitProvider) GetPrebuildWebhook(repo *GitRepository, endpointUrl string) (*string, error) { + client, err := b.getApiClient() + if err != nil { + return nil, err + } + + hooks, err := client.DefaultApi.FindWebhooks(repo.Owner, repo.Id, nil) + if err != nil { + return nil, b.FormatError(hooks.StatusCode, hooks.Message) + } + + if len(hooks.Values) == 0 { + return nil, nil + } + + hookList, err := bitbucketv1.GetWebhooksResponse(hooks) + if err != nil { + return nil, err + } + + for _, hook := range hookList { + if hook.Url == endpointUrl { + return util.Pointer(strconv.Itoa(int(hook.ID))), nil + } + } + + return nil, nil +} + +func (b *BitbucketServerGitProvider) UnregisterPrebuildWebhook(repo *GitRepository, id string) error { + client, err := b.getApiClient() + if err != nil { + return err + } + + idInt, err := strconv.Atoi(id) + if err != nil { + return fmt.Errorf("invalid webhook ID: %v", err) + } + + response, err := client.DefaultApi.DeleteWebhook(repo.Owner, repo.Id, int32(idInt)) + if err != nil { + return b.FormatError(response.StatusCode, response.Message) + } + + return nil +} + +func (b *BitbucketServerGitProvider) RegisterPrebuildWebhook(repo *GitRepository, endpointUrl string) (string, error) { + client, err := b.getApiClient() + if err != nil { + return "", err + } + + hook, err := client.DefaultApi.CreateWebhook(repo.Owner, repo.Id, &bitbucketv1.Webhook{ + Active: true, + Events: []string{"repo:push"}, + Url: endpointUrl, + }, []string{"repo:push"}) + if err != nil { + return "", b.FormatError(hook.StatusCode, hook.Message) + } + + webhook, err := bitbucketv1.GetWebhooksResponse(hook) + if err != nil { + return "", err + } + + return strconv.Itoa(webhook[0].ID), nil + +} + +func (b *BitbucketServerGitProvider) GetCommitsRange(repo *GitRepository, initialSha string, currentSha string) (int, error) { + client, err := b.getApiClient() + if err != nil { + return 0, err + } + + commits, err := client.DefaultApi.GetCommits(repo.Owner, repo.Id, map[string]interface{}{ + "since": initialSha, + "until": currentSha, + }) + if err != nil { + return 0, b.FormatError(commits.StatusCode, commits.Message) + } + + commitsResponse, err := bitbucketv1.GetCommitsResponse(commits) + if err != nil { + return 0, err + } + + return len(commitsResponse), nil +} + +func (b *BitbucketServerGitProvider) ParseEventData(request *http.Request) (*GitEventData, error) { + if request.Header.Get("X-Event-Key") != "repo:refs_changed" { + return nil, errors.New("invalid event key") + } + hook, err := bitbucketServerWebhook.New() + if err != nil { + return nil, err + } + + event, err := hook.Parse(request, bitbucketServerWebhook.RepositoryReferenceChangedEvent) + if err != nil { + return nil, err + } + + pushEvent, ok := event.(*bitbucketServerWebhook.RepositoryReferenceChangedPayload) + if !ok { + return nil, errors.New("invalid event payload") + } + + if len(pushEvent.Changes) == 0 { + return nil, errors.New("no changes in the push event") + } + + gitEventData := &GitEventData{ + Url: fmt.Sprintf("%s/scm/%s/%s.git", b.baseApiUrl, pushEvent.Repository.Project.Key, pushEvent.Repository.Slug), + Owner: pushEvent.Repository.Project.Key, + Branch: pushEvent.Changes[0].Reference.DisplayID, + Sha: pushEvent.Changes[0].ToHash, + } + + for _, change := range pushEvent.Changes { + gitEventData.AffectedFiles = append(gitEventData.AffectedFiles, change.ToHash) + } + + return gitEventData, nil +}