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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 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" }