From 3349fd8f7901d9a04769dff71d86fb67374e9395 Mon Sep 17 00:00:00 2001 From: "Dr. Tobias Quathamer" Date: Sun, 23 Jan 2022 14:46:30 +0100 Subject: [PATCH] Add packagist webhook (#18224) Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: wxiaoguang --- docs/content/doc/features/webhooks.en-us.md | 1 + docs/content/doc/features/webhooks.zh-cn.md | 1 + docs/content/doc/features/webhooks.zh-tw.md | 1 + models/webhook/webhook.go | 1 + modules/setting/webhook.go | 2 +- modules/structs/hook.go | 2 +- options/locale/locale_en-US.ini | 4 + public/img/packagist.png | Bin 0 -> 4573 bytes routers/web/repo/webhook.go | 99 +++++++++++++ routers/web/web.go | 4 + services/forms/repo_form.go | 14 ++ services/webhook/packagist.go | 112 ++++++++++++++ services/webhook/packagist_test.go | 140 ++++++++++++++++++ services/webhook/webhook.go | 4 + templates/admin/hook_new.tmpl | 3 + templates/org/settings/hook_new.tmpl | 3 + .../repo/settings/webhook/base_list.tmpl | 3 + templates/repo/settings/webhook/new.tmpl | 3 + .../repo/settings/webhook/packagist.tmpl | 19 +++ templates/swagger/v1_json.tmpl | 3 +- 20 files changed, 416 insertions(+), 3 deletions(-) create mode 100644 public/img/packagist.png create mode 100644 services/webhook/packagist.go create mode 100644 services/webhook/packagist_test.go create mode 100644 templates/repo/settings/webhook/packagist.tmpl diff --git a/docs/content/doc/features/webhooks.en-us.md b/docs/content/doc/features/webhooks.en-us.md index 5ded4512c3..2dba7b7f83 100644 --- a/docs/content/doc/features/webhooks.en-us.md +++ b/docs/content/doc/features/webhooks.en-us.md @@ -28,6 +28,7 @@ All event pushes are POST requests. The methods currently supported are: - Microsoft Teams - Feishu - Wechatwork +- Packagist ### Event information diff --git a/docs/content/doc/features/webhooks.zh-cn.md b/docs/content/doc/features/webhooks.zh-cn.md index f3a312eee2..76139460c0 100644 --- a/docs/content/doc/features/webhooks.zh-cn.md +++ b/docs/content/doc/features/webhooks.zh-cn.md @@ -27,5 +27,6 @@ Gitea 的存储 webhook。这可以有存储库管路设定页 `/:username/:repo - Microsoft Teams - Feishu - Wechatwork +- Packagist ## TBD diff --git a/docs/content/doc/features/webhooks.zh-tw.md b/docs/content/doc/features/webhooks.zh-tw.md index 697b413916..20fec3d62d 100644 --- a/docs/content/doc/features/webhooks.zh-tw.md +++ b/docs/content/doc/features/webhooks.zh-tw.md @@ -27,6 +27,7 @@ Gitea 的儲存庫事件支援 web hook。這可以有儲存庫管理員在設 - Microsoft Teams - Feishu - Wechatwork +- Packagist ### 事件資訊 diff --git a/models/webhook/webhook.go b/models/webhook/webhook.go index 21c01d9289..ffc9b72b64 100644 --- a/models/webhook/webhook.go +++ b/models/webhook/webhook.go @@ -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 diff --git a/modules/setting/webhook.go b/modules/setting/webhook.go index b576f9573b..0bfd7dcb4d 100644 --- a/modules/setting/webhook.go +++ b/modules/setting/webhook.go @@ -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 != "" { diff --git a/modules/structs/hook.go b/modules/structs/hook.go index bb62483cda..e4d7652c72 100644 --- a/modules/structs/hook.go +++ b/modules/structs/hook.go @@ -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"` diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 8ee7347c0d..de0d26d647 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1947,6 +1947,10 @@ settings.add_matrix_hook_desc = Integrate Matrix into your repo settings.add_msteams_hook_desc = Integrate Microsoft Teams into your repository. settings.add_feishu_hook_desc = Integrate Feishu into your repository. settings.add_Wechat_hook_desc = Integrate Wechatwork into your repository. +settings.add_packagist_hook_desc = Integrate Packagist 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. diff --git a/public/img/packagist.png b/public/img/packagist.png new file mode 100644 index 0000000000000000000000000000000000000000..76c0e62a20d7ca8f50ce24b1e5acb8b4af2b932f GIT binary patch literal 4573 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 wMG=Uq3p#q!-qn+a literal 0 HcmV?d00001 diff --git a/routers/web/repo/webhook.go b/routers/web/repo/webhook.go index 2ec2e8911f..fb984de7f5 100644 --- a/routers/web/repo/webhook.go +++ b/routers/web/repo/webhook.go @@ -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 @@ -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) @@ -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") diff --git a/routers/web/web.go b/routers/web/web.go index 4c50229906..545194aabd 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -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() { @@ -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() { @@ -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) @@ -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() { diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index 19b5a37664..e6bd088da4 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -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) +} + // .___ // | | ______ ________ __ ____ // | |/ ___// ___/ | \_/ __ \ diff --git a/services/webhook/packagist.go b/services/webhook/packagist.go new file mode 100644 index 0000000000..ace93b13ff --- /dev/null +++ b/services/webhook/packagist.go @@ -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) +} diff --git a/services/webhook/packagist_test.go b/services/webhook/packagist_test.go new file mode 100644 index 0000000000..08912924d2 --- /dev/null +++ b/services/webhook/packagist_test.go @@ -0,0 +1,140 @@ +// 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 ( + "testing" + + webhook_model "code.gitea.io/gitea/models/webhook" + api "code.gitea.io/gitea/modules/structs" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestPackagistPayload(t *testing.T) { + t.Run("Create", func(t *testing.T) { + p := createTestPayload() + + d := new(PackagistPayload) + pl, err := d.Create(p) + require.NoError(t, err) + require.Nil(t, pl) + }) + + t.Run("Delete", func(t *testing.T) { + p := deleteTestPayload() + + d := new(PackagistPayload) + pl, err := d.Delete(p) + require.NoError(t, err) + require.Nil(t, pl) + }) + + t.Run("Fork", func(t *testing.T) { + p := forkTestPayload() + + d := new(PackagistPayload) + pl, err := d.Fork(p) + require.NoError(t, err) + require.Nil(t, pl) + }) + + t.Run("Push", func(t *testing.T) { + p := pushTestPayload() + + d := new(PackagistPayload) + d.PackagistRepository.URL = "https://packagist.org/api/update-package?username=THEUSERNAME&apiToken=TOPSECRETAPITOKEN" + pl, err := d.Push(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &PackagistPayload{}, pl) + + assert.Equal(t, "https://packagist.org/api/update-package?username=THEUSERNAME&apiToken=TOPSECRETAPITOKEN", pl.(*PackagistPayload).PackagistRepository.URL) + }) + + t.Run("Issue", func(t *testing.T) { + p := issueTestPayload() + + d := new(PackagistPayload) + p.Action = api.HookIssueOpened + pl, err := d.Issue(p) + require.NoError(t, err) + require.Nil(t, pl) + + p.Action = api.HookIssueClosed + pl, err = d.Issue(p) + require.NoError(t, err) + require.Nil(t, pl) + }) + + t.Run("IssueComment", func(t *testing.T) { + p := issueCommentTestPayload() + + d := new(PackagistPayload) + pl, err := d.IssueComment(p) + require.NoError(t, err) + require.Nil(t, pl) + }) + + t.Run("PullRequest", func(t *testing.T) { + p := pullRequestTestPayload() + + d := new(PackagistPayload) + pl, err := d.PullRequest(p) + require.NoError(t, err) + require.Nil(t, pl) + }) + + t.Run("PullRequestComment", func(t *testing.T) { + p := pullRequestCommentTestPayload() + + d := new(PackagistPayload) + pl, err := d.IssueComment(p) + require.NoError(t, err) + require.Nil(t, pl) + }) + + t.Run("Review", func(t *testing.T) { + p := pullRequestTestPayload() + p.Action = api.HookIssueReviewed + + d := new(PackagistPayload) + pl, err := d.Review(p, webhook_model.HookEventPullRequestReviewApproved) + require.NoError(t, err) + require.Nil(t, pl) + }) + + t.Run("Repository", func(t *testing.T) { + p := repositoryTestPayload() + + d := new(PackagistPayload) + pl, err := d.Repository(p) + require.NoError(t, err) + require.Nil(t, pl) + }) + + t.Run("Release", func(t *testing.T) { + p := pullReleaseTestPayload() + + d := new(PackagistPayload) + pl, err := d.Release(p) + require.NoError(t, err) + require.Nil(t, pl) + }) +} + +func TestPackagistJSONPayload(t *testing.T) { + p := pushTestPayload() + + pl, err := new(PackagistPayload).Push(p) + require.NoError(t, err) + require.NotNil(t, pl) + require.IsType(t, &PackagistPayload{}, pl) + + json, err := pl.JSONPayload() + require.NoError(t, err) + assert.NotEmpty(t, json) +} diff --git a/services/webhook/webhook.go b/services/webhook/webhook.go index bb7a9692d1..607fac9634 100644 --- a/services/webhook/webhook.go +++ b/services/webhook/webhook.go @@ -58,6 +58,10 @@ var webhooks = map[webhook_model.HookType]*webhook{ name: webhook_model.WECHATWORK, payloadCreator: GetWechatworkPayload, }, + webhook_model.PACKAGIST: { + name: webhook_model.PACKAGIST, + payloadCreator: GetPackagistPayload, + }, } // RegisterWebhook registers a webhook diff --git a/templates/admin/hook_new.tmpl b/templates/admin/hook_new.tmpl index 2cd3fc826c..049e54ef83 100644 --- a/templates/admin/hook_new.tmpl +++ b/templates/admin/hook_new.tmpl @@ -34,6 +34,8 @@ {{else if eq .HookType "wechatwork"}} + {{else if eq .HookType "packagist"}} + {{end}} @@ -48,6 +50,7 @@ {{template "repo/settings/webhook/feishu" .}} {{template "repo/settings/webhook/matrix" .}} {{template "repo/settings/webhook/wechatwork" .}} + {{template "repo/settings/webhook/packagist" .}} {{template "repo/settings/webhook/history" .}} diff --git a/templates/org/settings/hook_new.tmpl b/templates/org/settings/hook_new.tmpl index 43351d0ceb..5e8ebb51e9 100644 --- a/templates/org/settings/hook_new.tmpl +++ b/templates/org/settings/hook_new.tmpl @@ -29,6 +29,8 @@ {{else if eq .HookType "wechatwork"}} + {{else if eq .HookType "packagist"}} + {{end}} @@ -43,6 +45,7 @@ {{template "repo/settings/webhook/feishu" .}} {{template "repo/settings/webhook/matrix" .}} {{template "repo/settings/webhook/wechatwork" .}} + {{template "repo/settings/webhook/packagist" .}} {{template "repo/settings/webhook/history" .}} diff --git a/templates/repo/settings/webhook/base_list.tmpl b/templates/repo/settings/webhook/base_list.tmpl index e96d086039..f16c43bad6 100644 --- a/templates/repo/settings/webhook/base_list.tmpl +++ b/templates/repo/settings/webhook/base_list.tmpl @@ -34,6 +34,9 @@ Wechatwork + + Packagist + diff --git a/templates/repo/settings/webhook/new.tmpl b/templates/repo/settings/webhook/new.tmpl index 6df128f40a..a438a4c71a 100644 --- a/templates/repo/settings/webhook/new.tmpl +++ b/templates/repo/settings/webhook/new.tmpl @@ -27,6 +27,8 @@ {{else if eq .HookType "wechatwork"}} + {{else if eq .HookType "packagist"}} + {{end}} @@ -41,6 +43,7 @@ {{template "repo/settings/webhook/feishu" .}} {{template "repo/settings/webhook/matrix" .}} {{template "repo/settings/webhook/wechatwork" .}} + {{template "repo/settings/webhook/packagist" .}} {{template "repo/settings/webhook/history" .}} diff --git a/templates/repo/settings/webhook/packagist.tmpl b/templates/repo/settings/webhook/packagist.tmpl new file mode 100644 index 0000000000..04161dc40f --- /dev/null +++ b/templates/repo/settings/webhook/packagist.tmpl @@ -0,0 +1,19 @@ +{{if eq .HookType "packagist"}} +

{{.i18n.Tr "repo.settings.add_packagist_hook_desc" "https://packagist.org" | Str2html}}

+
+ {{.CsrfTokenHtml}} +
+ + +
+
+ + +
+
+ + +
+ {{template "repo/settings/webhook/settings" .}} +
+{{end}} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index bba728363a..768c4c69ee 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -13517,7 +13517,8 @@ "slack", "telegram", "feishu", - "wechatwork" + "wechatwork", + "packagist" ], "x-go-name": "Type" }