Skip to content

Commit

Permalink
Add packagist webhook (go-gitea#18224)
Browse files Browse the repository at this point in the history
Co-authored-by: 6543 <6543@obermui.de>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
  • Loading branch information
3 people authored and Stelios Malathouras committed Mar 28, 2022
1 parent 5083656 commit ade8e00
Show file tree
Hide file tree
Showing 20 changed files with 416 additions and 3 deletions.
1 change: 1 addition & 0 deletions docs/content/doc/features/webhooks.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ All event pushes are POST requests. The methods currently supported are:
- Microsoft Teams
- Feishu
- Wechatwork
- Packagist

### Event information

Expand Down
1 change: 1 addition & 0 deletions docs/content/doc/features/webhooks.zh-cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@ Gitea 的存储 webhook。这可以有存储库管路设定页 `/:username/:repo
- Microsoft Teams
- Feishu
- Wechatwork
- Packagist

## TBD
1 change: 1 addition & 0 deletions docs/content/doc/features/webhooks.zh-tw.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Gitea 的儲存庫事件支援 web hook。這可以有儲存庫管理員在設
- Microsoft Teams
- Feishu
- Wechatwork
- Packagist

### 事件資訊

Expand Down
1 change: 1 addition & 0 deletions models/webhook/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ const (
FEISHU HookType = "feishu"
MATRIX HookType = "matrix"
WECHATWORK HookType = "wechatwork"
PACKAGIST HookType = "packagist"
)

// HookStatus is the status of a web hook
Expand Down
2 changes: 1 addition & 1 deletion modules/setting/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func newWebhookService() {
Webhook.DeliverTimeout = sec.Key("DELIVER_TIMEOUT").MustInt(5)
Webhook.SkipTLSVerify = sec.Key("SKIP_TLS_VERIFY").MustBool()
Webhook.AllowedHostList = sec.Key("ALLOWED_HOST_LIST").MustString("")
Webhook.Types = []string{"gitea", "gogs", "slack", "discord", "dingtalk", "telegram", "msteams", "feishu", "matrix", "wechatwork"}
Webhook.Types = []string{"gitea", "gogs", "slack", "discord", "dingtalk", "telegram", "msteams", "feishu", "matrix", "wechatwork", "packagist"}
Webhook.PagingNum = sec.Key("PAGING_NUM").MustInt(10)
Webhook.ProxyURL = sec.Key("PROXY_URL").MustString("")
if Webhook.ProxyURL != "" {
Expand Down
2 changes: 1 addition & 1 deletion modules/structs/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ type CreateHookOptionConfig map[string]string
// CreateHookOption options when create a hook
type CreateHookOption struct {
// required: true
// enum: dingtalk,discord,gitea,gogs,msteams,slack,telegram,feishu,wechatwork
// enum: dingtalk,discord,gitea,gogs,msteams,slack,telegram,feishu,wechatwork,packagist
Type string `json:"type" binding:"Required"`
// required: true
Config CreateHookOptionConfig `json:"config" binding:"Required"`
Expand Down
4 changes: 4 additions & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -1947,6 +1947,10 @@ settings.add_matrix_hook_desc = Integrate <a href="%s">Matrix</a> into your repo
settings.add_msteams_hook_desc = Integrate <a href="%s">Microsoft Teams</a> into your repository.
settings.add_feishu_hook_desc = Integrate <a href="%s">Feishu</a> into your repository.
settings.add_Wechat_hook_desc = Integrate <a href="%s">Wechatwork</a> into your repository.
settings.add_packagist_hook_desc = Integrate <a href="%s">Packagist</a> into your repository.
settings.packagist_username = Packagist username
settings.packagist_api_token = API token
settings.packagist_package_url = Packagist package URL
settings.deploy_keys = Deploy Keys
settings.add_deploy_key = Add Deploy Key
settings.deploy_key_desc = Deploy keys have read-only pull access to the repository.
Expand Down
Binary file added public/img/packagist.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
99 changes: 99 additions & 0 deletions routers/web/repo/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,59 @@ func WechatworkHooksNewPost(ctx *context.Context) {
ctx.Redirect(orCtx.Link)
}

// PackagistHooksNewPost response for creating packagist hook
func PackagistHooksNewPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.NewPackagistHookForm)
ctx.Data["Title"] = ctx.Tr("repo.settings")
ctx.Data["PageIsSettingsHooks"] = true
ctx.Data["PageIsSettingsHooksNew"] = true
ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
ctx.Data["HookType"] = webhook.PACKAGIST

orCtx, err := getOrgRepoCtx(ctx)
if err != nil {
ctx.ServerError("getOrgRepoCtx", err)
return
}

if ctx.HasError() {
ctx.HTML(http.StatusOK, orCtx.NewTemplate)
return
}

meta, err := json.Marshal(&webhook_service.PackagistMeta{
Username: form.Username,
APIToken: form.APIToken,
PackageURL: form.PackageURL,
})
if err != nil {
ctx.ServerError("Marshal", err)
return
}

w := &webhook.Webhook{
RepoID: orCtx.RepoID,
URL: fmt.Sprintf("https://packagist.org/api/update-package?username=%s&apiToken=%s", url.QueryEscape(form.Username), url.QueryEscape(form.APIToken)),
ContentType: webhook.ContentTypeJSON,
HookEvent: ParseHookEvent(form.WebhookForm),
IsActive: form.Active,
Type: webhook.PACKAGIST,
Meta: string(meta),
OrgID: orCtx.OrgID,
IsSystemWebhook: orCtx.IsSystemWebhook,
}
if err := w.UpdateEvent(); err != nil {
ctx.ServerError("UpdateEvent", err)
return
} else if err := webhook.CreateWebhook(db.DefaultContext, w); err != nil {
ctx.ServerError("CreateWebhook", err)
return
}

ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
ctx.Redirect(orCtx.Link)
}

func checkWebhook(ctx *context.Context) (*orgRepoCtx, *webhook.Webhook) {
ctx.Data["RequireHighlightJS"] = true

Expand Down Expand Up @@ -719,6 +772,8 @@ func checkWebhook(ctx *context.Context) (*orgRepoCtx, *webhook.Webhook) {
ctx.Data["TelegramHook"] = webhook_service.GetTelegramHook(w)
case webhook.MATRIX:
ctx.Data["MatrixHook"] = webhook_service.GetMatrixHook(w)
case webhook.PACKAGIST:
ctx.Data["PackagistHook"] = webhook_service.GetPackagistHook(w)
}

ctx.Data["History"], err = w.History(1)
Expand Down Expand Up @@ -1137,6 +1192,50 @@ func WechatworkHooksEditPost(ctx *context.Context) {
ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
}

// PackagistHooksEditPost response for editing packagist hook
func PackagistHooksEditPost(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.NewPackagistHookForm)
ctx.Data["Title"] = ctx.Tr("repo.settings")
ctx.Data["PageIsSettingsHooks"] = true
ctx.Data["PageIsSettingsHooksEdit"] = true

orCtx, w := checkWebhook(ctx)
if ctx.Written() {
return
}
ctx.Data["Webhook"] = w

if ctx.HasError() {
ctx.HTML(http.StatusOK, orCtx.NewTemplate)
return
}

meta, err := json.Marshal(&webhook_service.PackagistMeta{
Username: form.Username,
APIToken: form.APIToken,
PackageURL: form.PackageURL,
})
if err != nil {
ctx.ServerError("Marshal", err)
return
}

w.Meta = string(meta)
w.URL = fmt.Sprintf("https://packagist.org/api/update-package?username=%s&apiToken=%s", url.QueryEscape(form.Username), url.QueryEscape(form.APIToken))
w.HookEvent = ParseHookEvent(form.WebhookForm)
w.IsActive = form.Active
if err := w.UpdateEvent(); err != nil {
ctx.ServerError("UpdateEvent", err)
return
} else if err := webhook.UpdateWebhook(w); err != nil {
ctx.ServerError("UpdateWebhook", err)
return
}

ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
}

// TestWebhook test if web hook is work fine
func TestWebhook(ctx *context.Context) {
hookID := ctx.ParamsInt64(":id")
Expand Down
4 changes: 4 additions & 0 deletions routers/web/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,7 @@ func RegisterRoutes(m *web.Route) {
m.Post("/msteams/{id}", bindIgnErr(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost)
m.Post("/feishu/{id}", bindIgnErr(forms.NewFeishuHookForm{}), repo.FeishuHooksEditPost)
m.Post("/wechatwork/{id}", bindIgnErr(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksEditPost)
m.Post("/packagist/{id}", bindIgnErr(forms.NewPackagistHookForm{}), repo.PackagistHooksEditPost)
}, webhooksEnabled)

m.Group("/{configType:default-hooks|system-hooks}", func() {
Expand All @@ -462,6 +463,7 @@ func RegisterRoutes(m *web.Route) {
m.Post("/msteams/new", bindIgnErr(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost)
m.Post("/feishu/new", bindIgnErr(forms.NewFeishuHookForm{}), repo.FeishuHooksNewPost)
m.Post("/wechatwork/new", bindIgnErr(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksNewPost)
m.Post("/packagist/new", bindIgnErr(forms.NewPackagistHookForm{}), repo.PackagistHooksNewPost)
})

m.Group("/auths", func() {
Expand Down Expand Up @@ -657,6 +659,7 @@ func RegisterRoutes(m *web.Route) {
m.Post("/msteams/new", bindIgnErr(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost)
m.Post("/feishu/new", bindIgnErr(forms.NewFeishuHookForm{}), repo.FeishuHooksNewPost)
m.Post("/wechatwork/new", bindIgnErr(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksNewPost)
m.Post("/packagist/new", bindIgnErr(forms.NewPackagistHookForm{}), repo.PackagistHooksNewPost)
m.Group("/{id}", func() {
m.Get("", repo.WebHooksEdit)
m.Post("/test", repo.TestWebhook)
Expand All @@ -672,6 +675,7 @@ func RegisterRoutes(m *web.Route) {
m.Post("/msteams/{id}", bindIgnErr(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost)
m.Post("/feishu/{id}", bindIgnErr(forms.NewFeishuHookForm{}), repo.FeishuHooksEditPost)
m.Post("/wechatwork/{id}", bindIgnErr(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksEditPost)
m.Post("/packagist/{id}", bindIgnErr(forms.NewPackagistHookForm{}), repo.PackagistHooksEditPost)
}, webhooksEnabled)

m.Group("/keys", func() {
Expand Down
14 changes: 14 additions & 0 deletions services/forms/repo_form.go
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,20 @@ func (f *NewWechatWorkHookForm) Validate(req *http.Request, errs binding.Errors)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
}

// NewPackagistHookForm form for creating packagist hook
type NewPackagistHookForm struct {
Username string `binding:"Required"`
APIToken string `binding:"Required"`
PackageURL string `binding:"Required;ValidUrl"`
WebhookForm
}

// Validate validates the fields
func (f *NewPackagistHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
}

// .___
// | | ______ ________ __ ____
// | |/ ___// ___/ | \_/ __ \
Expand Down
112 changes: 112 additions & 0 deletions services/webhook/packagist.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package webhook

import (
"errors"

webhook_model "code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
api "code.gitea.io/gitea/modules/structs"
)

type (
// PackagistPayload represents
PackagistPayload struct {
PackagistRepository struct {
URL string `json:"url"`
} `json:"repository"`
}

// PackagistMeta contains the meta data for the webhook
PackagistMeta struct {
Username string `json:"username"`
APIToken string `json:"api_token"`
PackageURL string `json:"package_url"`
}
)

// GetPackagistHook returns packagist metadata
func GetPackagistHook(w *webhook_model.Webhook) *PackagistMeta {
s := &PackagistMeta{}
if err := json.Unmarshal([]byte(w.Meta), s); err != nil {
log.Error("webhook.GetPackagistHook(%d): %v", w.ID, err)
}
return s
}

// JSONPayload Marshals the PackagistPayload to json
func (f *PackagistPayload) JSONPayload() ([]byte, error) {
data, err := json.MarshalIndent(f, "", " ")
if err != nil {
return []byte{}, err
}
return data, nil
}

var _ PayloadConvertor = &PackagistPayload{}

// Create implements PayloadConvertor Create method
func (f *PackagistPayload) Create(p *api.CreatePayload) (api.Payloader, error) {
return nil, nil
}

// Delete implements PayloadConvertor Delete method
func (f *PackagistPayload) Delete(p *api.DeletePayload) (api.Payloader, error) {
return nil, nil
}

// Fork implements PayloadConvertor Fork method
func (f *PackagistPayload) Fork(p *api.ForkPayload) (api.Payloader, error) {
return nil, nil
}

// Push implements PayloadConvertor Push method
func (f *PackagistPayload) Push(p *api.PushPayload) (api.Payloader, error) {
return f, nil
}

// Issue implements PayloadConvertor Issue method
func (f *PackagistPayload) Issue(p *api.IssuePayload) (api.Payloader, error) {
return nil, nil
}

// IssueComment implements PayloadConvertor IssueComment method
func (f *PackagistPayload) IssueComment(p *api.IssueCommentPayload) (api.Payloader, error) {
return nil, nil
}

// PullRequest implements PayloadConvertor PullRequest method
func (f *PackagistPayload) PullRequest(p *api.PullRequestPayload) (api.Payloader, error) {
return nil, nil
}

// Review implements PayloadConvertor Review method
func (f *PackagistPayload) Review(p *api.PullRequestPayload, event webhook_model.HookEventType) (api.Payloader, error) {
return nil, nil
}

// Repository implements PayloadConvertor Repository method
func (f *PackagistPayload) Repository(p *api.RepositoryPayload) (api.Payloader, error) {
return nil, nil
}

// Release implements PayloadConvertor Release method
func (f *PackagistPayload) Release(p *api.ReleasePayload) (api.Payloader, error) {
return nil, nil
}

// GetPackagistPayload converts a packagist webhook into a PackagistPayload
func GetPackagistPayload(p api.Payloader, event webhook_model.HookEventType, meta string) (api.Payloader, error) {
s := new(PackagistPayload)

packagist := &PackagistMeta{}
if err := json.Unmarshal([]byte(meta), &packagist); err != nil {
return s, errors.New("GetPackagistPayload meta json:" + err.Error())
}
s.PackagistRepository.URL = packagist.PackageURL
return convertPayloader(s, p, event)
}
Loading

0 comments on commit ade8e00

Please sign in to comment.