From 5dfcfc542e8de81891aa433766f29a45da18db19 Mon Sep 17 00:00:00 2001 From: Gabriel Adrian Samfira Date: Fri, 16 May 2025 20:18:30 +0000 Subject: [PATCH] Implement webhooks install for gitea Signed-off-by: Gabriel Adrian Samfira --- database/watcher/filters.go | 2 +- util/github/client.go | 13 ++++- util/github/gitea.go | 100 ++++++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 util/github/gitea.go diff --git a/database/watcher/filters.go b/database/watcher/filters.go index 421fd6bf..1f747372 100644 --- a/database/watcher/filters.go +++ b/database/watcher/filters.go @@ -182,7 +182,7 @@ func WithEntityJobFilter(ghEntity params.ForgeEntity) dbCommon.PayloadFilterFunc } } -// WithGithubCredentialsFilter returns a filter function that filters payloads by Github credentials. +// WithForgeCredentialsFilter returns a filter function that filters payloads by Github credentials. func WithForgeCredentialsFilter(creds params.ForgeCredentials) dbCommon.PayloadFilterFunc { return func(payload dbCommon.ChangePayload) bool { var idGetter params.IDGetter diff --git a/util/github/client.go b/util/github/client.go index a46e4ab7..0e7fa7d6 100644 --- a/util/github/client.go +++ b/util/github/client.go @@ -92,7 +92,7 @@ func (g *githubClient) GetEntityHook(ctx context.Context, id int64) (ret *github return ret, err } -func (g *githubClient) CreateEntityHook(ctx context.Context, hook *github.Hook) (ret *github.Hook, err error) { +func (g *githubClient) createGithubEntityHook(ctx context.Context, hook *github.Hook) (ret *github.Hook, err error) { metrics.GithubOperationCount.WithLabelValues( "CreateHook", // label: operation g.entity.LabelScope(), // label: scope @@ -116,6 +116,17 @@ func (g *githubClient) CreateEntityHook(ctx context.Context, hook *github.Hook) return ret, err } +func (g *githubClient) CreateEntityHook(ctx context.Context, hook *github.Hook) (ret *github.Hook, err error) { + switch g.entity.Credentials.ForgeType { + case params.GithubEndpointType: + return g.createGithubEntityHook(ctx, hook) + case params.GiteaEndpointType: + return g.createGiteaEntityHook(ctx, hook) + default: + return nil, errors.New("invalid entity type") + } +} + func (g *githubClient) DeleteEntityHook(ctx context.Context, id int64) (ret *github.Response, err error) { metrics.GithubOperationCount.WithLabelValues( "DeleteHook", // label: operation diff --git a/util/github/gitea.go b/util/github/gitea.go new file mode 100644 index 00000000..4c83846c --- /dev/null +++ b/util/github/gitea.go @@ -0,0 +1,100 @@ +package github + +import ( + "context" + "fmt" + "net/http" + + "github.com/google/go-github/v71/github" + "github.com/pkg/errors" + + "github.com/cloudbase/garm/metrics" + "github.com/cloudbase/garm/params" +) + +type createGiteaHookOptions struct { + Type string `json:"type"` + Config map[string]string `json:"config"` + Events []string `json:"events"` + BranchFilter string `json:"branch_filter"` + Active bool `json:"active"` + AuthorizationHeader string `json:"authorization_header"` +} + +func (g *githubClient) createGiteaRepoHook(ctx context.Context, owner, name string, hook *github.Hook) (ret *github.Hook, err error) { + u := fmt.Sprintf("repos/%v/%v/hooks", owner, name) + createOpts := &createGiteaHookOptions{ + Type: "gitea", + Events: hook.Events, + Active: hook.GetActive(), + BranchFilter: "*", + Config: map[string]string{ + "content_type": hook.GetConfig().GetContentType(), + "url": hook.GetConfig().GetURL(), + "http_method": "post", + }, + } + + req, err := g.cli.NewRequest(http.MethodPost, u, createOpts) + if err != nil { + return nil, fmt.Errorf("failed to construct request: %w", err) + } + + hook = new(github.Hook) + _, err = g.cli.Do(ctx, req, hook) + if err != nil { + return nil, fmt.Errorf("request failed for %s: %w", req.URL.String(), err) + } + return hook, nil +} + +func (g *githubClient) createGiteaOrgHook(ctx context.Context, owner string, hook *github.Hook) (ret *github.Hook, err error) { + u := fmt.Sprintf("orgs/%v/hooks", owner) + createOpts := &createGiteaHookOptions{ + Type: "gitea", + Events: hook.Events, + Active: hook.GetActive(), + BranchFilter: "*", + Config: map[string]string{ + "content_type": hook.GetConfig().GetContentType(), + "url": hook.GetConfig().GetURL(), + "http_method": "post", + }, + } + + req, err := g.cli.NewRequest(http.MethodPost, u, createOpts) + if err != nil { + return nil, fmt.Errorf("failed to construct request: %w", err) + } + + hook = new(github.Hook) + _, err = g.cli.Do(ctx, req, hook) + if err != nil { + return nil, fmt.Errorf("request failed for %s: %w", req.URL.String(), err) + } + return hook, nil +} + +func (g *githubClient) createGiteaEntityHook(ctx context.Context, hook *github.Hook) (ret *github.Hook, err error) { + metrics.GithubOperationCount.WithLabelValues( + "CreateHook", // label: operation + g.entity.LabelScope(), // label: scope + ).Inc() + defer func() { + if err != nil { + metrics.GithubOperationFailedCount.WithLabelValues( + "CreateHook", // label: operation + g.entity.LabelScope(), // label: scope + ).Inc() + } + }() + switch g.entity.EntityType { + case params.ForgeEntityTypeRepository: + ret, err = g.createGiteaRepoHook(ctx, g.entity.Owner, g.entity.Name, hook) + case params.ForgeEntityTypeOrganization: + ret, err = g.createGiteaOrgHook(ctx, g.entity.Owner, hook) + default: + return nil, errors.New("invalid entity type") + } + return ret, err +}